├── .github
├── dependabot.yml
└── workflows
│ ├── android-ci.yml
│ └── dependency-submission.yml
├── .gitignore
├── CHANGELOG.md
├── GOOGLE_PLAY_PRIVACY_POLICY.md
├── JOTTINGS.md
├── LICENSE
├── README.md
├── SECURITY.md
├── app
├── build.gradle.kts
├── proguard-firebase-rules.pro
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── net
│ │ └── imknown
│ │ └── android
│ │ └── forefrontinfo
│ │ └── ExampleInstrumentedTest.kt
│ ├── debug
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── net
│ │ │ └── imknown
│ │ │ └── android
│ │ │ └── forefrontinfo
│ │ │ └── MyDebugApplication.kt
│ └── res
│ │ └── values
│ │ └── strings.xml
│ ├── main
│ ├── AndroidManifest.xml
│ ├── assets
│ │ └── lld.json
│ ├── ic_launcher-playstore.png
│ └── java
│ │ └── net
│ │ └── imknown
│ │ └── android
│ │ └── forefrontinfo
│ │ ├── base
│ │ ├── MyApplication.kt
│ │ ├── mvvm
│ │ │ ├── BaseFragment.kt
│ │ │ ├── BaseViewModel.kt
│ │ │ ├── Event.kt
│ │ │ ├── SharedPreferenceChangeEventLiveData.kt
│ │ │ ├── ToastExt.kt
│ │ │ └── ViewBindingExt.kt
│ │ ├── res
│ │ │ ├── resources.properties
│ │ │ ├── values-fr-rFR
│ │ │ │ └── strings.xml
│ │ │ ├── values-zh-rCN
│ │ │ │ └── strings.xml
│ │ │ ├── values-zh-rTW
│ │ │ │ └── strings.xml
│ │ │ └── values
│ │ │ │ └── strings.xml
│ │ ├── resBackup
│ │ │ └── xml
│ │ │ │ ├── backup_rules.xml
│ │ │ │ └── data_extraction_rules.xml
│ │ ├── resLauncher
│ │ │ ├── drawable
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── values-night
│ │ │ │ └── colors.xml
│ │ │ └── values
│ │ │ │ └── colors.xml
│ │ └── resTheme
│ │ │ ├── values-night
│ │ │ ├── colors.xml
│ │ │ └── themes.xml
│ │ │ ├── values-v23
│ │ │ └── colors.xml
│ │ │ ├── values-v27
│ │ │ └── colors.xml
│ │ │ └── values
│ │ │ ├── colors.xml
│ │ │ └── themes.xml
│ │ └── ui
│ │ ├── MainActivity.kt
│ │ ├── MainViewModel.kt
│ │ ├── base
│ │ ├── AndroidVersionExt.kt
│ │ ├── JsonIo.kt
│ │ └── list
│ │ │ ├── BaseListFragment.kt
│ │ │ ├── BaseListViewModel.kt
│ │ │ ├── BasePureListViewModel.kt
│ │ │ ├── MyAdapter.kt
│ │ │ ├── MyItemDecoration.kt
│ │ │ ├── MyModel.kt
│ │ │ ├── MyViewHolder.kt
│ │ │ └── res
│ │ │ ├── layout
│ │ │ ├── base_list_fragment.xml
│ │ │ └── my_view_holder.xml
│ │ │ └── values
│ │ │ ├── dimens.xml
│ │ │ └── styles.xml
│ │ ├── home
│ │ ├── GatewayApi.kt
│ │ ├── HomeFragment.kt
│ │ ├── HomeViewModel.kt
│ │ ├── model
│ │ │ ├── BaseInfo.kt
│ │ │ ├── Lld.kt
│ │ │ └── Subtitle.kt
│ │ └── res
│ │ │ ├── drawable
│ │ │ └── ic_home_24dp.xml
│ │ │ ├── values-fr-rFR
│ │ │ └── strings.xml
│ │ │ ├── values-night
│ │ │ └── colors.xml
│ │ │ ├── values-zh-rCN
│ │ │ └── strings.xml
│ │ │ ├── values-zh-rTW
│ │ │ └── strings.xml
│ │ │ └── values
│ │ │ ├── attrs.xml
│ │ │ ├── colors.xml
│ │ │ └── strings.xml
│ │ ├── others
│ │ ├── OthersFragment.kt
│ │ ├── OthersViewModel.kt
│ │ └── res
│ │ │ ├── drawable
│ │ │ └── ic_others_24dp.xml
│ │ │ ├── values-fr-rFR
│ │ │ └── strings.xml
│ │ │ ├── values-zh-rCN
│ │ │ └── strings.xml
│ │ │ ├── values-zh-rTW
│ │ │ └── strings.xml
│ │ │ └── values
│ │ │ └── strings.xml
│ │ ├── prop
│ │ ├── PropFragment.kt
│ │ ├── PropViewModel.kt
│ │ └── res
│ │ │ ├── drawable
│ │ │ └── ic_prop_24dp.xml
│ │ │ ├── values-fr-rFR
│ │ │ └── strings.xml
│ │ │ ├── values-zh-rCN
│ │ │ └── strings.xml
│ │ │ ├── values-zh-rTW
│ │ │ └── strings.xml
│ │ │ └── values
│ │ │ └── strings.xml
│ │ ├── res
│ │ ├── anim
│ │ │ └── drop_scale.xml
│ │ ├── layout
│ │ │ └── main_activity.xml
│ │ ├── menu
│ │ │ └── bottom_nav_menu.xml
│ │ ├── values-fr-rFR
│ │ │ └── strings.xml
│ │ ├── values-zh-rCN
│ │ │ └── strings.xml
│ │ ├── values-zh-rTW
│ │ │ └── strings.xml
│ │ └── values
│ │ │ └── strings.xml
│ │ └── settings
│ │ ├── SettingsFragment.kt
│ │ ├── SettingsViewModel.kt
│ │ └── res
│ │ ├── drawable
│ │ └── ic_settings_24dp.xml
│ │ ├── layout
│ │ └── preference_widget_material_switch.xml
│ │ ├── values-fr-rFR
│ │ └── strings.xml
│ │ ├── values-zh-rCN
│ │ └── strings.xml
│ │ ├── values-zh-rTW
│ │ └── strings.xml
│ │ ├── values
│ │ ├── arrays.xml
│ │ └── strings.xml
│ │ └── xml
│ │ └── preferences.xml
│ └── test
│ └── java
│ └── net
│ └── imknown
│ └── android
│ └── forefrontinfo
│ └── ExampleUnitTest.kt
├── base
├── build.gradle.kts
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── net
│ │ └── imknown
│ │ └── android
│ │ └── forefrontinfo
│ │ └── base
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ └── java
│ │ └── net
│ │ └── imknown
│ │ └── android
│ │ └── forefrontinfo
│ │ └── base
│ │ ├── extension
│ │ └── DateTimeExt.kt
│ │ ├── property
│ │ ├── IProperty.kt
│ │ ├── PropertyManager.kt
│ │ └── impl
│ │ │ ├── DefaultProperty.kt
│ │ │ └── ShizukuProperty.kt
│ │ └── shell
│ │ ├── IShell.kt
│ │ ├── ShellManager.kt
│ │ ├── ShellResult.kt
│ │ └── impl
│ │ ├── DefaultShell.kt
│ │ └── LibSuShell.kt
│ └── test
│ └── java
│ └── net
│ └── imknown
│ └── android
│ └── forefrontinfo
│ └── base
│ └── ExampleUnitTest.kt
├── binderDetector
├── build.gradle.kts
└── src
│ └── main
│ ├── cpp
│ ├── BinderDetector.cpp
│ ├── BinderDetector.h
│ └── CMakeLists.txt
│ └── kotlin
│ └── net
│ └── imknown
│ └── android
│ └── forefrontinfo
│ └── binderdetector
│ └── BinderDetector.kt
├── build.gradle.kts
├── buildScriptConfig.gradle
├── buildSrc
├── build.gradle.kts
└── src
│ └── main
│ └── java
│ ├── Ext.kt
│ ├── Misc.kt
│ └── Versions.kt
├── gradle.properties
├── gradle
├── toml
│ ├── android.toml
│ ├── build.toml
│ ├── google.toml
│ ├── kotlin.toml
│ └── thirdParty.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── keys
└── debug.keystore
├── metadata
└── en-US
│ ├── changelogs
│ └── 62.txt
│ ├── full_description.txt
│ ├── images
│ ├── icon.png
│ └── phoneScreenshots
│ │ ├── 1.png
│ │ └── 2.png
│ └── short_description.txt
└── settings.gradle.kts
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "gradle" # See documentation for possible values
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "weekly"
12 |
--------------------------------------------------------------------------------
/.github/workflows/android-ci.yml:
--------------------------------------------------------------------------------
1 | name: Android CI
2 |
3 | on:
4 | push:
5 | branches: [ "develop" ]
6 | pull_request:
7 | branches: [ "develop" ]
8 | workflow_dispatch:
9 |
10 | jobs:
11 | build:
12 |
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - uses: actions/checkout@v4
17 |
18 | # region [JDK]
19 | - name: Set up JDK
20 | uses: actions/setup-java@v4
21 | with:
22 | java-version: '21'
23 | distribution: 'temurin'
24 | cache: gradle
25 | # endregion [JDK]
26 |
27 | # region [Android SDK]
28 | # https://github.com/android-actions/setup-android
29 | - name: Setup Android SDK
30 | uses: android-actions/setup-android@v3
31 | with:
32 | packages: ''
33 | # https://developer.android.com/tools/sdkmanager#install
34 | # https://github.com/imknown/AndroidLowLevelDetector/blob/develop/gradle/toml/build.toml
35 | - name: Install Android SDK Cmake
36 | run: sdkmanager --install "cmake;4.0.2"
37 | - name: Install Android SDK NDK
38 | # https://github.com/android/ndk/releases
39 | run: sdkmanager --install "ndk;29.0.13113456"
40 | # endregion [Android SDK]
41 |
42 | # region [Gradle]
43 | - name: Grant execute permission for gradlew
44 | run: chmod +x gradlew
45 |
46 | - name: Build with Gradle
47 | run: ./gradlew assembleFossDebug
48 | # endregion [Gradle]
--------------------------------------------------------------------------------
/.github/workflows/dependency-submission.yml:
--------------------------------------------------------------------------------
1 | name: Dependency Submission
2 |
3 | on:
4 | push:
5 | branches: [ "develop" ]
6 | paths: [ "**.kts", "**.dcl", "**.toml", "**.properties" ]
7 | pull_request:
8 | branches: [ "develop" ]
9 | paths: [ "**.kts", "**.dcl", "**.toml", "**.properties" ]
10 | workflow_dispatch:
11 |
12 | permissions:
13 | contents: write
14 |
15 | jobs:
16 | # https://github.com/gradle/actions/blob/main/docs/dependency-submission.md
17 | # https://github.com/actions/gradle-build-tools-actions/blob/main/docs/dependency-submission.md
18 | dependency-submission:
19 |
20 | runs-on: ubuntu-latest
21 |
22 | steps:
23 | - uses: actions/checkout@v4
24 |
25 | - name: Set up Gradle
26 | uses: gradle/actions/setup-gradle@v4
27 |
28 | # region [Android SDK]
29 | # https://github.com/android-actions/setup-android
30 | - name: Setup Android SDK
31 | uses: android-actions/setup-android@v3
32 | with:
33 | packages: ''
34 | # https://developer.android.com/tools/sdkmanager#install
35 | # https://github.com/imknown/AndroidLowLevelDetector/blob/develop/gradle/toml/build.toml
36 | - name: Install Android SDK Cmake
37 | run: sdkmanager --install "cmake;4.0.2"
38 | - name: Install Android SDK NDK
39 | # https://github.com/android/ndk/releases
40 | run: sdkmanager --install "ndk;29.0.13113456"
41 | # endregion [Android SDK]
42 |
43 | - name: Run dependency submission
44 | uses: gradle/actions/dependency-submission@v4
45 | with:
46 | gradle-project-path: "."
47 | gradle-build-module: ":app"
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # IntelliJ Idea project files
2 | *.iml
3 | /.idea
4 |
5 | # Gradle files
6 | .gradle
7 | /local.properties
8 |
9 | # Generated files
10 | build
11 | .kotlin
12 |
13 | # Android Studio
14 | /captures
15 |
16 | # C/C++
17 | .cxx
18 |
19 | # Release keystore
20 | /keys/release.jks
21 |
22 | # Firebase
23 | google-services.json
24 |
25 | # macOS
26 | .DS_Store
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | https://github.com/imknown/AndroidLowLevelDetector/releases
--------------------------------------------------------------------------------
/GOOGLE_PLAY_PRIVACY_POLICY.md:
--------------------------------------------------------------------------------
1 | # LowLevelDetector privacy policy
2 |
3 | ## Data collections
4 |
5 | For me:
6 | - No user personal privacy data will be collected on user's phone
7 |
8 | For 3rd-parties:
9 | > - https://developers.google.com/assistant/console/firebase-services
10 | > - https://play.google.com/intl/en_us/about/privacy-security-deception
11 | > - https://firebase.google.com/terms
12 | > - https://firebase.google.com/support/privacy
13 |
14 | - Google Play will collect some data for statistics if your device is running Google Play Service,
15 | and Google Firebase will also collect some data for statistics if this app crashes or ANR.
16 | - These data are not requested by me forwardly;
17 | - I can see the backend report on their Consoles;
18 | - I will not share these data to any other people.
19 |
20 | ## Permissions
21 |
22 | - android.permission.INTERNET
23 | - This permission is used to fetch data from remote server;
24 | - This permission is only used when user turns on the switch in settings manually;
25 | - Please notice that some 3rd-parties dependencies, such as Firebase,
26 | still use this permission silently in the background.
27 |
28 | - android.permission.QUERY_ALL_PACKAGES
29 | - This permission is used to fetch target api, package name, etc. of apps;
30 | - This permission is only used to retrieve prebuilt ROM system apps,
31 | and no user installed apps will be retrieved.
32 |
33 | - Used by `Play services`:
34 | - `Play services measurement`:
35 | - android.permission.ACCESS_NETWORK_STATE
36 | - android.permission.INTERNET
37 | - android.permission.WAKE_LOCK
38 | - com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE
39 | - `Play services measurement api`:
40 | - android.permission.ACCESS_ADSERVICES_AD_ID
41 | - android.permission.ACCESS_ADSERVICES_ATTRIBUTION
42 | - android.permission.ACCESS_NETWORK_STAT
43 | - android.permission.INTERNET
44 | - android.permission.WAKE_LOCK
45 | - com.google.android.gms.permission.AD_ID
46 | - `Play services ads identifier`:
47 | - com.google.android.gms.permission.AD_ID
48 |
49 | - Used by `androidx.core`:
50 | - net.imknown.android.forefrontinfo.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION
51 |
52 | ## Security
53 |
54 | - You can review all the code here, so I promise there are no things below inside:
55 | - trojan
56 | - backdoor
57 | - virus
58 | - any other bad things
59 |
--------------------------------------------------------------------------------
/JOTTINGS.md:
--------------------------------------------------------------------------------
1 | # JOTTINGS
2 | Minds
3 |
4 | ## Features todo
5 |
6 | - JetPack
7 | - UseCase
8 | - Navigation
9 | - Hilt
10 | - Startup
11 | - Compose
12 | - Transitions
13 | - detect dm-verity (version)
14 | - detect FBE/FDE
15 | - detect HAL
16 | - Android version market share
17 | - OpenGL ES/Vulkan/GPU Driver version
18 | - icu (https://developer.android.com/guide/topics/resources/internationalization)
19 | - unicode
20 | - custom commands, result filters, scroll top, scrollbar (V2.0 ?)
21 | - settings
22 | - root mode
23 | - show commands
24 | - developer mode
25 | - network cache logic
26 | - select json server
27 | - documents
28 | - development references
29 | - user-readable explanation why mine not supported
30 | - copy result
31 | - acknowledge
32 | - etc.
33 |
34 | ## external
35 |
36 | https://android.googlesource.com/platform/external/
37 |
38 | ## avb
39 |
40 | https://source.android.com/security/verifiedboot/avb
41 | https://source.android.com/security/verifiedboot
42 | https://android.googlesource.com/platform/external/avb/+/master/README.md
43 | https://android.googlesource.com/platform/external/avb/+/master/avbtool
44 | https://android.googlesource.com/platform/external/avb/+/master/libavb/avb_version.h
45 |
46 | ``` sh
47 | adb shell "grep dm- /proc/mounts"
48 |
49 | adb shell getprop ro.boot.avb_version # avbtool version
50 | adb shell getprop ro.boot.veritymode
51 | adb shell getprop ro.boot.verifiedbootstate
52 | adb shell getprop ro.boot.flash.locked
53 |
54 | adb shell getprop ro.boot.secureboot # MIUI only?
55 | adb shell getprop ro.secure # Secure boot
56 | adb shell getprop ro.debuggable
57 | # * eng builds: ro.secure=0 and ro.debuggable=1
58 | # * userdebug builds: ro.secure=1 and ro.debuggable=1
59 | # * user builds: ro.secure=1 and ro.debuggable=0
60 |
61 | adb shell getprop ro.oem_unlock_supported
62 |
63 | adb shell getprop ro.boot.vbmeta.avb_version # required (lib)avb version
64 | adb shell getprop ro.boot.vbmeta.device_state
65 | ```
66 |
67 | ## busybox
68 |
69 | busybox (https://busybox.net/downloads/binaries/)
70 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AndroidLowLevelDetector
2 |
3 | [](https://github.com/imknown/AndroidLowLevelDetector/actions/workflows/android-ci.yml)
4 | [](https://github.com/imknown/AndroidLowLevelDetector/actions/workflows/dependabot/dependabot-updates)
5 | [](https://github.com/imknown/AndroidLowLevelDetector/actions/workflows/dependency-submission.yml)
6 |
7 | Detect Treble, GSI, Mainline, APEX, system-as-root(SAR), A/B, etc. .
8 | Some source codes refer to [Magisk][Magisk], [OpenGApps][OpenGApps], [TrebleInfo][TrebleInfo], [TrebleCheck][TrebleCheck], etc. .
9 |
10 | [Magisk]:https://github.com/topjohnwu/Magisk
11 | [OpenGApps]:https://github.com/opengapps/opengapps
12 | [TrebleInfo]:https://github.com/penn5/TrebleCheck
13 | [TrebleCheck]:https://github.com/kevintresuelo/treble
14 |
15 | ## Source
16 | 1. https://github.com/imknown/AndroidLowLevelDetector
17 | 1. https://gitee.com/imknown/AndroidLowLevelDetector (Mirror)
18 |
19 | ## Download
20 | 1. https://play.google.com/store/apps/details?id=net.imknown.android.forefrontinfo
21 | 1. https://github.com/imknown/AndroidLowLevelDetector/releases
22 | 1. https://gitee.com/imknown/AndroidLowLevelDetector/releases (Mirror)
23 |
24 | ## Features
25 |
26 | Click me
27 |
28 | - Detect Android version
29 | - Detect Android Build Id version
30 | - Detect Android security patch level
31 | - Detect Vendor security patch level
32 | - Detect Project Mainline module version (Google Play system update)
33 | - Detect Linux kernel
34 | - Detect A/B or A-Only
35 | - Detect Dynamic Partitions
36 | - Detect Dynamic System Update(DSU)
37 | - Detect Project Treble
38 | - Detect GSI compatibility
39 | - Detect Binder bitness
40 | - Detect Process/VM architecture
41 | - Detect Vendor NDK
42 | - Detect System-as-root
43 | - Detect (flattened) APEX
44 | - Detect Toybox
45 | - Detect WebView implement
46 | - Detect outdatedTargetSdkVersion apk
47 | - Dark mode supported
48 | - Online/offline mode (fetching data from remote server or local)
49 | - MultiWindow/FreeForm/Foldable/Landscape supported
50 | - Etc.
51 |
52 |
53 |
54 | ## Contribute
55 | Just use `Pull Request`.
56 | Translations are also welcome.
57 |
58 | ## Build
59 | ### Flavor
60 | - Firebase
61 | Choose `Firebase` in `Build Variants`, then follow Firebase Official Guide.
62 |
63 | - FOSS
64 | Choose `Foss` in `Build Variants`.
65 |
66 | ### Release
67 | Provide the whole following properties in file `$rootDir/local.properties`:
68 |
69 | ``` ini
70 | storeFile=
71 | storePassword=
72 | keyAlias=
73 | keyPassword=
74 | ```
75 |
76 | The location of `storeFile` can be `../keys/release.jks`.
77 | It has been already ignored in file `$rootDir/.gitingore` by default.
78 | So you can put your own private certificate or signing key there safely.
79 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ### Reporting
4 | We team and community take security bugs seriously. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions.
5 |
6 | To report a security issue, please use the GitHub Security Advisory ["Report a Vulnerability"](https://github.com/imknown/AndroidLowLevelDetector/security/advisories) tab.
7 |
8 | We will send a response indicating the next steps in handling your report. After the initial reply to your report, the security team will keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance.
9 |
10 | Report security bugs in third-party modules to the person or team maintaining the module.
11 |
12 | Please also pay attention to [release notes](https://github.com/imknown/AndroidLowLevelDetector/blob/develop/CHANGELOG.md).
13 |
14 | ## More About Security
15 | 1. https://docs.github.com/en/code-security/getting-started/adding-a-security-policy-to-your-repository
16 | 1. https://docs.github.com/en/code-security/security-advisories
17 |
--------------------------------------------------------------------------------
/app/proguard-firebase-rules.pro:
--------------------------------------------------------------------------------
1 | # https://firebase.google.com/docs/crashlytics/get-deobfuscated-reports-new-sdk?platform=android
2 |
3 | # To preserve the information Crashlytics requires for producing readable crash reports, add the following lines to your Proguard or Dexguard config file:
4 | -keepattributes SourceFile,LineNumberTable # Keep file names and line numbers.
5 | -keep public class * extends java.lang.Exception # Optional: Keep custom exceptions.
6 |
7 | # If you want a faster, obfuscated build with ProGuard, exclude Crashlytics by adding the following lines to your ProGuard config file:
8 | # -keep class com.google.firebase.crashlytics.** { *; }
9 | # -dontwarn com.google.firebase.crashlytics.**
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.kts.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/androidTest/java/net/imknown/android/forefrontinfo/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import androidx.test.platform.app.InstrumentationRegistry
5 | import org.junit.Assert.assertEquals
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | /**
10 | * Instrumented test, which will execute on an Android device.
11 | *
12 | * See [testing documentation](http://d.android.com/tools/testing).
13 | */
14 | @RunWith(AndroidJUnit4::class)
15 | class ExampleInstrumentedTest {
16 | @Test
17 | fun useAppContext() {
18 | // Context of the app under test.
19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
20 | assertEquals("net.imknown.android.forefrontinfo", appContext.packageName)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
--------------------------------------------------------------------------------
/app/src/debug/java/net/imknown/android/forefrontinfo/MyDebugApplication.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo
2 |
3 | import android.os.StrictMode
4 | import com.topjohnwu.superuser.Shell
5 | import kotlinx.coroutines.DEBUG_PROPERTY_NAME
6 | import kotlinx.coroutines.DEBUG_PROPERTY_VALUE_ON
7 | import net.imknown.android.forefrontinfo.base.MyApplication
8 |
9 | class MyDebugApplication : MyApplication() {
10 | override fun onCreate() {
11 | super.onCreate()
12 |
13 | StrictMode.enableDefaults()
14 |
15 | Shell.enableVerboseLogging = true
16 |
17 | System.setProperty(DEBUG_PROPERTY_NAME, DEBUG_PROPERTY_VALUE_ON)
18 | }
19 | }
--------------------------------------------------------------------------------
/app/src/debug/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | LLD Debug
4 | LLD Leaks
5 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
9 |
10 |
22 |
23 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/assets/lld.json:
--------------------------------------------------------------------------------
1 | {
2 | "scheme": 1,
3 | "version": "2025-06-05 20:00 +0800",
4 | "android": {
5 | "securityPatchLevel": "2025-05-05",
6 | "googlePlaySystemUpdates": "2025-05-01",
7 | "build": {
8 | "version": "2025-05-06",
9 | "details": [
10 | {
11 | "id": "BD4A.250505.003",
12 | "revision": "r31"
13 | },
14 | {
15 | "id": "BP1A.250505.005",
16 | "revision": "r32"
17 | },
18 | {
19 | "id": "BP1A.250505.005.A1",
20 | "revision": "r33"
21 | },
22 | {
23 | "id": "BP1A.250505.005.B1",
24 | "revision": "r34"
25 | },
26 | {
27 | "id": "BP1A.250505.005.C1",
28 | "revision": "r35"
29 | },
30 | {
31 | "id": "BP1A.250505.005.D1",
32 | "revision": "r36"
33 | }
34 | ]
35 | },
36 | "stable": {
37 | "name": "VanillaIceCream",
38 | "api": "35",
39 | "version": "15, VanillaIceCream"
40 | },
41 | "stablePreview": {
42 | "name": "Baklava",
43 | "api": "36",
44 | "version": "16 QPR1 Beta 1.1, Baklava"
45 | },
46 | "support": {
47 | "name": "Tiramisu",
48 | "api": "33",
49 | "version": "13, Tiramisu"
50 | },
51 | "preview": {
52 | "name": "Baklava",
53 | "api": "36",
54 | "version": "16 QPR1 Beta 1.1, Baklava",
55 | "phase": ""
56 | },
57 | "internal": {
58 | "name": "C",
59 | "api": "37",
60 | "version": "17 Internal, C"
61 | }
62 | },
63 | "linux": {
64 | "google": {
65 | "versions": [
66 | "6.12.32",
67 | "6.6.93",
68 | "6.1.141",
69 | "5.15.185",
70 | "5.10.238",
71 | "5.4.294"
72 | ]
73 | },
74 | "mainline": {
75 | "version": "6.15"
76 | }
77 | },
78 | "toybox": {
79 | "stable": {
80 | "version": "0.8.11"
81 | },
82 | "support": {
83 | "version": "0.8.6"
84 | },
85 | "mainline": {
86 | "version": "0.8.12"
87 | },
88 | "master": {
89 | "version": "0.8.12"
90 | }
91 | },
92 | "webView": {
93 | "stable": {
94 | "version": "137.0.7151.72"
95 | },
96 | "beta": {
97 | "version": "138.0.7204.14"
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imknown/AndroidLowLevelDetector/9b9ffa12d7324fd953cb7eaf34d1b9d66d1e432e/app/src/main/ic_launcher-playstore.png
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/MyApplication.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.base
2 |
3 | import android.app.Application
4 | import android.content.BroadcastReceiver
5 | import android.content.Context
6 | import android.content.Intent
7 | import android.content.IntentFilter
8 | import android.content.SharedPreferences
9 | import android.os.Environment
10 | import androidx.annotation.StringRes
11 | import androidx.appcompat.app.AppCompatDelegate
12 | import androidx.lifecycle.LiveData
13 | import androidx.lifecycle.MutableLiveData
14 | import androidx.preference.PreferenceManager
15 | import com.google.android.material.color.DynamicColors
16 | import com.topjohnwu.superuser.Shell
17 | import net.imknown.android.forefrontinfo.BuildConfig
18 | import net.imknown.android.forefrontinfo.R
19 | import net.imknown.android.forefrontinfo.base.mvvm.Event
20 | import net.imknown.android.forefrontinfo.base.property.PropertyManager
21 | import net.imknown.android.forefrontinfo.base.property.impl.DefaultProperty
22 | import net.imknown.android.forefrontinfo.base.shell.ShellManager
23 | import net.imknown.android.forefrontinfo.base.shell.impl.LibSuShell
24 | import java.io.File
25 |
26 | open class MyApplication : Application() {
27 |
28 | companion object {
29 | lateinit var instance: MyApplication
30 |
31 | val homeLanguageEvent: LiveData> by lazy { instance._homeLanguageEvent }
32 | val othersLanguageEvent: LiveData> by lazy { instance._othersLanguageEvent }
33 | val propLanguageEvent: LiveData> by lazy { instance._propLanguageEvent }
34 |
35 | val sharedPreferences: SharedPreferences by lazy {
36 | PreferenceManager.getDefaultSharedPreferences(instance)
37 | }
38 |
39 | fun getDownloadDir() = getFileDir(Environment.DIRECTORY_DOWNLOADS)
40 |
41 | private fun getFileDir(type: String): File {
42 | val externalFilesDir = instance.getExternalFilesDir(type)
43 | return if (externalFilesDir != null && externalFilesDir.exists()) {
44 | externalFilesDir
45 | } else {
46 | instance.filesDir.resolve(type)
47 | }.apply {
48 | mkdirs()
49 | }
50 | }
51 |
52 | fun getMyString(@StringRes resId: Int) =
53 | instance.getString(resId)
54 |
55 | fun getMyString(@StringRes resId: Int, vararg formatArgs: Any?) =
56 | instance.getString(resId, *formatArgs)
57 |
58 | fun setMyTheme(themesValue: String) {
59 | @AppCompatDelegate.NightMode val mode = when (themesValue) {
60 | getMyString(R.string.interface_themes_follow_system_value) -> {
61 | AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
62 | }
63 | getMyString(R.string.interface_themes_power_saver_value) -> {
64 | AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY
65 | }
66 | getMyString(R.string.interface_themes_always_light_value) -> {
67 | AppCompatDelegate.MODE_NIGHT_NO
68 | }
69 | getMyString(R.string.interface_themes_always_dark_value) -> {
70 | AppCompatDelegate.MODE_NIGHT_YES
71 | }
72 | else -> {
73 | AppCompatDelegate.MODE_NIGHT_YES
74 | }
75 | }
76 |
77 | AppCompatDelegate.setDefaultNightMode(mode)
78 | }
79 | }
80 |
81 | private val _homeLanguageEvent by lazy { MutableLiveData>() }
82 | private val _othersLanguageEvent by lazy { MutableLiveData>() }
83 | private val _propLanguageEvent by lazy { MutableLiveData>() }
84 |
85 | override fun onCreate() {
86 | super.onCreate()
87 |
88 | instance = this@MyApplication
89 |
90 | initTheme()
91 |
92 | initShellAndProperty()
93 |
94 | initLanguage()
95 | }
96 |
97 | private fun initTheme() {
98 | DynamicColors.applyToActivitiesIfAvailable(this)
99 |
100 | val themesValue = sharedPreferences.getString(
101 | getMyString(R.string.interface_themes_key),
102 | getMyString(R.string.interface_themes_follow_system_value)
103 | )!!
104 | setMyTheme(themesValue)
105 | }
106 |
107 | private fun initShellAndProperty() {
108 | Shell.enableVerboseLogging = BuildConfig.DEBUG
109 | Shell.enableLegacyStderrRedirection = true
110 | Shell.setDefaultBuilder(
111 | Shell.Builder.create()
112 | .setFlags(Shell.FLAG_NON_ROOT_SHELL)
113 | // .setInitializers(Shell.Initializer::class.java)
114 | )
115 |
116 | ShellManager.instance = ShellManager(LibSuShell)
117 |
118 | PropertyManager.instance = PropertyManager(DefaultProperty)
119 | }
120 |
121 | private fun initLanguage() {
122 | val receiver = object : BroadcastReceiver() {
123 | override fun onReceive(context: Context, intent: Intent) {
124 | _homeLanguageEvent.value = Event(Unit)
125 | _othersLanguageEvent.value = Event(Unit)
126 | _propLanguageEvent.value = Event(Unit)
127 | }
128 | }
129 |
130 | registerReceiver(receiver, IntentFilter(Intent.ACTION_LOCALE_CHANGED))
131 | }
132 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/mvvm/BaseFragment.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.base.mvvm
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.fragment.app.Fragment
8 | import androidx.viewbinding.ViewBinding
9 |
10 | abstract class BaseFragment : Fragment() {
11 | private var _binding: T? = null
12 | protected val binding by lazy { _binding!! }
13 |
14 | protected abstract val inflate: (LayoutInflater, ViewGroup?, Boolean) -> T
15 |
16 | override fun onCreateView(
17 | inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
18 | ): View? {
19 | _binding = viewBinding(inflate, container)
20 | return _binding?.root
21 | }
22 |
23 | override fun onDestroyView() {
24 | super.onDestroyView()
25 |
26 | _binding = null
27 | }
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/mvvm/BaseViewModel.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.base.mvvm
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.ViewModel
6 | import androidx.lifecycle.viewModelScope
7 | import kotlinx.coroutines.Dispatchers
8 | import kotlinx.coroutines.launch
9 | import kotlinx.coroutines.withContext
10 | import net.imknown.android.forefrontinfo.R
11 | import net.imknown.android.forefrontinfo.base.MyApplication
12 |
13 | abstract class BaseViewModel : ViewModel() {
14 | private val _changeScrollBarModeEvent by lazy { MutableLiveData>() }
15 | val changeScrollBarModeEvent: LiveData> by lazy { _changeScrollBarModeEvent }
16 |
17 | fun setScrollBarMode(
18 | scrollBarMode: String
19 | ) = viewModelScope.launch(Dispatchers.Default) {
20 | when (scrollBarMode) {
21 | MyApplication.getMyString(R.string.interface_no_scroll_bar_value) -> {
22 | withContext(Dispatchers.Main) {
23 | _changeScrollBarModeEvent.value = Event(false)
24 | }
25 | }
26 | MyApplication.getMyString(R.string.interface_normal_scroll_bar_value) -> {
27 | withContext(Dispatchers.Main) {
28 | _changeScrollBarModeEvent.value = Event(true)
29 | }
30 | }
31 | MyApplication.getMyString(R.string.interface_fast_scroll_bar_value) -> {
32 | withContext(Dispatchers.Main) {
33 | _changeScrollBarModeEvent.value = Event(false)
34 | }
35 | }
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/mvvm/Event.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.imknown.android.forefrontinfo.base.mvvm
18 |
19 | import androidx.lifecycle.Observer
20 |
21 | /**
22 | * Used as a wrapper for data that is exposed via a LiveData that represents an event.
23 | *
24 | * Copied from:
25 | * https://medium.com/androiddevelopers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150
26 | * https://github.com/android/architecture-samples/blob/master/app/src/main/java/com/example/android/architecture/blueprints/todoapp/Event.kt
27 | */
28 | open class Event(private val content: T) {
29 |
30 | @Suppress("MemberVisibilityCanBePrivate")
31 | var hasBeenHandled = false
32 | private set // Allow external read but not write
33 |
34 | /**
35 | * Returns the content and prevents its use again.
36 | */
37 | fun getContentIfNotHandled(): T? {
38 | return if (hasBeenHandled) {
39 | null
40 | } else {
41 | hasBeenHandled = true
42 | content
43 | }
44 | }
45 |
46 | /**
47 | * Returns the content, even if it's already been handled.
48 | */
49 | fun peekContent(): T = content
50 | }
51 |
52 | /**
53 | * An [Observer] for [Event]s, simplifying the pattern of checking if the [Event]'s content has
54 | * already been handled.
55 | *
56 | * [onEventUnhandledContent] is *only* called if the [Event]'s contents has not been handled.
57 | */
58 | class EventObserver(private val onEventUnhandledContent: (T) -> Unit) : Observer> {
59 | override fun onChanged(value: Event) {
60 | value.getContentIfNotHandled()?.let {
61 | onEventUnhandledContent(it)
62 | }
63 | }
64 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/mvvm/SharedPreferenceChangeEventLiveData.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.base.mvvm
2 |
3 | import android.content.SharedPreferences
4 | import androidx.lifecycle.LiveData
5 | import kotlinx.coroutines.CoroutineScope
6 | import kotlinx.coroutines.Dispatchers
7 | import kotlinx.coroutines.launch
8 | import kotlinx.coroutines.withContext
9 |
10 | /**
11 | * Copied from:
12 | * https://gist.github.com/rharter/1df1cd72ce4e9d1801bd2d49f2a96810
13 | */
14 | abstract class SharedPreferenceChangeEventLiveData(
15 | private val scope: CoroutineScope,
16 | protected val sharedPrefs: SharedPreferences,
17 | private val key: String,
18 | private val defValue: T
19 | ) : LiveData>() {
20 |
21 | private val preferenceChangeListener =
22 | SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
23 | scope.launch(Dispatchers.IO) {
24 | if (key == this@SharedPreferenceChangeEventLiveData.key) {
25 | val event = getValueFromPreferences(key, defValue)
26 |
27 | withContext(Dispatchers.Main) {
28 | value = event
29 | }
30 | }
31 | }
32 | }
33 |
34 | abstract suspend fun getValueFromPreferences(key: String, defValue: T): Event
35 |
36 | override fun onActive() {
37 | super.onActive()
38 | // value = getValueFromPreferences(key, defValue)
39 | sharedPrefs.registerOnSharedPreferenceChangeListener(preferenceChangeListener)
40 | }
41 |
42 | override fun onInactive() {
43 | super.onInactive()
44 | sharedPrefs.unregisterOnSharedPreferenceChangeListener(preferenceChangeListener)
45 | }
46 | }
47 |
48 | fun SharedPreferences.stringEventLiveData(scope: CoroutineScope, key: String, defValue: String) =
49 | object : SharedPreferenceChangeEventLiveData(scope, this, key, defValue) {
50 | override suspend fun getValueFromPreferences(key: String, defValue: String?) =
51 | Event(sharedPrefs.getString(key, defValue))
52 | }
53 |
54 | fun SharedPreferences.booleanEventLiveData(scope: CoroutineScope, key: String, defValue: Boolean) =
55 | object : SharedPreferenceChangeEventLiveData(scope, this, key, defValue) {
56 | override suspend fun getValueFromPreferences(key: String, defValue: Boolean) =
57 | Event(sharedPrefs.getBoolean(key, defValue))
58 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/mvvm/ToastExt.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.base.mvvm
2 |
3 | import android.content.Context
4 | import android.widget.Toast
5 | import androidx.annotation.StringRes
6 | import androidx.annotation.UiContext
7 |
8 | fun @receiver:UiContext Context.toast(@StringRes resId: Int) =
9 | Toast.makeText(this, resId, Toast.LENGTH_LONG).show()
10 |
11 | fun @receiver:UiContext Context.toast(text: String) =
12 | Toast.makeText(this, text, Toast.LENGTH_LONG).show()
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/mvvm/ViewBindingExt.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.base.mvvm
2 |
3 | import android.app.Activity
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.core.view.WindowInsetsCompat
8 | import androidx.fragment.app.Fragment
9 | import androidx.viewbinding.ViewBinding
10 |
11 | val windowInsetsCompatTypes = WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout()
12 |
13 | inline fun Activity.viewBinding(
14 | crossinline inflate: (LayoutInflater) -> T
15 | ) = lazy { inflate(layoutInflater) }
16 |
17 | inline fun Fragment.viewBinding(
18 | crossinline inflate: (LayoutInflater, ViewGroup?, Boolean) -> T,
19 | container: ViewGroup?
20 | ) = inflate(layoutInflater, container, false)
21 |
22 | inline fun ViewGroup.viewBinding(
23 | crossinline inflate: (LayoutInflater, ViewGroup?, Boolean) -> T
24 | ) = inflate(LayoutInflater.from(context), this, false)
25 |
26 | inline fun View.viewBinding(
27 | crossinline inflate: (LayoutInflater, ViewGroup?, Boolean) -> T
28 | ) = lazy { inflate(LayoutInflater.from(context), null, false) }
29 |
30 | inline fun View.viewBinding(
31 | crossinline bind: (View) -> T
32 | ) = lazy { bind(this) }
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/res/resources.properties:
--------------------------------------------------------------------------------
1 | unqualifiedResLocale=en-US
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/res/values-fr-rFR/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | LowLevelDetector
4 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/res/values-zh-rCN/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 底层探测器
4 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/res/values-zh-rTW/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 底層檢測器
4 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | LowLevelDetector
4 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/resBackup/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/resBackup/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/resLauncher/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
13 |
14 |
17 |
20 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/resLauncher/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/resLauncher/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/resLauncher/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imknown/AndroidLowLevelDetector/9b9ffa12d7324fd953cb7eaf34d1b9d66d1e432e/app/src/main/java/net/imknown/android/forefrontinfo/base/resLauncher/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/resLauncher/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imknown/AndroidLowLevelDetector/9b9ffa12d7324fd953cb7eaf34d1b9d66d1e432e/app/src/main/java/net/imknown/android/forefrontinfo/base/resLauncher/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/resLauncher/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imknown/AndroidLowLevelDetector/9b9ffa12d7324fd953cb7eaf34d1b9d66d1e432e/app/src/main/java/net/imknown/android/forefrontinfo/base/resLauncher/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/resLauncher/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imknown/AndroidLowLevelDetector/9b9ffa12d7324fd953cb7eaf34d1b9d66d1e432e/app/src/main/java/net/imknown/android/forefrontinfo/base/resLauncher/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/resLauncher/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imknown/AndroidLowLevelDetector/9b9ffa12d7324fd953cb7eaf34d1b9d66d1e432e/app/src/main/java/net/imknown/android/forefrontinfo/base/resLauncher/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/resLauncher/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imknown/AndroidLowLevelDetector/9b9ffa12d7324fd953cb7eaf34d1b9d66d1e432e/app/src/main/java/net/imknown/android/forefrontinfo/base/resLauncher/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/resLauncher/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imknown/AndroidLowLevelDetector/9b9ffa12d7324fd953cb7eaf34d1b9d66d1e432e/app/src/main/java/net/imknown/android/forefrontinfo/base/resLauncher/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/resLauncher/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imknown/AndroidLowLevelDetector/9b9ffa12d7324fd953cb7eaf34d1b9d66d1e432e/app/src/main/java/net/imknown/android/forefrontinfo/base/resLauncher/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/resLauncher/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imknown/AndroidLowLevelDetector/9b9ffa12d7324fd953cb7eaf34d1b9d66d1e432e/app/src/main/java/net/imknown/android/forefrontinfo/base/resLauncher/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/resLauncher/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imknown/AndroidLowLevelDetector/9b9ffa12d7324fd953cb7eaf34d1b9d66d1e432e/app/src/main/java/net/imknown/android/forefrontinfo/base/resLauncher/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/resLauncher/values-night/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #069868
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/resLauncher/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #069868
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/resTheme/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/resTheme/values-v23/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | @android:color/transparent
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/resTheme/values-v27/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | @android:color/transparent
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/base/resTheme/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
63 |
64 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.ui
2 |
3 | import android.os.Bundle
4 | import android.widget.TextView
5 | import androidx.activity.viewModels
6 | import androidx.annotation.IdRes
7 | import androidx.appcompat.app.AppCompatActivity
8 | import androidx.appcompat.widget.Toolbar
9 | import androidx.core.view.ViewCompat
10 | import androidx.core.view.WindowCompat
11 | import androidx.core.view.updatePadding
12 | import androidx.fragment.app.Fragment
13 | import androidx.fragment.app.FragmentManager
14 | import androidx.fragment.app.FragmentTransaction
15 | import androidx.fragment.app.commitNow
16 | import androidx.lifecycle.lifecycleScope
17 | import kotlinx.coroutines.Dispatchers
18 | import kotlinx.coroutines.launch
19 | import net.imknown.android.forefrontinfo.R
20 | import net.imknown.android.forefrontinfo.base.mvvm.viewBinding
21 | import net.imknown.android.forefrontinfo.base.mvvm.windowInsetsCompatTypes
22 | import net.imknown.android.forefrontinfo.databinding.MainActivityBinding
23 | import net.imknown.android.forefrontinfo.ui.home.HomeFragment
24 | import net.imknown.android.forefrontinfo.ui.others.OthersFragment
25 | import net.imknown.android.forefrontinfo.ui.prop.PropFragment
26 | import net.imknown.android.forefrontinfo.ui.settings.SettingsFragment
27 |
28 | class MainActivity : AppCompatActivity() {
29 |
30 | internal val binding by viewBinding(MainActivityBinding::inflate)
31 |
32 | private val mainViewModel by viewModels()
33 |
34 | override fun onCreate(savedInstanceState: Bundle?) {
35 | super.onCreate(savedInstanceState)
36 |
37 | setContentView(binding.root)
38 |
39 | initWindowInsets()
40 |
41 | initViews()
42 |
43 | if (savedInstanceState == null) {
44 | supportFragmentManager.switch(R.id.navigation_home, true)
45 | }
46 |
47 | // mainViewModel.dealWithShizuku()
48 | }
49 |
50 | override fun onDestroy() {
51 | super.onDestroy()
52 |
53 | // mainViewModel.removeRequestPermissionResultListener()
54 | }
55 |
56 | private fun initWindowInsets() {
57 | // https://developer.android.com/design/ui/mobile/guides/foundations/system-bars#button_modes
58 | // https://developer.android.com/develop/ui/views/layout/edge-to-edge#create-transparent
59 | // https://developer.android.com/develop/ui/views/layout/edge-to-edge-manually#change-color
60 | // https://developer.android.com/develop/ui/compose/layouts/system-bars#create-transparent
61 | // enableEdgeToEdge()
62 | WindowCompat.setDecorFitsSystemWindows(window, false)
63 |
64 | // https://developer.android.com/develop/ui/views/layout/edge-to-edge#material-components
65 | // https://developer.android.com/develop/ui/compose/layouts/insets#material3-components
66 | ViewCompat.setOnApplyWindowInsetsListener(binding.appBar) { appBar, windowInsetsCompat ->
67 | val insets = windowInsetsCompat.getInsets(windowInsetsCompatTypes)
68 | appBar.updatePadding(
69 | left = insets.left,
70 | right = insets.right,
71 | top = insets.top
72 | )
73 |
74 | windowInsetsCompat
75 | }
76 |
77 | // https://developer.android.com/develop/ui/views/layout/edge-to-edge#material-components
78 | // https://developer.android.com/develop/ui/compose/layouts/insets#material3-components
79 | ViewCompat.setOnApplyWindowInsetsListener(binding.bottomNavigationView) { bnView, windowInsetsCompat ->
80 | val insets = windowInsetsCompat.getInsets(windowInsetsCompatTypes)
81 | bnView.updatePadding(
82 | left = insets.left,
83 | right = insets.right,
84 | bottom = insets.bottom
85 | )
86 |
87 | windowInsetsCompat
88 | }
89 | }
90 |
91 | private fun initViews() {
92 | initSubtitle()
93 |
94 | binding.bottomNavigationView.setOnItemSelectedListener {
95 | supportFragmentManager.switch(it.itemId)
96 |
97 | true
98 | }
99 | }
100 |
101 | private fun initSubtitle() {
102 | setSupportActionBar(binding.toolbar)
103 |
104 | lifecycleScope.launch(Dispatchers.IO) {
105 | val subtitleTextView = Toolbar::class.java
106 | .getDeclaredField("mSubtitleTextView")
107 | .apply { isAccessible = true }
108 | .get(binding.toolbar) as TextView
109 | subtitleTextView.isVerticalScrollBarEnabled = false
110 | }
111 | }
112 |
113 | private fun FragmentManager.switch(@IdRes selectedId: Int, isFirst: Boolean = false) {
114 | val lastId = mainViewModel.lastId
115 | if (selectedId == lastId && !isFirst) {
116 | return
117 | }
118 |
119 | commitNow(true) {
120 | setCustomAnimations(R.anim.drop_scale, FragmentTransaction.TRANSIT_NONE)
121 |
122 | val selectedFragment = findFragmentByTag(selectedId.toString())
123 | ?: createFragment(selectedId)
124 | show(selectedFragment)
125 |
126 | findFragmentByTag(lastId.toString())?.let {
127 | hide(it)
128 | }
129 |
130 | setReorderingAllowed(true)
131 | }
132 |
133 | mainViewModel.setSavedStateLastId(selectedId)
134 | }
135 |
136 | private fun FragmentTransaction.createFragment(@IdRes id: Int): Fragment = when (id) {
137 | R.id.navigation_home -> HomeFragment.newInstance()
138 | R.id.navigation_others -> OthersFragment.newInstance()
139 | R.id.navigation_prop -> PropFragment.newInstance()
140 | R.id.navigation_settings -> SettingsFragment.newInstance()
141 | else -> throw Exception()
142 | }.apply {
143 | add(R.id.container, this, id.toString())
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/MainViewModel.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.ui
2 |
3 | import androidx.annotation.IdRes
4 | import androidx.lifecycle.SavedStateHandle
5 | import androidx.lifecycle.ViewModel
6 | import net.imknown.android.forefrontinfo.R
7 |
8 | class MainViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {
9 |
10 | companion object {
11 | private const val SAVED_STATE_HANDLE_KEY_LAST_ID = "SAVED_STATE_HANDLE_KEY_LAST_ID"
12 | }
13 |
14 | @IdRes
15 | var lastId = getSavedStateLastId()
16 |
17 | fun setSavedStateLastId(@IdRes id: Int) {
18 | lastId = id
19 | savedStateHandle[SAVED_STATE_HANDLE_KEY_LAST_ID] = id
20 | }
21 |
22 | @IdRes
23 | private fun getSavedStateLastId() = savedStateHandle[SAVED_STATE_HANDLE_KEY_LAST_ID]
24 | ?: R.id.navigation_home
25 |
26 | // private val requestPermissionResultListener =
27 | // Shizuku.OnRequestPermissionResultListener { _, grantResult ->
28 | // removeRequestPermissionResultListener()
29 | //
30 | // if (grantResult == PackageManager.PERMISSION_GRANTED) {
31 | // PropertyManager.instance = PropertyManager(ShizukuProperty)
32 | // }
33 | // }
34 | //
35 | // fun dealWithShizuku() {
36 | // if (Shizuku.isPreV11() || !Shizuku.pingBinder()) {
37 | // return
38 | // }
39 | //
40 | // if (Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED) {
41 | // PropertyManager.instance = PropertyManager(ShizukuProperty)
42 | // } else if (Shizuku.shouldShowRequestPermissionRationale()) {
43 | // // Doing nothing is OK
44 | // } else {
45 | // val shizukuFirstAskingKey =
46 | // MyApplication.getMyString(R.string.function_shizuku_first_asking_key)
47 | // val isShizukuFirstAsking =
48 | // MyApplication.sharedPreferences.getBoolean(shizukuFirstAskingKey, true)
49 | // if (!isShizukuFirstAsking) {
50 | // return
51 | // }
52 | //
53 | // MyApplication.sharedPreferences.edit {
54 | // putBoolean(shizukuFirstAskingKey, false)
55 | // }
56 | //
57 | // addRequestPermissionResultListener()
58 | //
59 | // Shizuku.requestPermission(0)
60 | // }
61 | // }
62 | //
63 | // private fun addRequestPermissionResultListener() {
64 | // Shizuku.addRequestPermissionResultListener(requestPermissionResultListener)
65 | // }
66 | //
67 | // fun removeRequestPermissionResultListener() {
68 | // Shizuku.removeRequestPermissionResultListener(requestPermissionResultListener)
69 | // }
70 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/base/AndroidVersionExt.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.ui.base
2 |
3 | import android.app.ActivityManager
4 | import android.content.Context
5 | import android.os.Build
6 | import androidx.core.content.ContextCompat
7 | import net.imknown.android.forefrontinfo.ui.home.model.Lld
8 |
9 | private const val CODENAME_RELEASE = "REL"
10 |
11 | fun isAtLeastStableAndroid6() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
12 | fun isAtLeastStableAndroid7() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
13 | fun isAtLeastStableAndroid8() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
14 | fun isAtLeastStableAndroid8P1() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1
15 | fun isAtLeastStableAndroid9() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
16 | fun isAtLeastStableAndroid10() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
17 | fun isAtLeastStableAndroid11() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
18 | fun isAtLeastStableAndroid12() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
19 | fun isAtLeastStableAndroid13() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
20 | // fun isAtLeastStableAndroid14() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
21 | // fun isAtLeastStableAndroid15() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM
22 |
23 | fun isStableAndroid() = Build.VERSION.CODENAME == CODENAME_RELEASE
24 | fun isPreviewAndroid() = !isStableAndroid()
25 |
26 | fun isLatestStableAndroid(lld: Lld) = isStableAndroid()
27 | && Build.VERSION.SDK_INT >= lld.android.stable.api.toInt()
28 |
29 | fun isLatestPreviewAndroid(lld: Lld) = isPreviewAndroid()
30 | && getAndroidVersionName() >= lld.android.preview.name[0].toString()
31 |
32 | fun isSupportedByUpstreamAndroid(lld: Lld) = isStableAndroid()
33 | && Build.VERSION.SDK_INT >= lld.android.support.api.toInt()
34 |
35 | /** For Android 11+, use `Build.VERSION.RELEASE_OR_CODENAME` (`ro.build.version.release_or_codename`) */
36 | fun getAndroidVersionName(): String = if (isStableAndroid()) {
37 | Build.VERSION.RELEASE
38 | } else {
39 | if (isAtLeastStableAndroid13()) {
40 | "${Build.VERSION.RELEASE_OR_PREVIEW_DISPLAY}, "
41 | } else {
42 | ""
43 | } + Build.VERSION.CODENAME
44 | }
45 |
46 | fun Context.isGoEdition() = isAtLeastStableAndroid8P1() && isLowRamDevice()
47 |
48 | private fun Context.isLowRamDevice() = ContextCompat.getSystemService(
49 | this, ActivityManager::class.java
50 | )?.isLowRamDevice == true
51 |
52 | fun getAndroidApiLevel() = if (isStableAndroid()) {
53 | Build.VERSION.SDK_INT
54 | } else {
55 | Build.VERSION_CODES.CUR_DEVELOPMENT
56 | }
57 |
58 | //fun Context.getAndroidApiLevelDynamic() = packageManager.getApplicationInfo(
59 | // "android", 0
60 | //).targetSdkVersion
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/base/JsonIo.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.ui.base
2 |
3 | import android.content.res.AssetManager
4 | import android.util.Log
5 | import kotlinx.serialization.json.Json
6 | import net.imknown.android.forefrontinfo.R
7 | import net.imknown.android.forefrontinfo.base.MyApplication
8 | import net.imknown.android.forefrontinfo.ui.home.model.Lld
9 | import java.io.BufferedReader
10 | import java.io.File
11 | import java.io.FileWriter
12 |
13 | object JsonIo {
14 | const val LLD_JSON_NAME = "lld.json"
15 |
16 | val savedLldJsonFile by lazy {
17 | File(MyApplication.getDownloadDir(), LLD_JSON_NAME)
18 | }
19 |
20 | private fun deleteDirtyDirectory() {
21 | if (!savedLldJsonFile.deleteRecursively()) {
22 | Log.e(javaClass.simpleName, "Delete dirty directory failed.")
23 | } else {
24 | Log.i(javaClass.simpleName, "Dirty directory deleted.")
25 | }
26 | }
27 |
28 | fun copyJsonIfNeeded() {
29 | var shouldCopy = false
30 |
31 | if (savedLldJsonFile.exists()) {
32 | if (savedLldJsonFile.isDirectory) {
33 | deleteDirtyDirectory()
34 | shouldCopy = true
35 | } else {
36 | val savedLldVersion = try {
37 | savedLldJsonFile.fromJson().version
38 | } catch (e: Exception) {
39 | val message =
40 | MyApplication.getMyString(R.string.lld_json_parse_failed, e.message)
41 | Log.e(javaClass.simpleName, message, e)
42 | null
43 | }
44 |
45 | if (savedLldVersion == null) {
46 | shouldCopy = true
47 | } else {
48 | val assetLldVersion = getAssetLldVersion(MyApplication.instance.assets)
49 | if (assetLldVersion != null && savedLldVersion < assetLldVersion) {
50 | shouldCopy = true
51 | }
52 | }
53 | }
54 | } else {
55 | shouldCopy = true
56 | }
57 |
58 | if (!shouldCopy) {
59 | return
60 | }
61 |
62 | copyAssetsFileToContextFilesDir(
63 | MyApplication.instance.assets,
64 | savedLldJsonFile,
65 | LLD_JSON_NAME
66 | )
67 | }
68 |
69 | /**
70 | * https://discuss.kotlinlang.org/t/copy-file-from-res/7068/10
71 | */
72 | private fun copyAssetsFileToContextFilesDir(
73 | assets: AssetManager,
74 | savedFile: File,
75 | assetName: String
76 | ) {
77 | assets.open(assetName).use { inStream ->
78 | savedFile.parentFile?.mkdirs()
79 |
80 | savedFile.outputStream().use { outStream ->
81 | outStream.let {
82 | inStream.copyTo(it)
83 | }
84 | }
85 | }
86 | }
87 |
88 | fun saveLldJsonFile(lldString: String) {
89 | if (savedLldJsonFile.exists() && savedLldJsonFile.isDirectory) {
90 | deleteDirtyDirectory()
91 | }
92 |
93 | FileWriter(savedLldJsonFile).use {
94 | it.write(lldString)
95 | }
96 | }
97 |
98 | fun getAssetLld(assets: AssetManager): Lld? =
99 | try {
100 | assets.open(LLD_JSON_NAME)
101 | .bufferedReader()
102 | .use(BufferedReader::readText)
103 | .fromJson()
104 | } catch (e: Exception) {
105 | e.printStackTrace()
106 | null
107 | }
108 |
109 | fun getAssetLldVersion(assets: AssetManager): String? = getAssetLld(assets)?.version
110 | }
111 |
112 | val json by lazy { Json { ignoreUnknownKeys = true } }
113 |
114 | @Throws
115 | inline fun File.fromJson(): T =
116 | readText().fromJson()
117 |
118 | @Throws
119 | inline fun String.fromJson(): T =
120 | json.decodeFromString(this)
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/base/list/BaseListFragment.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.ui.base.list
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.core.view.ViewCompat
8 | import androidx.core.view.doOnLayout
9 | import androidx.core.view.updatePadding
10 | import androidx.lifecycle.LiveData
11 | import com.google.android.material.color.MaterialColors
12 | import net.imknown.android.forefrontinfo.R
13 | import net.imknown.android.forefrontinfo.base.mvvm.BaseFragment
14 | import net.imknown.android.forefrontinfo.base.mvvm.Event
15 | import net.imknown.android.forefrontinfo.base.mvvm.EventObserver
16 | import net.imknown.android.forefrontinfo.base.mvvm.toast
17 | import net.imknown.android.forefrontinfo.base.mvvm.windowInsetsCompatTypes
18 | import net.imknown.android.forefrontinfo.databinding.BaseListFragmentBinding
19 | import net.imknown.android.forefrontinfo.ui.MainActivity
20 | import com.google.android.material.R as materialR
21 |
22 | abstract class BaseListFragment : BaseFragment() {
23 |
24 | override val inflate: (LayoutInflater, ViewGroup?, Boolean) -> BaseListFragmentBinding =
25 | BaseListFragmentBinding::inflate
26 |
27 | protected val myAdapter by lazy { MyAdapter() }
28 |
29 | protected abstract val listViewModel: BaseListViewModel
30 |
31 | protected fun observeLanguageEvent(event: LiveData>) {
32 | event.observe(viewLifecycleOwner, EventObserver {
33 | binding.swipeRefreshLayout.isRefreshing = true
34 |
35 | listViewModel.collectModels()
36 | })
37 | }
38 |
39 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
40 | // super.onViewCreated(view, savedInstanceState)
41 |
42 | initWindowInsets()
43 |
44 | initViews(savedInstanceState)
45 |
46 | listViewModel.changeScrollBarModeEvent.observe(viewLifecycleOwner, EventObserver {
47 | binding.recyclerView.isVerticalScrollBarEnabled = it
48 | })
49 |
50 | listViewModel.models.observe(viewLifecycleOwner) {
51 | listViewModel.showModels(myAdapter.myModels, it)
52 | }
53 |
54 | listViewModel.showModelsEvent.observe(viewLifecycleOwner, EventObserver {
55 | myAdapter.notifyDataSetChanged()
56 |
57 | binding.swipeRefreshLayout.isRefreshing = false
58 | })
59 |
60 | listViewModel.showErrorEvent.observe(viewLifecycleOwner, EventObserver {
61 | context?.toast(it)
62 |
63 | binding.swipeRefreshLayout.isRefreshing = false
64 | })
65 |
66 | listViewModel.scrollBarMode.observe(viewLifecycleOwner, EventObserver {
67 | it?.let { scrollBarMode ->
68 | listViewModel.setScrollBarMode(scrollBarMode)
69 | }
70 | })
71 |
72 | listViewModel.init(savedInstanceState)
73 | }
74 |
75 | private fun initWindowInsets() {
76 | ViewCompat.setOnApplyWindowInsetsListener(binding.recyclerView) { rv, windowInsetsCompat ->
77 | val insets = windowInsetsCompat.getInsets(windowInsetsCompatTypes)
78 | (activity as? MainActivity)?.binding?.bottomNavigationView?.doOnLayout { bnv ->
79 | rv.updatePadding(
80 | left = insets.left,
81 | right = insets.right,
82 | bottom = bnv.height
83 | )
84 | }
85 |
86 | windowInsetsCompat
87 | }
88 | }
89 |
90 | private fun initViews(savedInstanceState: Bundle?) {
91 | val color = MaterialColors.getColor(binding.root, materialR.attr.colorOnPrimaryContainer)
92 | binding.swipeRefreshLayout.setColorSchemeColors(color)
93 | val backgroundColor =
94 | MaterialColors.getColor(binding.root, materialR.attr.colorPrimaryContainer)
95 | binding.swipeRefreshLayout.setProgressBackgroundColorSchemeColor(backgroundColor)
96 |
97 | if (listViewModel.hasNoData(savedInstanceState)) {
98 | // When activity is recreated, data is filled by memory.
99 | // It is fast. No progress indicator needed indeed.
100 | binding.swipeRefreshLayout.isRefreshing = true
101 | }
102 |
103 | binding.swipeRefreshLayout.setOnRefreshListener {
104 | listViewModel.collectModels()
105 | }
106 |
107 | binding.recyclerView.apply {
108 | setHasFixedSize(true)
109 |
110 | addItemDecoration(
111 | MyItemDecoration(
112 | resources.getDimensionPixelSize(R.dimen.item_divider_space_horizontal),
113 | resources.getDimensionPixelSize(R.dimen.item_divider_space_vertical)
114 | )
115 | )
116 |
117 | adapter = myAdapter
118 | }
119 | }
120 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/base/list/BaseListViewModel.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.ui.base.list
2 |
3 | import android.os.Bundle
4 | import androidx.annotation.StringRes
5 | import androidx.lifecycle.LiveData
6 | import androidx.lifecycle.MutableLiveData
7 | import androidx.lifecycle.viewModelScope
8 | import kotlinx.coroutines.Dispatchers
9 | import kotlinx.coroutines.Job
10 | import kotlinx.coroutines.launch
11 | import kotlinx.coroutines.withContext
12 | import net.imknown.android.forefrontinfo.BuildConfig
13 | import net.imknown.android.forefrontinfo.R
14 | import net.imknown.android.forefrontinfo.base.MyApplication
15 | import net.imknown.android.forefrontinfo.base.mvvm.BaseViewModel
16 | import net.imknown.android.forefrontinfo.base.mvvm.Event
17 | import net.imknown.android.forefrontinfo.base.mvvm.stringEventLiveData
18 | import net.imknown.android.forefrontinfo.base.property.PropertyManager
19 | import net.imknown.android.forefrontinfo.base.shell.ShellManager
20 | import net.imknown.android.forefrontinfo.base.shell.ShellResult
21 |
22 | abstract class BaseListViewModel : BaseViewModel() {
23 | private val _models by lazy { MutableLiveData>() }
24 | val models: LiveData> by lazy { _models }
25 |
26 | private val _showModelsEvent by lazy { MutableLiveData>() }
27 | val showModelsEvent: LiveData> by lazy { _showModelsEvent }
28 |
29 | private val _showErrorEvent by lazy { MutableLiveData>() }
30 | val showErrorEvent: LiveData> by lazy { _showErrorEvent }
31 |
32 | private val _scrollBarMode by lazy {
33 | MyApplication.sharedPreferences.stringEventLiveData(
34 | viewModelScope,
35 | MyApplication.getMyString(R.string.interface_scroll_bar_key),
36 | MyApplication.getMyString(R.string.interface_no_scroll_bar_value)
37 | )
38 | }
39 | val scrollBarMode: LiveData> by lazy { _scrollBarMode }
40 |
41 | abstract fun collectModels(): Job
42 |
43 | fun init(savedInstanceState: Bundle?) {
44 | viewModelScope.launch(Dispatchers.IO) {
45 | val scrollBarMode = MyApplication.sharedPreferences.getString(
46 | MyApplication.getMyString(R.string.interface_scroll_bar_key),
47 | MyApplication.getMyString(R.string.interface_no_scroll_bar_value)
48 | )!!
49 | setScrollBarMode(scrollBarMode)
50 |
51 | // When activity is recreated, use LiveData to restore the data
52 | if (hasNoData(savedInstanceState)) {
53 | collectModels()
54 | }
55 | }
56 | }
57 |
58 | fun hasNoData(savedInstanceState: Bundle?) =
59 | (savedInstanceState == null || _showModelsEvent.value == null) && _showErrorEvent.value == null
60 |
61 | protected suspend fun setModels(tempModels: ArrayList) =
62 | withContext(Dispatchers.Main) {
63 | _models.value = tempModels
64 | }
65 |
66 | fun showModels(
67 | myModels: ArrayList,
68 | newModels: ArrayList
69 | ) = viewModelScope.launch(Dispatchers.Default) {
70 | if (newModels.isEmpty()) {
71 | return@launch
72 | }
73 |
74 | myModels.clear()
75 | myModels.addAll(newModels)
76 |
77 | withContext(Dispatchers.Main) {
78 | _showModelsEvent.value = Event(Unit)
79 | }
80 | }
81 |
82 | protected fun showError(@StringRes messageId: Int, cause: Throwable) =
83 | viewModelScope.launch(Dispatchers.Default) {
84 | withContext(Dispatchers.Main) {
85 | _showErrorEvent.value = Event(MyApplication.getMyString(messageId, cause.message))
86 | }
87 |
88 | if (BuildConfig.DEBUG) {
89 | cause.printStackTrace()
90 | }
91 | }
92 |
93 | protected fun getStringProperty(key: String, condition: Boolean = true): String =
94 | if (condition) {
95 | PropertyManager.instance.getString(
96 | key, MyApplication.getMyString(R.string.build_not_filled)
97 | )
98 | } else {
99 | MyApplication.getMyString(R.string.result_not_supported)
100 | }
101 |
102 | protected fun getBooleanProperty(key: String, condition: Boolean = true) =
103 | if (condition) {
104 | PropertyManager.instance.getBoolean(key, false)
105 | } else {
106 | false
107 | }
108 |
109 | protected fun sh(cmd: String, condition: Boolean = true): ShellResult {
110 | return if (condition) {
111 | ShellManager.instance.execute(cmd)
112 | } else {
113 | ShellResult()
114 | }
115 | }
116 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/base/list/BasePureListViewModel.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.ui.base.list
2 |
3 | import net.imknown.android.forefrontinfo.R
4 | import net.imknown.android.forefrontinfo.base.MyApplication
5 |
6 | abstract class BasePureListViewModel : BaseListViewModel() {
7 | protected fun add(tempModels: ArrayList, title: String, detail: String?) {
8 | val translatedDetail = if (detail.isNullOrEmpty()) {
9 | MyApplication.getMyString(R.string.build_not_filled)
10 | } else {
11 | detail.toString()
12 | }
13 |
14 | tempModels.add(MyModel(title, translatedDetail))
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/base/list/MyAdapter.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.ui.base.list
2 |
3 | import android.view.ViewGroup
4 | import androidx.annotation.AttrRes
5 | import androidx.core.view.isGone
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.google.android.material.color.MaterialColors
8 |
9 | class MyAdapter : RecyclerView.Adapter() {
10 | companion object {
11 | const val PAYLOAD_DETAILS = 1
12 | }
13 |
14 | val myModels by lazy { ArrayList() }
15 |
16 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = MyViewHolder(parent)
17 |
18 | override fun onBindViewHolder(holder: MyViewHolder, position: Int, payloads: MutableList) {
19 | if (payloads.isEmpty()) {
20 | onBindFullViewHolder(holder, position)
21 | } else {
22 | onBindPayloadViewHolder(holder, position, payloads)
23 | }
24 | }
25 |
26 | /** Do **NOT** use this function. Use [onBindViewHolder] above instead */
27 | override fun onBindViewHolder(holder: MyViewHolder, position: Int) {}
28 |
29 | private fun onBindFullViewHolder(holder: MyViewHolder, position: Int) {
30 | with(holder.binding) {
31 | @AttrRes val color = myModels[position].color
32 | if (color != 0) {
33 | val backgroundColor = MaterialColors.getColor(root, color)
34 | sivColor.setBackgroundColor(backgroundColor)
35 | } else {
36 | sivColor.isGone = true
37 | }
38 | tvTitle.text = myModels[position].title
39 | tvDetail.text = myModels[position].detail
40 | }
41 | }
42 |
43 | private fun onBindPayloadViewHolder(
44 | holder: MyViewHolder, position: Int, payloads: MutableList
45 | ) = payloads.forEach {
46 | if (it == PAYLOAD_DETAILS) {
47 | holder.binding.tvDetail.text = myModels[position].detail
48 | }
49 | }
50 |
51 | override fun getItemCount() = myModels.size
52 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/base/list/MyItemDecoration.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.ui.base.list
2 |
3 | import android.graphics.Rect
4 | import android.view.View
5 | import androidx.recyclerview.widget.RecyclerView
6 |
7 | class MyItemDecoration(
8 | private val spaceH: Int,
9 | private val spaceV: Int
10 | ) : RecyclerView.ItemDecoration() {
11 | override fun getItemOffsets(
12 | outRect: Rect,
13 | view: View,
14 | parent: RecyclerView,
15 | state: RecyclerView.State
16 | ) {
17 | with(outRect) {
18 | if (parent.getChildAdapterPosition(view) == 0) {
19 | top = spaceV
20 | }
21 | left = spaceH
22 | right = spaceH
23 | bottom = spaceV
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/base/list/MyModel.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.ui.base.list
2 |
3 | import androidx.annotation.AttrRes
4 |
5 | data class MyModel(
6 | val title: String,
7 | var detail: String,
8 | @param:AttrRes val color: Int = 0
9 | )
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/base/list/MyViewHolder.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.ui.base.list
2 |
3 | import android.view.ViewGroup
4 | import androidx.recyclerview.widget.RecyclerView
5 | import net.imknown.android.forefrontinfo.base.mvvm.viewBinding
6 | import net.imknown.android.forefrontinfo.databinding.MyViewHolderBinding
7 |
8 | class MyViewHolder(
9 | parent: ViewGroup,
10 | val binding: MyViewHolderBinding = parent.viewBinding(MyViewHolderBinding::inflate)
11 | ) : RecyclerView.ViewHolder(binding.root)
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/base/list/res/layout/base_list_fragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/base/list/res/layout/my_view_holder.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
20 |
21 |
25 |
26 |
34 |
35 |
42 |
43 |
44 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/base/list/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 10dp
4 |
5 | 12dp
6 | 12dp
7 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/base/list/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/home/GatewayApi.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.ui.home
2 |
3 | import io.ktor.client.HttpClient
4 | import io.ktor.client.call.body
5 | import io.ktor.client.engine.okhttp.OkHttp
6 | import io.ktor.client.plugins.logging.ANDROID
7 | import io.ktor.client.plugins.logging.LogLevel
8 | import io.ktor.client.plugins.logging.Logger
9 | import io.ktor.client.plugins.logging.Logging
10 | import io.ktor.client.request.get
11 | import io.ktor.client.statement.HttpResponse
12 | import io.ktor.http.headers
13 | import net.imknown.android.forefrontinfo.BuildConfig
14 | import net.imknown.android.forefrontinfo.base.extension.isChinaMainlandTimezone
15 | import net.imknown.android.forefrontinfo.ui.base.JsonIo
16 | import java.io.IOException
17 | import java.net.Proxy
18 | import java.net.ProxySelector
19 | import java.net.SocketAddress
20 | import java.net.URI
21 |
22 | object GatewayApi {
23 | private const val HEADER_REFERER_KEY = "Referer"
24 | private const val HEADER_REFERER_VALUE = BuildConfig.APPLICATION_ID
25 |
26 | private const val REPOSITORY_NAME = "imknown/AndroidLowLevelDetector"
27 |
28 | private const val URL_PREFIX_LLD_JSON_GITEE = "gitee.com/$REPOSITORY_NAME/raw"
29 | private const val URL_PREFIX_LLD_JSON_GITHUB = "raw.githubusercontent.com/$REPOSITORY_NAME"
30 |
31 | suspend fun fetchLldJson(): String {
32 | val urlPrefixLldJson = if (isChinaMainlandTimezone()) {
33 | URL_PREFIX_LLD_JSON_GITEE
34 | } else {
35 | URL_PREFIX_LLD_JSON_GITHUB
36 | }
37 |
38 | val client = HttpClient(OkHttp) {
39 | engine {
40 | config {
41 | // region [Proxy]
42 | // Fix: java.lang.IllegalArgumentException: port out of range:-1
43 | // Steps to reproduce (small probability): Change Wifi proxy from "Manual" to "PAC"
44 | // https://github.com/square/okhttp/issues/6877#issuecomment-1438554879
45 | val proxySelector = object : ProxySelector() {
46 | override fun select(uri: URI?): List = try {
47 | getDefault().select(uri)
48 | } catch (e: Exception) {
49 | e.printStackTrace()
50 | listOf(Proxy.NO_PROXY)
51 | }
52 |
53 | override fun connectFailed(uri: URI?, sa: SocketAddress?, ioe: IOException?) {
54 | getDefault().connectFailed(uri, sa, ioe)
55 | }
56 | }
57 | proxySelector(proxySelector)
58 | // endregion [Proxy]
59 | }
60 | }
61 |
62 | if (BuildConfig.DEBUG) {
63 | install(Logging) {
64 | logger = Logger.ANDROID
65 | level = LogLevel.ALL
66 | }
67 | }
68 | }
69 |
70 | val url = "https://$urlPrefixLldJson/${BuildConfig.GIT_BRANCH}/app/src/main/assets/${JsonIo.LLD_JSON_NAME}"
71 | val response: HttpResponse = client.get(url) {
72 | headers {
73 | append(HEADER_REFERER_KEY, HEADER_REFERER_VALUE)
74 | }
75 | }
76 | return client.use {
77 | response.body()
78 | }
79 | }
80 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/home/HomeFragment.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.ui.home
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import androidx.appcompat.app.AppCompatActivity
6 | import androidx.fragment.app.viewModels
7 | import net.imknown.android.forefrontinfo.base.MyApplication
8 | import net.imknown.android.forefrontinfo.base.mvvm.EventObserver
9 | import net.imknown.android.forefrontinfo.ui.base.list.BaseListFragment
10 | import net.imknown.android.forefrontinfo.ui.base.list.MyAdapter
11 |
12 | class HomeFragment : BaseListFragment() {
13 |
14 | companion object {
15 | fun newInstance() = HomeFragment()
16 | }
17 |
18 | override val listViewModel by viewModels()
19 |
20 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
21 | super.onViewCreated(view, savedInstanceState)
22 |
23 | observeLanguageEvent(MyApplication.homeLanguageEvent)
24 |
25 | listViewModel.subtitle.observe(viewLifecycleOwner) {
26 | val actionBar = (activity as AppCompatActivity).supportActionBar
27 | actionBar?.subtitle = MyApplication.getMyString(it.lldDataModeResId, it.dataVersion)
28 | }
29 |
30 | listViewModel.outdatedOrderProp.observe(viewLifecycleOwner, EventObserver {
31 | listViewModel.payloadOutdatedTargetSdkVersionApk(myAdapter.myModels)
32 | })
33 |
34 | listViewModel.showOutdatedOrderEvent.observe(viewLifecycleOwner, EventObserver {
35 | myAdapter.notifyItemChanged(myAdapter.myModels.lastIndex, MyAdapter.PAYLOAD_DETAILS)
36 | })
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/home/model/BaseInfo.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.ui.home.model
2 |
3 | import androidx.annotation.Keep
4 | import kotlinx.serialization.Serializable
5 |
6 | @Keep
7 | @Serializable
8 | open class BaseInfo {
9 | lateinit var version: String
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/home/model/Lld.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.ui.home.model
2 |
3 | import androidx.annotation.Keep
4 | import kotlinx.serialization.Serializable
5 |
6 | @Keep
7 | @Serializable
8 | data class Lld(
9 | val scheme: Int,
10 | val android: Androids,
11 | val linux: Linuxes,
12 | val toybox: Toyboxes,
13 | val webView: WebViews
14 | ) : BaseInfo() {
15 | companion object {
16 | const val SCHEME_VERSION = 1
17 | }
18 |
19 | // https://source.android.com/security/enhancements/enhancements9
20 | // https://source.android.com/setup/start/p-release-notes
21 | // https://developer.android.com/about/versions/10
22 | //
23 | // https://en.wikipedia.org/wiki/Android_version_history
24 | // https://developer.android.com/about/dashboards?hl=en
25 | // https://www.bidouille.org/misc/androidcharts
26 | //
27 | // https://mta.qq.com/mta/data/device/os
28 | // https://compass.umeng.com/#hardwareList
29 | // https://tongji.baidu.com/research/app
30 | // https://www.appbrain.com/stats/top-android-sdk-versions
31 | // https://gs.statcounter.com/android-version-market-share/
32 | @Keep
33 | @Serializable
34 | data class Androids(
35 | // https://source.android.com/security/bulletin#bulletins
36 | // https://source.android.com/setup/start/build-numbers?hl=en#source-code-tags-and-builds
37 | // https://android.googlesource.com/platform/build/+/master/core/version_defaults.mk
38 | val securityPatchLevel: String,
39 | // https://source.android.com/security/bulletin#bulletins
40 | val googlePlaySystemUpdates: String,
41 | // https://source.android.com/setup/start/build-numbers?hl=en#source-code-tags-and-builds
42 | // https://android.googlesource.com/platform/frameworks/base/+refs
43 | val build: Build,
44 | // https://www.android.com
45 | val stable: Android,
46 | // https://source.android.com/security/bulletin/
47 | val support: Android,
48 | // https://ci.android.com
49 | // https://developer.android.com/preview/overview
50 | val preview: Android,
51 | val internal: Android
52 | ) {
53 | @Keep
54 | @Serializable
55 | data class Android(
56 | val name: String,
57 | val api: String,
58 | val phase: String? = null
59 | ) : BaseInfo()
60 |
61 | // https://android.googlesource.com/platform/build/+refs
62 | // https://android.googlesource.com/platform/build/+/refs/tags/android-12.0.0_r29/core/build_id.mk
63 | // https://android.googlesource.com/platform/build/+/master/core/build_id.mk
64 | //
65 | // https://developers.google.com/android/images
66 | @Keep
67 | @Serializable
68 | data class Build(
69 | val details: List
70 | ) : BaseInfo() {
71 | @Keep
72 | @Serializable
73 | data class Detail(
74 | val id: String,
75 | val revision: String
76 | )
77 | }
78 | }
79 |
80 | // https://source.android.com/setup/build/building-kernels#downloading
81 | // https://source.android.com/devices/architecture/kernel/android-common
82 | // https://www.kernel.org
83 | // https://en.wikipedia.org/wiki/Linux_kernel_version_history
84 | @Keep
85 | @Serializable
86 | data class Linuxes(
87 | // https://android.googlesource.com/kernel/common
88 | val google: Versions,
89 | // https://android.googlesource.com/kernel/common/+/android-mainline/Makefile
90 | val mainline: BaseInfo
91 | ) {
92 | @Keep
93 | @Serializable
94 | data class Versions(
95 | val versions: List
96 | )
97 | }
98 |
99 | // https://github.com/landley/toybox
100 | // https://android.googlesource.com/platform/external/toybox/+refs
101 | // https://android.googlesource.com/platform/system/core/+/master/shell_and_utilities/
102 | @Keep
103 | @Serializable
104 | data class Toyboxes(
105 | // https://android.googlesource.com/platform/external/toybox/+/refs/tags/android-12.0.0_r29/toys.h
106 | val stable: BaseInfo,
107 | // https://android.googlesource.com/platform/external/toybox/+/refs/tags/android-security-10.0.0_r63/main.c
108 | val support: BaseInfo,
109 | // https://android.googlesource.com/platform/external/toybox/+/master/toys.h#135
110 | // https://android.googlesource.com/platform/external/toybox/+/upstream-master/toys.h#135
111 | val master: BaseInfo
112 | )
113 |
114 | // https://www.chromium.org/developers/calendar
115 | // https://chromiumdash.appspot.com/releases?platform=Android
116 | //
117 | // https://en.wikipedia.org/wiki/Google_Chrome_version_history
118 | @Keep
119 | @Serializable
120 | data class WebViews(
121 | val stable: BaseInfo,
122 | val beta: BaseInfo
123 | )
124 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/home/model/Subtitle.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.ui.home.model
2 |
3 | import androidx.annotation.StringRes
4 |
5 | data class Subtitle(
6 | @param:StringRes val lldDataModeResId: Int,
7 | val dataVersion: String
8 | )
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/home/res/drawable/ic_home_24dp.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/home/res/values-fr-rFR/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Menu
4 |
5 | Supporté
6 | Non supporté
7 | Non identifié
8 | Activée
9 | Désactivé
10 |
11 | "Version d'Android"
12 | %1$s (API %2$s)
13 | Moi: %1$s\n\nDernière stable: %2$s\nPlus faible prise en charge: %3$s\nDernière aperçu: %4$s\nDernière interne: %5$s
14 |
15 | Id de build Android
16 | Moi: %1$s\nMon system: %2$s\nMon vendor: %3$s\nMon ODM: %4$s\n\nDernière stable: %5$s\n%6$s
17 | %1$s (%2$s)
18 |
19 | Niveau du patch de sécurité Android
20 | Niveau du patch de sécurité Vendor
21 | Moi: %1$s\n\nDernière stable: %2$s
22 |
23 | Niveau de performance
24 |
25 | Version du noyau Linux
26 | Moi: %1$s\n\nDernière support: %2$s\nMainline: %3$s
27 |
28 | Mise à jour A/B seamless
29 | \u0020(Virtuel)
30 | \u0020(Virtuel, rénové)
31 | \u0020(Courant: %s)
32 |
33 | Status System-as-root
34 | Héritage, Android 9
35 |
36 | Status du Partitions dynamiques
37 | \u0020(Rénové)
38 |
39 | Status du Projet Treble
40 | \u0020(Héritage, pas de fragments)
41 | \u0020(Autre)
42 |
43 | Compatibilité GSI
44 | Conforme à Treble
45 | Non conforme à Treble
46 |
47 | Status du Mise à jour du système dynamique(DSU)
48 |
49 | Project Mainline module version\n(Mise à jour du système Google Play)
50 | Moi: %1$s (%2$s)\n\nDernière: %3$s
51 |
52 | Status du NDK Vendor integré
53 | \u0020(Niveau: %s)
54 |
55 | Status APEX
56 | \u0020(Héritage aplati)
57 |
58 | Statut des options pour de développeurs
59 |
60 | Statut de débogage ADB
61 |
62 | "Statut d'authentification ADB"
63 |
64 | Statut de chiffrement
65 | Crypté
66 | "Chiffré mais le mot de passe n'a pas été défini"
67 | Non crypté
68 |
69 | Status SELinux
70 | Mode forcé
71 | Mode permissif
72 |
73 | Version de Toybox intégrée
74 | Moi: %1$s\n\nDernier stable: %2$s\nPlus faible support: %3$s\nMaster: %4$s
75 |
76 | Version WebView
77 | Dernier stable: %1$s\nDernier béta: %2$s
78 | Mon system WebView integré
79 | %1$s ou %2$s
80 | Autonome
81 | Pas installé
82 | "Doit être choisi par l'utilisateur"
83 | Se retirer
84 | Signé
85 | Mon implémentation de WebView
86 |
87 | "API Android à la sortie d'usine: %s\n\n"
88 | Les application system avec targetVersionSdk dépassé
89 | "Pas d'application dépassé"
90 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/home/res/values-night/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #D02B5128
4 | #D07E581F
5 | #D0812F2F
6 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/home/res/values-zh-rCN/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 首页
4 |
5 | 支持
6 | 不支持
7 | 无法辨别
8 | 已启用
9 | 已禁用
10 |
11 | Android 版本
12 | %1$s(API %2$s)
13 | 我的:%1$s\n\n最新稳定:%2$s\n最低支持:%3$s\n最新预览:%4$s\n最新内部:%5$s
14 |
15 | Android 构建 id
16 | %1$s(%2$s)
17 | 我的:%1$s\n我的系统:%2$s\n我的供应商:%3$s\n我的 ODM:%4$s\n\n最新稳定:%5$s\n%6$s
18 |
19 | Android 安全补丁等级
20 | 供应商安全补丁等级
21 | 我的:%1$s\n\n最新稳定:%2$s
22 |
23 | 性能等级
24 |
25 | Linux 内核版本
26 | 我的:%1$s\n\n最新支持:%2$s\nMainline:%3$s
27 |
28 | A/B 无缝升级状态
29 | (虚拟的)
30 | (虚拟的,改造的)
31 | (当前:%s)
32 |
33 | System-as-root 状态
34 | 旧版,Android 9
35 |
36 | 动态分区状态
37 | (改造的)
38 |
39 | Project Treble 状态
40 | (旧版,无片段)
41 | (其他)
42 |
43 | GSI 兼容性
44 | 遵从 Treble
45 | 未遵从 Treble
46 |
47 | 动态系统更新(DSU)状态
48 |
49 | Project Mainline 模块版本\n(Google Play 系统更新)
50 | 我的:%1$s(%2$s)\n\n最新:%3$s
51 |
52 | 内建供应商 NDK 状态
53 | (等级:%s)
54 |
55 | APEX 状态
56 | (旧版扁平的)
57 |
58 | 开发者选项状态
59 |
60 | ADB 调试状态
61 |
62 | ADB 验证状态
63 |
64 | 加密状态
65 | 已加密
66 | 已加密,但密码未设置
67 | 未加密
68 |
69 | SELinux 状态
70 | 强制模式
71 | 宽容模式
72 |
73 | 内建 Toybox 版本
74 | 我的:%1$s\n\n最新稳定:%2$s\n最低支持:%3$s\nMaster:%4$s
75 |
76 | WebView 版本
77 | 最新稳定:%1$s\n最新测试:%2$s
78 | 我的内建 WebView
79 | %1$s 或者 %2$s
80 | 独立的
81 | 未安装
82 | 必须由用户选择
83 | 备用
84 | 已签名
85 | 我的 WebView 实现
86 |
87 | 我的出厂 Android API:%s\n\n
88 | 过时 targetSdkVersion 系统程序
89 | 没有过时的系统程序
90 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/home/res/values-zh-rTW/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 首頁
4 |
5 | 支援
6 | 不支援
7 | 無法辨別
8 | 已啟用
9 | 已停用
10 |
11 | Android 版本
12 | %1$s(API %2$s)
13 | 我的:%1$s\n\n最新穩定:%2$s\n最低支援:%3$s\n最新預覽:%4$s\n最新內部:%5$s
14 |
15 | Android 構建 ID
16 | %1$s(%2$s)
17 | 我的:%1$s\n我的系統:%2$s\n我的供應商:%3$s\n我的 ODM:%4$s\n\n最新穩定:%5$s\n%6$s
18 |
19 | Android 安全性修補等級
20 | Vendor 安全性修補等級
21 | 我的:%1$s\n\n最新穩定:%2$s
22 |
23 | 性能等級
24 |
25 | Linux Kernel 版本
26 | 我的:%1$s\n\n最新支援:%2$s\nMainline:%3$s
27 |
28 | A/B 無縫升級狀態
29 | (虛擬的)
30 | (虛擬的,改造的)
31 | (目前:%s)
32 |
33 | System-as-root 狀態
34 | 舊版,Android 9
35 |
36 | 動態分區狀態
37 | (改造的)
38 |
39 | Project Treble 狀態
40 | (舊版,無片段)
41 | (其他)
42 |
43 | GSI 相容性
44 | 遵從 Treble
45 | 未遵從 Treble
46 |
47 | 動態系統更新(DSU)狀態
48 |
49 | Project Mainline 模組版本\n(Google Play 系統更新)
50 | 我的:%1$s(%2$s)\n\n最新:%3$s
51 |
52 | 內建 Vendor NDK 狀態
53 | (等級:%s)
54 |
55 | APEX 狀態
56 | (舊版扁平的)
57 |
58 | 開發者選項狀態
59 |
60 | ADB 偵錯狀態
61 |
62 | ADB 驗證狀態
63 |
64 | 加密狀態
65 | 已加密
66 | 已加密 但密碼未設定
67 | 未加密
68 |
69 | SELinux 狀態
70 | 強制模式
71 | 寬鬆模式
72 |
73 | 內建 Toybox 版本
74 | 我的:%1$s\n\n最新穩定:%2$s\n最低支援:%3$s\nMaster:%4$s
75 |
76 | WebView 版本
77 | 最新穩定:%1$s\n最新測試:%2$s
78 | 我的內建 WebView
79 | %1$s 或者 %2$s
80 | 獨立的
81 | 未安裝
82 | 必須由用戶選擇
83 | 備用
84 | 已簽名
85 | 我的 WebView 實現
86 |
87 | 我的出廠 Android API:%s\n\n
88 | 過時 targetSdkVersion 系統程式
89 | 沒有過時的系統程式
90 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/home/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/home/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #D0ACDDB7
4 | #D0FDD18F
5 | #D0FFB1AC
6 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/home/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Home
4 |
5 | Supported
6 | Not supported
7 | Unidentified
8 | Enabled
9 | Disabled
10 |
11 | Android version
12 | %1$s (API %2$s)
13 | Mine: %1$s\n\nLatest stable: %2$s\nLowest support: %3$s\nLatest preview: %4$s\nLatest internal: %5$s
14 |
15 | Android build ids
16 | Mine: %1$s\nMy system: %2$s\nMy vendor: %3$s\nMy ODM: %4$s\n\nLatest stable: %5$s\n%6$s
17 | %1$s (%2$s)
18 |
19 | Android security patch level
20 | Vendor security patch level
21 | Mine: %1$s\n\nLatest stable: %2$s
22 |
23 | Performance class
24 | API %s
25 |
26 | Linux kernel version
27 | Mine: %1$s\n\nLatest support: %2$s\nMainline: %3$s
28 |
29 | A/B seamless update status
30 | \u0020(Virtual)
31 | \u0020(Virtual, retrofitted)
32 | \u0020(Current: %s)
33 |
34 | System-as-root status
35 | Legacy, Android 9
36 | 2-Stage-Init
37 | Recovery
38 | /
39 |
40 | Dynamic partitions status
41 | \u0020(Retrofitted)
42 |
43 | Project Treble status
44 | \u0020(Legacy, no fragments)
45 | \u0020(Other)
46 |
47 | GSI compatibility
48 | Treble compliant
49 | Not Treble compliant
50 |
51 | Dynamic System Update(DSU) status
52 |
53 | Project Mainline module version\n(Google Play system update)
54 | Mine: %1$s (%2$s)\n\nLatest: %3$s
55 |
56 | Built-in Vendor NDK status
57 | \u0020(Level: %s)
58 |
59 | APEX status
60 | \u0020(Legacy flattened)
61 |
62 | Developer options status
63 |
64 | ADB debugging status
65 |
66 | ADB authentication status
67 |
68 | Encryption status
69 | Encrypted
70 | Encrypted but password not been set
71 | Not encrypted
72 |
73 | SELinux status
74 | Enforcing mode
75 | Permissive mode
76 |
77 | Built-in Toybox version
78 | Mine: %1$s\n\nLatest stable: %2$s\nLowest support: %3$s\nMaster: %4$s
79 |
80 | WebView version
81 | Latest stable: %1$s\nLatest beta: %2$s
82 | My built-in WebView
83 | %1$s or %2$s
84 | Standalone
85 | Not installed
86 | \n %s
87 | Must be chosen by user
88 | Fallback
89 | Signed
90 | \n\n
91 | My WebView implementation
92 |
93 | My factory Android API: %s\n\n
94 | Outdated targetSdkVersion system apps
95 | No outdated system apps
96 | %1$s (%2$s)
97 | (%2$s) %1$s
98 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/others/OthersFragment.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.ui.others
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import androidx.fragment.app.viewModels
6 | import net.imknown.android.forefrontinfo.base.MyApplication
7 | import net.imknown.android.forefrontinfo.ui.base.list.BaseListFragment
8 |
9 | class OthersFragment : BaseListFragment() {
10 |
11 | companion object {
12 | fun newInstance() = OthersFragment()
13 | }
14 |
15 | override val listViewModel by viewModels()
16 |
17 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
18 | super.onViewCreated(view, savedInstanceState)
19 |
20 | observeLanguageEvent(MyApplication.othersLanguageEvent)
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/others/res/drawable/ic_others_24dp.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/others/res/values-fr-rFR/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Autres
4 |
5 | Marque
6 | Constructeur
7 | Nom du model
8 | Nom de code
9 | Nom global du produit
10 | Nom du matériel
11 | Modèle de carte mère
12 | Modèle SoC
13 | Fabricant de SoC
14 | SKU ou matériel
15 | Vendor SKU
16 | SKU ou matériel ODM
17 |
18 | 32bit
19 | 64bit
20 | Bit de Binder
21 | Bit de HW Binder
22 | Bit de VND Binder
23 | Bit de processus actuel
24 | Architecture de processus actuel
25 | ABI de processus actuel
26 | Système actuel ABI
27 | 32bit CPU ABIs supportés
28 | 64bit CPU ABIs supportés
29 |
30 | Constructeur de la ROM
31 | Nom de la machine lors du build
32 | Date du build
33 | ROM basé sur (Développement secondaire)
34 | %s empreinte
35 | Empreinte de la ROM originale
36 | "Empreinte digitale d'aperçu de la ROM stock"
37 | ID de build ROM
38 | "ID de build ROM pour l'utilisateur"
39 | Type de build
40 | Tags de build
41 | Git commit hash
42 | "Nom de code de la branche Git (REL: Version stable de production)"
43 | "Révision de l'aperçu du développeur de la pré-version du SDK"
44 |
45 | WebView User-Agent
46 | Noyau Linux
47 |
48 | Version du Bootloader
49 | Version de la bande de base
50 |
51 | **NON REMPLI**
52 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/others/res/values-zh-rCN/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 其他
4 |
5 | 品牌
6 | 制造商
7 | 销售型号
8 | 开发代号
9 | 总产品名称
10 | 硬件名称
11 | 主板型号
12 | SoC 型号
13 | SoC 制造商
14 | 硬件 SKU
15 | 供应商 SKU
16 | ODM 硬件 SKU
17 |
18 | 32 位
19 | 64 位
20 | Binder 位数
21 | HW Binder 位数
22 | VND Binder 位数
23 | 当前进程位数
24 | 当前进程架构
25 | 当前进程 ABI
26 | 当前系统 ABI
27 | 支持的 32 位 CPU ABI
28 | 支持的 64 位 CPU ABI
29 |
30 | ROM 构建者
31 | ROM 构建的主机名
32 | ROM 构建时间
33 | 基于的系统(二次开发)
34 | %s 指纹
35 | 原机系统指纹
36 | 原机系统预览指纹
37 | ROM 构建 ID
38 | 给用户看的 ROM 构建 ID
39 | ROM 构建类型
40 | ROM 构建描述标签
41 | Git 提交的 Hash
42 | Git 分支代号(REL:正式)
43 | 预发行 SDK 开发者预览修订号
44 |
45 | WebView User-Agent
46 | Linux 内核
47 |
48 | Bootloader 版本
49 | 基带版本
50 |
51 | **未填写**
52 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/others/res/values-zh-rTW/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 其他
4 |
5 | 品牌
6 | 製造商
7 | 銷售型號
8 | 開發代號
9 | 總產品名稱
10 | 硬體名稱
11 | 主板型號
12 | SoC 型號
13 | SoC 製造商
14 | 硬體 SKU
15 | Vendor SKU
16 | ODM 硬體 SKU
17 |
18 | 32 位
19 | 64 位
20 | Binder 位數
21 | HW Binder 位數
22 | VND Binder 位數
23 | 目前處理程序位元數
24 | 目前處理程序架構
25 | 目前處理程序 ABI
26 | 目前系統 ABI
27 | 支援的 32 位 CPU ABI
28 | 支援的 64 位 CPU ABI
29 |
30 | ROM 構建者
31 | ROM 構建的主機名
32 | ROM 構建時間
33 | 基於的系統(二次開發)
34 | %s 指紋
35 | 原機系統指紋
36 | 原機系統預覽指紋
37 | ROM 構建 ID
38 | 給用戶看的 ROM 構建 ID
39 | ROM 構建類型
40 | ROM 構建描述標籤
41 | Git 提交的 Hash
42 | Git 分支代號(REL:正式)
43 | 預先發行 SDK 開發者預覽修訂號
44 |
45 | WebView User-Agent
46 | Linux Kernel
47 |
48 | Bootloader 版本
49 | 基帶版本
50 |
51 | **未填寫**
52 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/others/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Others
4 |
5 | Brand
6 | Manufacturer
7 | Model name for sales
8 | Codename for the developer
9 | Name of overall product
10 | Hardware name
11 | Board model
12 | SoC model
13 | SoC manufacturer
14 | SKU of hardware
15 | Vendor SKU
16 | SKU of ODM hardware
17 |
18 | 32bit
19 | 64bit
20 | Binder bit
21 | HW Binder bit
22 | VND Binder bit
23 | Current process bit
24 | Current process architecture
25 | Current process ABI
26 | Current system ABI
27 | Supported 32bit CPU ABIs
28 | Supported 64bit CPU ABIs
29 |
30 | ROM builder
31 | ROM build machine name
32 | ROM build time
33 | ROM based on (Secondary development)
34 | %s fingerprint
35 | Stock ROM fingerprint
36 | Stock ROM preview fingerprint
37 | ROM build ID
38 | ROM build ID for user
39 | ROM build type
40 | ROM build tags
41 | Git commit hash
42 | Git branch codename (REL: stable release version)
43 | Developer preview revision of pre-release SDK
44 |
45 | WebView User-Agent
46 | Linux kernel
47 |
48 | Bootloader version
49 | Baseband version
50 |
51 | **NOT FILLED**
52 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/prop/PropFragment.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.ui.prop
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import androidx.fragment.app.viewModels
6 | import net.imknown.android.forefrontinfo.base.MyApplication
7 | import net.imknown.android.forefrontinfo.ui.base.list.BaseListFragment
8 |
9 | class PropFragment : BaseListFragment() {
10 |
11 | companion object {
12 | fun newInstance() = PropFragment()
13 | }
14 |
15 | override val listViewModel by viewModels()
16 |
17 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
18 | super.onViewCreated(view, savedInstanceState)
19 |
20 | observeLanguageEvent(MyApplication.propLanguageEvent)
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/prop/PropViewModel.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.ui.prop
2 |
3 | import android.content.ContentResolver
4 | import android.provider.Settings
5 | import android.util.Log
6 | import androidx.lifecycle.viewModelScope
7 | import kotlinx.coroutines.Dispatchers
8 | import kotlinx.coroutines.launch
9 | import net.imknown.android.forefrontinfo.R
10 | import net.imknown.android.forefrontinfo.base.MyApplication
11 | import net.imknown.android.forefrontinfo.ui.base.list.BasePureListViewModel
12 | import net.imknown.android.forefrontinfo.ui.base.list.MyModel
13 | import java.util.Locale
14 | import java.util.Properties
15 | import kotlin.reflect.KClass
16 |
17 | class PropViewModel : BasePureListViewModel() {
18 |
19 | companion object {
20 | private const val CMD_GETPROP = "getprop"
21 |
22 | private const val UNIX_LIKE_NEWLINE_ORIGIN = "\\n"
23 | }
24 |
25 | override fun collectModels() = viewModelScope.launch(Dispatchers.IO) {
26 | try {
27 | val tempModels = ArrayList()
28 |
29 | getSystemProp(tempModels)
30 | getSettings(tempModels, Settings.System::class)
31 | getSettings(tempModels, Settings.Secure::class)
32 | getSettings(tempModels, Settings.Global::class)
33 | getBuildProp(tempModels)
34 |
35 | setModels(tempModels)
36 | } catch (e: Exception) {
37 | showError(R.string.lld_json_detect_failed, e)
38 | }
39 | }
40 |
41 | private fun getSystemProp(tempModels: ArrayList) {
42 | val systemProperties = System.getProperties()
43 | val defaultsProperties = Properties::class.java
44 | .getDeclaredField("defaults")
45 | .also { it.isAccessible = true }
46 | .get(systemProperties) as Properties
47 |
48 | (defaultsProperties + systemProperties)
49 | .toList()
50 | .sortedBy { it.first.toString() }
51 | .forEach {
52 | add(
53 | tempModels,
54 | it.first.toString(),
55 | if (it.second == System.lineSeparator()) {
56 | UNIX_LIKE_NEWLINE_ORIGIN
57 | } else {
58 | it.second.toString()
59 | }
60 | )
61 | }
62 | }
63 |
64 | private fun getSettings(tempModels: ArrayList, subSettingsKClass: KClass)
65 | where T : Settings.NameValueTable {
66 | subSettingsKClass.java.declaredFields
67 | .filter {
68 | it.isAccessible = true
69 | it.type == String::class.java
70 | }.map { it.get(null) as String }
71 | .sortedBy { it.uppercase(Locale.US) }
72 | .forEach {
73 | var key = it
74 | try {
75 | val value = subSettingsKClass.java.getDeclaredMethod(
76 | "getString",
77 | ContentResolver::class.java,
78 | String::class.java
79 | ).invoke(null, MyApplication.instance.contentResolver, key) as? String
80 | ?: MyApplication.getMyString(R.string.build_not_filled)
81 | key = "${subSettingsKClass.qualifiedName}.$key"
82 | tempModels.add(MyModel(key, value))
83 | } catch (e: Exception) {
84 | Log.e(javaClass.simpleName, e.cause?.message.toString())
85 | }
86 | }
87 | }
88 |
89 | private fun getBuildProp(tempModels: ArrayList) {
90 | var temp = ""
91 | sh(CMD_GETPROP).output.forEach {
92 | if (it.startsWith("[") && it.endsWith("]")) {
93 | addRawProp(tempModels, it)
94 | } else {
95 | temp += "$it\n"
96 |
97 | if (it.endsWith("]")) {
98 | addRawProp(tempModels, temp)
99 |
100 | temp = ""
101 | }
102 | }
103 | }
104 | }
105 |
106 | private fun addRawProp(tempModels: ArrayList, text: String) {
107 | val result = text.split(": ")
108 | add(tempModels, removeSquareBrackets(result[0]), removeSquareBrackets(result[1]))
109 | }
110 |
111 | private fun removeSquareBrackets(text: String) =
112 | text.substringAfter("[").substringBefore(']').trimIndent()
113 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/prop/res/drawable/ic_prop_24dp.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/prop/res/values-fr-rFR/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Propriétés
4 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/prop/res/values-zh-rCN/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 属性
4 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/prop/res/values-zh-rTW/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 屬性
4 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/prop/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Properties
4 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/res/anim/drop_scale.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
13 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/res/layout/main_activity.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
22 |
23 |
24 |
29 |
30 |
37 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/res/menu/bottom_nav_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
25 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/res/values-fr-rFR/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | CHARGEMENT DES…
4 | HORS-LIGNE (%s)
5 | ENLIGNE (%s)
6 | "Impossible d'atteindre les données en ligne.\nUtilisation des données locales.\n\n%s"
7 | "L'enregistrement des données a échoué.\nVeuillez vérifier votre stockage.\n\n%s"
8 | "Impossible de parser les données.\nEssayez de mettre à jour l'application.\n\n%s"
9 | "La détection a échoué.\nEssayez de mettre à jour l'application.\n\n%s"
10 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/res/values-zh-rCN/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 加载中…
4 | 离线(%s)
5 | 在线(%s)
6 | 在线数据获取失败。\n使用离线数据代替。\n\n%s
7 | 数据保存失败。\n请检查你的存储。\n\n%s
8 | 数据解析失败。\n请尝试升级本程序。\n\n%s
9 | 检测失败。\n请尝试升级本程序。\n\n%s
10 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/res/values-zh-rTW/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 載入中…
4 | 離線(%s)
5 | 在線上(%s)
6 | 線上數據獲取失敗。\n使用離線數據代替。\n\n%s
7 | 數據儲存失敗。\n請檢查你的儲存。\n\n%s
8 | 數據分析失敗。\n請嘗試升級本程式。\n\n%s
9 | 檢查失敗。\n請嘗試升級本程式。\n\n%s
10 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | LOADING…
4 | OFFLINE (%s)
5 | ONLINE (%s)
6 | Online data fetching failed.\nUse local data instead.\n\n%s
7 | Data saving failed.\nPlease check your storage.\n\n%s
8 | Data parsing failed.\nPlease try to upgrade this app.\n\n%s
9 | Detecting failed.\nPlease try to upgrade this app.\n\n%s
10 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/settings/SettingsFragment.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.ui.settings
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import androidx.annotation.StringRes
6 | import androidx.core.view.ViewCompat
7 | import androidx.core.view.doOnLayout
8 | import androidx.core.view.updatePadding
9 | import androidx.fragment.app.viewModels
10 | import androidx.preference.ListPreference
11 | import androidx.preference.Preference
12 | import androidx.preference.PreferenceFragmentCompat
13 | import net.imknown.android.forefrontinfo.R
14 | import net.imknown.android.forefrontinfo.base.MyApplication
15 | import net.imknown.android.forefrontinfo.base.extension.isChinaMainlandTimezone
16 | import net.imknown.android.forefrontinfo.base.mvvm.EventObserver
17 | import net.imknown.android.forefrontinfo.base.mvvm.toast
18 | import net.imknown.android.forefrontinfo.base.mvvm.windowInsetsCompatTypes
19 | import net.imknown.android.forefrontinfo.ui.MainActivity
20 |
21 | class SettingsFragment : PreferenceFragmentCompat() {
22 |
23 | companion object {
24 | fun newInstance() = SettingsFragment()
25 | }
26 |
27 | private val settingsViewModel by viewModels()
28 |
29 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
30 | setPreferencesFromResource(R.xml.preferences, rootKey)
31 | }
32 |
33 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
34 | super.onViewCreated(view, savedInstanceState)
35 |
36 | initWindowInsets()
37 |
38 | initViews()
39 | }
40 |
41 | private fun initWindowInsets() {
42 | listView.clipToPadding = false
43 |
44 | ViewCompat.setOnApplyWindowInsetsListener(listView) { rv, windowInsetsCompat ->
45 | val insets = windowInsetsCompat.getInsets(windowInsetsCompatTypes)
46 | (activity as? MainActivity)?.binding?.bottomNavigationView?.doOnLayout { bnv ->
47 | rv.updatePadding(
48 | left = insets.left,
49 | right = insets.right,
50 | bottom = bnv.height
51 | )
52 | }
53 |
54 | windowInsetsCompat
55 | }
56 | }
57 |
58 | private fun initViews() {
59 | // region [Theme]
60 | settingsViewModel.themesPrefChangeEvent.observe(viewLifecycleOwner, EventObserver {
61 | it?.let { themesValue ->
62 | MyApplication.setMyTheme(themesValue)
63 | }
64 | })
65 | // endregion [Theme]
66 |
67 | // region [Scroll Bar Mode]
68 | settingsViewModel.scrollBarModeChangedEvent.observe(viewLifecycleOwner, EventObserver {
69 | it?.let { scrollBarMode ->
70 | settingsViewModel.setScrollBarMode(scrollBarMode)
71 | }
72 | })
73 |
74 | settingsViewModel.changeScrollBarModeEvent.observe(viewLifecycleOwner, EventObserver {
75 | listView.isVerticalScrollBarEnabled = it
76 | })
77 |
78 | val scrollBarModePref =
79 | findPreference(MyApplication.getMyString(R.string.interface_scroll_bar_key))!!
80 | settingsViewModel.setScrollBarMode(scrollBarModePref.value)
81 | // endregion [Scroll Bar Mode]
82 |
83 | settingsViewModel.showMessageEvent.observe(viewLifecycleOwner, EventObserver {
84 | context?.toast(it)
85 | })
86 |
87 | val aboutShopPref = findPreference(R.string.about_shop_key)
88 | setOnOpenInExternalListener(
89 | aboutShopPref, if (isChinaMainlandTimezone()) {
90 | R.string.about_shop_china_mainland_uri
91 | } else {
92 | R.string.about_shop_uri
93 | }
94 | )
95 |
96 | val aboutSourcePref = findPreference(R.string.about_source_key)
97 | setOnOpenInExternalListener(aboutSourcePref, R.string.about_source_uri)
98 |
99 | val aboutPrivacyPolicyPref = findPreference(R.string.about_privacy_policy_key)
100 | setOnOpenInExternalListener(aboutPrivacyPolicyPref, R.string.about_privacy_policy_uri)
101 |
102 | val aboutLicensesPref = findPreference(R.string.about_licenses_key)
103 | setOnOpenInExternalListener(aboutLicensesPref, R.string.about_licenses_uri)
104 |
105 | val aboutTranslatorMoreInfoPref = findPreference(R.string.about_translator_more_info_key)
106 | setOnOpenInExternalListener(
107 | aboutTranslatorMoreInfoPref,
108 | R.string.translator_website
109 | )
110 |
111 | // region [Version Info]
112 | val versionPref = findPreference(R.string.about_version_key)
113 | settingsViewModel.version.observe(viewLifecycleOwner) {
114 | versionPref.summary = MyApplication.getMyString(
115 | it.id,
116 | it.versionName,
117 | it.versionCode,
118 | it.assetLldVersion,
119 | it.distributor,
120 | it.installer,
121 | it.firstInstallTime,
122 | it.lastUpdateTime
123 | )
124 | }
125 |
126 | settingsViewModel.setBuiltInDataVersion(
127 | MyApplication.instance.packageName,
128 | MyApplication.instance.packageManager
129 | )
130 | // endregion [Version Info]
131 |
132 | // region [Version Click]
133 | versionPref.setOnPreferenceClickListener {
134 | settingsViewModel.versionClicked()
135 |
136 | true
137 | }
138 | // endregion [Version Click]
139 | }
140 |
141 | private fun findPreference(@StringRes resId: Int) =
142 | findPreference(MyApplication.getMyString(resId))!!
143 |
144 | private fun setOnOpenInExternalListener(pref: Preference, @StringRes uriResId: Int) {
145 | pref.setOnPreferenceClickListener {
146 | settingsViewModel.openInExternal(uriResId)
147 |
148 | true
149 | }
150 | }
151 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/settings/res/drawable/ic_settings_24dp.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/settings/res/layout/preference_widget_material_switch.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/settings/res/values-fr-rFR/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Paramètres
4 |
5 | Interface
6 | Mode de défilement
7 | Cliquez ici pour voir le mode de défilement
8 | Aucun
9 | Normale
10 |
11 |
12 | Thème
13 | Cliquez pour voir le selecteur de thème
14 | Système
15 | Economiseur de batterie
16 | Toujours clair
17 | Toujours Sombre (AMOLED)
18 |
19 | Fonctions
20 | Activer les données en ligne
21 | Lorsque cette option est activée, les données seront préalablement extraites du serveur réseau distant, puis mises en cache sur le stockage local.
22 |
23 | "Commandez d'abord par nom de colis"
24 | "Commandez d'abord les applications obsolètes par nom de package, sinon par targetSdkVersion d'abord."
25 |
26 | À propos
27 |
28 | Aucun navigateur disponible
29 |
30 | Play Store
31 | "Ouvre la page de l'application dans Play Store"
32 |
33 | Code source
34 | "L'adresse GitHub du code sourcce de l'application"
35 |
36 | Politique de confidentialité
37 | "Voyez ce qui se passe quand vous utilisez l'application"
38 |
39 | Licences
40 | Les dépots open-source.
41 |
42 | Français (FR), traducteur: LiteApplication
43 | https://litepress.ddns.net
44 | https://github.com/LiteApplication
45 |
46 | Version
47 | Version: %1$s (Iteration times: %2$d)\nDonnées intégrées: %3$s\nDistributeur: %4$s\nInstallateur: %5$s\nPremière installation: %6$s\nDernière mise à jour: %7$s
48 |
49 | Ligne de commande
50 |
51 | Google
52 | imknown
53 | Débogage public
54 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/settings/res/values-zh-rCN/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 设置
4 |
5 | 界面
6 | 滚动条模式
7 | 点击以显示滚动条模式
8 | 无
9 | 普通
10 |
11 |
12 | 主题
13 | 点击以显示主题选择器
14 | 跟随系统
15 | 跟随省电模式
16 | 总是亮色
17 | 总是暗色
18 |
19 | 功能
20 | 允许网络数据
21 | 若启用,数据将优先从远端网络服务器获取,并被缓存在本地存储。
22 |
23 | 优先按包名排序
24 | 优先按包名排序过时程序,否则按 targetSdkVersion 优先。
25 |
26 | 关于
27 |
28 | 无可用浏览器
29 |
30 | 应用商店页
31 | 打开本程序商店所在的页面
32 |
33 | 源码
34 | 本程序源码的 GitHub 地址
35 |
36 | 隐私政策
37 | 看看使用本程序时会发生什么
38 |
39 | 协议
40 | 引用的开源库
41 |
42 | 简体中文(中国),翻译:imknown
43 | https://imknown.net
44 | https://github.com/imknown
45 |
46 | 版本
47 | 版本号:%1$s(迭代次数:%2$d)\n内建数据版本:%3$s\n分发者:%4$s\n安装者:%5$s\n首次安装:%6$s\n上次更新:%7$s
48 |
49 | 命令行
50 |
51 | 谷歌
52 | imknown
53 | 公共调试
54 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/settings/res/values-zh-rTW/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 設定
4 |
5 | 界面
6 | 滾動條模式
7 | 點擊以顯示滾動條模式
8 | 無
9 | 普通
10 |
11 |
12 | 主題
13 | 點擊以顯示主題選擇器
14 | 跟隨系統
15 | 跟隨省電模式
16 | 總是淺色
17 | 總是深色
18 |
19 | 功能
20 | 允許網路數據
21 | 若啟用,數據將優先從遠端網路伺服器獲取,並被快取在本地儲存。
22 |
23 | 優先按包名排序
24 | 優先按包名排序過時程式,否則按 targetSdkVersion 優先。
25 |
26 | 關於
27 |
28 | 無可用瀏覽器
29 |
30 | 應用商店頁面
31 | 打開本程式商店所在的頁面
32 |
33 | 原始碼
34 | 本程式原始碼的 GitHub 網址
35 |
36 | 隱私政策
37 | 看看使用本程式時會發生什麼
38 |
39 | 協議
40 | 引用的開源庫
41 |
42 | 繁體中文(臺灣),翻譯:bluehomewu
43 | https://github.com/bluehomewu
44 | https://github.com/bluehomewu
45 |
46 | 版本
47 | 版本號:%1$s(疊代次數:%2$d)\n內建數據版本:%3$s\n分發者:%4$s\n安裝者:%5$s\n首次安裝:%6$s\n上次更新:%7$s
48 |
49 | 命令行
50 |
51 | Google
52 | imknown
53 | 公共偵錯
54 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/settings/res/values/arrays.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - @string/interface_themes_follow_system_key
5 | - @string/interface_themes_power_saver_key
6 | - @string/interface_themes_always_light_key
7 | - @string/interface_themes_always_dark_key
8 |
9 |
10 |
11 | - @string/interface_themes_follow_system_value
12 | - @string/interface_themes_power_saver_value
13 | - @string/interface_themes_always_light_value
14 | - @string/interface_themes_always_dark_value
15 |
16 |
17 |
18 | - @string/interface_no_scroll_bar_key
19 | - @string/interface_normal_scroll_bar_key
20 |
21 |
22 |
23 |
24 | - @string/interface_no_scroll_bar_value
25 | - @string/interface_normal_scroll_bar_value
26 | - @string/interface_fast_scroll_bar_value
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/settings/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Settings
4 |
5 | Interface
6 | interface_scroll_bar_key
7 | Scroll bar mode
8 | Click to show the scroll bar mode
9 | None
10 | 0
11 | Normal
12 | 1
13 |
14 | 2
15 |
16 | interface_themes_key
17 | Themes
18 | Click to show the theme chooser
19 | Follow system
20 | -1
21 | Follow power saver
22 | 1
23 | Always light
24 | 2
25 | Always dark
26 | 3
27 |
28 | Function
29 | about_allow_network_data_key
30 | Allow network data
31 | When enabled, data will be prior fetched from remote network server, then be cached on the local storage.
32 |
33 | function_outdated_target_order_by_package_name_key
34 | Order by package name first
35 | Order the outdated apps by package name first, otherwise by the targetSdkVersion first.
36 |
37 |
38 |
39 | About
40 |
41 | No available browser
42 |
43 | about_shop_key
44 | Store page
45 | Open the page of current app in the store
46 | https://play.google.com/store/apps/details?id=net.imknown.android.forefrontinfo
47 | https://github.com/imknown/AndroidLowLevelDetector
48 |
49 | about_source_key
50 | Source code
51 | GitHub address of the app source code
52 | https://github.com/imknown/AndroidLowLevelDetector
53 |
54 | about_privacy_policy_key
55 | Privacy policy
56 | See what will happen when using this app
57 | https://github.com/imknown/AndroidLowLevelDetector/blob/master/GOOGLE_PLAY_PRIVACY_POLICY.md
58 |
59 | about_licenses_key
60 | License
61 | Referenced open-source repositories
62 | https://github.com/imknown/AndroidLowLevelDetector/blob/master/gradle/toml
63 |
64 | about_translator_more_info_key
65 | English (US), translator: imknown
66 | https://imknown.net
67 | https://github.com/imknown
68 |
69 | about_version_key
70 | Version
71 | Version: %1$s (Iteration times: %2$d)\nBuilt-in data version: %3$s\nDistributor: %4$s\nInstaller: %5$s\nFirst install: %6$s\nLast update: %7$s
72 | 🐈==) 🐕===))
73 |
74 | Command-line
75 |
76 | Google
77 | imknown
78 | Public debug
79 |
--------------------------------------------------------------------------------
/app/src/main/java/net/imknown/android/forefrontinfo/ui/settings/res/xml/preferences.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
9 |
17 |
18 |
26 |
27 |
28 |
31 |
32 |
38 |
39 |
45 |
46 |
47 |
50 |
55 |
56 |
61 |
62 |
67 |
68 |
73 |
74 |
79 |
80 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/app/src/test/java/net/imknown/android/forefrontinfo/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | /**
7 | * Example local unit test, which will execute on the development machine (host).
8 | *
9 | * See [testing documentation](http://d.android.com/tools/testing).
10 | */
11 | class ExampleUnitTest {
12 | @Test
13 | fun addition_isCorrect() {
14 | assertEquals(4, 2 + 2)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/base/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libsAndroid.plugins.android.library)
3 |
4 | alias(libsKotlin.plugins.kotlin.android)
5 | }
6 |
7 | android {
8 | namespace = "net.imknown.android.forefrontinfo.base"
9 |
10 | compileSdk = libsBuild.versions.compileSdk.get().toInt()
11 | // compileSdkExtension = libsBuild.versions.compileSdkExtension.get().toInt()
12 | buildToolsVersion = libsBuild.versions.buildTools.get()
13 | val isPreview = libsBuild.versions.isPreview.get().toBoolean()
14 | if (isPreview) {
15 | compileSdkPreview = libsBuild.versions.compileSdkPreview.get()
16 | buildToolsVersion = libsBuild.versions.buildToolsPreview.get()
17 | }
18 |
19 | defaultConfig {
20 | minSdk = libsBuild.versions.minSdk.get().toInt()
21 |
22 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
23 |
24 | consumerProguardFiles("consumer-rules.pro")
25 | }
26 |
27 | compileOptions {
28 | isCoreLibraryDesugaringEnabled = true
29 | }
30 | }
31 |
32 | // region [Toolchain]
33 | // https://developer.android.com/build/jdks
34 | // https://kotlinlang.org/docs/gradle-configure-project.html
35 | // https://docs.gradle.org/current/userguide/toolchains.html
36 | private val javaToolchain = libsBuild.versions.javaToolchain.get().toInt()
37 | kotlin {
38 | jvmToolchain(javaToolchain)
39 | }
40 | // endregion [Toolchain]
41 |
42 | dependencies {
43 | api(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
44 |
45 | coreLibraryDesugaring(libsAndroid.desugarJdkLibs)
46 |
47 | // region [AndroidX]
48 | androidTestImplementation(libsAndroid.bundles.test)
49 | // endregion [AndroidX]
50 |
51 | // region [3rd Parties]
52 | api(libsThirdParty.libsu)
53 |
54 | // api(libsThirdParty.bundles.shizuku)
55 | // endregion [3rd Parties]
56 | }
--------------------------------------------------------------------------------
/base/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imknown/AndroidLowLevelDetector/9b9ffa12d7324fd953cb7eaf34d1b9d66d1e432e/base/consumer-rules.pro
--------------------------------------------------------------------------------
/base/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.kts.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/base/src/androidTest/java/net/imknown/android/forefrontinfo/base/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.base
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import androidx.test.platform.app.InstrumentationRegistry
5 | import org.junit.Assert.assertEquals
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | /**
10 | * Instrumented test, which will execute on an Android device.
11 | *
12 | * See [testing documentation](http://d.android.com/tools/testing).
13 | */
14 | @RunWith(AndroidJUnit4::class)
15 | class ExampleInstrumentedTest {
16 | @Test
17 | fun useAppContext() {
18 | // Context of the app under test.
19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
20 | assertEquals("net.imknown.android.forefrontinfo.base", appContext.packageName)
21 | }
22 | }
--------------------------------------------------------------------------------
/base/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/base/src/main/java/net/imknown/android/forefrontinfo/base/extension/DateTimeExt.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.base.extension
2 |
3 | import java.time.Instant
4 | import java.time.ZoneId
5 | import java.time.ZonedDateTime
6 | import java.time.format.DateTimeFormatter
7 |
8 | fun String.formatToLocalZonedDatetimeString(): String {
9 | val pattern = "yyyy-MM-dd HH:mm Z"
10 | val formatter = DateTimeFormatter.ofPattern(pattern)
11 | val instant = ZonedDateTime.parse(this, formatter).toInstant()
12 | val datetime = instant.atZone(ZoneId.systemDefault())
13 | return formatter.format(datetime)
14 | }
15 |
16 | fun Long.formatToLocalZonedDatetimeString(): String {
17 | val pattern = "yyyy-MM-dd HH:mm:ss Z"
18 | val formatter = DateTimeFormatter.ofPattern(pattern)
19 | val instant = Instant.ofEpochMilli(this)
20 | val datetime = instant.atZone(ZoneId.systemDefault())
21 | return formatter.format(datetime)
22 | }
23 |
24 | fun isChinaMainlandTimezone() = with(ZoneId.systemDefault()) {
25 | id == "Asia/Shanghai" || id == "Asia/Urumqi"
26 | }
--------------------------------------------------------------------------------
/base/src/main/java/net/imknown/android/forefrontinfo/base/property/IProperty.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.base.property
2 |
3 | interface IProperty {
4 | fun getString(key: String, default: String): String
5 | fun getBoolean(key: String, default: Boolean): Boolean
6 | }
--------------------------------------------------------------------------------
/base/src/main/java/net/imknown/android/forefrontinfo/base/property/PropertyManager.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.base.property
2 |
3 | class PropertyManager(property: IProperty) : IProperty by property {
4 | companion object {
5 | lateinit var instance: PropertyManager
6 | }
7 | }
--------------------------------------------------------------------------------
/base/src/main/java/net/imknown/android/forefrontinfo/base/property/impl/DefaultProperty.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.base.property.impl
2 |
3 | import android.annotation.SuppressLint
4 | import net.imknown.android.forefrontinfo.base.property.IProperty
5 |
6 | @SuppressLint("PrivateApi")
7 | object DefaultProperty : IProperty {
8 | private val systemPropertiesClass = Class.forName("android.os.SystemProperties")
9 |
10 | override fun getString(key: String, default: String): String =
11 | systemPropertiesClass.getDeclaredMethod(
12 | "get", String::class.java, String::class.java
13 | ).invoke(null, key, default) as String
14 |
15 | override fun getBoolean(key: String, default: Boolean): Boolean =
16 | systemPropertiesClass.getDeclaredMethod(
17 | "getBoolean", String::class.java, Boolean::class.java
18 | ).invoke(null, key, default) as Boolean
19 | }
--------------------------------------------------------------------------------
/base/src/main/java/net/imknown/android/forefrontinfo/base/property/impl/ShizukuProperty.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.base.property.impl
2 |
3 | //object ShizukuProperty : IProperty {
4 | // override fun getString(key: String, default: String): String =
5 | // ShizukuSystemProperties.get(key, default)
6 | //
7 | // override fun getBoolean(key: String, default: Boolean): Boolean =
8 | // ShizukuSystemProperties.getBoolean(key, default)
9 | //}
--------------------------------------------------------------------------------
/base/src/main/java/net/imknown/android/forefrontinfo/base/shell/IShell.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.base.shell
2 |
3 | interface IShell {
4 | fun execute(cmd: String): ShellResult
5 | }
--------------------------------------------------------------------------------
/base/src/main/java/net/imknown/android/forefrontinfo/base/shell/ShellManager.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.base.shell
2 |
3 | class ShellManager(shell: IShell) : IShell by shell {
4 | companion object {
5 | lateinit var instance: ShellManager
6 | }
7 | }
--------------------------------------------------------------------------------
/base/src/main/java/net/imknown/android/forefrontinfo/base/shell/ShellResult.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.base.shell
2 |
3 | class ShellResult(
4 | /** Stdout + Stderr */
5 | val output: List = emptyList(),
6 | val isSuccess: Boolean = false,
7 | val exitCode: Int? = null
8 | )
--------------------------------------------------------------------------------
/base/src/main/java/net/imknown/android/forefrontinfo/base/shell/impl/DefaultShell.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.base.shell.impl
2 |
3 | import net.imknown.android.forefrontinfo.base.shell.IShell
4 | import net.imknown.android.forefrontinfo.base.shell.ShellResult
5 |
6 | object DefaultShell : IShell {
7 | override fun execute(cmd: String): ShellResult {
8 | val output = mutableListOf()
9 | var exitCode: Int? = null
10 | var isSuccess = false
11 |
12 | try {
13 | val process = Runtime.getRuntime().exec(arrayOf("sh", "-c", "$cmd\nexit"))
14 | process.waitFor()
15 |
16 | exitCode = process.exitValue()
17 | isSuccess = (exitCode == 0)
18 |
19 | val stream = with(process) { if (isSuccess) inputStream else errorStream }
20 | output += stream.bufferedReader().readLines()
21 | } catch (e: Exception) {
22 | output += (e.message ?: "")
23 | }
24 |
25 | return ShellResult(output, isSuccess, exitCode)
26 | }
27 | }
--------------------------------------------------------------------------------
/base/src/main/java/net/imknown/android/forefrontinfo/base/shell/impl/LibSuShell.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.base.shell.impl
2 |
3 | import com.topjohnwu.superuser.Shell
4 | import net.imknown.android.forefrontinfo.base.shell.IShell
5 | import net.imknown.android.forefrontinfo.base.shell.ShellResult
6 |
7 | object LibSuShell : IShell {
8 | override fun execute(cmd: String): ShellResult {
9 | val result = Shell.cmd(cmd).exec()
10 | return ShellResult(result.out, result.isSuccess, result.code)
11 | }
12 | }
--------------------------------------------------------------------------------
/base/src/test/java/net/imknown/android/forefrontinfo/base/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.base
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | /**
7 | * Example local unit test, which will execute on the development machine (host).
8 | *
9 | * See [testing documentation](http://d.android.com/tools/testing).
10 | */
11 | class ExampleUnitTest {
12 | @Test
13 | fun addition_isCorrect() {
14 | assertEquals(4, 2 + 2)
15 | }
16 | }
--------------------------------------------------------------------------------
/binderDetector/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import kotlin.collections.plusAssign
2 |
3 | plugins {
4 | alias(libsAndroid.plugins.android.library)
5 |
6 | alias(libsKotlin.plugins.kotlin.android)
7 | }
8 |
9 | android {
10 | namespace = "net.imknown.android.forefrontinfo.binderDetector"
11 |
12 | compileSdk = libsBuild.versions.compileSdk.get().toInt()
13 | // compileSdkExtension = libsBuild.versions.compileSdkExtension.get().toInt()
14 | buildToolsVersion = libsBuild.versions.buildTools.get()
15 | val isPreview = libsBuild.versions.isPreview.get().toBoolean()
16 | if (isPreview) {
17 | compileSdkPreview = libsBuild.versions.compileSdkPreview.get()
18 | buildToolsVersion = libsBuild.versions.buildToolsPreview.get()
19 | }
20 |
21 | defaultConfig {
22 | minSdk = libsBuild.versions.minSdk.get().toInt()
23 |
24 | externalNativeBuild {
25 | cmake {
26 | arguments += listOf("-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang")
27 |
28 | cFlags += listOf("-D__STDC_FORMAT_MACROS")
29 |
30 | cppFlags += listOf("-fexceptions", "-frtti", "-std=c++17")
31 | }
32 | }
33 | }
34 |
35 | ndkVersion = libsBuild.versions.ndk.get()
36 |
37 | externalNativeBuild {
38 | cmake {
39 | path("src/main/cpp/CMakeLists.txt")
40 | version = libsBuild.versions.cmake.get()
41 | }
42 | }
43 | }
44 |
45 | // region [Toolchain]
46 | // https://developer.android.com/build/jdks
47 | // https://kotlinlang.org/docs/gradle-configure-project.html
48 | // https://docs.gradle.org/current/userguide/toolchains.html
49 | private val javaToolchain = libsBuild.versions.javaToolchain.get().toInt()
50 | kotlin {
51 | jvmToolchain(javaToolchain)
52 | }
53 | // endregion [Toolchain]
--------------------------------------------------------------------------------
/binderDetector/src/main/cpp/BinderDetector.cpp:
--------------------------------------------------------------------------------
1 | // region [Comments]
2 | // region [Read file, but root needed]
3 | // https://android.googlesource.com/platform/test/vts/+/master/utils/python/controllers/android_device.py#726
4 | // https://android.googlesource.com/platform/test/vts-testcase/kernel/+/master/api/binder/VtsKernelBinderTest.py
5 | // endregion [Read file, but root needed]
6 |
7 | // region [Native]
8 | // "/dev/binder"
9 | // "/dev/binderfs"
10 | // "/dev/binderfs/my-binder"
11 | // "/dev/binderfs/binder-control"
12 | // "/dev/hwbinder"
13 | // "/dev/vndbinder"
14 | //
15 | // https://android.googlesource.com/platform/frameworks/native/+/master/libs/binder/ProcessState.cpp#349
16 | // https://android.googlesource.com/platform/system/libhwbinder/+/master/ProcessState.cpp#406
17 | // https://android.googlesource.com/platform/frameworks/native/+/master/libs/binder/tests/binderDriverInterfaceTest.cpp
18 | // https://android.googlesource.com/platform/external/linux-kselftest/+/master/tools/testing/selftests/filesystems/binderfs/binderfs_test.c#135
19 | //
20 | // https://android.googlesource.com/platform/external/kernel-headers/+/master/original/uapi/linux/android/binder.h#102
21 | // https://android.googlesource.com/platform/bionic/+/master/libc/kernel/uapi/linux/android/binder.h#98
22 | //
23 | // https://android.googlesource.com/platform/external/kernel-headers/+/master/original/uapi/linux/android/binder.h#114
24 | // endregion [Native]
25 |
26 | // region [Doc]
27 | // https://source.android.com/setup/build/gsi#legacy-gsi-build-targets
28 | // https://source.android.com/setup/build/gsi#gsi-build-targets
29 | // endregion [Doc]
30 | // endregion [Comments]
31 |
32 | #include "BinderDetector.h"
33 |
34 | void release(JNIEnv *env, jstring driver,
35 | const char *driverChars, int fd) {
36 | env->ReleaseStringUTFChars(driver, driverChars);
37 | close(fd);
38 | }
39 |
40 | int getErrorNo(JNIEnv *env, jstring driver,
41 | int ret, const char *function,
42 | const char *driverChars, int fd) {
43 | __android_log_print(
44 | ANDROID_LOG_WARN,
45 | "BinderDetector",
46 | "%s: Driver: %s, ret: %d, errorNo: %d, error: %s",
47 | function, driverChars, ret, errno, strerror(errno)
48 | );
49 |
50 | int errorNo = -abs(errno);
51 |
52 | release(env, driver, driverChars, fd);
53 |
54 | return errorNo;
55 | }
56 |
57 | extern "C" JNIEXPORT jint JNICALL
58 | Java_net_imknown_android_forefrontinfo_binderdetector_BinderDetector_getBinderVersion(
59 | JNIEnv *env,
60 | jobject instance,
61 | jstring driver
62 | ) {
63 | const char *driverChars = env->GetStringUTFChars(driver, nullptr);
64 | int fd = open(driverChars, O_RDONLY | O_CLOEXEC);
65 | if (fd < 0) {
66 | return getErrorNo(env, driver, fd, "open", driverChars, fd);
67 | }
68 |
69 | int version = _IOWR('b', 9, struct binder_version);
70 | int binderVersion = -1;
71 | int ioctlRet = ioctl(fd, version, &binderVersion);
72 | if (ioctlRet < 0) {
73 | return getErrorNo(env, driver, ioctlRet, "ioctl", driverChars, fd);
74 | }
75 |
76 | release(env, driver, driverChars, fd);
77 |
78 | return binderVersion;
79 | }
--------------------------------------------------------------------------------
/binderDetector/src/main/cpp/BinderDetector.h:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | struct binder_version {
11 | __s32 protocol_version;
12 | };
--------------------------------------------------------------------------------
/binderDetector/src/main/cpp/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # For more information about using CMake with Android Studio, read the
2 | # documentation: https://d.android.com/studio/projects/add-native-code.html
3 |
4 | # Sets the minimum version of CMake required to build the native library.
5 | cmake_minimum_required(VERSION 3.10)
6 |
7 | project(BinderDetector)
8 |
9 | set(nativeLibName "BinderDetector")
10 |
11 | # Creates and names a library, sets it as either STATIC
12 | # or SHARED, and provides the relative paths to its source code.
13 | # You can define multiple libraries, and CMake builds them for you.
14 | # Gradle automatically packages shared libraries with your APK.
15 | add_library(
16 | # Sets the name of the library.
17 | ${nativeLibName}
18 | # Sets the library as a shared library.
19 | SHARED
20 | # Provides a relative path to your source file(s).
21 | BinderDetector.cpp
22 | )
23 |
24 | # Searches for a specified prebuilt library and stores the path as a
25 | # variable. Because CMake includes system libraries in the search path by
26 | # default, you only need to specify the name of the public NDK library
27 | # you want to add. CMake verifies that the library exists before
28 | # completing its build.
29 | find_library(
30 | # Sets the name of the path variable.
31 | log-lib
32 | # Specifies the name of the NDK library that
33 | # you want CMake to locate.
34 | log
35 | )
36 |
37 | # Specifies libraries CMake should link to your target library. You
38 | # can link multiple libraries, such as libraries you define in this
39 | # build script, prebuilt third-party libraries, or system libraries.
40 | target_link_libraries(
41 | # Specifies the target library.
42 | ${nativeLibName}
43 | # Links the target library to the log library
44 | # included in the NDK.
45 | ${log-lib}
46 | )
--------------------------------------------------------------------------------
/binderDetector/src/main/kotlin/net/imknown/android/forefrontinfo/binderdetector/BinderDetector.kt:
--------------------------------------------------------------------------------
1 | package net.imknown.android.forefrontinfo.binderdetector
2 |
3 | object BinderDetector {
4 | const val PREFIX = "BinderDetector"
5 |
6 | external fun getBinderVersion(driver: String): Int
7 | }
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | alias(libsAndroid.plugins.android.application) apply false
4 | alias(libsAndroid.plugins.android.library) apply false
5 |
6 | alias(libsKotlin.plugins.kotlin.android) apply false
7 | alias(libsKotlin.plugins.kotlinx.serialization) apply false
8 |
9 | alias(libsGoogle.plugins.googleServices) apply false
10 | alias(libsGoogle.plugins.firebase.crashlytics) apply false
11 | }
--------------------------------------------------------------------------------
/buildScriptConfig.gradle:
--------------------------------------------------------------------------------
1 | // See: https://github.com/imknown/AndroidLowLevelDetector/blob/master/gradle/toml
--------------------------------------------------------------------------------
/buildSrc/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | `kotlin-dsl`
3 | }
4 |
5 | repositories {
6 | gradlePluginPortal()
7 | }
--------------------------------------------------------------------------------
/buildSrc/src/main/java/Ext.kt:
--------------------------------------------------------------------------------
1 | import org.gradle.api.provider.ProviderFactory
2 | import java.time.Instant
3 | import java.time.ZoneId
4 | import java.time.format.DateTimeFormatter
5 |
6 | fun getCurrentDatetime(): String {
7 | val pattern = "yyyyMMdd-HHmm"
8 | val formatter = DateTimeFormatter.ofPattern(pattern)
9 | val instant = Instant.now()
10 | val datetime = instant.atZone(ZoneId.systemDefault())
11 | return formatter.format(datetime)
12 | }
13 |
14 | fun ProviderFactory.execute(vararg args: Any) =
15 | exec { commandLine(*args) }.standardOutput.asText.get().trim()
--------------------------------------------------------------------------------
/buildSrc/src/main/java/Misc.kt:
--------------------------------------------------------------------------------
1 | enum class IssueTracker {
2 | Foss, Firebase
3 | }
--------------------------------------------------------------------------------
/buildSrc/src/main/java/Versions.kt:
--------------------------------------------------------------------------------
1 | // See: https://github.com/imknown/AndroidLowLevelDetector/blob/master/gradle/toml
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | #
6 | # For more details on how to configure your build environment visit
7 | # http://www.gradle.org/docs/current/userguide/build_environment.html
8 | #
9 | # region [Gradle]
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1024M -Dkotlin.daemon.jvm.options\="-Xmx1024M" -XX\:+HeapDumpOnOutOfMemoryError -Dfile.encoding\=UTF-8 -XX\:+UseParallelGC -XX\:MaxMetaspaceSize\=512m
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
18 | #
19 | org.gradle.caching=true
20 | org.gradle.configureondemand=true
21 | #
22 | org.gradle.configuration-cache=true
23 | org.gradle.configuration-cache.parallel=true
24 | org.gradle.configuration-cache.integrity-check=true
25 | #
26 | org.gradle.warning.mode=all
27 | # endregion [Gradle]
28 | #
29 | # region [Android]
30 | # AndroidX package structure to make it clearer which packages are bundled with the
31 | # Android operating system, and which are packaged with your app's APK
32 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
33 | android.useAndroidX=true
34 | # Automatically convert third-party libraries to use AndroidX
35 | # android.enableJetifier=true
36 | #
37 | android.enableBuildConfigAsBytecode=true
38 | #
39 | android.debug.obsoleteApi=true
40 | # endregion [Android]
41 | #
42 | # region [Kotlin]
43 | # Kotlin code style for this project: "official" or "obsolete":
44 | kotlin.code.style=official
45 | # endregion [Kotlin]
--------------------------------------------------------------------------------
/gradle/toml/android.toml:
--------------------------------------------------------------------------------
1 | # https://maven.google.com
2 | [versions]
3 | # https://developer.android.com/build/releases/gradle-plugin
4 | # https://developer.android.com/studio/releases
5 | # https://androidstudio.googleblog.com/
6 | # https://maven.google.com/web/index.html#com.android.tools.build:gradle
7 | androidGradlePlugin = "8.11.0-rc01"
8 | androidGradlePlugin-beta = "8.11.0-rc01"
9 | androidGradlePlugin-canary = "8.12.0-alpha03"
10 |
11 | # https://developer.android.com/studio/write/java8-support#library-desugaring
12 | # https://maven.google.com/web/index.html?q=desugar_jdk_libs
13 | # https://github.com/google/desugar_jdk_libs/blob/master/CHANGELOG.md
14 | desugarJdkLibs = "2.1.5"
15 |
16 | # region [AndroidX]
17 | # https://developer.android.com/jetpack/androidx/releases/activity
18 | activity = "1.11.0-rc01"
19 |
20 | # https://developer.android.com/jetpack/androidx/releases/annotation
21 | annotation = "1.9.1"
22 |
23 | annotation-experimental = "1.5.0"
24 |
25 | # https://developer.android.com/jetpack/androidx/releases/appcompat
26 | appcompat = "1.7.1"
27 |
28 | # https://developer.android.com/jetpack/androidx/releases/arch-core
29 | arch-core = "2.2.0"
30 |
31 | # https://developer.android.com/jetpack/androidx/releases/cardview
32 | cardView = "1.0.0"
33 |
34 | # https://developer.android.com/jetpack/androidx/releases/constraintlayout
35 | constraintLayout = "2.2.1"
36 |
37 | # https://developer.android.com/jetpack/androidx/releases/coordinatorlayout
38 | coordinatorLayout = "1.3.0"
39 |
40 | # https://developer.android.com/jetpack/androidx/releases/core
41 | core = "1.16.0"
42 |
43 | # https://developer.android.com/jetpack/androidx/releases/fragment
44 | fragment = "1.8.8"
45 |
46 | # https://developer.android.com/jetpack/androidx/releases/lifecycle
47 | lifecycle = "2.9.1"
48 |
49 | # https://developer.android.com/jetpack/androidx/releases/preference
50 | preference = "1.2.1"
51 |
52 | # https://developer.android.com/jetpack/androidx/releases/recyclerview
53 | recyclerView = "1.4.0"
54 |
55 | # https://developer.android.com/jetpack/androidx/releases/savedstate
56 | savedState = "1.3.0"
57 |
58 | # https://developer.android.com/jetpack/androidx/releases/swiperefreshlayout
59 | swipeRefreshLayout = "1.1.0"
60 |
61 | # https://developer.android.com/jetpack/androidx/releases/webkit
62 | webkit = "1.14.0"
63 |
64 | # https://developer.android.com/jetpack/androidx/releases/test
65 | # region [Test]
66 | test-core = "1.6.1"
67 | test-espresso = "3.6.1"
68 | test-ext-junit = "1.2.1"
69 | # endregion [Test]
70 | # endregion [AndroidX]
71 |
72 | [libraries]
73 | desugarJdkLibs = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "desugarJdkLibs" }
74 |
75 | activity-ktx = { group = "androidx.activity", name = "activity-ktx", version.ref = "activity" }
76 |
77 | annotation = { group = "androidx.annotation", name = "annotation", version.ref = "annotation" }
78 | annotation-experimental = { group = "androidx.annotation", name = "annotation-experimental", version.ref = "annotation-experimental" }
79 |
80 | appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
81 | appcompat-resources = { group = "androidx.appcompat", name = "appcompat-resources", version.ref = "appcompat" }
82 |
83 | arch-core-common = { group = "androidx.arch.core", name = "core-common", version.ref = "arch-core" }
84 | arch-core-runtime = { group = "androidx.arch.core", name = "core-runtime", version.ref = "arch-core" }
85 |
86 | cardView = { group = "androidx.cardview", name = "cardview", version.ref = "cardView" }
87 |
88 | constraintLayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintLayout" }
89 |
90 | coordinatorLayout = { group = "androidx.coordinatorlayout", name = "coordinatorlayout", version.ref = "coordinatorLayout" }
91 |
92 | core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core" }
93 |
94 | fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "fragment" }
95 |
96 | lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycle" }
97 | lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycle" }
98 | lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle" }
99 | lifecycle-viewmodel-savedstate = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-savedstate", version.ref = "lifecycle" }
100 |
101 | preference-ktx = { group = "androidx.preference", name = "preference-ktx", version.ref = "preference" }
102 |
103 | recyclerView = { group = "androidx.recyclerview", name = "recyclerview", version.ref = "recyclerView" }
104 |
105 | savedState-ktx = { group = "androidx.savedstate", name = "savedstate-ktx", version.ref = "savedState" }
106 |
107 | swipeRefreshLayout = { group = "androidx.swiperefreshlayout", name = "swiperefreshlayout", version.ref = "swipeRefreshLayout" }
108 |
109 | webkit = { group = "androidx.webkit", name = "webkit", version.ref = "webkit" }
110 |
111 | test-core-ktx = { group = "androidx.test", name = "core-ktx", version.ref = "test-core" }
112 | test-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "test-espresso" }
113 | test-ext-junit-ktx = { group = "androidx.test.ext", name = "junit-ktx", version.ref = "test-ext-junit" }
114 |
115 | [bundles]
116 | annotation = ["annotation", "annotation-experimental"]
117 | appcompat = ["appcompat", "appcompat-resources"]
118 | arch-core = ["arch-core-common", "arch-core-runtime"]
119 | lifecycle = ["lifecycle-viewmodel-ktx", "lifecycle-livedata-ktx", "lifecycle-runtime-ktx", "lifecycle-viewmodel-savedstate"]
120 |
121 | test = ["test-core-ktx", "test-espresso-core", "test-ext-junit-ktx"]
122 |
123 | [plugins]
124 | android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
125 | android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" }
126 |
--------------------------------------------------------------------------------
/gradle/toml/build.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | isPreview = "false" # true
3 |
4 | # https://developer.android.com/studio/releases/build-tools.html#notes
5 | buildTools = "36.0.0"
6 | buildToolsPreview = "36.0.0"
7 |
8 | minSdk = "21"
9 |
10 | compileSdk = "36"
11 | compileSdkPreview = "C"
12 |
13 | compileSdkExtension = "17" # 36-ext17 (Android 16 Beta 3)
14 |
15 | targetSdk = "36"
16 | targetSdkPreview = "C"
17 |
18 | # https://github.com/android/ndk/releases
19 | ndk = "29.0.13113456 rc1"
20 | # https://cmake.org/cmake/help/latest/release/index.html
21 | cmake = "4.0.2"
22 |
23 | versionCode = "65"
24 | versionName = "1.17.3_unpublished"
25 |
26 | javaToolchain = "21"
27 |
28 | [libraries]
29 |
30 | [bundles]
31 |
32 | [plugins]
--------------------------------------------------------------------------------
/gradle/toml/google.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | # https://developers.google.com/android/guides/releases
3 | # https://firebase.google.com/support/release-notes/android
4 | # https://firebase.google.com/docs/android/setup#available-libraries
5 | googleServices-gradlePlugin = "4.4.2"
6 |
7 | # https://firebase.google.com/docs/android/learn-more#bom
8 | firebase-bom = "33.15.0"
9 |
10 | # https://firebase.google.com/docs/crashlytics/get-started?platform=android
11 | # https://firebase.google.com/docs/crashlytics/ndk-reports
12 | firebase-crashlytics-gradlePlugin = "3.0.3"
13 |
14 | # https://github.com/material-components/material-components-android/releases
15 | material = "1.12.0"
16 |
17 | [libraries]
18 | firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebase-bom" }
19 | firebase-analytics = { group = "com.google.firebase", name = "firebase-analytics" }
20 | firebase-crashlytics-ndk = { group = "com.google.firebase", name = "firebase-crashlytics-ndk" }
21 |
22 | material = { group = "com.google.android.material", name = "material", version.ref = "material" }
23 |
24 | [bundles]
25 | firebase = ["firebase-analytics", "firebase-crashlytics-ndk"]
26 |
27 | [plugins]
28 | googleServices = { id = "com.google.gms.google-services", version.ref = "googleServices-gradlePlugin" }
29 | firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebase-crashlytics-gradlePlugin" }
30 |
--------------------------------------------------------------------------------
/gradle/toml/kotlin.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | # https://github.com/JetBrains/kotlin/releases
3 | kotlin = "2.2.0-RC2"
4 |
5 | # https://github.com/Kotlin/kotlinx.coroutines/releases
6 | kotlinx-coroutines = "1.10.2"
7 |
8 | # https://github.com/Kotlin/kotlinx.serialization/releases
9 | kotlinx-serialization = "1.8.1"
10 |
11 | # https://github.com/ktorio/ktor/releases
12 | ktor = "3.1.3"
13 |
14 | [libraries]
15 | kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }
16 | kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
17 |
18 | ktor-client-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" }
19 | ktor-client-okhttp = { group = "io.ktor", name = "ktor-client-okhttp", version.ref = "ktor" }
20 | ktor-client-logging = { group = "io.ktor", name = "ktor-client-logging", version.ref = "ktor" }
21 |
22 | [bundles]
23 | ktor-client = ["ktor-client-core", "ktor-client-okhttp", "ktor-client-logging"]
24 |
25 | [plugins]
26 | kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
27 |
28 | kotlinx-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
29 |
--------------------------------------------------------------------------------
/gradle/toml/thirdParty.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | # https://github.com/topjohnwu/libsu/releases
3 | libsu = "6.0.0"
4 |
5 | # https://github.com/square/leakcanary/releases
6 | leakCanary = "2.14"
7 |
8 | # https://github.com/G00fY2/version-compare/releases
9 | versionCompare = "1.5.0"
10 |
11 | # https://github.com/RikkaApps/Shizuku-API#add-dependency
12 | shizuku = "13.1.5"
13 |
14 | [libraries]
15 | libsu = { group = "com.github.topjohnwu.libsu", name = "core", version.ref = "libsu" }
16 |
17 | leakCanary-android = { group = "com.squareup.leakcanary", name = "leakcanary-android", version.ref = "leakCanary" }
18 | leakCanary-plumber-android = { group = "com.squareup.leakcanary", name = "plumber-android", version.ref = "leakCanary" }
19 |
20 | versionCompare = { group = "io.github.g00fy2", name = "versioncompare", version.ref = "versionCompare" }
21 |
22 | shizuku-api = { group = "dev.rikka.shizuku", name = "api", version.ref = "shizuku" }
23 | shizuku-provider = { group = "dev.rikka.shizuku", name = "provider", version.ref = "shizuku" }
24 |
25 | [bundles]
26 | leakCanary = ["leakCanary-android"] # "leakCanary-plumber-android"
27 |
28 | shizuku = ["shizuku-api", "shizuku-provider"]
29 |
30 | [plugins]
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imknown/AndroidLowLevelDetector/9b9ffa12d7324fd953cb7eaf34d1b9d66d1e432e/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | # https://services.gradle.org/distributions/
2 | # https://docs.gradle.org/current/userguide/compatibility.html
3 | # https://gradle.org/release-checksums/
4 | #
5 | # ./gradlew wrapper --gradle-version 8.14.1 --distribution-type all \
6 | # --gradle-distribution-sha256-sum d7042b3c11565c192041fc8c4703f541b888286404b4f267138c1d094d8ecdca
7 | #
8 | distributionBase=GRADLE_USER_HOME
9 | distributionPath=wrapper/dists
10 | distributionSha256Sum=d7042b3c11565c192041fc8c4703f541b888286404b4f267138c1d094d8ecdca
11 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-all.zip
12 | networkTimeout=10000
13 | validateDistributionUrl=true
14 | zipStoreBase=GRADLE_USER_HOME
15 | zipStorePath=wrapper/dists
16 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 | set CLASSPATH=
74 |
75 |
76 | @rem Execute Gradle
77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
78 |
79 | :end
80 | @rem End local scope for the variables with windows NT shell
81 | if %ERRORLEVEL% equ 0 goto mainEnd
82 |
83 | :fail
84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
85 | rem the _cmd.exe /c_ return code!
86 | set EXIT_CODE=%ERRORLEVEL%
87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
89 | exit /b %EXIT_CODE%
90 |
91 | :mainEnd
92 | if "%OS%"=="Windows_NT" endlocal
93 |
94 | :omega
95 |
--------------------------------------------------------------------------------
/keys/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imknown/AndroidLowLevelDetector/9b9ffa12d7324fd953cb7eaf34d1b9d66d1e432e/keys/debug.keystore
--------------------------------------------------------------------------------
/metadata/en-US/changelogs/62.txt:
--------------------------------------------------------------------------------
1 | * Initial F-Droid release
2 | * New translations
3 | * Database updates
4 | * Updated dependencies
5 |
--------------------------------------------------------------------------------
/metadata/en-US/full_description.txt:
--------------------------------------------------------------------------------
1 | Features:
2 | * Detect Android version
3 | * Detect Android Build Id version
4 | * Detect Android security patch level
5 | * Detect Vendor security patch level
6 | * Detect Project Mainline module version (Google Play system update)
7 | * Detect Linux kernel
8 | * Detect A/B or A-Only
9 | * Detect Dynamic Partitions
10 | * Detect Dynamic System Update(DSU)
11 | * Detect Project Treble
12 | * Detect GSI compatibility
13 | * Detect Binder bitness
14 | * Detect Process/VM architecture
15 | * Detect Vendor NDK
16 | * Detect System-as-root
17 | * Detect (flattened) APEX
18 | * Detect Toybox
19 | * Detect WebView implement
20 | * Detect outdatedTargetSdkVersion apk
21 | * Dark mode supported
22 | * Online/offline mode (fetching data from remote server or local)
23 | * MultiWindow/FreeForm/Foldable/Landscape supported
24 |
--------------------------------------------------------------------------------
/metadata/en-US/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imknown/AndroidLowLevelDetector/9b9ffa12d7324fd953cb7eaf34d1b9d66d1e432e/metadata/en-US/images/icon.png
--------------------------------------------------------------------------------
/metadata/en-US/images/phoneScreenshots/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imknown/AndroidLowLevelDetector/9b9ffa12d7324fd953cb7eaf34d1b9d66d1e432e/metadata/en-US/images/phoneScreenshots/1.png
--------------------------------------------------------------------------------
/metadata/en-US/images/phoneScreenshots/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imknown/AndroidLowLevelDetector/9b9ffa12d7324fd953cb7eaf34d1b9d66d1e432e/metadata/en-US/images/phoneScreenshots/2.png
--------------------------------------------------------------------------------
/metadata/en-US/short_description.txt:
--------------------------------------------------------------------------------
1 | Detect Treble, GSI, Mainline, APEX, system-as-root(SAR), A/B, etc.
2 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.gradle.kotlin.dsl.support.uppercaseFirstChar
2 |
3 | pluginManagement {
4 | repositories {
5 | google {
6 | content {
7 | includeGroupByRegex("com\\.android.*")
8 | includeGroupByRegex("com\\.google.*")
9 | includeGroupByRegex("androidx.*")
10 | }
11 | }
12 | mavenCentral()
13 | gradlePluginPortal()
14 | }
15 | }
16 |
17 | dependencyResolutionManagement {
18 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
19 | repositories {
20 | google()
21 | mavenCentral()
22 | maven("https://jitpack.io")
23 | }
24 |
25 | versionCatalogs {
26 | fun String.toToml() {
27 | val originalName = this
28 | val libName = "libs${originalName.uppercaseFirstChar()}"
29 | create(libName) {
30 | from(files("gradle/toml/$originalName.toml"))
31 | }
32 | }
33 |
34 | "build".toToml()
35 | "android".toToml()
36 | "kotlin".toToml()
37 | "google".toToml()
38 | "thirdParty".toToml()
39 | }
40 | }
41 |
42 | rootProject.name = "AndroidLowLevelDetector"
43 |
44 | include(":binderDetector")
45 | include(":base")
46 | include(":app")
47 |
48 | // region [Build Scan]
49 | // https://gradle.com/scans/gradle/
50 | // https://docs.gradle.com/develocity/gradle-plugin/current/
51 | plugins {
52 | // https://plugins.gradle.org/plugin/com.gradle.develocity
53 | id("com.gradle.develocity") version "4.0.2"
54 | }
55 |
56 | develocity {
57 | buildScan {
58 | termsOfUseUrl = "https://gradle.com/help/legal-terms-of-use"
59 | termsOfUseAgree = "yes"
60 |
61 | publishing.onlyIf { false }
62 | }
63 | }
64 | // endregion [Build Scan]
--------------------------------------------------------------------------------