first
15
.gitignore
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
/local.properties
|
||||||
|
/.idea/caches
|
||||||
|
/.idea/libraries
|
||||||
|
/.idea/modules.xml
|
||||||
|
/.idea/workspace.xml
|
||||||
|
/.idea/navEditor.xml
|
||||||
|
/.idea/assetWizardSettings.xml
|
||||||
|
.DS_Store
|
||||||
|
/build
|
||||||
|
/captures
|
||||||
|
.externalNativeBuild
|
||||||
|
.cxx
|
||||||
|
local.properties
|
||||||
5
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# GitHub Copilot persisted chat sessions
|
||||||
|
/copilot/chatSessions
|
||||||
1
.idea/.name
generated
Normal file
@ -0,0 +1 @@
|
|||||||
|
MyAndriodInfo
|
||||||
6
.idea/AndroidProjectSystem.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AndroidProjectSystem">
|
||||||
|
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/compiler.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<bytecodeTargetLevel target="1.8" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
53
.idea/deploymentTargetSelector.xml
generated
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="deploymentTargetSelector">
|
||||||
|
<selectionStates>
|
||||||
|
<SelectionState runConfigName="app">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
<DropdownSelection timestamp="2025-08-04T08:42:47.697960Z">
|
||||||
|
<Target type="DEFAULT_BOOT">
|
||||||
|
<handle>
|
||||||
|
<DeviceId pluginId="LocalEmulator" identifier="path=/Users/mac/.android/avd/Pixel_3a_API_33_arm64-v8a.avd" />
|
||||||
|
</handle>
|
||||||
|
</Target>
|
||||||
|
</DropdownSelection>
|
||||||
|
<DialogSelection />
|
||||||
|
</SelectionState>
|
||||||
|
<SelectionState runConfigName="mydev">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
<DropdownSelection timestamp="2025-08-16T10:30:19.858602Z">
|
||||||
|
<Target type="DEFAULT_BOOT">
|
||||||
|
<handle>
|
||||||
|
<DeviceId pluginId="PhysicalDevice" identifier="serial=RF8N10DEVMK" />
|
||||||
|
</handle>
|
||||||
|
</Target>
|
||||||
|
</DropdownSelection>
|
||||||
|
<DialogSelection />
|
||||||
|
</SelectionState>
|
||||||
|
<SelectionState runConfigName="MainActivity">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
</SelectionState>
|
||||||
|
<SelectionState runConfigName="PreviewMessageCard">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
</SelectionState>
|
||||||
|
<SelectionState runConfigName="InfoListPreview">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
</SelectionState>
|
||||||
|
<SelectionState runConfigName="myinfohook">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
</SelectionState>
|
||||||
|
<SelectionState runConfigName="app.myphoneinfo">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
</SelectionState>
|
||||||
|
<SelectionState runConfigName="myphoneinfo">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
</SelectionState>
|
||||||
|
<SelectionState runConfigName="GreetingPreview">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
</SelectionState>
|
||||||
|
<SelectionState runConfigName="MainActivity (1)">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
</SelectionState>
|
||||||
|
</selectionStates>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
13
.idea/deviceManager.xml
generated
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DeviceTable">
|
||||||
|
<option name="columnSorters">
|
||||||
|
<list>
|
||||||
|
<ColumnSorterState>
|
||||||
|
<option name="column" value="Name" />
|
||||||
|
<option name="order" value="ASCENDING" />
|
||||||
|
</ColumnSorterState>
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
26
.idea/gradle.xml
generated
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||||
|
<component name="GradleSettings">
|
||||||
|
<option name="linkedExternalProjectsSettings">
|
||||||
|
<GradleProjectSettings>
|
||||||
|
<option name="testRunner" value="CHOOSE_PER_TEST" />
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||||
|
<option name="modules">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
<option value="$PROJECT_DIR$/app" />
|
||||||
|
<option value="$PROJECT_DIR$/app/myphoneinfo" />
|
||||||
|
<option value="$PROJECT_DIR$/ksp-annotations" />
|
||||||
|
<option value="$PROJECT_DIR$/ksp-processor" />
|
||||||
|
<option value="$PROJECT_DIR$/mydev" />
|
||||||
|
<option value="$PROJECT_DIR$/myinfohook" />
|
||||||
|
<option value="$PROJECT_DIR$/myphoneinfo" />
|
||||||
|
<option value="$PROJECT_DIR$/myphoneinfo/andinfo" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</GradleProjectSettings>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
61
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="ComposePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ComposePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ComposePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ComposePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewDeviceShouldUseNewSpec" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewParameterProviderOnFirstParameter" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
||||||
6
.idea/kotlinc.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="KotlinJpsPluginSettings">
|
||||||
|
<option name="version" value="2.0.0" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
10
.idea/migrations.xml
generated
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectMigrations">
|
||||||
|
<option name="MigrateToGradleLocalJavaHome">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
16
.idea/misc.xml
generated
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<project version="4">
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectType">
|
||||||
|
<option name="id" value="Android" />
|
||||||
|
</component>
|
||||||
|
<component name="VisualizationToolProject">
|
||||||
|
<option name="state">
|
||||||
|
<ProjectState>
|
||||||
|
<option name="scale" value="0.16835016835016836" />
|
||||||
|
</ProjectState>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
17
.idea/runConfigurations.xml
generated
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RunConfigurationProducerService">
|
||||||
|
<option name="ignoredProducers">
|
||||||
|
<set>
|
||||||
|
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
1
app/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
81
app/build.gradle.kts
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
plugins {
|
||||||
|
alias(libs.plugins.android.application)
|
||||||
|
alias(libs.plugins.kotlin.android)
|
||||||
|
id("com.google.devtools.ksp") version "2.0.0-1.0.24"
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "com.xyzshell.myandriodinfo"
|
||||||
|
compileSdk = 35
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId = "com.xyzshell.myandriodinfo"
|
||||||
|
minSdk = 29
|
||||||
|
targetSdk = 35
|
||||||
|
versionCode = 1
|
||||||
|
versionName = "1.0"
|
||||||
|
|
||||||
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
ndk {
|
||||||
|
abiFilters.addAll(listOf("armeabi-v7a", "arm64-v8a"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
isMinifyEnabled = false
|
||||||
|
proguardFiles(
|
||||||
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
|
"proguard-rules.pro"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "1.8"
|
||||||
|
}
|
||||||
|
buildFeatures {
|
||||||
|
viewBinding = true
|
||||||
|
prefab = true
|
||||||
|
aidl = true
|
||||||
|
}
|
||||||
|
|
||||||
|
externalNativeBuild {
|
||||||
|
cmake {
|
||||||
|
path = file("src/main/cpp/CMakeLists.txt")
|
||||||
|
version = "3.22.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ndkVersion = "25.2.9519653"
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
implementation(libs.androidx.core.ktx)
|
||||||
|
implementation(libs.androidx.appcompat)
|
||||||
|
implementation(libs.material)
|
||||||
|
implementation(libs.androidx.constraintlayout)
|
||||||
|
implementation(libs.androidx.lifecycle.livedata.ktx)
|
||||||
|
implementation(libs.androidx.lifecycle.viewmodel.ktx)
|
||||||
|
implementation(libs.androidx.navigation.fragment.ktx)
|
||||||
|
implementation(libs.androidx.navigation.ui.ktx)
|
||||||
|
implementation(libs.kotlinx.serialization.json)
|
||||||
|
// implementation(com.bytedance.android.shadowhook)
|
||||||
|
// implementation("org.nanohttpd:nanohttpd:2.3.1")
|
||||||
|
implementation(libs.json.json)
|
||||||
|
implementation(libs.androidx.webkit)
|
||||||
|
implementation(libs.play.services.appset)
|
||||||
|
implementation(libs.play.services.ads.identifier)
|
||||||
|
implementation(libs.squareup.okhttp)
|
||||||
|
// 引入注解
|
||||||
|
implementation(project(":ksp-annotations"))
|
||||||
|
// 引入KSP处理器
|
||||||
|
ksp(project(":ksp-processor"))
|
||||||
|
|
||||||
|
testImplementation(libs.junit)
|
||||||
|
androidTestImplementation(libs.androidx.junit)
|
||||||
|
androidTestImplementation(libs.androidx.espresso.core)
|
||||||
|
}
|
||||||
21
app/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package com.xyzshell.myandriodinfo
|
||||||
|
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
import org.junit.Assert.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instrumented test, which will execute on an Android device.
|
||||||
|
*
|
||||||
|
* See [testing documentation](http://d.android.com/tools/testing).
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class ExampleInstrumentedTest {
|
||||||
|
@Test
|
||||||
|
fun useAppContext() {
|
||||||
|
// Context of the app under test.
|
||||||
|
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
|
assertEquals("com.xyzshell.myandriodinfo", appContext.packageName)
|
||||||
|
}
|
||||||
|
}
|
||||||
36
app/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
|
||||||
|
tools:ignore="ProtectedPermissions" />
|
||||||
|
<!-- AndroidManifest.xml -->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
|
<application
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/Theme.MyAndriodInfo"
|
||||||
|
android:usesCleartextTraffic="true"
|
||||||
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
|
tools:targetApi="31">
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:exported="true"
|
||||||
|
>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
40
app/src/main/cpp/CMakeLists.txt
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
|
||||||
|
# For more information about using CMake with Android Studio, read the
|
||||||
|
# documentation: https://d.android.com/studio/projects/add-native-code.html.
|
||||||
|
# For more examples on how to use CMake, see https://github.com/android/ndk-samples.
|
||||||
|
|
||||||
|
# Sets the minimum CMake version required for this project.
|
||||||
|
cmake_minimum_required(VERSION 3.22.1)
|
||||||
|
|
||||||
|
# Declares the project name. The project name can be accessed via ${ PROJECT_NAME},
|
||||||
|
# Since this is the top level CMakeLists.txt, the project name is also accessible
|
||||||
|
# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level
|
||||||
|
# build script scope).
|
||||||
|
project("myandriodinfo")
|
||||||
|
|
||||||
|
# Creates and names a library, sets it as either STATIC
|
||||||
|
# or SHARED, and provides the relative paths to its source code.
|
||||||
|
# You can define multiple libraries, and CMake builds them for you.
|
||||||
|
# Gradle automatically packages shared libraries with your APK.
|
||||||
|
#
|
||||||
|
# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define
|
||||||
|
# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME}
|
||||||
|
# is preferred for the same purpose.
|
||||||
|
#
|
||||||
|
# In order to load a library into your app from Java/Kotlin, you must call
|
||||||
|
# System.loadLibrary() and pass the name of the library defined here;
|
||||||
|
# for GameActivity/NativeActivity derived applications, the same library name must be
|
||||||
|
# used in the AndroidManifest.xml file.
|
||||||
|
add_library(${CMAKE_PROJECT_NAME} SHARED
|
||||||
|
# List C/C++ source files with relative paths to this CMakeLists.txt.
|
||||||
|
myandriodinfo.cpp)
|
||||||
|
|
||||||
|
# Specifies libraries CMake should link to your target library. You
|
||||||
|
# can link libraries from various origins, such as libraries defined in this
|
||||||
|
# build script, prebuilt third-party libraries, or Android system libraries.
|
||||||
|
target_link_libraries(${CMAKE_PROJECT_NAME}
|
||||||
|
# List libraries link to the target library
|
||||||
|
android
|
||||||
|
log)
|
||||||
|
|
||||||
|
|
||||||
17
app/src/main/cpp/myandriodinfo.cpp
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Write C++ code here.
|
||||||
|
//
|
||||||
|
// Do not forget to dynamically load the C++ library into your application.
|
||||||
|
//
|
||||||
|
// For instance,
|
||||||
|
//
|
||||||
|
// In MainActivity.java:
|
||||||
|
// static {
|
||||||
|
// System.loadLibrary("myandriodinfo");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Or, in MainActivity.kt:
|
||||||
|
// companion object {
|
||||||
|
// init {
|
||||||
|
// System.loadLibrary("myandriodinfo")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
42
app/src/main/java/com/xyzshell/myandriodinfo/MainActivity.kt
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package com.xyzshell.myandriodinfo
|
||||||
|
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.navigation.findNavController
|
||||||
|
import androidx.navigation.ui.AppBarConfiguration
|
||||||
|
import androidx.navigation.ui.setupActionBarWithNavController
|
||||||
|
import androidx.navigation.ui.setupWithNavController
|
||||||
|
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
|
|
||||||
|
import com.xyzshell.myandriodinfo.databinding.ActivityMainBinding
|
||||||
|
|
||||||
|
|
||||||
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private lateinit var binding: ActivityMainBinding
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
val navView: BottomNavigationView = binding.navView
|
||||||
|
|
||||||
|
val navController = findNavController(R.id.nav_host_fragment_activity_main)
|
||||||
|
// Passing each menu ID as a set of Ids because each
|
||||||
|
// menu should be considered as top level destinations.
|
||||||
|
val appBarConfiguration = AppBarConfiguration(
|
||||||
|
setOf(
|
||||||
|
R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications
|
||||||
|
)
|
||||||
|
)
|
||||||
|
setupActionBarWithNavController(navController, appBarConfiguration)
|
||||||
|
navView.setupWithNavController(navController)
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
package com.xyzshell.myandriodinfo.service
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import okhttp3.Call
|
||||||
|
import okhttp3.Callback
|
||||||
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.RequestBody
|
||||||
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
|
import okhttp3.Response
|
||||||
|
import okio.IOException
|
||||||
|
import org.json.JSONObject
|
||||||
|
import org.json.JSONStringer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class HttpSender {
|
||||||
|
private val mediaType = "application/json".toMediaType()
|
||||||
|
private val client = OkHttpClient()
|
||||||
|
|
||||||
|
fun post(url:String, obj:JSONObject){
|
||||||
|
val jsonString = obj.toString()
|
||||||
|
val body = jsonString.toRequestBody(mediaType)
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.post(body)
|
||||||
|
.build()
|
||||||
|
client.newCall(request).enqueue(object : Callback {
|
||||||
|
override fun onFailure(call: Call, e: IOException) {
|
||||||
|
Log.e("NetworkManager", "POST request failed", e)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResponse(call: Call, response: Response) {
|
||||||
|
val body = response.body?.string()
|
||||||
|
println(body)
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
Log.d("NetworkManager", "POST request successful: $body")
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log.e("NetworkManager", "POST request failed with code: ${response.code}")
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
package com.xyzshell.myandriodinfo.service
|
||||||
|
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
|
||||||
|
|
||||||
|
class JsonSerializer {
|
||||||
|
companion object {
|
||||||
|
val json = Json {
|
||||||
|
prettyPrint = true
|
||||||
|
ignoreUnknownKeys = true
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T> toJson(obj: T): String {
|
||||||
|
return json.encodeToString(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T> fromJson(jsonString: String): T {
|
||||||
|
return json.decodeFromString(jsonString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
package com.xyzshell.myandriodinfo.service
|
||||||
|
|
||||||
|
import com.xyzshell.ksp_annotations.AutoBuilder
|
||||||
|
|
||||||
|
@AutoBuilder
|
||||||
|
data class TestData(val name: String, val age: Int) {
|
||||||
|
}
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
package com.xyzshell.myandriodinfo.ui.dashboard
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import com.xyzshell.myandriodinfo.databinding.FragmentDashboardBinding
|
||||||
|
|
||||||
|
class DashboardFragment : Fragment() {
|
||||||
|
|
||||||
|
private var _binding: FragmentDashboardBinding? = null
|
||||||
|
|
||||||
|
// This property is only valid between onCreateView and
|
||||||
|
// onDestroyView.
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
val dashboardViewModel =
|
||||||
|
ViewModelProvider(this).get(DashboardViewModel::class.java)
|
||||||
|
|
||||||
|
_binding = FragmentDashboardBinding.inflate(inflater, container, false)
|
||||||
|
val root: View = binding.root
|
||||||
|
|
||||||
|
val textView: TextView = binding.textDashboard
|
||||||
|
dashboardViewModel.text.observe(viewLifecycleOwner) {
|
||||||
|
textView.text = it
|
||||||
|
}
|
||||||
|
return root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package com.xyzshell.myandriodinfo.ui.dashboard
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
|
||||||
|
class DashboardViewModel : ViewModel() {
|
||||||
|
|
||||||
|
private val _text = MutableLiveData<String>().apply {
|
||||||
|
value = "This is dashboard Fragment"
|
||||||
|
}
|
||||||
|
val text: LiveData<String> = _text
|
||||||
|
}
|
||||||
@ -0,0 +1,105 @@
|
|||||||
|
//package com.xyzshell.myandriodinfo.ui.home
|
||||||
|
//
|
||||||
|
//import android.content.ComponentName
|
||||||
|
//import android.content.Context
|
||||||
|
//import android.content.Intent
|
||||||
|
//import android.content.ServiceConnection
|
||||||
|
//import android.os.IBinder
|
||||||
|
//import android.os.IInterface
|
||||||
|
//import android.os.Looper
|
||||||
|
//import android.os.Parcel
|
||||||
|
//import android.os.RemoteException
|
||||||
|
//import java.util.concurrent.LinkedBlockingQueue
|
||||||
|
//
|
||||||
|
// class AdvertisingIdClient {
|
||||||
|
// @Throws(Exception::class)
|
||||||
|
// fun GetGoogleAdId(context: Context): String? {
|
||||||
|
// if (Looper.getMainLooper() == Looper.myLooper()) {
|
||||||
|
// return "Cannot call in the main thread, You must call in the other thread"
|
||||||
|
// }
|
||||||
|
// val pm = context.packageManager
|
||||||
|
// pm.getPackageInfo("com.android.vending", 0)
|
||||||
|
// val connection: AdvertisingConnection = AdvertisingConnection()
|
||||||
|
// val intent = Intent(
|
||||||
|
// "com.google.android.gms.ads.identifier.service.START"
|
||||||
|
// )
|
||||||
|
// intent.setPackage("com.google.android.gms")
|
||||||
|
// if (context.bindService(intent, connection, Context.BIND_AUTO_CREATE)) {
|
||||||
|
// try {
|
||||||
|
// val adInterface: AdvertisingInterface = AdvertisingInterface(
|
||||||
|
// connection.binder
|
||||||
|
// )
|
||||||
|
// return adInterface.id
|
||||||
|
// } finally {
|
||||||
|
// context.unbindService(connection)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return ""
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// class AdvertisingConnection : ServiceConnection {
|
||||||
|
// var retrieved: Boolean = false
|
||||||
|
// private val queue = LinkedBlockingQueue<IBinder>(1)
|
||||||
|
//
|
||||||
|
// override fun onServiceConnected(name: ComponentName, service: IBinder) {
|
||||||
|
// try {
|
||||||
|
// queue.put(service)
|
||||||
|
// } catch (localInterruptedException: InterruptedException) {
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// override fun onServiceDisconnected(name: ComponentName) {
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @get:Throws(InterruptedException::class)
|
||||||
|
// val binder: IBinder
|
||||||
|
// get() {
|
||||||
|
// check(!this.retrieved)
|
||||||
|
// this.retrieved = true
|
||||||
|
// return queue.take()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// class AdvertisingInterface(private val binder: IBinder) : IInterface {
|
||||||
|
// override fun asBinder(): IBinder {
|
||||||
|
// return binder
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @get:Throws(RemoteException::class)
|
||||||
|
// val id: String?
|
||||||
|
// get() {
|
||||||
|
// val data = Parcel.obtain()
|
||||||
|
// val reply = Parcel.obtain()
|
||||||
|
// val id: String?
|
||||||
|
// try {
|
||||||
|
// data.writeInterfaceToken("com.google.android.gms.ads.identifier.internal.IAdvertisingIdService")
|
||||||
|
// binder.transact(1, data, reply, 0)
|
||||||
|
// reply.readException()
|
||||||
|
// id = reply.readString()
|
||||||
|
// } finally {
|
||||||
|
// reply.recycle()
|
||||||
|
// data.recycle()
|
||||||
|
// }
|
||||||
|
// return id
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Throws(RemoteException::class)
|
||||||
|
// fun isLimitAdTrackingEnabled(paramBoolean: Boolean): Boolean {
|
||||||
|
// val data = Parcel.obtain()
|
||||||
|
// val reply = Parcel.obtain()
|
||||||
|
// val limitAdTracking: Boolean
|
||||||
|
// try {
|
||||||
|
// data.writeInterfaceToken("com.google.android.gms.ads.identifier.internal.IAdvertisingIdService")
|
||||||
|
// data.writeInt(if (paramBoolean) 1 else 0)
|
||||||
|
// binder.transact(2, data, reply, 0)
|
||||||
|
// reply.readException()
|
||||||
|
// limitAdTracking = 0 != reply.readInt()
|
||||||
|
// } finally {
|
||||||
|
// reply.recycle()
|
||||||
|
// data.recycle()
|
||||||
|
// }
|
||||||
|
// return limitAdTracking
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//}
|
||||||
412
app/src/main/java/com/xyzshell/myandriodinfo/ui/home/AppInfo.kt
Normal file
@ -0,0 +1,412 @@
|
|||||||
|
package com.xyzshell.myandriodinfo.ui.home
|
||||||
|
|
||||||
|
import android.adservices.AdServicesState
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.ActivityManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import android.content.pm.ApplicationInfo
|
||||||
|
import android.content.pm.ConfigurationInfo
|
||||||
|
import android.content.pm.PackageInfo
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.graphics.Rect
|
||||||
|
import android.hardware.Sensor
|
||||||
|
import android.hardware.SensorManager
|
||||||
|
import android.media.AudioManager
|
||||||
|
import android.net.ConnectivityManager
|
||||||
|
import android.net.wifi.WifiManager
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Environment
|
||||||
|
import android.os.PowerManager
|
||||||
|
import android.os.Process
|
||||||
|
import android.os.SystemClock
|
||||||
|
import android.os.ext.SdkExtensions
|
||||||
|
import android.provider.Settings
|
||||||
|
import android.telephony.TelephonyManager
|
||||||
|
import android.util.DisplayMetrics
|
||||||
|
import android.view.RoundedCorner
|
||||||
|
import android.view.WindowManager
|
||||||
|
import android.widget.ExpandableListView
|
||||||
|
import android.widget.ListView
|
||||||
|
import androidx.annotation.RequiresExtension
|
||||||
|
import com.google.android.gms.ads.identifier.AdvertisingIdClient
|
||||||
|
import com.google.android.gms.appset.AppSet
|
||||||
|
import com.google.android.gms.appset.AppSetIdClient
|
||||||
|
import com.google.android.gms.appset.AppSetIdInfo
|
||||||
|
import com.google.android.gms.tasks.Task
|
||||||
|
import java.io.File
|
||||||
|
import java.net.NetworkInterface
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
|
import java.util.TimeZone
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class AppInfo(private val context:Context, private val listView: ExpandableListView) {
|
||||||
|
|
||||||
|
private val mkv: MutableMap<String, AppInfoItem> = mutableMapOf()
|
||||||
|
|
||||||
|
private lateinit var adapter: MyListViewAdapter
|
||||||
|
private val activityManager: ActivityManager = this.context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
||||||
|
private val connectivityManager = this.context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||||
|
private val packageManager: PackageManager = this.context.packageManager
|
||||||
|
private val applicationInfo: ApplicationInfo = this.context.applicationInfo
|
||||||
|
private val audioManager:AudioManager = this.context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||||
|
private val windowManager = this.context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
||||||
|
private val telephonyManager = this.context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
|
||||||
|
private val packageInfo:PackageInfo =
|
||||||
|
this.packageManager.getPackageInfo(this.context.packageName,0)
|
||||||
|
private val powerManager = this.context.getSystemService(Context.POWER_SERVICE) as PowerManager
|
||||||
|
private val wifiManager = this.context.getSystemService(Context.WIFI_SERVICE) as WifiManager
|
||||||
|
fun initList() {
|
||||||
|
val groups = GroupInfo()
|
||||||
|
mkv.forEach {
|
||||||
|
groups.set("信息", it.key, it.value.name, it.value.desc)
|
||||||
|
}
|
||||||
|
|
||||||
|
val sp = SystemProp().getInfo()
|
||||||
|
sp.kv.forEach {
|
||||||
|
groups.set("SystemProperties", it.key, it.key, it.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
BuildProp().getInfo().kv.forEach {
|
||||||
|
groups.set("BUILD", it.key, it.key, it.value)
|
||||||
|
}
|
||||||
|
GpuInfo().getInfo()
|
||||||
|
adapter = MyListViewAdapter(this.context, groups)
|
||||||
|
this.listView.setAdapter(this.adapter)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setMkv(k: String, name: String, value: Any) {
|
||||||
|
mkv[k] = AppInfoItem(name, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setAppInfo() {
|
||||||
|
val v = packageManager.getApplicationLabel(applicationInfo)
|
||||||
|
var install_name = packageManager.getInstallerPackageName(applicationInfo.packageName)
|
||||||
|
if(install_name == null) {
|
||||||
|
install_name = ""
|
||||||
|
}
|
||||||
|
setMkv("app_name", "App Name", v)
|
||||||
|
setMkv("install_name", "install name", install_name)
|
||||||
|
this.packageInfo.versionName?.let { setMkv("app_version", "Version Name", it) }
|
||||||
|
setMkv("app_version_code", "Version Code", this.packageInfo.versionCode)
|
||||||
|
setMkv("firstInstallTime", "安装时间", this.packageInfo.firstInstallTime)
|
||||||
|
setMkv("package_name", "Package Name", applicationInfo.packageName)
|
||||||
|
setMkv("target_sdk", "Target SDK", applicationInfo.targetSdkVersion)
|
||||||
|
setMkv("idfv", "idfv", "")
|
||||||
|
setMkv("idfv_scope", "idfv scope", -1)
|
||||||
|
val client = AppSet.getClient(this.context) as AppSetIdClient
|
||||||
|
val task: Task<AppSetIdInfo> = client.appSetIdInfo as Task<AppSetIdInfo>
|
||||||
|
task.addOnSuccessListener {
|
||||||
|
// Determine current scope of app set ID.
|
||||||
|
val scope: Int = it.scope
|
||||||
|
|
||||||
|
// Read app set ID value, which uses version 4 of the
|
||||||
|
// universally unique identifier (UUID) format.
|
||||||
|
val id: String = it.id
|
||||||
|
|
||||||
|
setMkv("idfv", "idfv", id)
|
||||||
|
setMkv("idfv_scope", "idfv scope", scope)
|
||||||
|
initList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setFsLm() {
|
||||||
|
val v = File(applicationInfo.sourceDir).lastModified()
|
||||||
|
setMkv("fslm", "app安装文件时间", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setPermissionInfo() {
|
||||||
|
val ps_tpg = checkPermission("android.permission.ACCESS_ADSERVICES_TOPICS")
|
||||||
|
setMkv("ps_tpg","允许应用访问用户的兴趣话题信息", ps_tpg)
|
||||||
|
val ps_apg = checkPermission("android.permission.ACCESS_ADSERVICES_ATTRIBUTION")
|
||||||
|
setMkv("ps_apg", "广告归因追踪", ps_apg)
|
||||||
|
var ps_aipg = checkPermission("android.permission.ACCESS_ADSERVICES_AD_ID")
|
||||||
|
setMkv("ps_aipg", "广告ID访问", ps_aipg)
|
||||||
|
val ps_capg = checkPermission("android.permission.ACCESS_ADSERVICES_CUSTOM_AUDIENCE")
|
||||||
|
setMkv("ps_capg", "允许应用创建和管理自定义广告受众群体",ps_capg)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setNetworkInfo() {
|
||||||
|
setMkv("connectivity", "网络状态",
|
||||||
|
connectivityManager.restrictBackgroundStatus != ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED)
|
||||||
|
val sb = StringBuilder()
|
||||||
|
val interfaces = NetworkInterface.getNetworkInterfaces()
|
||||||
|
if (interfaces != null) {
|
||||||
|
for (intf in interfaces) {
|
||||||
|
try {
|
||||||
|
if (!intf.isUp || intf.isLoopback) continue
|
||||||
|
sb.append("name: ${intf.name}, displayName: ${intf.displayName},")
|
||||||
|
val addresses = intf.inetAddresses
|
||||||
|
for (addr in addresses) {
|
||||||
|
sb.append("address: ${addr.hostAddress}\n")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setMkv("NetworkInterface", "网络状态", sb.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setBuildInfo() {
|
||||||
|
setMkv("VERSION.SDK_INT","Build.VERSION.SDK_INT", Build.VERSION.SDK_INT)
|
||||||
|
setMkv("TAGS","Build.TAGS", Build.TAGS)
|
||||||
|
setMkv("SUPPORTED_ABIS","Build.SUPPORTED_ABIS", Build.SUPPORTED_ABIS)
|
||||||
|
setMkv("CPU_ABI","Build.CPU_ABI", Build.CPU_ABI)
|
||||||
|
setMkv("CPU_ABI2","Build.CPU_ABI2", Build.CPU_ABI2)
|
||||||
|
setMkv("MANUFACTURER","Build.MANUFACTURER",Build.MANUFACTURER)
|
||||||
|
setMkv("BRAND","Build.BRAND",Build.BRAND)
|
||||||
|
setMkv("HARDWARE","Build.HARDWARE",Build.HARDWARE)
|
||||||
|
setMkv("MODEL","Build.MODEL",Build.MODEL)
|
||||||
|
setMkv("DEVICE","Build.DEVICE",Build.DEVICE)
|
||||||
|
setMkv("Build.VERSION", "os version", Build.VERSION.RELEASE)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setFont() {
|
||||||
|
setMkv("font_scale", "Font Scale", Settings.System.getFloat(this.context.contentResolver,"font_scale"))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setLocal() {
|
||||||
|
setMkv("tz", "TimeZone",
|
||||||
|
Math.round(TimeZone.getDefault().getOffset(Date().time) * 10.0 / 3600000.0) / 10.0)
|
||||||
|
setMkv("tz_offset", "tz_offset",Math.round(
|
||||||
|
TimeZone.getDefault().getOffset(
|
||||||
|
Date().time
|
||||||
|
) * 10.0 / 3600000.0
|
||||||
|
) / 10.0)
|
||||||
|
setMkv("local","local",Locale.getDefault().toString())
|
||||||
|
val localeList = this.context.resources.configuration.locales
|
||||||
|
val stringBuilder = StringBuilder()
|
||||||
|
for (i in 0 until localeList.size()) {
|
||||||
|
val locale = localeList.get(i)
|
||||||
|
stringBuilder.append("Locale: ${locale.toString()}, Language: ${locale.language}, Country: ${locale.country}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
setMkv("local_list", "Locale List", stringBuilder.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setScreen() {
|
||||||
|
val screen_brightness = Settings.System.getInt(this.context.contentResolver, Settings.System.SCREEN_BRIGHTNESS) / 255.0F * 100.0F
|
||||||
|
setMkv("screen_brightness","屏幕亮度", screen_brightness)
|
||||||
|
setMkv("screen_orientation", "屏幕方向", this.context.resources.configuration.orientation)
|
||||||
|
|
||||||
|
val rect = Rect()
|
||||||
|
windowManager.defaultDisplay.getRectSize(rect)
|
||||||
|
val displayMetrics = DisplayMetrics()
|
||||||
|
windowManager.defaultDisplay.getMetrics(displayMetrics)
|
||||||
|
val sb = StringBuilder()
|
||||||
|
sb.append("w:${rect.width()},h:${rect.height()}\n")
|
||||||
|
sb.append("W:${displayMetrics.widthPixels},H: ${displayMetrics.heightPixels}\n")
|
||||||
|
sb.append("density:${displayMetrics.density}\n")
|
||||||
|
sb.append("densityDpi:${displayMetrics.densityDpi}\n")
|
||||||
|
sb.append("xdpi:${displayMetrics.xdpi},ydpi:${displayMetrics.ydpi}\n")
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
sb.append("tl${windowManager.defaultDisplay.getRoundedCorner(RoundedCorner.POSITION_TOP_LEFT)}\n")
|
||||||
|
sb.append("tr${windowManager.defaultDisplay.getRoundedCorner(RoundedCorner.POSITION_TOP_RIGHT)}\n")
|
||||||
|
sb.append("bl${windowManager.defaultDisplay.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT)}\n")
|
||||||
|
sb.append("br${windowManager.defaultDisplay.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT)}\n")
|
||||||
|
}
|
||||||
|
setMkv("screen", "屏幕", sb.toString())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setDisk() {
|
||||||
|
setMkv("disk_free","磁盘剩余", Environment.getDataDirectory().freeSpace)
|
||||||
|
setMkv("disk_total", "磁盘", Environment.getDataDirectory().totalSpace)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun getProcessName():String {
|
||||||
|
try {
|
||||||
|
val packageManager = context.packageManager
|
||||||
|
val applicationInfo = packageManager.getApplicationInfo(
|
||||||
|
context.packageName,
|
||||||
|
PackageManager.GET_META_DATA // 0x80
|
||||||
|
)
|
||||||
|
val metaData = applicationInfo.metaData
|
||||||
|
|
||||||
|
try {
|
||||||
|
val processName = applicationInfo.processName
|
||||||
|
return processName
|
||||||
|
} catch (e: PackageManager.NameNotFoundException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
} catch (e: PackageManager.NameNotFoundException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
fun getProcessName1():String {
|
||||||
|
val pid = Process.myPid()
|
||||||
|
val activityManager = this.context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
||||||
|
val runningManager = activityManager.runningAppProcesses
|
||||||
|
runningManager?.forEach {
|
||||||
|
if(it.pid == pid) {
|
||||||
|
return it.processName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSystem() {
|
||||||
|
setMkv("pc", "Processors", Runtime.getRuntime().availableProcessors())
|
||||||
|
setMkv("boot_ms","bt_ms", System.currentTimeMillis() - SystemClock.elapsedRealtime())
|
||||||
|
setMkv("processName1", "Process Name", getProcessName())
|
||||||
|
setMkv("processName2", "Process Name 1", getProcessName1())
|
||||||
|
val b1 = packageManager.hasSystemFeature("com.google.android.play.feature.HPE_EXPERIENCE")
|
||||||
|
val b2 = packageManager.hasSystemFeature(PackageManager.FEATURE_PC)
|
||||||
|
val kb = this.context.resources.configuration.keyboard
|
||||||
|
var is_pc = true
|
||||||
|
if(kb != Configuration.KEYBOARD_QWERTY || (!b1 && !b2)) {
|
||||||
|
is_pc = false
|
||||||
|
}
|
||||||
|
setMkv("is_pc", "is pc", is_pc)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setMem() {
|
||||||
|
val memoryInfo = ActivityManager.MemoryInfo()
|
||||||
|
activityManager.getMemoryInfo(memoryInfo)
|
||||||
|
val sb = StringBuilder()
|
||||||
|
setMkv("availMem", "可用内存", memoryInfo.availMem)
|
||||||
|
setMkv("totalMem", "总内存", memoryInfo.totalMem)
|
||||||
|
setMkv("threshold", "threshold", memoryInfo.threshold)
|
||||||
|
setMkv("lowMemory", "lowMemory", memoryInfo.lowMemory)
|
||||||
|
setMkv("GlEsVersion", "GlEsVersion", activityManager.deviceConfigurationInfo.glEsVersion)
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSensor() {
|
||||||
|
val sensorManager = this.context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
|
||||||
|
val isGYROSCOPE = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) != null
|
||||||
|
setMkv("isGYROSCOPE","陀螺仪", isGYROSCOPE)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun setBatteryInfo() {
|
||||||
|
val intentFilter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
|
||||||
|
val intent = this.context.registerReceiver(null, intentFilter)
|
||||||
|
var level = -1
|
||||||
|
var scale = -1
|
||||||
|
var adn = -1
|
||||||
|
var status = -1
|
||||||
|
if(intent!=null) {
|
||||||
|
level = intent.getIntExtra("level", -1)
|
||||||
|
setMkv("battery_level", "电池Level", level)
|
||||||
|
scale = intent.getIntExtra("scale", -1)
|
||||||
|
setMkv("battery_scale", "电池scale", level)
|
||||||
|
if(level > 0 && scale > 0) {
|
||||||
|
adn = ((level / scale * 100.0F).toInt())
|
||||||
|
setMkv("battery_adn", "电池adn", adn)
|
||||||
|
}
|
||||||
|
status = intent.getIntExtra("status", -1)
|
||||||
|
setMkv("battery_status", "电池状态", status)
|
||||||
|
}
|
||||||
|
var plugged = false
|
||||||
|
if(intent != null) {
|
||||||
|
// stay_on_while_plugged_in
|
||||||
|
val plugged1:Int = intent.getIntExtra("plugged", -1)
|
||||||
|
if(plugged1 > 0) {
|
||||||
|
plugged = true
|
||||||
|
}
|
||||||
|
setMkv("battery_plugged", "电池充电", plugged)
|
||||||
|
}
|
||||||
|
setMkv("isPowerSaveMode", "Power Save Mode", powerManager.isPowerSaveMode)
|
||||||
|
// setMkv("sowpie","插电模式:sowpie", Settings.Global.getInt(this.context.contentResolver, "stay_on_while_plugged_in", -1) > 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun setAudio() {
|
||||||
|
setMkv("MusicActive", "MusicActive", audioManager.isMusicActive)
|
||||||
|
setMkv("volume", "volume", audioManager.getStreamVolume(AudioManager.STREAM_MUSIC))
|
||||||
|
setMkv("SpeakerphoneOn","SpeakerphoneOn", audioManager.isSpeakerphoneOn)
|
||||||
|
setMkv("ringerMode", "ringerMode", audioManager.ringerMode)
|
||||||
|
val sb = StringBuilder()
|
||||||
|
val arrayOfAudioDeviceInfo = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
|
||||||
|
for (i in arrayOfAudioDeviceInfo.indices) {
|
||||||
|
val audioDeviceInfo = arrayOfAudioDeviceInfo[i]
|
||||||
|
if (audioDeviceInfo != null) {
|
||||||
|
// 3,7,8
|
||||||
|
// public static final int TYPE_BLUETOOTH_A2DP = 8;
|
||||||
|
// public static final int TYPE_BLUETOOTH_SCO = 7;
|
||||||
|
// public static final int TYPE_WIRED_HEADSET = 3;
|
||||||
|
sb.append("name: " + audioDeviceInfo.productName + ", type: " + audioDeviceInfo.type + "\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append("isWiredHeadsetOn:${audioManager.isWiredHeadsetOn()}\n")
|
||||||
|
sb.append("isBluetoothScoOn:${audioManager.isBluetoothScoOn()}\n")
|
||||||
|
sb.append("isBluetoothA2dpOn:${audioManager.isBluetoothA2dpOn()}\n")
|
||||||
|
setMkv("AudioDeviceInfo", "AudioDeviceInfo", sb.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("HardwareIds")
|
||||||
|
fun setPhoneInfo() {
|
||||||
|
setMkv("simCountryIso","Country Iso",telephonyManager.simCountryIso.toUpperCase(Locale.ENGLISH))
|
||||||
|
setMkv("NetworkOperatorName","NetworkOperatorName",telephonyManager.networkOperatorName)
|
||||||
|
setMkv("NetworkOperator","NetworkOperator",telephonyManager.networkOperator)
|
||||||
|
var manufacturerCode = ""
|
||||||
|
if (!telephonyManager.manufacturerCode.isNullOrEmpty()) {
|
||||||
|
manufacturerCode = telephonyManager.manufacturerCode.toString()
|
||||||
|
}
|
||||||
|
setMkv("ManufacturerCode","ManufacturerCode",manufacturerCode)
|
||||||
|
try {
|
||||||
|
setMkv("tel_deviceId","Telephony DeviceId",telephonyManager.getDeviceId())
|
||||||
|
}catch (e:Throwable) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkPermission(key:String):Boolean {
|
||||||
|
return this.context.packageManager.checkPermission(key,this.context.packageName) == PackageManager.PERMISSION_GRANTED
|
||||||
|
}
|
||||||
|
|
||||||
|
fun GetAppInfo() : AppInfo {
|
||||||
|
setAppInfo()
|
||||||
|
setFsLm()
|
||||||
|
setPermissionInfo()
|
||||||
|
setSystem()
|
||||||
|
setMem()
|
||||||
|
setNetworkInfo()
|
||||||
|
setBuildInfo()
|
||||||
|
setFont()
|
||||||
|
setLocal()
|
||||||
|
setScreen()
|
||||||
|
setDisk()
|
||||||
|
setPhoneInfo()
|
||||||
|
setAudio()
|
||||||
|
setBatteryInfo()
|
||||||
|
setSensor()
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES) >= 4) {
|
||||||
|
setAD_SERVICES()
|
||||||
|
}
|
||||||
|
initAdid()
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
|
||||||
|
fun setAD_SERVICES() {
|
||||||
|
val v = AdServicesState.isAdServicesStateEnabled()
|
||||||
|
setMkv("isAdServicesStateEnabled", "Android Ad Services",v )
|
||||||
|
}
|
||||||
|
|
||||||
|
fun initAdid() {
|
||||||
|
setMkv("adid", "Ad Id", "")
|
||||||
|
Executors.newSingleThreadExecutor().execute {
|
||||||
|
try {
|
||||||
|
val adid = AdvertisingIdClient(this.context).info
|
||||||
|
setMkv("adid", "Ad Id", adid.id?:"")
|
||||||
|
initList()
|
||||||
|
|
||||||
|
} catch (e: java.lang.Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
package com.xyzshell.myandriodinfo.ui.home
|
||||||
|
|
||||||
|
class AppInfoItem(val name:String, val v: Any) {
|
||||||
|
var desc: String = "属性值:${v}"
|
||||||
|
}
|
||||||
@ -0,0 +1,93 @@
|
|||||||
|
package com.xyzshell.myandriodinfo.ui.home
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import android.util.ArraySet
|
||||||
|
import com.xyzshell.myandriodinfo.service.HttpSender
|
||||||
|
|
||||||
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
class BuildProp {
|
||||||
|
|
||||||
|
val kv = mutableMapOf<String, String>()
|
||||||
|
val js_build = JSONObject()
|
||||||
|
val js_ver = JSONObject()
|
||||||
|
val js_verc = JSONObject()
|
||||||
|
|
||||||
|
private fun call(name:String, obj: Any, js: JSONObject) {
|
||||||
|
if (obj is Array<*>) {
|
||||||
|
val jarr = JSONArray()
|
||||||
|
for (item in obj) {
|
||||||
|
if (item != null) {
|
||||||
|
jarr.put(item)
|
||||||
|
} else {
|
||||||
|
jarr.put("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kv[name] = jarr.toString()
|
||||||
|
js.put(name, jarr)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
var v = ""
|
||||||
|
v = obj.toString()
|
||||||
|
kv[name] = v
|
||||||
|
|
||||||
|
if(obj is ArraySet<*>) {
|
||||||
|
val tjs = JSONObject()
|
||||||
|
tjs.put("type", obj.javaClass.name)
|
||||||
|
val jarr = JSONArray()
|
||||||
|
for (item in obj) {
|
||||||
|
if (item != null) {
|
||||||
|
jarr.put(item)
|
||||||
|
} else {
|
||||||
|
jarr.put("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tjs.put("val", jarr)
|
||||||
|
js.put(name, tjs)
|
||||||
|
} else {
|
||||||
|
js.put(name, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getInfo() : BuildProp {
|
||||||
|
val buildCls = Build::class.java
|
||||||
|
val fi = buildCls.declaredFields
|
||||||
|
for (it in fi) {
|
||||||
|
val obj = it.get(null)
|
||||||
|
call(it.name, obj, js_build)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val buildVerCLs = Build.VERSION::class.java
|
||||||
|
val vfi = buildVerCLs.declaredFields
|
||||||
|
for (it in vfi) {
|
||||||
|
val obj =it.get(null)
|
||||||
|
call(it.name, obj, js_ver)
|
||||||
|
}
|
||||||
|
|
||||||
|
val buildVerCode = Build.VERSION_CODES::class.java
|
||||||
|
val vcfi = buildVerCode.declaredFields
|
||||||
|
for (it in vcfi) {
|
||||||
|
val obj =it.get(null)
|
||||||
|
call(it.name, obj, js_verc)
|
||||||
|
}
|
||||||
|
val js = JSONObject()
|
||||||
|
js.put("BUILD", js_build)
|
||||||
|
js.put("BUILD.VERSION", js_ver)
|
||||||
|
js.put("BUILD.VERSION_CODES", js_ver)
|
||||||
|
try {
|
||||||
|
HttpSender().post("http://192.168.1.100:9999/devices/android", js)
|
||||||
|
}catch (e:Throwable) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun runOnUiThread(any: Any) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package com.xyzshell.myandriodinfo.ui.home
|
||||||
|
|
||||||
|
import android.opengl.GLES10
|
||||||
|
import android.opengl.GLES10Ext
|
||||||
|
import android.opengl.GLES20
|
||||||
|
import android.opengl.GLES30
|
||||||
|
import android.opengl.GLES32
|
||||||
|
import com.xyzshell.myandriodinfo.service.HttpSender
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
class GpuInfo {
|
||||||
|
fun getInfo() {
|
||||||
|
//GLES10.glGetString(GLES10.GL_VENDOR)
|
||||||
|
val gl_vendor = GLES20.glGetString(GLES20.GL_VENDOR)
|
||||||
|
val gl_version = GLES20.glGetString(GLES20.GL_VERSION)
|
||||||
|
val gl_renderer = GLES20.glGetString(GLES20.GL_RENDERER)
|
||||||
|
val gl_extensions = GLES20.glGetString(GLES20.GL_EXTENSIONS)
|
||||||
|
|
||||||
|
val js = JSONObject()
|
||||||
|
js.put("GL_VENDOR", gl_vendor)
|
||||||
|
js.put("GL_VERSION", gl_version)
|
||||||
|
js.put("GL_RENDERER", gl_renderer)
|
||||||
|
js.put("GL_EXTENSIONS", gl_extensions)
|
||||||
|
HttpSender().post("http://192.168.1.100:9999/devices/android", js)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
package com.xyzshell.myandriodinfo.ui.home
|
||||||
|
|
||||||
|
class GroupInfo {
|
||||||
|
private val groups: MutableMap<String,Group> = mutableMapOf()
|
||||||
|
|
||||||
|
private val groupKeys get() = groups.keys.toList()
|
||||||
|
private var nextId = 1L
|
||||||
|
|
||||||
|
val size get() = groups.size
|
||||||
|
|
||||||
|
fun getGroup(position: Int) : Group {
|
||||||
|
val key = groupKeys[position]
|
||||||
|
return groups[key]!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun set(groupName: String, key:String, name: String, value: Any) {
|
||||||
|
var group = groups[groupName]
|
||||||
|
if (group == null) {
|
||||||
|
group = Group(nextId++, groupName, "")
|
||||||
|
groups[groupName] = group
|
||||||
|
}
|
||||||
|
group.addItem(key, AppInfoItem(name, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
class Group(val id: Long, var name: String, var desc: String) {
|
||||||
|
private val items: MutableMap<String, AppInfoItem> = mutableMapOf()
|
||||||
|
private val keys get() = items.keys.toList()
|
||||||
|
|
||||||
|
val size get() = items.size
|
||||||
|
|
||||||
|
fun get(position: Int) : AppInfoItem {
|
||||||
|
val key = keys[position]
|
||||||
|
return items[key] ?: AppInfoItem("", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addItem(key: String, item: AppInfoItem) {
|
||||||
|
items[key] = item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
package com.xyzshell.myandriodinfo.ui.home
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ExpandableListView
|
||||||
|
import android.widget.ListView
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import com.xyzshell.myandriodinfo.databinding.FragmentHomeBinding
|
||||||
|
|
||||||
|
class HomeFragment : Fragment() {
|
||||||
|
|
||||||
|
private var _binding: FragmentHomeBinding? = null
|
||||||
|
|
||||||
|
// This property is only valid between onCreateView and
|
||||||
|
// onDestroyView.
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
|
||||||
|
|
||||||
|
_binding = FragmentHomeBinding.inflate(inflater, container, false)
|
||||||
|
val root: View = binding.root
|
||||||
|
|
||||||
|
val listView: ExpandableListView = binding.infoList
|
||||||
|
|
||||||
|
context?.let { it1 -> AppInfo(it1, listView).GetAppInfo() }?.initList()
|
||||||
|
|
||||||
|
return root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package com.xyzshell.myandriodinfo.ui.home
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
|
||||||
|
class HomeViewModel : ViewModel() {
|
||||||
|
|
||||||
|
private val _text = MutableLiveData<String>().apply {
|
||||||
|
value = "This is home Fragment"
|
||||||
|
}
|
||||||
|
val text: LiveData<String> = _text
|
||||||
|
}
|
||||||
@ -0,0 +1,99 @@
|
|||||||
|
package com.xyzshell.myandriodinfo.ui.home
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.BaseExpandableListAdapter
|
||||||
|
import android.widget.TextView
|
||||||
|
import com.xyzshell.myandriodinfo.R
|
||||||
|
|
||||||
|
class MyListViewAdapter(context: Context, private val data: GroupInfo) : BaseExpandableListAdapter() {
|
||||||
|
private val inflater:LayoutInflater = LayoutInflater.from(context)
|
||||||
|
|
||||||
|
|
||||||
|
class ViewHolder() {
|
||||||
|
lateinit var titleTextView: TextView
|
||||||
|
lateinit var descriptionTextView: TextView
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getGroupCount(): Int {
|
||||||
|
return data.size
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getChildrenCount(groupPosition: Int): Int {
|
||||||
|
return data.getGroup(groupPosition).size
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getGroup(groupPosition: Int): Any {
|
||||||
|
return data.getGroup(groupPosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getChild(groupPosition: Int, childPosition: Int): Any {
|
||||||
|
return data.getGroup(groupPosition).get(childPosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getGroupId(groupPosition: Int): Long {
|
||||||
|
return groupPosition.toLong()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getChildId(groupPosition: Int, childPosition: Int): Long {
|
||||||
|
return groupPosition * 1000 + childPosition.toLong()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hasStableIds(): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getGroupView(
|
||||||
|
groupPosition: Int,
|
||||||
|
isExpanded: Boolean,
|
||||||
|
convertView: View?,
|
||||||
|
parent: ViewGroup?
|
||||||
|
): View {
|
||||||
|
//1.item view
|
||||||
|
|
||||||
|
val v = inflater.inflate(
|
||||||
|
android.R.layout.simple_expandable_list_item_1, parent, false) as TextView
|
||||||
|
|
||||||
|
//2.item data
|
||||||
|
|
||||||
|
val group = this.getGroup(groupPosition) as GroupInfo.Group
|
||||||
|
|
||||||
|
//3.set item data add to item view
|
||||||
|
|
||||||
|
v.text = group.name;
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getChildView(
|
||||||
|
groupPosition: Int,
|
||||||
|
childPosition: Int,
|
||||||
|
isLastChild: Boolean,
|
||||||
|
convertView: View?,
|
||||||
|
parent: ViewGroup?
|
||||||
|
): View? {
|
||||||
|
val holder: ViewHolder
|
||||||
|
var ctv: View? = convertView
|
||||||
|
if (ctv == null) {
|
||||||
|
ctv = inflater.inflate(R.layout.list_view_item, parent, false);
|
||||||
|
holder = ViewHolder()
|
||||||
|
holder.titleTextView = ctv.findViewById(R.id.titleTextView);
|
||||||
|
holder.descriptionTextView = ctv.findViewById(R.id.descriptionTextView);
|
||||||
|
ctv.tag = holder
|
||||||
|
} else {
|
||||||
|
holder = ctv.tag as ViewHolder
|
||||||
|
}
|
||||||
|
val item: AppInfoItem = this.getChild(groupPosition, childPosition) as AppInfoItem
|
||||||
|
holder.titleTextView.text = item.name
|
||||||
|
holder.descriptionTextView.text = item.desc
|
||||||
|
|
||||||
|
return ctv;
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isChildSelectable(groupPosition: Int, childPosition: Int): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package com.xyzshell.myandriodinfo.ui.home
|
||||||
|
|
||||||
|
import android.hardware.Sensor
|
||||||
|
import android.hardware.SensorEvent
|
||||||
|
import android.hardware.SensorEventListener
|
||||||
|
|
||||||
|
class SensorEventData: SensorEventListener {
|
||||||
|
override fun onSensorChanged(p0: SensorEvent?) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAccuracyChanged(p0: Sensor?, p1: Int) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,154 @@
|
|||||||
|
package com.xyzshell.myandriodinfo.ui.home
|
||||||
|
|
||||||
|
import android.util.ArraySet
|
||||||
|
import com.xyzshell.myandriodinfo.service.HttpSender
|
||||||
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
|
||||||
|
class SystemProp {
|
||||||
|
private fun call(name:String, obj: Any, js: JSONObject) {
|
||||||
|
if (obj is Array<*>) {
|
||||||
|
val jarr = JSONArray()
|
||||||
|
for (item in obj) {
|
||||||
|
if (item != null) {
|
||||||
|
jarr.put(item)
|
||||||
|
} else {
|
||||||
|
jarr.put("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kv[name] = jarr.toString()
|
||||||
|
js.put(name, jarr)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
var v = ""
|
||||||
|
v = obj.toString()
|
||||||
|
kv[name] = v
|
||||||
|
|
||||||
|
if(obj is ArraySet<*>) {
|
||||||
|
val tjs = JSONObject()
|
||||||
|
tjs.put("type", obj.javaClass.name)
|
||||||
|
val jarr = JSONArray()
|
||||||
|
for (item in obj) {
|
||||||
|
if (item != null) {
|
||||||
|
jarr.put(item)
|
||||||
|
} else {
|
||||||
|
jarr.put("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tjs.put("val", jarr)
|
||||||
|
js.put(name, tjs)
|
||||||
|
} else {
|
||||||
|
js.put(name, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private val keys = listOf(
|
||||||
|
"ro.product.model",
|
||||||
|
"ro.product.name",
|
||||||
|
"ro.product.device",
|
||||||
|
"ro.product.board",
|
||||||
|
"ro.product.manufacturer",
|
||||||
|
"ro.bootloader",
|
||||||
|
"ro.build.product",
|
||||||
|
"ro.product.brand",
|
||||||
|
"ro.build.fingerprint",
|
||||||
|
"ro.build.description",
|
||||||
|
"gsm.version.baseband",
|
||||||
|
"ro.csc.active",
|
||||||
|
"ro.knox.warranty",
|
||||||
|
"ro.product.mfg_date",
|
||||||
|
"ro.product.code",
|
||||||
|
"drm.service.enabled",
|
||||||
|
"ro.vendor.mtk_widevine_drm_l1_support",
|
||||||
|
"ro.vendor.mtk_widevine_drm_l3_support",
|
||||||
|
"ro.serialno",
|
||||||
|
"ro.boot.serialno",
|
||||||
|
"gsf.id",
|
||||||
|
"ro.gms.version",
|
||||||
|
"ro.build.id",
|
||||||
|
"ro.build.version.release",
|
||||||
|
"ro.build.version.sdk",
|
||||||
|
"ro.build.version.security_patch",
|
||||||
|
"ro.kernel.version",
|
||||||
|
"ro.csc.country_code",
|
||||||
|
"ro.csc.sales_code",
|
||||||
|
"ro.csc.countryiso_code",
|
||||||
|
"ro.product.model.full",
|
||||||
|
"ro.product.model_code",
|
||||||
|
"ro.product.full_name",
|
||||||
|
"ril.product_code",
|
||||||
|
"ril.official_cscver",
|
||||||
|
"ro.csc.omcnw_code",
|
||||||
|
"ro.csc.omcnw_code2",
|
||||||
|
"ro.hardware",
|
||||||
|
"ro.product.cpu.abi",
|
||||||
|
"ro.product.cpu.abilist",
|
||||||
|
"ro.product.cpu.abilist64",
|
||||||
|
"ro.product.cpu.abilist32",
|
||||||
|
"ro.treble.enabled",
|
||||||
|
"ro.debuggable",
|
||||||
|
"ro.secure",
|
||||||
|
"ro.build.type",
|
||||||
|
"ro.build.tags",
|
||||||
|
"ro.build.date.utc",
|
||||||
|
"ro.sf.lcd_density",
|
||||||
|
"ro.sf.physical_density",
|
||||||
|
"vendor.display.lcd_density",
|
||||||
|
"qemu.sf.lcd_density",
|
||||||
|
"ro.display.max_brightness",
|
||||||
|
"vendor.display.brightness_max",
|
||||||
|
"ro.vendor.display.brightness_max",
|
||||||
|
"vendor.display.hdr_support",
|
||||||
|
"ro.vendor.display.hdr10_support",
|
||||||
|
"vendor.display.hdr10plus_support",
|
||||||
|
"vendor.display.hlg_support",
|
||||||
|
"vendor.display.dolby_vision_support",
|
||||||
|
"ro.surface_flinger.refresh_rate_switching",
|
||||||
|
"vendor.display.refresh_rate_switching",
|
||||||
|
"ro.vendor.display.refresh_rates",
|
||||||
|
"vendor.display.supported_refresh_rates",
|
||||||
|
"vendor.display.wide_color_support",
|
||||||
|
"ro.vendor.display.wcg_support",
|
||||||
|
"vendor.display.color_gamut",
|
||||||
|
"vendor.display.p3_support",
|
||||||
|
"ro.vendor.display.p3_support",
|
||||||
|
"ro.surface_flinger.has_wide_color_display",
|
||||||
|
"ro.surface_flinger.wcg_composition_dataspace",
|
||||||
|
"ro.surface_flinger.use_color_management",
|
||||||
|
"ro.vendor.display.peak_brightness",
|
||||||
|
"ro.product.cpu.abi",
|
||||||
|
"ro.product.cpu.abilist",
|
||||||
|
"ro.product.cpu.abilist64",
|
||||||
|
"ro.product.cpu.abilist32",
|
||||||
|
"ro.chipname",
|
||||||
|
"ro.board.platform",
|
||||||
|
"ro.hardware",
|
||||||
|
"ro.boot.ddr_type"
|
||||||
|
)
|
||||||
|
|
||||||
|
private val mkv = mutableMapOf<String, String>()
|
||||||
|
|
||||||
|
val kv get() = mkv
|
||||||
|
|
||||||
|
fun getInfo(): SystemProp {
|
||||||
|
val cls = Class.forName("android.os.SystemProperties")
|
||||||
|
val method = cls.getMethod("get", String::class.java)
|
||||||
|
val js = JSONObject()
|
||||||
|
keys.forEach { k ->
|
||||||
|
method.invoke(null, k)?.let {
|
||||||
|
call(k, it, js)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
HttpSender().post("http://192.168.1.100:9999/devices/android", js)
|
||||||
|
}catch (e:Throwable) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
package com.xyzshell.myandriodinfo.ui.notifications
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import com.xyzshell.myandriodinfo.databinding.FragmentNotificationsBinding
|
||||||
|
|
||||||
|
class NotificationsFragment : Fragment() {
|
||||||
|
|
||||||
|
private var _binding: FragmentNotificationsBinding? = null
|
||||||
|
|
||||||
|
// This property is only valid between onCreateView and
|
||||||
|
// onDestroyView.
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
val notificationsViewModel =
|
||||||
|
ViewModelProvider(this).get(NotificationsViewModel::class.java)
|
||||||
|
|
||||||
|
_binding = FragmentNotificationsBinding.inflate(inflater, container, false)
|
||||||
|
val root: View = binding.root
|
||||||
|
|
||||||
|
val textView: TextView = binding.textNotifications
|
||||||
|
notificationsViewModel.text.observe(viewLifecycleOwner) {
|
||||||
|
textView.text = it
|
||||||
|
}
|
||||||
|
return root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package com.xyzshell.myandriodinfo.ui.notifications
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
|
||||||
|
class NotificationsViewModel : ViewModel() {
|
||||||
|
|
||||||
|
private val _text = MutableLiveData<String>().apply {
|
||||||
|
value = "This is notifications Fragment"
|
||||||
|
}
|
||||||
|
val text: LiveData<String> = _text
|
||||||
|
}
|
||||||
9
app/src/main/res/drawable/ic_dashboard_black_24dp.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M3,13h8L11,3L3,3v10zM3,21h8v-6L3,15v6zM13,21h8L21,11h-8v10zM13,3v6h8L21,3h-8z" />
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/ic_home_black_24dp.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z" />
|
||||||
|
</vector>
|
||||||
170
app/src/main/res/drawable/ic_launcher_background.xml
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path
|
||||||
|
android:fillColor="#3DDC84"
|
||||||
|
android:pathData="M0,0h108v108h-108z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M9,0L9,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,0L19,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,0L29,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,0L39,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,0L49,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,0L59,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,0L69,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,0L79,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M89,0L89,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M99,0L99,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,9L108,9"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,19L108,19"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,29L108,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,39L108,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,49L108,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,59L108,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,69L108,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,79L108,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,89L108,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,99L108,99"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,29L89,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,39L89,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,49L89,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,59L89,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,69L89,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,79L89,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,19L29,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,19L39,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,19L49,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,19L59,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,19L69,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,19L79,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
</vector>
|
||||||
30
app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:endX="85.84757"
|
||||||
|
android:endY="92.4963"
|
||||||
|
android:startX="42.9492"
|
||||||
|
android:startY="49.59793"
|
||||||
|
android:type="linear">
|
||||||
|
<item
|
||||||
|
android:color="#44000000"
|
||||||
|
android:offset="0.0" />
|
||||||
|
<item
|
||||||
|
android:color="#00000000"
|
||||||
|
android:offset="1.0" />
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:fillType="nonZero"
|
||||||
|
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:strokeColor="#00000000" />
|
||||||
|
</vector>
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.89,2 2,2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2z" />
|
||||||
|
</vector>
|
||||||
36
app/src/main/res/layout/activity_main.xml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
|
android:id="@+id/nav_view"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="0dp"
|
||||||
|
android:layout_marginEnd="0dp"
|
||||||
|
android:background="?android:attr/windowBackground"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:menu="@menu/bottom_nav_menu" />
|
||||||
|
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/nav_host_fragment_activity_main"
|
||||||
|
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginLeft="1dp"
|
||||||
|
android:layout_marginTop="1dp"
|
||||||
|
android:layout_marginRight="1dp"
|
||||||
|
android:layout_marginBottom="66dp"
|
||||||
|
app:defaultNavHost="true"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/nav_view"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:navGraph="@navigation/mobile_navigation" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
22
app/src/main/res/layout/fragment_dashboard.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".ui.dashboard.DashboardFragment">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_dashboard"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textSize="20sp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
29
app/src/main/res/layout/fragment_home.xml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".ui.home.HomeFragment">
|
||||||
|
<ExpandableListView
|
||||||
|
android:id="@+id/info_list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:divider="#E0E0E0"
|
||||||
|
android:dividerHeight="1dp"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
android:scrollbarStyle="outsideOverlay"
|
||||||
|
android:scrollbarSize="8dp"
|
||||||
|
android:fadeScrollbars="true"
|
||||||
|
android:scrollbarFadeDuration="1000"
|
||||||
|
android:layout_weight="1"
|
||||||
|
tools:ignore="MissingConstraints"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
/>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
22
app/src/main/res/layout/fragment_notifications.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".ui.notifications.NotificationsFragment">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_notifications"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textSize="20sp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
34
app/src/main/res/layout/list_view_item.xml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:baselineAligned="false"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
tools:ignore="UselessParent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/titleTextView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="#333333"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:ellipsize="end" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/descriptionTextView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="#666666"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:maxLines="10"
|
||||||
|
android:ellipsize="end" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
19
app/src/main/res/menu/bottom_nav_menu.xml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/navigation_home"
|
||||||
|
android:icon="@drawable/ic_home_black_24dp"
|
||||||
|
android:title="@string/title_home" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/navigation_dashboard"
|
||||||
|
android:icon="@drawable/ic_dashboard_black_24dp"
|
||||||
|
android:title="@string/title_dashboard" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/navigation_notifications"
|
||||||
|
android:icon="@drawable/ic_notifications_black_24dp"
|
||||||
|
android:title="@string/title_notifications" />
|
||||||
|
|
||||||
|
</menu>
|
||||||
6
app/src/main/res/mipmap-anydpi/ic_launcher.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@drawable/ic_launcher_background" />
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
</adaptive-icon>
|
||||||
6
app/src/main/res/mipmap-anydpi/ic_launcher_round.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@drawable/ic_launcher_background" />
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
</adaptive-icon>
|
||||||
BIN
app/src/main/res/mipmap-hdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 982 B |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
25
app/src/main/res/navigation/mobile_navigation.xml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/mobile_navigation"
|
||||||
|
app:startDestination="@+id/navigation_home">
|
||||||
|
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/navigation_home"
|
||||||
|
android:name="com.xyzshell.myandriodinfo.ui.home.HomeFragment"
|
||||||
|
android:label="@string/title_home"
|
||||||
|
tools:layout="@layout/fragment_home" />
|
||||||
|
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/navigation_dashboard"
|
||||||
|
android:name="com.xyzshell.myandriodinfo.ui.dashboard.DashboardFragment"
|
||||||
|
android:label="@string/title_dashboard"
|
||||||
|
tools:layout="@layout/fragment_dashboard" />
|
||||||
|
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/navigation_notifications"
|
||||||
|
android:name="com.xyzshell.myandriodinfo.ui.notifications.NotificationsFragment"
|
||||||
|
android:label="@string/title_notifications"
|
||||||
|
tools:layout="@layout/fragment_notifications" />
|
||||||
|
</navigation>
|
||||||
16
app/src/main/res/values-night/themes.xml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="Theme.MyAndriodInfo" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||||
|
<!-- Primary brand color. -->
|
||||||
|
<item name="colorPrimary">@color/purple_200</item>
|
||||||
|
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||||
|
<item name="colorOnPrimary">@color/black</item>
|
||||||
|
<!-- Secondary brand color. -->
|
||||||
|
<item name="colorSecondary">@color/teal_200</item>
|
||||||
|
<item name="colorSecondaryVariant">@color/teal_200</item>
|
||||||
|
<item name="colorOnSecondary">@color/black</item>
|
||||||
|
<!-- Status bar color. -->
|
||||||
|
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
|
||||||
|
<!-- Customize your theme here. -->
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
10
app/src/main/res/values/colors.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="purple_200">#FFBB86FC</color>
|
||||||
|
<color name="purple_500">#FF6200EE</color>
|
||||||
|
<color name="purple_700">#FF3700B3</color>
|
||||||
|
<color name="teal_200">#FF03DAC5</color>
|
||||||
|
<color name="teal_700">#FF018786</color>
|
||||||
|
<color name="black">#FF000000</color>
|
||||||
|
<color name="white">#FFFFFFFF</color>
|
||||||
|
</resources>
|
||||||
5
app/src/main/res/values/dimens.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<resources>
|
||||||
|
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||||
|
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||||
|
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||||
|
</resources>
|
||||||
6
app/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name">MyAndriodInfo</string>
|
||||||
|
<string name="title_home">信息列表</string>
|
||||||
|
<string name="title_dashboard">Dashboard</string>
|
||||||
|
<string name="title_notifications">Notifications</string>
|
||||||
|
</resources>
|
||||||
16
app/src/main/res/values/themes.xml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="Theme.MyAndriodInfo" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||||
|
<!-- Primary brand color. -->
|
||||||
|
<item name="colorPrimary">@color/purple_500</item>
|
||||||
|
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||||
|
<item name="colorOnPrimary">@color/white</item>
|
||||||
|
<!-- Secondary brand color. -->
|
||||||
|
<item name="colorSecondary">@color/teal_200</item>
|
||||||
|
<item name="colorSecondaryVariant">@color/teal_700</item>
|
||||||
|
<item name="colorOnSecondary">@color/black</item>
|
||||||
|
<!-- Status bar color. -->
|
||||||
|
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
|
||||||
|
<!-- Customize your theme here. -->
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
13
app/src/main/res/xml/backup_rules.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
Sample backup rules file; uncomment and customize as necessary.
|
||||||
|
See https://developer.android.com/guide/topics/data/autobackup
|
||||||
|
for details.
|
||||||
|
Note: This file is ignored for devices older that API 31
|
||||||
|
See https://developer.android.com/about/versions/12/backup-restore
|
||||||
|
-->
|
||||||
|
<full-backup-content>
|
||||||
|
<!--
|
||||||
|
<include domain="sharedpref" path="."/>
|
||||||
|
<exclude domain="sharedpref" path="device.xml"/>
|
||||||
|
-->
|
||||||
|
</full-backup-content>
|
||||||
19
app/src/main/res/xml/data_extraction_rules.xml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
Sample data extraction rules file; uncomment and customize as necessary.
|
||||||
|
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
|
||||||
|
for details.
|
||||||
|
-->
|
||||||
|
<data-extraction-rules>
|
||||||
|
<cloud-backup>
|
||||||
|
<!-- TODO: Use <include> and <exclude> to control what is backed up.
|
||||||
|
<include .../>
|
||||||
|
<exclude .../>
|
||||||
|
-->
|
||||||
|
</cloud-backup>
|
||||||
|
<!--
|
||||||
|
<device-transfer>
|
||||||
|
<include .../>
|
||||||
|
<exclude .../>
|
||||||
|
</device-transfer>
|
||||||
|
-->
|
||||||
|
</data-extraction-rules>
|
||||||
8
app/src/main/res/xml/network_security_config.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<network-security-config>
|
||||||
|
<domain-config cleartextTrafficPermitted="true">
|
||||||
|
<domain includeSubdomains="true">192.168.1.100</domain>
|
||||||
|
<domain includeSubdomains="true">10.0.2.2</domain> <!-- Android模拟器localhost -->
|
||||||
|
<domain includeSubdomains="true">localhost</domain>
|
||||||
|
</domain-config>
|
||||||
|
</network-security-config>
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
package com.xyzshell.myandriodinfo
|
||||||
|
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
import org.junit.Assert.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example local unit test, which will execute on the development machine (host).
|
||||||
|
*
|
||||||
|
* See [testing documentation](http://d.android.com/tools/testing).
|
||||||
|
*/
|
||||||
|
class ExampleUnitTest {
|
||||||
|
@Test
|
||||||
|
fun addition_isCorrect() {
|
||||||
|
assertEquals(4, 2 + 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
8
build.gradle.kts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
plugins {
|
||||||
|
alias(libs.plugins.android.application) apply false
|
||||||
|
alias(libs.plugins.kotlin.android) apply false
|
||||||
|
alias(libs.plugins.jetbrains.kotlin.jvm) apply false
|
||||||
|
alias(libs.plugins.compose.compiler) apply false
|
||||||
|
alias(libs.plugins.android.library) apply false
|
||||||
|
}
|
||||||
23
gradle.properties
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Project-wide Gradle settings.
|
||||||
|
# IDE (e.g. Android Studio) users:
|
||||||
|
# Gradle settings configured through the IDE *will override*
|
||||||
|
# any settings specified in this file.
|
||||||
|
# For more details on how to configure your build environment visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||||
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
|
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||||
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
|
# This option should only be used with decoupled projects. For more details, visit
|
||||||
|
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
|
||||||
|
# org.gradle.parallel=true
|
||||||
|
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||||
|
# Android operating system, and which are packaged with your app's APK
|
||||||
|
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||||
|
android.useAndroidX=true
|
||||||
|
# Kotlin code style for this project: "official" or "obsolete":
|
||||||
|
kotlin.code.style=official
|
||||||
|
# Enables namespacing of each library's R class so that its R class includes only the
|
||||||
|
# resources declared in the library itself and none from the library's dependencies,
|
||||||
|
# thereby reducing the size of the R class for that library
|
||||||
|
android.nonTransitiveRClass=true
|
||||||
101
gradle/libs.versions.toml
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
[versions]
|
||||||
|
accompanistSystemuicontroller = "0.36.0"
|
||||||
|
activityCompose = "1.10.1"
|
||||||
|
adaptive = "1.1.0"
|
||||||
|
agp = "8.6.1"
|
||||||
|
apiVersion = "82"
|
||||||
|
datastore = "1.1.7"
|
||||||
|
datastorePreferences = "1.1.7"
|
||||||
|
googleSymbolProcessingApi = "2.0.0-1.0.24"
|
||||||
|
gson = "2.13.1"
|
||||||
|
jsonVersion = "20250517"
|
||||||
|
kotlin = "2.0.0"
|
||||||
|
coreKtx = "1.16.0"
|
||||||
|
junit = "4.13.2"
|
||||||
|
junitVersion = "1.3.0"
|
||||||
|
espressoCore = "3.7.0"
|
||||||
|
appcompat = "1.7.1"
|
||||||
|
kotlinpoet = "1.15.3"
|
||||||
|
kotlinpoetKsp = "1.15.3"
|
||||||
|
kotlinxSerializationJson = "1.6.2"
|
||||||
|
lifecycleViewmodelCompose = "2.9.2"
|
||||||
|
material = "1.12.0"
|
||||||
|
constraintlayout = "2.2.1"
|
||||||
|
lifecycleLivedataKtx = "2.9.2"
|
||||||
|
lifecycleViewmodelKtx = "2.9.2"
|
||||||
|
navigationFragmentKtx = "2.9.3"
|
||||||
|
navigationUiKtx = "2.9.3"
|
||||||
|
protobufJava = "4.29.0"
|
||||||
|
squareupOkhttp = "4.12.0"
|
||||||
|
webkit = "1.14.0"
|
||||||
|
playServicesAppset = "16.1.0"
|
||||||
|
playServicesAdsIdentifier = "18.2.0"
|
||||||
|
jetbrainsKotlinJvm = "2.0.0"
|
||||||
|
runtimeAndroid = "1.8.3"
|
||||||
|
uiToolingPreviewAndroid = "1.8.3"
|
||||||
|
material3Android = "1.3.2"
|
||||||
|
lifecycleRuntimeKtx = "2.9.2"
|
||||||
|
composeBom = "2024.04.01"
|
||||||
|
koin = "4.1.0"
|
||||||
|
|
||||||
|
[libraries]
|
||||||
|
accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanistSystemuicontroller" }
|
||||||
|
adaptive = { module = "androidx.compose.material3.adaptive:adaptive", version.ref = "adaptive" }
|
||||||
|
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activityCompose" }
|
||||||
|
androidx-adaptive = { module = "androidx.compose.material3.adaptive:adaptive" }
|
||||||
|
androidx-adaptive-layout = { module = "androidx.compose.material3.adaptive:adaptive-layout" }
|
||||||
|
androidx-adaptive-navigation = { module = "androidx.compose.material3.adaptive:adaptive-navigation" }
|
||||||
|
androidx-compose-material3-adaptive-adaptive = { module = "androidx.compose.material3.adaptive:adaptive" }
|
||||||
|
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||||
|
androidx-datastore = { module = "androidx.datastore:datastore", version.ref = "datastore" }
|
||||||
|
androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" }
|
||||||
|
androidx-foundation = { module = "androidx.compose.foundation:foundation" }
|
||||||
|
androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleViewmodelCompose" }
|
||||||
|
androidx-material-icons-core = { module = "androidx.compose.material:material-icons-core" }
|
||||||
|
androidx-material-icons-extended = { module = "androidx.compose.material:material-icons-extended" }
|
||||||
|
androidx-material3 = { module = "androidx.compose.material3:material3" }
|
||||||
|
androidx-runtime-livedata = { module = "androidx.compose.runtime:runtime-livedata" }
|
||||||
|
androidx-runtime-rxjava2 = { module = "androidx.compose.runtime:runtime-rxjava2" }
|
||||||
|
androidx-ui = { module = "androidx.compose.ui:ui" }
|
||||||
|
androidx-ui-tooling = { module = "androidx.compose.ui:ui-tooling" }
|
||||||
|
androidx-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" }
|
||||||
|
google-symbol-processing-api = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "googleSymbolProcessingApi" }
|
||||||
|
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
|
||||||
|
json-json = { module = "org.json:json", version.ref = "jsonVersion" }
|
||||||
|
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||||
|
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||||
|
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
||||||
|
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
||||||
|
kotlinpoet-ksp = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlinpoetKsp" }
|
||||||
|
kotlinpoet = { module = "com.squareup:kotlinpoet", version.ref = "kotlinpoet" }
|
||||||
|
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
|
||||||
|
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
|
||||||
|
koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
|
||||||
|
koin-android = { module = "io.insert-koin:koin-android", version.ref = "koin" }
|
||||||
|
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
|
||||||
|
androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycleLivedataKtx" }
|
||||||
|
androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" }
|
||||||
|
androidx-navigation-fragment-ktx = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "navigationFragmentKtx" }
|
||||||
|
androidx-navigation-ui-ktx = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "navigationUiKtx" }
|
||||||
|
androidx-webkit = { group = "androidx.webkit", name = "webkit", version.ref = "webkit" }
|
||||||
|
play-services-appset = { group = "com.google.android.gms", name = "play-services-appset", version.ref = "playServicesAppset" }
|
||||||
|
play-services-ads-identifier = { group = "com.google.android.gms", name = "play-services-ads-identifier", version.ref = "playServicesAdsIdentifier" }
|
||||||
|
protobuf-java = { module = "com.google.protobuf:protobuf-java", version.ref = "protobufJava" }
|
||||||
|
squareup-okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "squareupOkhttp" }
|
||||||
|
androidx-runtime-android = { group = "androidx.compose.runtime", name = "runtime-android", version.ref = "runtimeAndroid" }
|
||||||
|
androidx-ui-tooling-preview-android = { group = "androidx.compose.ui", name = "ui-tooling-preview-android", version.ref = "uiToolingPreviewAndroid" }
|
||||||
|
androidx-material3-android = { group = "androidx.compose.material3", name = "material3-android", version.ref = "material3Android" }
|
||||||
|
xposed-api = { module = "de.robv.android.xposed:api", version.ref = "apiVersion" }
|
||||||
|
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
|
||||||
|
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
||||||
|
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
|
||||||
|
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
||||||
|
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
||||||
|
|
||||||
|
[plugins]
|
||||||
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||||
|
jetbrains-kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "jetbrainsKotlinJvm" }
|
||||||
|
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||||
|
android-library = { id = "com.android.library", version.ref = "agp" }
|
||||||
|
|
||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#Mon Aug 04 16:31:54 CST 2025
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.7-bin.zip
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
185
gradlew
vendored
Executable file
@ -0,0 +1,185 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2015 the original author or authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=`expr $i + 1`
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
0) set -- ;;
|
||||||
|
1) set -- "$args0" ;;
|
||||||
|
2) set -- "$args0" "$args1" ;;
|
||||||
|
3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save () {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=`save "$@"`
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
89
gradlew.bat
vendored
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
1
ksp-annotations/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
14
ksp-annotations/build.gradle
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
plugins {
|
||||||
|
id 'org.jetbrains.kotlin.jvm'
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
compileKotlin {
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "1.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
package com.xyzshell.ksp_annotations
|
||||||
|
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.SOURCE)
|
||||||
|
annotation class AutoBuilder
|
||||||
1
ksp-processor/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
21
ksp-processor/build.gradle
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
plugins {
|
||||||
|
id 'org.jetbrains.kotlin.jvm'
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
compileKotlin {
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "1.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation project(':ksp-annotations')
|
||||||
|
implementation libs.google.symbol.processing.api
|
||||||
|
implementation libs.kotlinpoet
|
||||||
|
implementation libs.kotlinpoet.ksp
|
||||||
|
}
|
||||||
@ -0,0 +1,118 @@
|
|||||||
|
package com.xyzshell.ksp_processor
|
||||||
|
|
||||||
|
import com.google.devtools.ksp.processing.*
|
||||||
|
import com.google.devtools.ksp.symbol.*
|
||||||
|
import com.google.devtools.ksp.validate
|
||||||
|
import com.squareup.kotlinpoet.*
|
||||||
|
import com.squareup.kotlinpoet.ksp.toClassName
|
||||||
|
import com.squareup.kotlinpoet.ksp.writeTo
|
||||||
|
import com.xyzshell.ksp_annotations.AutoBuilder
|
||||||
|
|
||||||
|
class AutoBuilderProcessor(
|
||||||
|
private val codeGenerator: CodeGenerator,
|
||||||
|
private val logger: KSPLogger
|
||||||
|
) : SymbolProcessor {
|
||||||
|
|
||||||
|
override fun process(resolver: Resolver): List<KSAnnotated> {
|
||||||
|
val symbols = resolver.getSymbolsWithAnnotation(AutoBuilder::class.qualifiedName!!)
|
||||||
|
val ret = symbols.filter { !it.validate() }.toList()
|
||||||
|
|
||||||
|
symbols
|
||||||
|
.filter { it is KSClassDeclaration && it.validate() }
|
||||||
|
.forEach { it ->
|
||||||
|
generateBuilderClass(it as KSClassDeclaration)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateBuilderClass(classDeclaration: KSClassDeclaration) {
|
||||||
|
val packageName = classDeclaration.containingFile!!.packageName.asString()
|
||||||
|
val className = classDeclaration.simpleName.asString()
|
||||||
|
val builderClassName = "${className}Builder"
|
||||||
|
|
||||||
|
logger.info("Generating builder for $className")
|
||||||
|
|
||||||
|
// 获取类的构造函数参数
|
||||||
|
val constructor = classDeclaration.primaryConstructor
|
||||||
|
if (constructor == null) {
|
||||||
|
logger.error("Class $className must have a primary constructor", classDeclaration)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val properties = constructor.parameters
|
||||||
|
|
||||||
|
// 创建Builder类
|
||||||
|
val builderClass = TypeSpec.classBuilder(builderClassName)
|
||||||
|
.addKdoc("Auto-generated builder for %L", className)
|
||||||
|
|
||||||
|
// 添加属性字段
|
||||||
|
properties.forEach { param ->
|
||||||
|
val paramName = param.name!!.asString()
|
||||||
|
val paramType = param.type.resolve()
|
||||||
|
val typeName = paramType.toClassName()
|
||||||
|
|
||||||
|
builderClass.addProperty(
|
||||||
|
PropertySpec.builder(paramName, typeName.copy(nullable = true))
|
||||||
|
.mutable()
|
||||||
|
.initializer("null")
|
||||||
|
.addModifiers(KModifier.PRIVATE)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加setter方法
|
||||||
|
properties.forEach { param ->
|
||||||
|
val paramName = param.name!!.asString()
|
||||||
|
val paramType = param.type.resolve()
|
||||||
|
val typeName = paramType.toClassName()
|
||||||
|
|
||||||
|
val setterMethod = FunSpec.builder(paramName)
|
||||||
|
.addParameter(paramName, typeName)
|
||||||
|
.returns(ClassName(packageName, builderClassName))
|
||||||
|
.addStatement("this.%L = %L", paramName, paramName)
|
||||||
|
.addStatement("return this")
|
||||||
|
.build()
|
||||||
|
|
||||||
|
builderClass.addFunction(setterMethod)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加build方法
|
||||||
|
val buildMethod = FunSpec.builder("build")
|
||||||
|
.returns(ClassName(packageName, className))
|
||||||
|
.apply {
|
||||||
|
val paramNames = properties.map { it.name!!.asString() }
|
||||||
|
val paramChecks = paramNames.map { paramName ->
|
||||||
|
"checkNotNull($paramName) { \"$paramName must be set\" }"
|
||||||
|
}
|
||||||
|
|
||||||
|
addStatement(
|
||||||
|
"return %T(%L)",
|
||||||
|
ClassName(packageName, className),
|
||||||
|
paramChecks.joinToString(", ")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
|
||||||
|
builderClass.addFunction(buildMethod)
|
||||||
|
|
||||||
|
// 添加静态工厂方法
|
||||||
|
val companionObject = TypeSpec.companionObjectBuilder()
|
||||||
|
.addFunction(
|
||||||
|
FunSpec.builder("builder")
|
||||||
|
.returns(ClassName(packageName, builderClassName))
|
||||||
|
.addStatement("return %T()", ClassName(packageName, builderClassName))
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
builderClass.addType(companionObject)
|
||||||
|
|
||||||
|
// 生成文件
|
||||||
|
val file = FileSpec.builder(packageName, builderClassName)
|
||||||
|
.addType(builderClass.build())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
file.writeTo(codeGenerator, Dependencies(true, classDeclaration.containingFile!!))
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
package com.xyzshell.ksp_processor
|
||||||
|
|
||||||
|
import com.google.devtools.ksp.processing.SymbolProcessor
|
||||||
|
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
|
||||||
|
import com.google.devtools.ksp.processing.SymbolProcessorProvider
|
||||||
|
|
||||||
|
class AutoBuilderProcessorProvider : SymbolProcessorProvider {
|
||||||
|
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
|
||||||
|
return AutoBuilderProcessor(
|
||||||
|
codeGenerator = environment.codeGenerator,
|
||||||
|
logger = environment.logger
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
com.xyzshell.ksp_processor.AutoBuilderProcessorProvider
|
||||||
1
mydev/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
82
mydev/build.gradle.kts
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
plugins {
|
||||||
|
alias(libs.plugins.android.application)
|
||||||
|
alias(libs.plugins.kotlin.android)
|
||||||
|
alias(libs.plugins.compose.compiler)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "com.xyzshell.mydev"
|
||||||
|
compileSdk = 35
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId = "com.xyzshell.mydev"
|
||||||
|
minSdk = 29
|
||||||
|
//noinspection OldTargetApi
|
||||||
|
targetSdk = 35
|
||||||
|
versionCode = 1
|
||||||
|
versionName = "1.0"
|
||||||
|
|
||||||
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
isMinifyEnabled = false
|
||||||
|
proguardFiles(
|
||||||
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
|
"proguard-rules.pro"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "1.8"
|
||||||
|
}
|
||||||
|
buildFeatures {
|
||||||
|
compose = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
implementation(libs.androidx.core.ktx)
|
||||||
|
implementation(libs.androidx.appcompat)
|
||||||
|
implementation(libs.material)
|
||||||
|
implementation(libs.androidx.runtime.android)
|
||||||
|
implementation(libs.androidx.ui.tooling.preview.android)
|
||||||
|
implementation(libs.androidx.ui)
|
||||||
|
implementation(libs.androidx.foundation)
|
||||||
|
implementation(libs.androidx.ui.tooling.preview)
|
||||||
|
implementation(libs.androidx.material3.android)
|
||||||
|
implementation(libs.adaptive)
|
||||||
|
implementation(libs.androidx.adaptive.layout)
|
||||||
|
implementation(libs.androidx.adaptive.navigation)
|
||||||
|
implementation(libs.play.services.ads.identifier)
|
||||||
|
implementation(libs.play.services.appset)
|
||||||
|
implementation(libs.kotlinx.serialization.json)
|
||||||
|
implementation(libs.accompanist.systemuicontroller)
|
||||||
|
implementation(libs.protobuf.java)
|
||||||
|
debugImplementation(libs.androidx.ui.tooling)
|
||||||
|
testImplementation(libs.junit)
|
||||||
|
|
||||||
|
implementation(libs.androidx.material.icons.core)
|
||||||
|
// Optional - Add full set of material icons
|
||||||
|
implementation(libs.androidx.material.icons.extended)
|
||||||
|
// Optional - Add window size utils
|
||||||
|
implementation(libs.androidx.adaptive)
|
||||||
|
|
||||||
|
// Optional - Integration with activities
|
||||||
|
implementation(libs.androidx.activity.compose)
|
||||||
|
// Optional - Integration with ViewModels
|
||||||
|
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
||||||
|
// Optional - Integration with LiveData
|
||||||
|
implementation(libs.androidx.runtime.livedata)
|
||||||
|
// Optional - Integration with RxJava
|
||||||
|
implementation(libs.androidx.runtime.rxjava2)
|
||||||
|
|
||||||
|
androidTestImplementation(libs.androidx.junit)
|
||||||
|
androidTestImplementation(libs.androidx.espresso.core)
|
||||||
|
}
|
||||||
21
mydev/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package com.xyzshell.mydev
|
||||||
|
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
import org.junit.Assert.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instrumented test, which will execute on an Android device.
|
||||||
|
*
|
||||||
|
* See [testing documentation](http://d.android.com/tools/testing).
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class ExampleInstrumentedTest {
|
||||||
|
@Test
|
||||||
|
fun useAppContext() {
|
||||||
|
// Context of the app under test.
|
||||||
|
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
|
assertEquals("com.xyzshell.mydev", appContext.packageName)
|
||||||
|
}
|
||||||
|
}
|
||||||
36
mydev/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
|
||||||
|
tools:ignore="ProtectedPermissions" />
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
|
<application
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/Theme.MyAndriodInfo">
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:windowSoftInputMode="adjustResize"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<activity android:name=".ViewBrowseActivity"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="javascript" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
9
mydev/src/main/java/com/xyzshell/mydev/Info.kt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package com.xyzshell.mydev
|
||||||
|
|
||||||
|
// 数据类
|
||||||
|
typealias Callback = () -> String
|
||||||
|
data class InfoItem(
|
||||||
|
val propertyName: String,
|
||||||
|
val propertyValue: String,
|
||||||
|
val callback: Callback? = null
|
||||||
|
)
|
||||||
450
mydev/src/main/java/com/xyzshell/mydev/InfoData.kt
Normal file
@ -0,0 +1,450 @@
|
|||||||
|
package com.xyzshell.mydev
|
||||||
|
|
||||||
|
import android.adservices.AdServicesState
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.ActivityManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import android.content.pm.ApplicationInfo
|
||||||
|
import android.content.pm.PackageInfo
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.graphics.Rect
|
||||||
|
import android.hardware.Sensor
|
||||||
|
import android.hardware.SensorManager
|
||||||
|
import android.media.AudioManager
|
||||||
|
import android.net.ConnectivityManager
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Environment
|
||||||
|
import android.os.PowerManager
|
||||||
|
import android.os.Process
|
||||||
|
import android.os.SystemClock
|
||||||
|
import android.os.ext.SdkExtensions
|
||||||
|
import android.provider.Settings
|
||||||
|
import android.telephony.TelephonyManager
|
||||||
|
import android.util.DisplayMetrics
|
||||||
|
import android.view.RoundedCorner
|
||||||
|
import android.view.WindowManager
|
||||||
|
import androidx.annotation.RequiresExtension
|
||||||
|
import com.google.android.gms.ads.identifier.AdvertisingIdClient
|
||||||
|
import com.google.android.gms.appset.AppSet
|
||||||
|
import com.google.android.gms.appset.AppSetIdClient
|
||||||
|
import com.google.android.gms.appset.AppSetIdInfo
|
||||||
|
import com.google.android.gms.tasks.Task
|
||||||
|
import com.xyzshell.mydev.data.SystemPropKeys
|
||||||
|
import com.xyzshell.mydev.utils.DeviceInfo
|
||||||
|
import com.xyzshell.mydev.utils.MemoryInfoType
|
||||||
|
import java.io.BufferedReader
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.InputStreamReader
|
||||||
|
import java.net.NetworkInterface
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
|
import java.util.TimeZone
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
|
|
||||||
|
class InfoData(private val context:Context) {
|
||||||
|
|
||||||
|
private val activityManager: ActivityManager = this.context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
||||||
|
private val connectivityManager = this.context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||||
|
private val packageManager: PackageManager = this.context.packageManager
|
||||||
|
private val applicationInfo: ApplicationInfo = this.context.applicationInfo
|
||||||
|
private val audioManager: AudioManager = this.context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||||
|
|
||||||
|
private val windowManager = this.context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
||||||
|
private val telephonyManager = this.context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
|
||||||
|
private val packageInfo: PackageInfo =
|
||||||
|
this.packageManager.getPackageInfo(this.context.packageName,0)
|
||||||
|
private val powerManager = this.context.getSystemService(Context.POWER_SERVICE) as PowerManager
|
||||||
|
|
||||||
|
private fun getStr(obj: Any) : String {
|
||||||
|
var info = ""
|
||||||
|
if(obj!=null) {
|
||||||
|
info = if (obj is Array<*>) {
|
||||||
|
obj.joinToString(",")
|
||||||
|
} else {
|
||||||
|
obj.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
fun getInfo(): List<InfoItem> {
|
||||||
|
val cls = Class.forName("android.os.SystemProperties")
|
||||||
|
val method = cls.getMethod("get", String::class.java)
|
||||||
|
val infos = mutableListOf<InfoItem>()
|
||||||
|
SystemPropKeys.forEach { k ->
|
||||||
|
method.invoke(null, k)?.let {
|
||||||
|
infos.add(InfoItem(k, getStr(it)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return infos
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPropInfo(): List<InfoItem> {
|
||||||
|
val reg = Regex("^\\[(.+)].+\\[(.+)]$")
|
||||||
|
val res = mutableListOf<InfoItem>()
|
||||||
|
try {
|
||||||
|
val process: java.lang.Process? = Runtime.getRuntime().exec("getprop")
|
||||||
|
if(process!=null) {
|
||||||
|
val reader = BufferedReader(InputStreamReader(process.inputStream))
|
||||||
|
var line: String?
|
||||||
|
while ((reader.readLine().also { line = it }) != null) {
|
||||||
|
val a = reg.find(line!!)?.groupValues
|
||||||
|
if(a!=null && a.size == 3) {
|
||||||
|
res.add(InfoItem(a[1], a[2]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
private val allInfos = mutableMapOf<String,InfoItem>()
|
||||||
|
private fun setMkv(k: String, name: String, value: Any) {
|
||||||
|
allInfos[k] = InfoItem(name, value.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setAppInfo() {
|
||||||
|
val v = packageManager.getApplicationLabel(applicationInfo)
|
||||||
|
var install_name = packageManager.getInstallerPackageName(applicationInfo.packageName)
|
||||||
|
if(install_name == null) {
|
||||||
|
install_name = ""
|
||||||
|
}
|
||||||
|
setMkv("app_name", "App Name", v)
|
||||||
|
setMkv("install_name", "install name", install_name)
|
||||||
|
this.packageInfo.versionName?.let { setMkv("app_version", "Version Name", it) }
|
||||||
|
setMkv("app_version_code", "Version Code", this.packageInfo.versionCode)
|
||||||
|
setMkv("firstInstallTime", "安装时间", this.packageInfo.firstInstallTime)
|
||||||
|
setMkv("package_name", "Package Name", applicationInfo.packageName)
|
||||||
|
setMkv("target_sdk", "Target SDK", applicationInfo.targetSdkVersion)
|
||||||
|
setMkv("idfv", "idfv", "")
|
||||||
|
setMkv("idfv_scope", "idfv scope", -1)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setFsLm() {
|
||||||
|
val v = File(applicationInfo.sourceDir).lastModified()
|
||||||
|
setMkv("fslm", "app安装文件时间", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setPermissionInfo() {
|
||||||
|
val ps_tpg = checkPermission("android.permission.ACCESS_ADSERVICES_TOPICS")
|
||||||
|
setMkv("ps_tpg","允许应用访问用户的兴趣话题信息", ps_tpg)
|
||||||
|
val ps_apg = checkPermission("android.permission.ACCESS_ADSERVICES_ATTRIBUTION")
|
||||||
|
setMkv("ps_apg", "广告归因追踪", ps_apg)
|
||||||
|
var ps_aipg = checkPermission("android.permission.ACCESS_ADSERVICES_AD_ID")
|
||||||
|
setMkv("ps_aipg", "广告ID访问", ps_aipg)
|
||||||
|
val ps_capg = checkPermission("android.permission.ACCESS_ADSERVICES_CUSTOM_AUDIENCE")
|
||||||
|
setMkv("ps_capg", "允许应用创建和管理自定义广告受众群体",ps_capg)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setNetworkInfo() {
|
||||||
|
setMkv("connectivity", "网络状态",
|
||||||
|
connectivityManager.restrictBackgroundStatus != ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED)
|
||||||
|
val sb = StringBuilder()
|
||||||
|
val interfaces = NetworkInterface.getNetworkInterfaces()
|
||||||
|
if (interfaces != null) {
|
||||||
|
for (intf in interfaces) {
|
||||||
|
try {
|
||||||
|
if (!intf.isUp || intf.isLoopback) continue
|
||||||
|
sb.append("name: ${intf.name}, displayName: ${intf.displayName},")
|
||||||
|
val addresses = intf.inetAddresses
|
||||||
|
for (addr in addresses) {
|
||||||
|
sb.append("address: ${addr.hostAddress}\n")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setMkv("NetworkInterface", "网络状态", sb.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun setFont() {
|
||||||
|
setMkv("font_scale", "Font Scale", Settings.System.getFloat(this.context.contentResolver,"font_scale"))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setLocal() {
|
||||||
|
setMkv("tz", "TimeZone",
|
||||||
|
Math.round(TimeZone.getDefault().getOffset(Date().time) * 10.0 / 3600000.0) / 10.0)
|
||||||
|
setMkv("tz_offset", "tz_offset",Math.round(
|
||||||
|
TimeZone.getDefault().getOffset(
|
||||||
|
Date().time
|
||||||
|
) * 10.0 / 3600000.0
|
||||||
|
) / 10.0)
|
||||||
|
setMkv("local","local", Locale.getDefault().toString())
|
||||||
|
val localeList = this.context.resources.configuration.locales
|
||||||
|
val stringBuilder = StringBuilder()
|
||||||
|
for (i in 0 until localeList.size()) {
|
||||||
|
val locale = localeList.get(i)
|
||||||
|
stringBuilder.append("Locale: ${locale.toString()}, Language: ${locale.language}, Country: ${locale.country}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
setMkv("local_list", "Locale List", stringBuilder.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setScreen() {
|
||||||
|
val screen_brightness = Settings.System.getInt(this.context.contentResolver, Settings.System.SCREEN_BRIGHTNESS) / 255.0F * 100.0F
|
||||||
|
setMkv("screen_brightness","屏幕亮度", screen_brightness)
|
||||||
|
setMkv("screen_orientation", "屏幕方向", this.context.resources.configuration.orientation)
|
||||||
|
|
||||||
|
val rect = Rect()
|
||||||
|
windowManager.defaultDisplay.getRectSize(rect)
|
||||||
|
val displayMetrics = DisplayMetrics()
|
||||||
|
windowManager.defaultDisplay.getMetrics(displayMetrics)
|
||||||
|
val sb = StringBuilder()
|
||||||
|
sb.append("w:${rect.width()},h:${rect.height()}\n")
|
||||||
|
sb.append("W:${displayMetrics.widthPixels},H: ${displayMetrics.heightPixels}\n")
|
||||||
|
sb.append("density:${displayMetrics.density}\n")
|
||||||
|
sb.append("densityDpi:${displayMetrics.densityDpi}\n")
|
||||||
|
sb.append("xdpi:${displayMetrics.xdpi},ydpi:${displayMetrics.ydpi}\n")
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
sb.append("tl${windowManager.defaultDisplay.getRoundedCorner(RoundedCorner.POSITION_TOP_LEFT)}\n")
|
||||||
|
sb.append("tr${windowManager.defaultDisplay.getRoundedCorner(RoundedCorner.POSITION_TOP_RIGHT)}\n")
|
||||||
|
sb.append("bl${windowManager.defaultDisplay.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT)}\n")
|
||||||
|
sb.append("br${windowManager.defaultDisplay.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT)}\n")
|
||||||
|
}
|
||||||
|
setMkv("screen", "屏幕", sb.toString())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setDisk() {
|
||||||
|
setMkv("disk_free","磁盘剩余", Environment.getDataDirectory().freeSpace)
|
||||||
|
setMkv("disk_total", "磁盘", Environment.getDataDirectory().totalSpace)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun getProcessName():String {
|
||||||
|
try {
|
||||||
|
val packageManager = context.packageManager
|
||||||
|
val applicationInfo = packageManager.getApplicationInfo(
|
||||||
|
context.packageName,
|
||||||
|
PackageManager.GET_META_DATA // 0x80
|
||||||
|
)
|
||||||
|
val metaData = applicationInfo.metaData
|
||||||
|
|
||||||
|
try {
|
||||||
|
val processName = applicationInfo.processName
|
||||||
|
return processName
|
||||||
|
} catch (e: PackageManager.NameNotFoundException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
} catch (e: PackageManager.NameNotFoundException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
fun getProcessName1():String {
|
||||||
|
val pid = Process.myPid()
|
||||||
|
val activityManager = this.context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
||||||
|
val runningManager = activityManager.runningAppProcesses
|
||||||
|
runningManager?.forEach {
|
||||||
|
if(it.pid == pid) {
|
||||||
|
return it.processName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSystem() {
|
||||||
|
setMkv("pc", "Processors", Runtime.getRuntime().availableProcessors())
|
||||||
|
setMkv("boot_ms","bt_ms", System.currentTimeMillis() - SystemClock.elapsedRealtime())
|
||||||
|
setMkv("processName1", "Process Name", getProcessName())
|
||||||
|
setMkv("processName2", "Process Name 1", getProcessName1())
|
||||||
|
val b1 = packageManager.hasSystemFeature("com.google.android.play.feature.HPE_EXPERIENCE")
|
||||||
|
val b2 = packageManager.hasSystemFeature(PackageManager.FEATURE_PC)
|
||||||
|
val kb = this.context.resources.configuration.keyboard
|
||||||
|
var is_pc = true
|
||||||
|
if(kb != Configuration.KEYBOARD_QWERTY || (!b1 && !b2)) {
|
||||||
|
is_pc = false
|
||||||
|
}
|
||||||
|
setMkv("is_pc", "is pc", is_pc)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setMem() {
|
||||||
|
val memoryInfo = ActivityManager.MemoryInfo()
|
||||||
|
activityManager.getMemoryInfo(memoryInfo)
|
||||||
|
val sb = StringBuilder()
|
||||||
|
setMkv("availMem", "可用内存", memoryInfo.availMem)
|
||||||
|
setMkv("totalMem", "总内存", memoryInfo.totalMem)
|
||||||
|
setMkv("threshold", "threshold", memoryInfo.threshold)
|
||||||
|
setMkv("lowMemory", "lowMemory", memoryInfo.lowMemory)
|
||||||
|
setMkv("GlEsVersion", "GlEsVersion", activityManager.deviceConfigurationInfo.glEsVersion)
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSensor() {
|
||||||
|
val sensorManager = this.context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
|
||||||
|
val isGYROSCOPE = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) != null
|
||||||
|
setMkv("isGYROSCOPE","陀螺仪", isGYROSCOPE)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun setBatteryInfo() {
|
||||||
|
val intentFilter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
|
||||||
|
val intent = this.context.registerReceiver(null, intentFilter)
|
||||||
|
var level = -1
|
||||||
|
var scale = -1
|
||||||
|
var adn = -1
|
||||||
|
var status = -1
|
||||||
|
if(intent!=null) {
|
||||||
|
level = intent.getIntExtra("level", -1)
|
||||||
|
setMkv("battery_level", "电池Level", level)
|
||||||
|
scale = intent.getIntExtra("scale", -1)
|
||||||
|
setMkv("battery_scale", "电池scale", scale)
|
||||||
|
if(level > 0 && scale > 0) {
|
||||||
|
adn = (((level.toFloat() / scale.toFloat()) * 100.0F).toInt())
|
||||||
|
setMkv("battery_adn", "电池adn", adn)
|
||||||
|
}
|
||||||
|
status = intent.getIntExtra("status", -1)
|
||||||
|
setMkv("battery_status", "电池状态", status)
|
||||||
|
}
|
||||||
|
var plugged = false
|
||||||
|
if(intent != null) {
|
||||||
|
// stay_on_while_plugged_in
|
||||||
|
val plugged1:Int = intent.getIntExtra("plugged", -1)
|
||||||
|
if(plugged1 > 0) {
|
||||||
|
plugged = true
|
||||||
|
}
|
||||||
|
setMkv("battery_plugged", "电池充电", plugged)
|
||||||
|
}
|
||||||
|
setMkv("isPowerSaveMode", "Power Save Mode", powerManager.isPowerSaveMode)
|
||||||
|
// setMkv("sowpie","插电模式:sowpie", Settings.Global.getInt(this.context.contentResolver, "stay_on_while_plugged_in", -1) > 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun setAudio() {
|
||||||
|
setMkv("MusicActive", "MusicActive", audioManager.isMusicActive)
|
||||||
|
setMkv("volume", "volume", audioManager.getStreamVolume(AudioManager.STREAM_MUSIC))
|
||||||
|
setMkv("SpeakerphoneOn","SpeakerphoneOn", audioManager.isSpeakerphoneOn)
|
||||||
|
setMkv("ringerMode", "ringerMode", audioManager.ringerMode)
|
||||||
|
val sb = StringBuilder()
|
||||||
|
val arrayOfAudioDeviceInfo = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
|
||||||
|
for (i in arrayOfAudioDeviceInfo.indices) {
|
||||||
|
val audioDeviceInfo = arrayOfAudioDeviceInfo[i]
|
||||||
|
if (audioDeviceInfo != null) {
|
||||||
|
// 3,7,8
|
||||||
|
// public static final int TYPE_BLUETOOTH_A2DP = 8;
|
||||||
|
// public static final int TYPE_BLUETOOTH_SCO = 7;
|
||||||
|
// public static final int TYPE_WIRED_HEADSET = 3;
|
||||||
|
sb.append("name: " + audioDeviceInfo.productName + ", type: " + audioDeviceInfo.type + "\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append("isWiredHeadsetOn:${audioManager.isWiredHeadsetOn()}\n")
|
||||||
|
sb.append("isBluetoothScoOn:${audioManager.isBluetoothScoOn()}\n")
|
||||||
|
sb.append("isBluetoothA2dpOn:${audioManager.isBluetoothA2dpOn()}\n")
|
||||||
|
setMkv("AudioDeviceInfo", "AudioDeviceInfo", sb.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("HardwareIds")
|
||||||
|
fun setPhoneInfo() {
|
||||||
|
setMkv("simCountryIso","Country Iso",telephonyManager.simCountryIso.toUpperCase(Locale.ENGLISH))
|
||||||
|
setMkv("NetworkOperatorName","NetworkOperatorName",telephonyManager.networkOperatorName)
|
||||||
|
setMkv("NetworkOperator","NetworkOperator",telephonyManager.networkOperator)
|
||||||
|
var manufacturerCode = ""
|
||||||
|
if (!telephonyManager.manufacturerCode.isNullOrEmpty()) {
|
||||||
|
manufacturerCode = telephonyManager.manufacturerCode.toString()
|
||||||
|
}
|
||||||
|
setMkv("ManufacturerCode","ManufacturerCode",manufacturerCode)
|
||||||
|
try {
|
||||||
|
// setMkv("tel_deviceId","Telephony DeviceId",telephonyManager.getDeviceId())
|
||||||
|
}catch (e:Throwable) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkPermission(key:String):Boolean {
|
||||||
|
return this.context.packageManager.checkPermission(key,this.context.packageName) == PackageManager.PERMISSION_GRANTED
|
||||||
|
}
|
||||||
|
|
||||||
|
fun GetAppInfo() {
|
||||||
|
setAppInfo()
|
||||||
|
setFsLm()
|
||||||
|
setPermissionInfo()
|
||||||
|
setSystem()
|
||||||
|
setMem()
|
||||||
|
setNetworkInfo()
|
||||||
|
setFont()
|
||||||
|
setLocal()
|
||||||
|
setScreen()
|
||||||
|
setDisk()
|
||||||
|
setPhoneInfo()
|
||||||
|
setAudio()
|
||||||
|
setBatteryInfo()
|
||||||
|
setSensor()
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && SdkExtensions.getExtensionVersion(
|
||||||
|
SdkExtensions.AD_SERVICES) >= 4) {
|
||||||
|
setAD_SERVICES()
|
||||||
|
}
|
||||||
|
initAdid()
|
||||||
|
}
|
||||||
|
|
||||||
|
val AllInfos get() = allInfos.values.toList()
|
||||||
|
|
||||||
|
@RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
|
||||||
|
fun setAD_SERVICES() {
|
||||||
|
val v = AdServicesState.isAdServicesStateEnabled()
|
||||||
|
setMkv("isAdServicesStateEnabled", "Android Ad Services",v )
|
||||||
|
}
|
||||||
|
|
||||||
|
fun initAdid() {
|
||||||
|
setMkv("adid", "Ad Id", "")
|
||||||
|
setMkv("ApiLevel", "ApiLevel", DeviceInfo.instance.ApiLevel)
|
||||||
|
setMkv("ApkDigest", "ApkDigest", DeviceInfo.instance.ApkDigest)
|
||||||
|
setMkv("ApkDigestSize", "ApkDigestSize", DeviceInfo.instance.ApkDigestSize)
|
||||||
|
setMkv("ApkDigestTimeout", "ApkDigestTimeout", DeviceInfo.instance.ApkDigestTimeout)
|
||||||
|
setMkv("TimeZone1", "TimeZone", TimeZone.getDefault().id)
|
||||||
|
DeviceInfo.instance.getMemoryInfo(MemoryInfoType.FREE_MEMORY)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadAsyncData(onUpdate: (List<InfoItem>) -> Unit) {
|
||||||
|
DeviceInfo.instance.onAdInit {
|
||||||
|
allInfos["GAID"] = InfoItem("GAID",DeviceInfo.instance.AdvertisingTrackingId)
|
||||||
|
allInfos["LimitedAdTracking"] = InfoItem("LimitedAdTracking",DeviceInfo.instance.LimitedAdTracking.toString())
|
||||||
|
onUpdate(allInfos.values.toList())
|
||||||
|
}
|
||||||
|
Executors.newSingleThreadExecutor().execute {
|
||||||
|
try {
|
||||||
|
val adid = AdvertisingIdClient(this.context).info
|
||||||
|
if (adid.id != null) {
|
||||||
|
allInfos["adid"] = InfoItem("Ad Id", adid.id!!)
|
||||||
|
}
|
||||||
|
DeviceInfo.instance.getApkDigest()
|
||||||
|
setMkv("ApkDigest", "ApkDigest", DeviceInfo.instance.ApkDigest)
|
||||||
|
setMkv("ApkDigestSize", "ApkDigestSize", DeviceInfo.instance.ApkDigestSize)
|
||||||
|
setMkv("ApkDigestTimeout", "ApkDigestTimeout", DeviceInfo.instance.ApkDigestTimeout)
|
||||||
|
setMkv( "CertificateFingerprint", "CertificateFingerprint", DeviceInfo.instance.getCertificateFingerprint())
|
||||||
|
setMkv( "connectionType", "connectionType", DeviceInfo.instance.getConnectionType())
|
||||||
|
onUpdate(allInfos.values.toList())
|
||||||
|
|
||||||
|
} catch (e: java.lang.Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val client = AppSet.getClient(this.context) as AppSetIdClient
|
||||||
|
val task: Task<AppSetIdInfo> = client.appSetIdInfo as Task<AppSetIdInfo>
|
||||||
|
task.addOnSuccessListener {
|
||||||
|
// Determine current scope of app set ID.
|
||||||
|
val scope: Int = it.scope
|
||||||
|
|
||||||
|
// Read app set ID value, which uses version 4 of the
|
||||||
|
// universally unique identifier (UUID) format.
|
||||||
|
val id: String = it.id
|
||||||
|
allInfos["idfv"] = InfoItem("idfv", it.id)
|
||||||
|
allInfos["idfv_scope"] = InfoItem("idfv_scope", it.scope.toString())
|
||||||
|
onUpdate(allInfos.values.toList())
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
294
mydev/src/main/java/com/xyzshell/mydev/MainActivity.kt
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
package com.xyzshell.mydev
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.compose.setContent
|
||||||
|
import androidx.activity.enableEdgeToEdge
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.statusBarsPadding
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextField
|
||||||
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.compose.ui.unit.times
|
||||||
|
import com.xyzshell.mydev.utils.DeviceInfo
|
||||||
|
import com.xyzshell.mydev.utils.ProtobufExtensionsKt
|
||||||
|
|
||||||
|
|
||||||
|
class MainActivity : ComponentActivity() {
|
||||||
|
private lateinit var infos:InfoData
|
||||||
|
@SuppressLint("QueryPermissionsNeeded")
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
val isAndroid15 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM //VANILLA_ICE_CREAM是35
|
||||||
|
DeviceInfo.instance.init(this.applicationContext)
|
||||||
|
infos = InfoData(this.applicationContext)
|
||||||
|
val t = ProtobufExtensionsKt().fromBase64("EpoDEpcDfMK7VlzA4rkFO61o03LOZRMV3qg-q6xuwRLaqgLASA0AHNCtUXZcIPszSRkfJ2lql3TMcvAd4ug3C1Q_TsDU5R3jV_d4ms0pCu2e9xao_zX8StWgFqGpM2v_k7hWK3tmN8r3T9tPW0qgX4yPA6dsssCyaS8IggHTaijlVJZ4afGfgh9tcASLnmBoQY5ivNpkAUyLCEvMk4ZdQa7j4MqukZi82g6WmIqPLVaS-UEZXSVLuymI8cyT9aIHZQjeQVH8xqE_LbtGw5kl_jK7oo7W7abwh8qWvyubA1x0IILG1ArfMjdTc9jZDiLl0cTFy8PkiKhnyaE9sO8_vmyMWl4h6aW2ZwoTnPnwn24WROcqktXcCIk2X1ToLTOGxnL8cDD6AH7V25dLYt41GgttM-9DcQchxJ_fRJaDOJ0UtHXSAFnlQbEVs5yWD8CNbR2SzFW7AJnmrf4bRfLzThn4m_c28T3qUuBHkCKrXxOvE1K2fLWcaQ41i4OR8xrwEZr-iSgNrVqr2obs54cb7g0blXiIZIDRoAhD5sU=",true)
|
||||||
|
val t1 = ProtobufExtensionsKt().toUUID(t)
|
||||||
|
setContent {
|
||||||
|
MaterialTheme {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.verticalScroll(rememberScrollState())
|
||||||
|
.fillMaxWidth().statusBarsPadding().navigationBarsPadding(),
|
||||||
|
) {
|
||||||
|
Spacer(modifier = Modifier.height(36.dp))
|
||||||
|
Button({
|
||||||
|
val uri = Uri.parse("https://uutool.cn/ua/")
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW, uri)
|
||||||
|
//intent.setClass(this@MainActivity, ViewBrowseActivity::class.java)
|
||||||
|
//intent.setData(uri)
|
||||||
|
//intent.addCategory(Intent.CATEGORY_BROWSABLE)
|
||||||
|
//startActivity(intent)
|
||||||
|
//intent.setPackage("com.android.chrome")
|
||||||
|
//startActivity(intent)
|
||||||
|
startActivity(Intent.createChooser(intent, "选择浏览器"))
|
||||||
|
if (intent.resolveActivity(packageManager) != null) {
|
||||||
|
startActivity(intent)
|
||||||
|
} else {
|
||||||
|
// 如果没有浏览器应用可用,显示一个提示
|
||||||
|
Log.i("App Info","没有可用的浏览器应用")
|
||||||
|
}
|
||||||
|
}) { Text("查看浏览器UserAgent") }
|
||||||
|
Row(Modifier.padding(10.dp)) {
|
||||||
|
TextField("", onValueChange = {
|
||||||
|
|
||||||
|
}, Modifier.fillMaxWidth().padding(1.dp), placeholder = {Text("输入名称")})
|
||||||
|
}
|
||||||
|
BaseInfo(infos)
|
||||||
|
Spacer(modifier = Modifier.height(6.dp))
|
||||||
|
BuildInfo()
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
BuildVerInfo()
|
||||||
|
Spacer(modifier = Modifier.height(6.dp))
|
||||||
|
BuildVerCodiInfo()
|
||||||
|
Spacer(modifier = Modifier.height(6.dp))
|
||||||
|
SystemPropInfo(infos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BaseInfo(infoData: InfoData) {
|
||||||
|
infoData.GetAppInfo()
|
||||||
|
var stateData by remember { mutableStateOf(infoData.AllInfos) }
|
||||||
|
// 模拟异步数据加载
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
infoData.loadAsyncData { updatedItems ->
|
||||||
|
stateData = updatedItems
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InfoBox("基本信息", stateData)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SystemPropInfo(infoData: InfoData) {
|
||||||
|
val infos = infoData.getPropInfo()
|
||||||
|
InfoBox("System Properties", infos)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BuildInfo() {
|
||||||
|
val buildCls = Build::class.java
|
||||||
|
val fi = buildCls.declaredFields
|
||||||
|
val infos = mutableListOf<InfoItem>()
|
||||||
|
for (it in fi) {
|
||||||
|
val obj = it.get(null)
|
||||||
|
val info = getStr(obj)
|
||||||
|
infos.add(InfoItem(it.name, info))
|
||||||
|
}
|
||||||
|
InfoBox("Build Info", infos)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BuildVerInfo() {
|
||||||
|
val buildVerCLs = Build.VERSION::class.java
|
||||||
|
val vfi = buildVerCLs.declaredFields
|
||||||
|
val infos = mutableListOf<InfoItem>()
|
||||||
|
for (it in vfi) {
|
||||||
|
val obj =it.get(null)
|
||||||
|
val info = getStr(obj)
|
||||||
|
infos.add(InfoItem(it.name, info))
|
||||||
|
}
|
||||||
|
InfoBox("Build Ver Info", infos)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BuildVerCodiInfo() {
|
||||||
|
val buildVerCode = Build.VERSION_CODES::class.java
|
||||||
|
val vcfi = buildVerCode.declaredFields
|
||||||
|
val infos = mutableListOf<InfoItem>()
|
||||||
|
for (it in vcfi) {
|
||||||
|
val obj =it.get(null)
|
||||||
|
val info = getStr(obj)
|
||||||
|
infos.add(InfoItem(it.name, info))
|
||||||
|
}
|
||||||
|
InfoBox("Build Ver Code Info", infos)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun getStr(obj: Any) : String {
|
||||||
|
var info = ""
|
||||||
|
if(obj!=null) {
|
||||||
|
info = if (obj is Array<*>) {
|
||||||
|
obj.joinToString(",")
|
||||||
|
} else {
|
||||||
|
obj.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
@Composable
|
||||||
|
fun InfoBox(title:String,infoItems: List<InfoItem>) {
|
||||||
|
Column {
|
||||||
|
Text(title, modifier = Modifier.padding(16.dp))
|
||||||
|
HorizontalDivider(thickness = 2.dp)
|
||||||
|
InfoListScreen(infoItems)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun InfoListScreen(infoItems: List<InfoItem>) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(16.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
infoItems.forEach { item ->
|
||||||
|
BoxItem(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BoxItem(item: InfoItem) {
|
||||||
|
Card() {
|
||||||
|
AdaptiveInfoLayoutWithMeasure(item = item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果你需要更精确的测量,可以使用这个版本
|
||||||
|
@SuppressLint("UnusedBoxWithConstraintsScope")
|
||||||
|
@Composable
|
||||||
|
fun AdaptiveInfoLayoutWithMeasure(item: InfoItem) {
|
||||||
|
var useVerticalLayout by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
BoxWithConstraints {
|
||||||
|
val maxWidth = maxWidth
|
||||||
|
val density = LocalDensity.current
|
||||||
|
|
||||||
|
// 简单的启发式判断:如果文本长度超过屏幕宽度的一半,使用垂直布局
|
||||||
|
val textWidth = with(density) {
|
||||||
|
item.propertyValue.length * 8.dp // 大概估算每个字符8dp宽度
|
||||||
|
}
|
||||||
|
|
||||||
|
useVerticalLayout = textWidth > maxWidth * 0.6f || item.propertyValue.length > 20
|
||||||
|
|
||||||
|
if (useVerticalLayout) {
|
||||||
|
// 垂直布局
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = item.propertyName,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = item.propertyValue,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
lineHeight = 20.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 水平布局
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(16.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = item.propertyName,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.width(16.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = item.propertyValue,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
modifier = Modifier.weight(2f),
|
||||||
|
textAlign = TextAlign.End,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 预览
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
@Composable
|
||||||
|
fun InfoListPreview() {
|
||||||
|
MaterialTheme {
|
||||||
|
// InfoListScreen()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
10
mydev/src/main/java/com/xyzshell/mydev/ViewBrowseActivity.kt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package com.xyzshell.mydev
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
|
||||||
|
class ViewBrowseActivity: ComponentActivity() {
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
}
|
||||||
|
}
|
||||||
7
mydev/src/main/java/com/xyzshell/mydev/ViewDataItem.kt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package com.xyzshell.mydev
|
||||||
|
|
||||||
|
class ViewDataItem {
|
||||||
|
val name: String = ""
|
||||||
|
val value: String = ""
|
||||||
|
|
||||||
|
}
|
||||||
11
mydev/src/main/java/com/xyzshell/mydev/biz/CollectData.kt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package com.xyzshell.mydev.biz
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
|
||||||
|
|
||||||
|
class CollectData {
|
||||||
|
private val buildId: String get() = Build.ID
|
||||||
|
private val model: String get() = Build.MODEL
|
||||||
|
private val device: String get() = Build.DEVICE
|
||||||
|
|
||||||
|
}
|
||||||
184
mydev/src/main/java/com/xyzshell/mydev/data/AndroidInfo.kt
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
package com.xyzshell.mydev.data
|
||||||
|
|
||||||
|
data class AndroidInfo(
|
||||||
|
val id: String,
|
||||||
|
val device:DeviceInfo,
|
||||||
|
val system: SystemInfo
|
||||||
|
)
|
||||||
|
|
||||||
|
class DeviceInfo {
|
||||||
|
/**
|
||||||
|
* 设备型号
|
||||||
|
*/
|
||||||
|
val model: String = ""
|
||||||
|
|
||||||
|
// '产品名称'")
|
||||||
|
val product: String = ""
|
||||||
|
|
||||||
|
// '设备代号'")
|
||||||
|
val device: String = ""
|
||||||
|
|
||||||
|
// '主板名称'")
|
||||||
|
val board: String = ""
|
||||||
|
|
||||||
|
// '制造商'")
|
||||||
|
val manufacturer: String = ""
|
||||||
|
|
||||||
|
// '品牌'")
|
||||||
|
val brand: String = ""
|
||||||
|
|
||||||
|
// '硬件标识'")
|
||||||
|
val hardware: String = ""
|
||||||
|
|
||||||
|
// '认证用制造商'")
|
||||||
|
val manufacturerForAttestation: String = ""
|
||||||
|
|
||||||
|
// '产品代码'")
|
||||||
|
val productCode: String = ""
|
||||||
|
|
||||||
|
// '产品全名'")
|
||||||
|
val productFullName: String = ""
|
||||||
|
|
||||||
|
// '产品制造日期'")
|
||||||
|
val productMfgDate: String = ""
|
||||||
|
|
||||||
|
// CPU相关
|
||||||
|
// 'CPU架构'")
|
||||||
|
val cpuAbi: String = ""
|
||||||
|
|
||||||
|
// 'CPU核心数'")
|
||||||
|
val cpuCores: Int = 0
|
||||||
|
|
||||||
|
// '芯片名称'")
|
||||||
|
val chipName: String = ""
|
||||||
|
|
||||||
|
// 'SoC制造商'")
|
||||||
|
val socManufacturer: String = ""
|
||||||
|
|
||||||
|
// 'SoC型号'")
|
||||||
|
val socModel: String = ""
|
||||||
|
|
||||||
|
// '主板平台'")
|
||||||
|
val boardPlatform: String = ""
|
||||||
|
|
||||||
|
// GPU相关
|
||||||
|
// 'GPU厂商'")
|
||||||
|
val gpuVendor: String = ""
|
||||||
|
|
||||||
|
// 'GPU型号'")
|
||||||
|
val gpuModel: String = ""
|
||||||
|
|
||||||
|
// 'GPU渲染器'")
|
||||||
|
val gpuRenderer: String = ""
|
||||||
|
|
||||||
|
// 'OpenGL版本'")
|
||||||
|
val openglVersion: String = ""
|
||||||
|
|
||||||
|
// 'OpenGL扩展'")
|
||||||
|
val openglExtensions: String = ""
|
||||||
|
|
||||||
|
// 'Vulkan版本'")
|
||||||
|
val vulkanVersion: String = ""
|
||||||
|
|
||||||
|
// 屏幕相关
|
||||||
|
// '屏幕宽度像素'")
|
||||||
|
val widthPixels: Int = 0
|
||||||
|
|
||||||
|
// '屏幕高度像素'")
|
||||||
|
val heightPixels: Int = 0
|
||||||
|
|
||||||
|
// '密度DPI'")
|
||||||
|
val densityDpi: Int = 0
|
||||||
|
|
||||||
|
// '物理DPI'")
|
||||||
|
val dpi: Int = 0
|
||||||
|
|
||||||
|
// 存储内存
|
||||||
|
// '总存储空间'")
|
||||||
|
val totalSpace: Long = 0
|
||||||
|
|
||||||
|
// '总内存'")
|
||||||
|
val totalMemory: Long = 0
|
||||||
|
|
||||||
|
// 基带相关
|
||||||
|
// '基带版本'")
|
||||||
|
val radio: String = ""
|
||||||
|
|
||||||
|
// 'RIL产品代码'")
|
||||||
|
val rilProductCode: String = ""
|
||||||
|
|
||||||
|
// 'RIL官方CSC版本'")
|
||||||
|
val rilOfficialCscver: String = ""
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class SystemInfo {
|
||||||
|
// 系统版本信息
|
||||||
|
// '发布版本,如Android 13, 14等'")
|
||||||
|
val orRelease: String = ""
|
||||||
|
|
||||||
|
// 'SDK版本整数'")
|
||||||
|
val sdkInt: Int= 0
|
||||||
|
|
||||||
|
// '代号,如TIRAMISU, UPSIDE_DOWN_CAKE等'")
|
||||||
|
val codename: String = ""
|
||||||
|
|
||||||
|
// '基础OS版本'")
|
||||||
|
val baseOs: String = ""
|
||||||
|
|
||||||
|
// '增量版本'")
|
||||||
|
val incremental: String = ""
|
||||||
|
|
||||||
|
// '安全补丁日期'")
|
||||||
|
val securityPatch: String = ""
|
||||||
|
|
||||||
|
// 构建信息
|
||||||
|
// '构建ID'")
|
||||||
|
val buildId: String = ""
|
||||||
|
|
||||||
|
// '系统指纹'")
|
||||||
|
val fingerprint: String = ""
|
||||||
|
|
||||||
|
// '显示版本'")
|
||||||
|
val display: String = ""
|
||||||
|
|
||||||
|
// '构建主机'")
|
||||||
|
val host: String = ""
|
||||||
|
|
||||||
|
// '构建标签'")
|
||||||
|
val tags: String = ""
|
||||||
|
|
||||||
|
// '构建类型,如user, userdebug等'")
|
||||||
|
val type: String = ""
|
||||||
|
|
||||||
|
// '构建用户'")
|
||||||
|
val user: String = ""
|
||||||
|
|
||||||
|
// '构建时间戳'")
|
||||||
|
val time: Long = 0
|
||||||
|
|
||||||
|
// 系统组件
|
||||||
|
// '引导程序版本'")
|
||||||
|
val bootloader: String = ""
|
||||||
|
|
||||||
|
// '内核版本'")
|
||||||
|
val kernelVersion: String = ""
|
||||||
|
|
||||||
|
// '厂商API版本'")
|
||||||
|
val vendorApi: Int = 0
|
||||||
|
|
||||||
|
// Google服务
|
||||||
|
// 'GSF ID'")
|
||||||
|
val gsfId: String = ""
|
||||||
|
|
||||||
|
// 'GMS版本'")
|
||||||
|
val gmsVersion: String = ""
|
||||||
|
|
||||||
|
// 系统标识
|
||||||
|
// 'Android ID'")
|
||||||
|
val androidId: String = ""
|
||||||
|
|
||||||
|
// '用户代理字符串'")
|
||||||
|
val userAgent: String = ""
|
||||||
|
}
|
||||||