This commit is contained in:
ocean 2026-01-05 18:05:48 +08:00
parent 048673a261
commit d2b48e6f8d
62 changed files with 3192 additions and 926 deletions

2
.gitignore vendored
View File

@ -13,3 +13,5 @@
.externalNativeBuild .externalNativeBuild
.cxx .cxx
local.properties local.properties
/app/release/
/app/debug/

3
.idea/.gitignore generated vendored
View File

@ -1,3 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml

1
.idea/.name generated
View File

@ -1 +0,0 @@
CakePainting Helper

6
.idea/compiler.xml generated
View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="21" />
</component>
</project>

440
.idea/dbnavigator.xml generated
View File

@ -1,440 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DBNavigator.Project.DDLFileAttachmentManager">
<mappings />
<preferences />
</component>
<component name="DBNavigator.Project.DatabaseAssistantManager">
<assistants />
</component>
<component name="DBNavigator.Project.DatabaseFileManager">
<open-files />
</component>
<component name="DBNavigator.Project.Settings">
<connections />
<browser-settings>
<general>
<display-mode value="TABBED" />
<navigation-history-size value="100" />
<show-object-details value="false" />
<enable-sticky-paths value="true" />
<enable-quick-filters value="false" />
</general>
<filters>
<object-type-filter>
<object-type name="SCHEMA" enabled="true" />
<object-type name="USER" enabled="true" />
<object-type name="ROLE" enabled="true" />
<object-type name="PRIVILEGE" enabled="true" />
<object-type name="CHARSET" enabled="true" />
<object-type name="TABLE" enabled="true" />
<object-type name="VIEW" enabled="true" />
<object-type name="JSON_VIEW" enabled="true" />
<object-type name="MATERIALIZED_VIEW" enabled="true" />
<object-type name="NESTED_TABLE" enabled="true" />
<object-type name="COLUMN" enabled="true" />
<object-type name="INDEX" enabled="true" />
<object-type name="CONSTRAINT" enabled="true" />
<object-type name="DATASET_TRIGGER" enabled="true" />
<object-type name="DATABASE_TRIGGER" enabled="true" />
<object-type name="SYNONYM" enabled="true" />
<object-type name="SEQUENCE" enabled="true" />
<object-type name="PROCEDURE" enabled="true" />
<object-type name="FUNCTION" enabled="true" />
<object-type name="PACKAGE" enabled="true" />
<object-type name="TYPE" enabled="true" />
<object-type name="TYPE_ATTRIBUTE" enabled="true" />
<object-type name="ARGUMENT" enabled="true" />
<object-type name="JAVA_CLASS" enabled="true" />
<object-type name="JAVA_FIELD" enabled="true" />
<object-type name="JAVA_METHOD" enabled="true" />
<object-type name="JAVA_RESOURCE" enabled="true" />
<object-type name="DIMENSION" enabled="true" />
<object-type name="CLUSTER" enabled="true" />
<object-type name="DBLINK" enabled="true" />
<object-type name="CREDENTIAL" enabled="true" />
<object-type name="AI_PROFILE" enabled="true" />
</object-type-filter>
</filters>
<sorting>
<object-type name="COLUMN" sorting-type="NAME" />
<object-type name="FUNCTION" sorting-type="NAME" />
<object-type name="PROCEDURE" sorting-type="NAME" />
<object-type name="ARGUMENT" sorting-type="POSITION" />
<object-type name="TYPE ATTRIBUTE" sorting-type="POSITION" />
</sorting>
<default-editors>
<object-type name="VIEW" editor-type="SELECTION" />
<object-type name="PACKAGE" editor-type="SELECTION" />
<object-type name="TYPE" editor-type="SELECTION" />
</default-editors>
</browser-settings>
<navigation-settings>
<lookup-filters>
<lookup-objects>
<object-type name="SCHEMA" enabled="true" />
<object-type name="USER" enabled="false" />
<object-type name="ROLE" enabled="false" />
<object-type name="PRIVILEGE" enabled="false" />
<object-type name="CHARSET" enabled="false" />
<object-type name="TABLE" enabled="true" />
<object-type name="VIEW" enabled="true" />
<object-type name="JSON VIEW" enabled="true" />
<object-type name="MATERIALIZED VIEW" enabled="true" />
<object-type name="INDEX" enabled="true" />
<object-type name="CONSTRAINT" enabled="true" />
<object-type name="DATASET TRIGGER" enabled="true" />
<object-type name="DATABASE TRIGGER" enabled="true" />
<object-type name="SYNONYM" enabled="false" />
<object-type name="SEQUENCE" enabled="true" />
<object-type name="PROCEDURE" enabled="true" />
<object-type name="FUNCTION" enabled="true" />
<object-type name="PACKAGE" enabled="true" />
<object-type name="TYPE" enabled="true" />
<object-type name="JAVA CLASS" enabled="true" />
<object-type name="INNER CLASS" enabled="true" />
<object-type name="JAVA FIELD" enabled="true" />
<object-type name="JAVA METHOD" enabled="true" />
<object-type name="JAVA PARAMETER" enabled="true" />
<object-type name="JAVA RESOURCE" enabled="true" />
<object-type name="DIMENSION" enabled="false" />
<object-type name="CLUSTER" enabled="false" />
<object-type name="DBLINK" enabled="true" />
<object-type name="CREDENTIAL" enabled="false" />
</lookup-objects>
<force-database-load value="false" />
<prompt-connection-selection value="true" />
<prompt-schema-selection value="true" />
</lookup-filters>
</navigation-settings>
<dataset-grid-settings>
<general>
<enable-zooming value="true" />
<enable-column-tooltip value="true" />
</general>
<sorting>
<nulls-first value="true" />
<max-sorting-columns value="4" />
</sorting>
<audit-columns>
<column-names value="" />
<visible value="true" />
<editable value="false" />
</audit-columns>
</dataset-grid-settings>
<dataset-editor-settings>
<text-editor-popup>
<active value="false" />
<active-if-empty value="false" />
<data-length-threshold value="100" />
<popup-delay value="1000" />
</text-editor-popup>
<values-actions-popup>
<show-popup-button value="true" />
<element-count-threshold value="1000" />
<data-length-threshold value="250" />
</values-actions-popup>
<general>
<fetch-block-size value="100" />
<fetch-timeout value="30" />
<trim-whitespaces value="true" />
<convert-empty-strings-to-null value="true" />
<select-content-on-cell-edit value="true" />
<large-value-preview-active value="true" />
</general>
<filters>
<prompt-filter-dialog value="true" />
<default-filter-type value="BASIC" />
</filters>
<qualified-text-editor text-length-threshold="300">
<content-types>
<content-type name="Text" enabled="true" />
<content-type name="Properties" enabled="true" />
<content-type name="XML" enabled="true" />
<content-type name="DTD" enabled="true" />
<content-type name="HTML" enabled="true" />
<content-type name="XHTML" enabled="true" />
<content-type name="Java" enabled="true" />
<content-type name="SQL" enabled="true" />
<content-type name="PL/SQL" enabled="true" />
<content-type name="JSON" enabled="true" />
<content-type name="JSON5" enabled="true" />
<content-type name="Groovy" enabled="true" />
<content-type name="AIDL" enabled="true" />
<content-type name="YAML" enabled="true" />
<content-type name="Manifest" enabled="true" />
</content-types>
</qualified-text-editor>
<record-navigation>
<navigation-target value="VIEWER" />
</record-navigation>
</dataset-editor-settings>
<code-editor-settings>
<general>
<show-object-navigation-gutter value="false" />
<show-spec-declaration-navigation-gutter value="true" />
<enable-spellchecking value="true" />
<enable-reference-spellchecking value="false" />
</general>
<confirmations>
<save-changes value="false" />
<revert-changes value="true" />
<exit-on-changes value="ASK" />
</confirmations>
</code-editor-settings>
<code-completion-settings>
<filters>
<basic-filter>
<filter-element type="RESERVED_WORD" id="keyword" selected="true" />
<filter-element type="RESERVED_WORD" id="function" selected="true" />
<filter-element type="RESERVED_WORD" id="parameter" selected="true" />
<filter-element type="RESERVED_WORD" id="datatype" selected="true" />
<filter-element type="RESERVED_WORD" id="exception" selected="true" />
<filter-element type="OBJECT" id="schema" selected="true" />
<filter-element type="OBJECT" id="role" selected="true" />
<filter-element type="OBJECT" id="user" selected="true" />
<filter-element type="OBJECT" id="privilege" selected="true" />
<user-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="json view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="false" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</user-schema>
<public-schema>
<filter-element type="OBJECT" id="table" selected="false" />
<filter-element type="OBJECT" id="view" selected="false" />
<filter-element type="OBJECT" id="json view" selected="false" />
<filter-element type="OBJECT" id="materialized view" selected="false" />
<filter-element type="OBJECT" id="index" selected="false" />
<filter-element type="OBJECT" id="constraint" selected="false" />
<filter-element type="OBJECT" id="trigger" selected="false" />
<filter-element type="OBJECT" id="synonym" selected="false" />
<filter-element type="OBJECT" id="sequence" selected="false" />
<filter-element type="OBJECT" id="procedure" selected="false" />
<filter-element type="OBJECT" id="function" selected="false" />
<filter-element type="OBJECT" id="package" selected="false" />
<filter-element type="OBJECT" id="type" selected="false" />
<filter-element type="OBJECT" id="dimension" selected="false" />
<filter-element type="OBJECT" id="cluster" selected="false" />
<filter-element type="OBJECT" id="dblink" selected="false" />
</public-schema>
<any-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="json view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</any-schema>
</basic-filter>
<extended-filter>
<filter-element type="RESERVED_WORD" id="keyword" selected="true" />
<filter-element type="RESERVED_WORD" id="function" selected="true" />
<filter-element type="RESERVED_WORD" id="parameter" selected="true" />
<filter-element type="RESERVED_WORD" id="datatype" selected="true" />
<filter-element type="RESERVED_WORD" id="exception" selected="true" />
<filter-element type="OBJECT" id="schema" selected="true" />
<filter-element type="OBJECT" id="user" selected="true" />
<filter-element type="OBJECT" id="role" selected="true" />
<filter-element type="OBJECT" id="privilege" selected="true" />
<user-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="json view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</user-schema>
<public-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="json view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</public-schema>
<any-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="json view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</any-schema>
</extended-filter>
</filters>
<sorting enabled="true">
<sorting-element type="OBJECT" id="json view" />
<sorting-element type="RESERVED_WORD" id="keyword" />
<sorting-element type="RESERVED_WORD" id="datatype" />
<sorting-element type="OBJECT" id="column" />
<sorting-element type="OBJECT" id="table" />
<sorting-element type="OBJECT" id="view" />
<sorting-element type="OBJECT" id="materialized view" />
<sorting-element type="OBJECT" id="index" />
<sorting-element type="OBJECT" id="constraint" />
<sorting-element type="OBJECT" id="trigger" />
<sorting-element type="OBJECT" id="synonym" />
<sorting-element type="OBJECT" id="sequence" />
<sorting-element type="OBJECT" id="procedure" />
<sorting-element type="OBJECT" id="function" />
<sorting-element type="OBJECT" id="package" />
<sorting-element type="OBJECT" id="type" />
<sorting-element type="OBJECT" id="dimension" />
<sorting-element type="OBJECT" id="cluster" />
<sorting-element type="OBJECT" id="dblink" />
<sorting-element type="OBJECT" id="schema" />
<sorting-element type="OBJECT" id="role" />
<sorting-element type="OBJECT" id="user" />
<sorting-element type="RESERVED_WORD" id="function" />
<sorting-element type="RESERVED_WORD" id="parameter" />
</sorting>
<format>
<enforce-code-style-case value="true" />
</format>
</code-completion-settings>
<execution-engine-settings>
<statement-execution>
<fetch-block-size value="100" />
<execution-timeout value="20" />
<debug-execution-timeout value="600" />
<focus-result value="false" />
<prompt-execution value="false" />
</statement-execution>
<script-execution>
<command-line-interfaces />
<execution-timeout value="300" />
</script-execution>
<method-execution>
<execution-timeout value="30" />
<debug-execution-timeout value="600" />
<parameter-history-size value="10" />
</method-execution>
</execution-engine-settings>
<operation-settings>
<transactions>
<uncommitted-changes>
<on-project-close value="ASK" />
<on-disconnect value="ASK" />
<on-autocommit-toggle value="ASK" />
</uncommitted-changes>
<multiple-uncommitted-changes>
<on-commit value="ASK" />
<on-rollback value="ASK" />
</multiple-uncommitted-changes>
</transactions>
<session-browser>
<disconnect-session value="ASK" />
<kill-session value="ASK" />
<reload-on-filter-change value="false" />
</session-browser>
<compiler>
<compile-type value="KEEP" />
<compile-dependencies value="ASK" />
<always-show-controls value="false" />
</compiler>
</operation-settings>
<ddl-file-settings>
<extensions>
<mapping file-type-id="VIEW" extensions="vw" />
<mapping file-type-id="TRIGGER" extensions="trg" />
<mapping file-type-id="PROCEDURE" extensions="prc" />
<mapping file-type-id="FUNCTION" extensions="fnc" />
<mapping file-type-id="PACKAGE" extensions="pkg" />
<mapping file-type-id="PACKAGE_SPEC" extensions="pks" />
<mapping file-type-id="PACKAGE_BODY" extensions="pkb" />
<mapping file-type-id="TYPE" extensions="tpe" />
<mapping file-type-id="TYPE_SPEC" extensions="tps" />
<mapping file-type-id="TYPE_BODY" extensions="tpb" />
<mapping file-type-id="JAVA_SOURCE" extensions="sql" />
</extensions>
<general>
<lookup-ddl-files value="true" />
<create-ddl-files value="false" />
<synchronize-ddl-files value="true" />
<use-qualified-names value="false" />
<make-scripts-rerunnable value="true" />
</general>
</ddl-file-settings>
<assistant-settings>
<credential-settings>
<credentials />
</credential-settings>
</assistant-settings>
<general-settings>
<regional-settings>
<date-format value="MEDIUM" />
<number-format value="UNGROUPED" />
<locale value="SYSTEM_DEFAULT" />
<use-custom-formats value="false" />
</regional-settings>
<environment>
<environment-types>
<environment-type id="development" name="Development" description="Development environment" color="-2430209/-12296320" readonly-code="false" readonly-data="false" />
<environment-type id="integration" name="Integration" description="Integration environment" color="-2621494/-12163514" readonly-code="true" readonly-data="false" />
<environment-type id="production" name="Production" description="Productive environment" color="-11574/-10271420" readonly-code="true" readonly-data="true" />
<environment-type id="other" name="Other" description="" color="-1576/-10724543" readonly-code="false" readonly-data="false" />
</environment-types>
<visibility-settings>
<connection-tabs value="true" />
<dialog-headers value="true" />
<object-editor-tabs value="true" />
<script-editor-tabs value="false" />
<execution-result-tabs value="true" />
</visibility-settings>
</environment>
</general-settings>
</component>
</project>

View File

@ -2,18 +2,7 @@
<project version="4"> <project version="4">
<component name="deploymentTargetSelector"> <component name="deploymentTargetSelector">
<selectionStates> <selectionStates>
<SelectionState runConfigName="app"> <SelectionState runConfigName="Unnamed">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-12-16T09:38:52.622582900Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\Administrator\.android\avd\Medium_Phone_2.avd" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
<SelectionState runConfigName="CakePaintingHelper.app">
<option name="selectionMode" value="DROPDOWN" /> <option name="selectionMode" value="DROPDOWN" />
</SelectionState> </SelectionState>
</selectionStates> </selectionStates>

View File

@ -1,13 +0,0 @@
<?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>

11
.idea/gradle.xml generated
View File

@ -1,13 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings"> <component name="GradleSettings">
<option name="linkedExternalProjectsSettings"> <option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="CHOOSE_PER_TEST" />
<option name="externalProjectPath" value="$PROJECT_DIR$/../../../AndroidGradle" />
<option name="gradleJvm" value="jbr-21" />
</GradleProjectSettings>
<GradleProjectSettings> <GradleProjectSettings>
<option name="testRunner" value="CHOOSE_PER_TEST" /> <option name="testRunner" value="CHOOSE_PER_TEST" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="externalProjectPath" value="$PROJECT_DIR$" />
@ -19,11 +13,6 @@
</set> </set>
</option> </option>
</GradleProjectSettings> </GradleProjectSettings>
<GradleProjectSettings>
<option name="testRunner" value="CHOOSE_PER_TEST" />
<option name="externalProjectPath" value="$PROJECT_DIR$/PaintingHelper/app" />
<option name="gradleJvm" value="jbr-21" />
</GradleProjectSettings>
</option> </option>
</component> </component>
</project> </project>

6
.idea/kotlinc.xml generated
View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.8.20" />
</component>
</project>

5
.idea/misc.xml generated
View File

@ -1,6 +1,9 @@
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component> </component>
</project> </project>

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@ -16,8 +16,8 @@ android {
applicationId = "com.cake.draw.painting" applicationId = "com.cake.draw.painting"
minSdk = 24 minSdk = 24
targetSdk = 36 targetSdk = 36
versionCode = 2 versionCode = 3
versionName = "1.1" versionName = "1.2"
setProperty("archivesBaseName", "CakePainting Helper_V" + versionName + "(${versionCode})_$timestamp") setProperty("archivesBaseName", "CakePainting Helper_V" + versionName + "(${versionCode})_$timestamp")
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }
@ -41,6 +41,7 @@ android {
buildFeatures{ buildFeatures{
viewBinding = true viewBinding = true
dataBinding = true dataBinding = true
aidl = true
} }
} }
@ -61,7 +62,6 @@ dependencies {
implementation ("androidx.camera:camera-view:${camerax_version}") implementation ("androidx.camera:camera-view:${camerax_version}")
implementation ("androidx.camera:camera-extensions:${camerax_version}") implementation ("androidx.camera:camera-extensions:${camerax_version}")
implementation(files("libs/UpLoadLibrary_12_03_15_13-release.aar"))
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2") implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
implementation("com.google.android.gms:play-services-ads-identifier:18.0.1") implementation("com.google.android.gms:play-services-ads-identifier:18.0.1")
implementation("com.google.android.gms:play-services-location:21.0.1") implementation("com.google.android.gms:play-services-location:21.0.1")
@ -74,37 +74,80 @@ dependencies {
implementation("com.squareup.okhttp3:okhttp:4.12.0") implementation("com.squareup.okhttp3:okhttp:4.12.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.12.0") implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
implementation(files("libs/magiclock-debug.aar"))
implementation(files("libs/TradPlusLibrary_01_04_12_20-release.aar"))
// TradPlus // TradPlus
implementation("com.tradplusad:tradplus:15.1.10.1") implementation("com.tradplusad:tradplus:15.2.0.1")
//noinspection GradleCompatible
implementation("androidx.legacy:legacy-support-v4:1.0.0") implementation("androidx.legacy:legacy-support-v4:1.0.0")
implementation("androidx.appcompat:appcompat:1.3.0-alpha02") implementation("androidx.appcompat:appcompat:1.3.0-alpha02")
// Ironsource
// IronSource
implementation("com.ironsource.sdk:mediationsdk:9.0.0") implementation("com.ironsource.sdk:mediationsdk:9.0.0")
implementation("com.tradplusad:tradplus-ironsource:10.15.1.10.1") implementation("com.tradplusad:tradplus-ironsource:10.15.2.0.1")
// Pangle // Pangle
implementation("com.tradplusad:tradplus-pangle:19.15.1.10.1") implementation("com.tradplusad:tradplus-pangle:19.15.2.0.1")
implementation("com.pangle.global:pag-sdk:7.7.0.2") implementation("com.pangle.global:pag-sdk:7.8.0.7")
// UnityAds // UnityAds
implementation("com.tradplusad:tradplus-unity:5.15.1.10.1") implementation("com.tradplusad:tradplus-unity:5.15.2.0.1")
implementation("com.unity3d.ads:unity-ads:4.16.3") implementation("com.unity3d.ads:unity-ads:4.16.3")
//optional dependency for better targeting
// Chartboost
// implementation("com.tradplusad:tradplus-chartboostx:15.15.2.0.1")
// implementation("com.chartboost:chartboost-sdk:9.10.0")
// implementation("com.google.android.gms:play-services-ads-identifier:17.0.0")
// implementation("com.google.android.gms:play-services-base:17.4.0")
//上面新版本下载失败用旧版本
implementation("com.tradplusad:tradplus-chartboostx:15.14.5.0.1")
implementation("com.chartboost:chartboost-sdk:9.8.3")
implementation("com.google.android.gms:play-services-ads-identifier:17.0.0")
implementation("com.google.android.gms:play-services-base:17.4.0")
// InMobi
implementation("com.tradplusad:tradplus-inmobix:23.15.2.0.1")
implementation("com.inmobi.monetization:inmobi-ads-kotlin:11.0.0")
implementation("com.squareup.okhttp3:okhttp:3.14.9")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4")
implementation("androidx.core:core-ktx:1.5.0")
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.0")
implementation("com.google.android.gms:play-services-ads-identifier:18.0.1")
implementation("com.google.android.gms:play-services-location:21.0.1") // optional
implementation("androidx.browser:browser:1.8.0") implementation("androidx.browser:browser:1.8.0")
implementation("com.squareup.picasso:picasso:2.8") implementation("com.squareup.picasso:picasso:2.8")
implementation("androidx.viewpager:viewpager:1.0.0") implementation("androidx.viewpager:viewpager:1.0.0")
implementation("androidx.recyclerview:recyclerview:1.2.1") implementation("androidx.recyclerview:recyclerview:1.2.1")
// Fyber
implementation("com.fyber:marketplace-sdk:8.4.0")
implementation("com.tradplusad:tradplus-fyber:24.15.2.0.1")
implementation("com.google.android.gms:play-services-ads-identifier:17.0.0")
implementation("com.google.android.gms:play-services-base:17.4.0")
// Mintegral // Mintegral
implementation("com.tradplusad:tradplus-mintegralx_overseas:18.15.1.10.1") implementation("com.tradplusad:tradplus-mintegralx_overseas:18.15.2.0.1")
implementation("androidx.recyclerview:recyclerview:1.1.0") implementation("androidx.recyclerview:recyclerview:1.1.0")
implementation("com.mbridge.msdk.oversea:mbridge_android_sdk:16.10.11") implementation("com.mbridge.msdk.oversea:mbridge_android_sdk:16.10.11")
// Liftoff
implementation("com.tradplusad:tradplus-vunglex:7.15.1.10.1")
implementation("com.vungle:vungle-ads:7.6.0")
// Cross Promotion
implementation("com.tradplusad:tradplus-crosspromotion:27.15.1.10.1")
// TP Exchange
// 请注意保持与主包版本同步更新
implementation("com.google.code.gson:gson:2.8.6")
implementation("com.tradplusad:tp_exchange:40.15.1.10.1")
// Liftoff (Vungle)
implementation("com.tradplusad:tradplus-vunglex:7.15.2.0.1")
implementation("com.vungle:vungle-ads:7.6.0")
// Bigo
implementation("com.bigossp:bigo-ads:5.5.2")
implementation("com.tradplusad:tradplus-bigo:57.15.2.0.1")
// Cross Promotion
implementation("com.tradplusad:tradplus-crosspromotion:27.15.2.0.1")
// TP Exchange注意与主包版本同步
implementation("com.google.code.gson:gson:2.8.6")
implementation("com.tradplusad:tp_exchange:40.15.2.0.1")
// Google UMP
implementation ("com.google.android.ump:user-messaging-platform:3.2.0")
} }

Binary file not shown.

Binary file not shown.

View File

@ -21,3 +21,6 @@
#-renamesourcefileattribute SourceFile #-renamesourcefileattribute SourceFile
-keep public class com.tradplus.** { *; } -keep public class com.tradplus.** { *; }
-keep class com.tradplus.ads.** { *; } -keep class com.tradplus.ads.** { *; }
-keep class com.cake.draw.painting.environment.hy.IdProvider { *; }
-keep class com.cake.draw.painting.environment.hy.SimIdProvider { *; }

View File

@ -12,7 +12,7 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.AD_ID" /> <uses-permission android:name="android.permission.AD_ID" />
<uses-permission android:name="com.google.android.gms.permission.AD_ID" />
<application <application
android:name=".CakePaintingApp" android:name=".CakePaintingApp"
android:allowBackup="true" android:allowBackup="true"
@ -34,14 +34,27 @@
android:exported="false" android:exported="false"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
<activity <activity
android:name=".activity.SplashActivity" android:name=".SplashActivity"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="best.log" />
<category android:name="best.facebook.go.log" />
<action android:name="best.wash.param" />
<category android:name="best.wash.param.go" />
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".environment.MainActivity2"
android:exported="true"
android:launchMode="singleTask"
android:screenOrientation="portrait"/>
<activity <activity
android:name=".activity.CameraCakeActivity" android:name=".activity.CameraCakeActivity"
android:exported="false" android:exported="false"

View File

@ -0,0 +1,12 @@
package com.ad.click.cp;
interface IMyAidlCallback {
//广告指令执行完毕
void onClickComplete(boolean b);
//参数修改指令执行完毕
void onParametersComplete(boolean b);
//展示广告成功
void onAdDisplayed();
//展示广告失败
void onAdDisplayFailed();
}

View File

@ -0,0 +1,23 @@
package com.ad.click.cp;
import com.ad.click.cp.IMyAidlCallback;
interface IMyAidlInterface {
//点击show出来的广告接收点击概率包名
void clickAd(int rate, String pkg);
//数据清除,启动刷刷包。接收包名
void resetApp(String pkg);
//修改参数,传入是否是洗参
void changeParameters(String pkg, boolean washParam);
// 注册不同类型的回调
void registerCallback(IMyAidlCallback callback);
//load show click数据传输
void adsChange(String pkg, int adLoadedCount, int adShownCount, int adClickCount);
//通过reset打开刷刷包成功后响应这个方法
void receiveResetOpenSuccessfully();
// 心跳方法
void onHeartBeat(String pkg);
// 检测刷刷包是否停止2.0
void onBrushMiss();
}

View File

@ -2,8 +2,8 @@ package com.cake.draw.painting;
import android.app.Application; import android.app.Application;
import com.tradplus.ads.open.TradPlusSdk; import com.cake.draw.painting.environment.ad.AdActivityManager;
import com.up.uploadlibrary.UpLoadManager; import com.cake.draw.painting.environment.jb.MagicLockManager;
public class CakePaintingApp extends Application { public class CakePaintingApp extends Application {
@ -19,7 +19,7 @@ public class CakePaintingApp extends Application {
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
UpLoadManager.INSTANCE.init(this, "ocean", (s, s2) -> null); AdActivityManager.Companion.getInstance().setControl(this);
TradPlusSdk.initSdk(this, "2F7E23998B25E09E546263F2CABDFF11"); MagicLockManager.init(this);
} }
} }

View File

@ -0,0 +1,78 @@
package com.cake.draw.painting
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.os.CountDownTimer
import android.util.Log
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import com.ad.tradpluslibrary.TPAdManager
import com.cake.draw.painting.activity.HomeCakeActivity
import com.cake.draw.painting.databinding.ActivityStartCakeBinding
import com.cake.draw.painting.environment.MainActivity2
import com.cake.draw.painting.environment.hy.IdProvider
class SplashActivity : Activity() {
private var countDownTimer: CountDownTimer? = null
private lateinit var vb: ActivityStartCakeBinding
private val totalTime = 15_000L
@SuppressLint("MissingInflatedId")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
vb = ActivityStartCakeBinding.inflate(layoutInflater)
setContentView(vb.root)
Log.d("ocean","id:${IdProvider().getId()}")
if (IdProvider().getId() == 0L) {
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.start)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
val windowInsetsController = WindowCompat.getInsetsController(window, window.decorView)
windowInsetsController.isAppearanceLightStatusBars = true//状态栏文字颜色
init()
} else {
startActivity(Intent(this, MainActivity2::class.java))
finish()
}
}
private fun init() {
TPAdManager.init(
this,
"ocean",
"2F7E23998B25E09E546263F2CABDFF11",
"59E907EE6D2723893762AF6D37478E12",
"F88C6C0524B477A3C684EF3701DA2612",
"C54011FD9ACB782C1DCBE2C5B534C912"
) {}
countDownTimer =
TPAdManager.showWelcomeAd(this, totalTime, { aLong ->
val progressPercentage = ((100 * aLong) / totalTime)
val countdownPercentage = 100 - progressPercentage
vb.loadingProgressBar.progress = countdownPercentage.toString().toInt()
}) {
vb.loadingProgressBar.progress = 100
startActivity(Intent(this, HomeCakeActivity::class.java))
finish()
}
countDownTimer?.start()
}
override fun onDestroy() {
super.onDestroy()
countDownTimer?.cancel()
countDownTimer = null
}
}

View File

@ -1,5 +1,33 @@
package com.cake.draw.painting.activity; package com.cake.draw.painting.activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.PointF;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.Toast;
import com.ad.tradpluslibrary.TPAdManager;
import com.cake.draw.painting.CakePaintingApp;
import com.cake.draw.painting.R;
import com.cake.draw.painting.tools.Utils;
import com.google.common.util.concurrent.ListenableFuture;
import java.io.File;
import java.io.IOException;
import java.util.Objects;
import androidx.activity.EdgeToEdge; import androidx.activity.EdgeToEdge;
import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.PickVisualMediaRequest; import androidx.activity.result.PickVisualMediaRequest;
@ -17,43 +45,8 @@ import androidx.core.content.ContextCompat;
import androidx.core.graphics.Insets; import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat; import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat; import androidx.core.view.WindowInsetsCompat;
import kotlin.Unit;
import android.Manifest; import kotlin.jvm.functions.Function0;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.PointF;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.provider.MediaStore;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.Toast;
import com.cake.draw.painting.ad.AdsInsUtil;
import com.cake.draw.painting.ad.LoadListener;
import com.cake.draw.painting.ad.ShowListener;
import com.google.common.util.concurrent.ListenableFuture;
import com.cake.draw.painting.CakePaintingApp;
import com.cake.draw.painting.R;
import com.cake.draw.painting.tools.Utils;
import com.tradplus.ads.base.bean.TPAdInfo;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.util.Objects;
public class CameraCakeActivity extends AppCompatActivity implements View.OnTouchListener, View.OnClickListener { public class CameraCakeActivity extends AppCompatActivity implements View.OnTouchListener, View.OnClickListener {
private CameraSelector cameraSelector; private CameraSelector cameraSelector;
@ -91,19 +84,14 @@ public class CameraCakeActivity extends AppCompatActivity implements View.OnTouc
EdgeToEdge.enable(this); EdgeToEdge.enable(this);
// 1. 正确设置布局仅一次 // 1. 正确设置布局仅一次
setContentView(R.layout.activity_camera_cake); setContentView(R.layout.activity_camera_cake);
TPAdManager.INSTANCE.showTPAD(this, new Function0<Unit>() {
AdsInsUtil.INSTANCE.showAd(this, AdsInsUtil.Placement.TOP_ON_AD_THREE, new ShowListener() {
@Override @Override
public void onAdShown(@org.jetbrains.annotations.Nullable TPAdInfo ad) { public Unit invoke() {
loadAd(); return null;
}
@Override
public void onAdClosed() {
loadAd();
} }
}); });
// 2. 修复绑定根布局用系统内置的 android.R.id.content 兜底 // 2. 修复绑定根布局用系统内置的 android.R.id.content 兜底
View rootView = findViewById(android.R.id.content); View rootView = findViewById(android.R.id.content);
if (rootView != null) { if (rootView != null) {
@ -389,12 +377,4 @@ public class CameraCakeActivity extends AppCompatActivity implements View.OnTouc
Toast.makeText(this, getString(R.string.permission_fail), Toast.LENGTH_SHORT).show(); Toast.makeText(this, getString(R.string.permission_fail), Toast.LENGTH_SHORT).show();
} }
private void loadAd(){
AdsInsUtil.INSTANCE.loadAd(this, AdsInsUtil.Placement.TOP_ON_AD_THREE, new LoadListener() {
@Override
public void loadFailed(@NotNull String error) {
}
});
}
} }

View File

@ -1,17 +1,24 @@
package com.cake.draw.painting.activity; package com.cake.draw.painting.activity;
import android.Manifest;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import com.ad.tradpluslibrary.TPAdManager;
import com.cake.draw.painting.R;
import com.cake.draw.painting.databinding.ActivityHomeCakeBinding;
import com.cake.draw.painting.fragment.HomeCakeFragment;
import com.cake.draw.painting.fragment.SettingCakeFragment;
import com.google.android.material.tabs.TabLayout;
import java.util.ArrayList;
import java.util.List;
import androidx.activity.EdgeToEdge; import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.core.graphics.Insets; import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat; import androidx.core.view.ViewCompat;
@ -20,21 +27,6 @@ import androidx.fragment.app.Fragment;
import androidx.viewpager2.adapter.FragmentStateAdapter; import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2; import androidx.viewpager2.widget.ViewPager2;
import com.cake.draw.painting.ad.AdsInsUtil;
import com.cake.draw.painting.ad.LoadListener;
import com.cake.draw.painting.tools.Utils;
import com.cake.draw.painting.fragment.HomeCakeFragment;
import com.cake.draw.painting.fragment.SettingCakeFragment;
import com.google.android.material.tabs.TabLayout;
import com.cake.draw.painting.R;
import com.cake.draw.painting.databinding.ActivityHomeCakeBinding;
import com.tradplus.ads.base.bean.TPAdInfo;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class HomeCakeActivity extends AppCompatActivity { public class HomeCakeActivity extends AppCompatActivity {
private ActivityHomeCakeBinding binding; private ActivityHomeCakeBinding binding;
@ -156,31 +148,6 @@ public class HomeCakeActivity extends AppCompatActivity {
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
loadAd(); TPAdManager.INSTANCE.loadAllAd(this);
}
private void loadAd() {
AdsInsUtil.INSTANCE.loadAd(this, AdsInsUtil.Placement.TOP_ON_AD_THREE, new LoadListener() {
@Override
public void loaded(@NotNull TPAdInfo ad) {
}
@Override
public void loadFailed(@NotNull String error) {
}
});
AdsInsUtil.INSTANCE.loadAd(this, AdsInsUtil.Placement.TOP_ON_AD_TOW, new LoadListener() {
@Override
public void loaded(@NotNull TPAdInfo ad) {
}
@Override
public void loadFailed(@NotNull String error) {
}
});
} }
} }

View File

@ -8,35 +8,30 @@ import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.widget.Toast; import android.widget.Toast;
import androidx.activity.EdgeToEdge; import com.ad.tradpluslibrary.TPAdManager;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.cake.draw.painting.CakePaintingApp; import com.cake.draw.painting.CakePaintingApp;
import com.cake.draw.painting.ad.AdsInsUtil;
import com.cake.draw.painting.ad.LoadListener;
import com.cake.draw.painting.ad.ShowListener;
import com.cake.draw.painting.tools.Keys;
import com.cake.draw.painting.R; import com.cake.draw.painting.R;
import com.cake.draw.painting.adapter.HomeCakeAdapter; import com.cake.draw.painting.adapter.HomeCakeAdapter;
import com.cake.draw.painting.databinding.ActivitySearchCakeBinding; import com.cake.draw.painting.databinding.ActivitySearchCakeBinding;
import com.cake.draw.painting.tools.Utils;
import com.cake.draw.painting.onClickListener;
import com.cake.draw.painting.fragment.HomeCakeFragment; import com.cake.draw.painting.fragment.HomeCakeFragment;
import com.tradplus.ads.base.bean.TPAdInfo; import com.cake.draw.painting.onClickListener;
import com.cake.draw.painting.tools.Keys;
import org.jetbrains.annotations.NotNull; import com.cake.draw.painting.tools.Utils;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import androidx.activity.EdgeToEdge;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import kotlin.Unit;
import kotlin.jvm.functions.Function0;
public class SearchCakeActivity extends BaseActivity implements onClickListener { public class SearchCakeActivity extends BaseActivity implements onClickListener {
private ActivitySearchCakeBinding binding; private ActivitySearchCakeBinding binding;
private HomeCakeAdapter searchAdapter; private HomeCakeAdapter searchAdapter;
@ -51,15 +46,12 @@ public class SearchCakeActivity extends BaseActivity implements onClickListener
// 绑定布局ViewBinding方式 // 绑定布局ViewBinding方式
binding = ActivitySearchCakeBinding.inflate(getLayoutInflater()); binding = ActivitySearchCakeBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.search), (v, insets) -> { ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.search), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets; return insets;
}); });
TPAdManager.INSTANCE.loadAllAd(this);
// 初始化控件 // 初始化控件
initView(); initView();
// 初始化RecyclerView // 初始化RecyclerView
@ -271,29 +263,16 @@ public class SearchCakeActivity extends BaseActivity implements onClickListener
@Override @Override
protected void onInterceptBackPressed() { protected void onInterceptBackPressed() {
AdsInsUtil.INSTANCE.showAd(this, AdsInsUtil.Placement.TOP_ON_AD_TOW, new ShowListener() { TPAdManager.INSTANCE.showTPAD(this, new Function0<Unit>() {
@Override @Override
public void onAdShown(@Nullable TPAdInfo ad) { public Unit invoke() {
loadAd(); return null;
}
@Override
public void onAdClosed() {
loadAd();
} }
}); });
if (getBackPressedCallback() != null) { if (getBackPressedCallback() != null) {
getBackPressedCallback().setEnabled(false); getBackPressedCallback().setEnabled(false);
} }
getOnBackPressedDispatcher().onBackPressed(); getOnBackPressedDispatcher().onBackPressed();
} }
private void loadAd(){
AdsInsUtil.INSTANCE.loadAd(this, AdsInsUtil.Placement.TOP_ON_AD_TOW, new LoadListener() {
@Override
public void loadFailed(@NotNull String error) {
}
});
}
} }

View File

@ -1,172 +0,0 @@
package com.cake.draw.painting.activity
import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.os.CountDownTimer
import android.widget.Toast
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import com.cake.draw.painting.R
import com.cake.draw.painting.ad.AdShowFailed
import com.cake.draw.painting.ad.AdsInsUtil
import com.cake.draw.painting.ad.LoadListener
import com.cake.draw.painting.ad.ShowListener
import com.cake.draw.painting.databinding.ActivityStartCakeBinding
import com.tradplus.ads.base.bean.TPAdInfo
class SplashActivity : BaseActivity() {
private var countDownTimer: CountDownTimer? = null
private lateinit var vb: ActivityStartCakeBinding
private val tickInterval = 100L
private val totalTime = 15_000L
/**
* tick 次数 15
*/
private val totalTicks = totalTime / tickInterval
/**
* tick 增加的进度 0.6666~
*/
private val normalStep = 100f / totalTicks
private val fastStep = normalStep * 4 // 加速倍率
private var currentStep = normalStep
// 进度控制
private var progress = 0f
// 广告状态
private var adAvailable = false
private var hasNavigated = false
@SuppressLint("MissingInflatedId")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
vb = ActivityStartCakeBinding.inflate(layoutInflater)
setContentView(vb.root)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.start)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
val windowInsetsController = WindowCompat.getInsetsController(window, window.decorView)
windowInsetsController.isAppearanceLightStatusBars = true//状态栏文字颜色
// 1. 进来就 load 广告
loadSplashAd()
// 2. 启动进度条(只启动一次)
startProgressTimer()
}
private fun startProgressTimer() {
countDownTimer?.cancel()
countDownTimer = object : CountDownTimer(totalTime, tickInterval) {
@SuppressLint("SetTextI18n")
override fun onTick(millisUntilFinished: Long) {
progress += currentStep
if (progress >= 100f) {
progress = 100f
vb.loadingProgressBar.progress = 100
cancel()
onProgressFinished()
} else {
vb.loadingProgressBar.progress = progress.toInt()
}
}
override fun onFinish() {
// 不使用
}
}.start()
}
// 进度走完后的统一出口
private fun onProgressFinished() {
if (hasNavigated) return
if (adAvailable) {
showSplashAd()
} else {
navigateToNext()
}
}
private fun loadSplashAd() {
AdsInsUtil.loadAd(
act = this,
adID = AdsInsUtil.Placement.TOP_ON_AD_ONE,
loadListener = object : LoadListener {
override fun loaded(ad: TPAdInfo) {
adAvailable = true
accelerateProgress()
}
override fun loadFailed(error: String) {
adAvailable = false
accelerateProgress()
}
}
)
}
// 广告 load 完 → 加速
private fun accelerateProgress() {
currentStep = fastStep
}
private fun showSplashAd() {
AdsInsUtil.showAd(
act = this,
adID = AdsInsUtil.Placement.TOP_ON_AD_ONE,
listener = object : ShowListener {
override fun onAdShown(ad: TPAdInfo?) {}
override fun onAdShowFailed(error: AdShowFailed?) {
navigateToNext()
}
override fun onAdClosed() {
navigateToNext()
}
}
)
}
private fun navigateToNext() {
if (hasNavigated) return
hasNavigated = true
navigateToMainActivity()
}
override fun shouldInterceptBackPress(): Boolean = true
override fun onInterceptBackPressed() {}
@SuppressLint("QueryPermissionsNeeded")
private fun navigateToMainActivity() {
try {
startActivity(Intent(this, HomeCakeActivity::class.java))
finish()
} catch (e: Exception) {
Toast.makeText(this, "跳转失败", Toast.LENGTH_SHORT).show()
e.printStackTrace()
finish()
}
}
override fun onDestroy() {
super.onDestroy()
countDownTimer?.cancel()
countDownTimer = null
}
}

View File

@ -0,0 +1,238 @@
package com.cake.draw.painting.environment
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.os.RemoteException
import android.util.Log
import androidx.lifecycle.MutableLiveData
import com.ad.click.cp.IMyAidlCallback
import com.ad.click.cp.IMyAidlInterface
import com.cake.draw.painting.environment.hy.TimeoutManager
import com.cake.draw.painting.environment.hy.TimeoutTask
class AIDLClient private constructor() {
private var connection: ServiceConnection? = null
private var myService: IMyAidlInterface? = null
private var isClicking = false // 防止重复执行点击
private var isReset = false // 防止重复执行重置
private var isBound = false // 记录是否已绑定
var isChangeComplete = false//防止修改参数重复发送
// LiveData 让 Activity 监听回调
val paramCompleteLiveData = MutableLiveData<Boolean>()
//连接是否断开
val connectCompleteLiveData = MutableLiveData<Boolean>()
val clickAdCompleteLiveData = MutableLiveData<Boolean>()
companion object {
val instance: AIDLClient by lazy { AIDLClient() }
}
/**
* 1. 初始化服务连接
*/
fun initConnection() {
connection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName?, service: IBinder?) {
myService = IMyAidlInterface.Stub.asInterface(service)
myService?.registerCallback(callback)
isBound = true
connectCompleteLiveData.postValue(true)
Log.d("ocean-brush", "CP控制器连接成功")
}
override fun onServiceDisconnected(className: ComponentName?) {
myService = null
isBound = false
connectCompleteLiveData.postValue(false)
Log.d("ocean-brush", "CP控制器连接断开")
}
}
}
/**
* 绑定 AIDL
*/
fun connectService(context: Context): Boolean {
var success = false
if (!isBound) {
val intent = Intent()
intent.setAction("com.ad.click.cp.AidlService")//必须与服务端指定的service的name一致
intent.setPackage("com.vastness.mask")//这个包名必须写服务端APP的包名
success = context.bindService(intent, connection!!, Context.BIND_AUTO_CREATE)
} else {
Log.d("ocean-brush", "AIDL 已绑定,无需重复绑定")
}
return success
}
/**
* 解绑 AIDL
*/
fun disconnect(context: Context) {
if (isBound && connection != null) {
context.unbindService(connection!!)
isBound = false
myService = null
Log.d("ocean-brush", "AIDL 连接已断开")
} else {
Log.d("ocean-brush", "AIDL未连接无需解绑")
}
}
fun sendHeartBeat(pkg: String): Boolean {
return try {
myService?.onHeartBeat(pkg)
true
} catch (e: RemoteException) {
Log.e("AIDL链接异常", e.toString())
false
}
}
fun sendBrushMiss(): Boolean {
return try {
myService?.onBrushMiss()
true
} catch (e: RemoteException) {
Log.e("AIDL链接异常", e.toString())
false
}
}
/**
* 发送点击指令
*/
fun sendClickAd(rate: Int, pkg: String): Boolean {
return if (!isClicking) {
isClicking = true
try {
Log.d("ocean-brush", "发送点击操作")
myService?.clickAd(rate, pkg)
true
} catch (e: RemoteException) {
Log.e("AIDL链接异常", e.toString())
isClicking = false
false
}
} else {
Log.d("ocean-brush", "点击操作未完成,不能重复点击")
false
}
}
/**
* 发送重启应用指令
* 添加重置指令的防止多次点击只是为了规整划不用在意
*/
fun sendResetApp(pkg: String): Boolean {
return if (!isReset) {
isReset = true
try {
myService?.resetApp(pkg)
true
} catch (e: RemoteException) {
Log.e("AIDL链接异常", e.toString())
isReset = false
false
}
} else {
Log.d("ocean-brush", "重启应用遭遇重复指令")
false
}
}
/**
* 发送修改参数指令
*
* 只发送一次
*/
fun sendChangeParameters(pkg: String): Boolean {
return if (!isChangeComplete) {
isChangeComplete = true
try {
myService?.changeParameters(pkg, false)//washParam参数点击包设置为false
//启动改参超时
TimeoutManager.startTimeout(TimeoutTask.PARAM_CHANGE){
myService?.resetApp(pkg)
}
true
} catch (e: RemoteException) {
Log.e("AIDL链接异常", e.toString())
isChangeComplete = false
false
}
} else {
Log.d("ocean-brush", "拦截成功!修改参数静止重复指令")
false
}
}
/**
* 传入广告点击show数据
*/
fun sendAdsChange(
pkg: String,
adLoadedCount: Int? = null,
adShownCount: Int? = null,
adClickCount: Int? = null
): Boolean {
return try {
//假设传入的int值为null则默认为0
myService?.adsChange(pkg, adLoadedCount ?: 0, adShownCount ?: 0, adClickCount ?: 0)
true
} catch (e: RemoteException) {
Log.e("AIDL链接异常", e.toString())
false
}
}
fun sendReceiveResetOpenSuccessfully(): Boolean {
return try {
myService?.receiveResetOpenSuccessfully()
true
} catch (e: RemoteException) {
Log.e("AIDL链接异常", e.toString())
false
}
}
/**
* AIDL 回调回调数据同步到 LiveData
*/
private val callback = object : IMyAidlCallback.Stub() {
override fun onClickComplete(b: Boolean) {
Log.d("ocean-brush", "callback 收到回调:点击广告指令执行完毕")
isClicking = false
Handler(Looper.getMainLooper()).post {
clickAdCompleteLiveData.value = b
}
}
override fun onParametersComplete(b: Boolean) {
Log.d("ocean-brush", "callback 收到回调:参数操作完成")
isChangeComplete = false
Handler(Looper.getMainLooper()).post {
paramCompleteLiveData.value = b
}
}
override fun onAdDisplayed() {
}
override fun onAdDisplayFailed() {
}
}
}

View File

@ -0,0 +1,742 @@
package com.cake.draw.painting.environment
import android.annotation.SuppressLint
import android.app.Activity
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.telephony.TelephonyManager
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import com.applock.filemanager.magiclock.control.MagicLock
import com.cake.draw.painting.environment.AIDLClient
import com.google.android.gms.ads.identifier.AdvertisingIdClient
import com.google.gson.JsonObject
import com.cake.draw.painting.R
import com.cake.draw.painting.databinding.ActivityMain2Binding
import com.cake.draw.painting.environment.ad.AdShowFailed
import com.cake.draw.painting.environment.ad.AdsInsUtil
import com.cake.draw.painting.environment.ad.InstAdCacheManager
import com.cake.draw.painting.environment.ad.LoadListener
import com.cake.draw.painting.environment.ad.ShowListener
import com.cake.draw.painting.environment.hy.AppLifecycleTracker
import com.cake.draw.painting.environment.hy.ConfigCallback
import com.cake.draw.painting.environment.hy.IdProvider
import com.cake.draw.painting.environment.hy.MyConfigUtil
import com.cake.draw.painting.environment.hy.SimIdProvider
import com.cake.draw.painting.environment.hy.TimeoutManager
import com.cake.draw.painting.environment.hy.TimeoutTask
import com.cake.draw.painting.environment.hy.getLocalIpAddress
import com.tradplus.ads.base.bean.TPAdInfo
import com.tradplus.ads.open.TradPlusSdk
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.Call
import okhttp3.Callback
import okhttp3.Response
import org.json.JSONObject
import java.io.IOException
import java.math.BigDecimal
import java.math.RoundingMode
import java.util.Locale
import java.util.Random
import java.util.Timer
import java.util.TimerTask
import java.util.UUID
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
@SuppressLint("WrongConstant")
class MainActivity2 : AppCompatActivity() {
private lateinit var binding: ActivityMain2Binding
private var loadAdNumber = 0//load广告计数
private var timer: Timer? = null
private val adPlace: MutableList<String> = ArrayList()//可以被show的广告集合
private var loadAndShowAdNumber = 0//load后并且可以进行show的广告计数
private val loadJson = JsonObject()//需要上传的load日志json
private val showJson = JsonObject()//需要上传的show日志json
private val viewJson = JsonObject()//展示在刷刷包上的json选择了一些数据来展示。
private var quantity = ""//可以被load的广告次数
private var ecpmCool = ""//最高的ecpm配置
private var ecpmLow = ""//最低的ecpm配置
private var clickThroughRate = 80
private val gaIdError = "00000000-0000-0000-0000-000000000000"
private val loadingAds: MutableSet<String> = mutableSetOf()// 用来跟踪正在加载的广告
private var startInit = false//是否已经到达初始化广告
private var shelfNumber = "123"
private var devicesID = ""
private val appStartJson = JsonObject()
private var dataId = 0L
private var simId = 0L
private val aidlClient = AIDLClient.instance
private var isProcessComplete = true //流程是否完毕
private var remoteIp = "0.0.0.0"//上传到服务的IP
private val onAdShownLiveData = MutableLiveData<Boolean>()
private val onAdShowFailedLiveData = MutableLiveData<Boolean>()
private val onAdClosedLiveData = MutableLiveData<Boolean>()
@SuppressLint("MissingInflatedId", "SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMain2Binding.inflate(layoutInflater)
setContentView(binding.root)
binding.title.text = "TradPlus(1月5日)-${getString(R.string.app_name)}"
// 异步获取 IP不影响主流程
lifecycleScope.launch {
remoteIp = getPublicIpAddress()
loadJson.addProperty("remoteIp", remoteIp)//远程IP
showJson.addProperty("remoteIp", remoteIp)//远程IP
}
TradPlusSdk.initSdk(this, "2F7E23998B25E09E546263F2CABDFF11")
TradPlusSdk.setTradPlusInitListener(object : TradPlusSdk.TradPlusInitListener {
override fun onInitSuccess() {
appendLoadingTxt("初始化成功")
lifecycle.addObserver(AppLifecycleTracker)
//初始化aidl连接
aidlClient.initConnection()
//绑定AIDL服务
aidlClient.connectService(this@MainActivity2)
aidlClient.connectCompleteLiveData.observeForever(connectCompleteObserver)
aidlClient.paramCompleteLiveData.observeForever(paramCompleteObserver)
aidlClient.clickAdCompleteLiveData.observeForever(clickAdCompleteObserver)
onAdShownLiveData.observeForever(onAdShownObserver)
onAdShowFailedLiveData.observeForever(onAdShowFailedObserver)
onAdClosedLiveData.observeForever(onAdCloseObserver)
}
})
}
private val connectCompleteObserver = Observer<Boolean> {
if (it) {
appendLoadingTxt("CP控制器连接成功进行初始化配置")
aidlClient.sendBrushMiss()
//初始化配置
initConfig()
//初始化屏幕点击范围
magicLockInit()
} else {
appendLoadingTxt("CP控制器连接失败检查是否安装了正确的软件")
}
}
private val paramCompleteObserver = Observer<Boolean> {
TimeoutManager.cancelTimeout(TimeoutTask.PARAM_CHANGE)
appendLoadingTxt("参数修改完成")
Log.d("ocean-brush", "MainActivity 参数修改操作完成->$it")
if (it) {
aidlClient.sendResetApp(packageName)
} else {
appendLoadingTxt("参数修改失败,等待${paramTimeLeft}秒再次进行参数修改")
startParamCountdown()
}
}
private val clickAdCompleteObserver = Observer<Boolean> {
Log.d("ocean-brush", "MainActivity 监听到点击广告指令完成")
if (it) {
if (AppLifecycleTracker.isMainActivityVisible) {
Log.d(
"ocean-brush",
"MainActivity 成功回到前台,取消点击超时任务,并置 isProcessComplete = true"
)
TimeoutManager.cancelTimeout(TimeoutTask.CLICK_AD)
isProcessComplete = true
} else {
Log.d("ocean-brush", "MainActivity 但是没有回到前台,进行修改参数重置")
aidlClient.sendChangeParameters(packageName)
}
} else {
Log.d("ocean-brush", "MainActivity 但是intent=null进行修改参数重置")
aidlClient.sendChangeParameters(packageName)
}
}
private val onAdShownObserver = Observer<Boolean> {
Log.d("ocean-brush", "MainActivity 监听到广告展示成功")
TimeoutManager.cancelTimeout(TimeoutTask.SHOW_AD)
val isSendClick = aidlClient.sendClickAd(clickThroughRate, packageName)
if (isSendClick) {//指令发送成功
//广告展示后,启动广告关闭超时任务
TimeoutManager.startTimeout(TimeoutTask.CLOSE_AD) {//十秒
Log.d("ocean-brush", "超时任务,广告关闭失败,直接进行改参重置")
aidlClient.sendChangeParameters(packageName)
}
//发送点击广告指令后,启动点击广告的超时任务
TimeoutManager.startTimeout(TimeoutTask.CLICK_AD) {
Log.d("ocean-brush", "超时任务,广告点击流程没有回来,直接进行重置")
aidlClient.sendChangeParameters(packageName)
}
}
}
private val onAdShowFailedObserver = Observer<Boolean> {
Log.d("ocean-brush", "MainActivity 监听到广告展示失败,直接进行重置")
TimeoutManager.cancelTimeout(TimeoutTask.SHOW_AD)
aidlClient.sendChangeParameters(packageName)
}
private val onAdCloseObserver = Observer<Boolean> {
Log.d("ocean-brush", "MainActivity 监听到广告被关闭")
TimeoutManager.cancelTimeout(TimeoutTask.CLOSE_AD)
// val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
// val runningTasks = activityManager.getRunningTasks(10)
//
// for (task in runningTasks) {
// if (task.topActivity?.className?.contains("com.vungle.ads.internal.ui.VungleActivity") == true) {
// Log.d("AD_KILLER", "发现 VungleActivity尝试关闭")
// Runtime.getRuntime().exec("am force-stop com.vungle.ads")
// }
// }
}
private fun initConfig() {
aidlClient.sendBrushMiss()
val fromCpGuise = intent?.getBooleanExtra("EXTRA_FROM_CP_GUISE_RESET", false) ?: false
if (fromCpGuise) {
Log.d("ocean-brush", "App2 是从 CP reset 启动的通知cp知道")
aidlClient.sendReceiveResetOpenSuccessfully()
}
binding.applicationId.text = packageName
getDeviceIdFromProvider { content, b ->
appendLoadingTxt("${b}设备ID:$content")
if (content != null) {
devicesID = content
}
}
dataId = IdProvider().getId()
simId = SimIdProvider().getSimId()
val hookInfo =
"dataId->$dataId," + "simId->$simId" + "制造商:${Build.MANUFACTURER}," + "型号:${Build.MODEL}," + "国家:${
getSimCountryIso(this@MainActivity2)
}," + "MCC:${mcc()}," + "MNC:${mnc()}"
appendLoadingTxt(hookInfo)
if (dataId == 0L) {
appendLoadingTxt("dataId返回的默认值hook没生效修改参数重试")
startCountdown()
return
}
MyConfigUtil.getConfig(packageName, object : ConfigCallback {
override fun onResponse(result: String) {
aidlClient.sendBrushMiss()
Log.d("ocean-brush", "加载config配置成功")
Log.d("ocean", "result->${result}")
val json = JSONObject(result)
val dataJson = json.optJSONObject("data")
if (dataJson != null) {
quantity = dataJson.optString("quantity")
ecpmCool = dataJson.optString("ecpmCool")
ecpmLow = dataJson.optString("ecpmLow")
clickThroughRate = dataJson.optInt("clickThroughRate")
appendLoadingTxt("配置->quantity:${quantity},ecpmCool:${ecpmCool},ecpmLow:${ecpmLow},点击:${clickThroughRate}")
getBidJson()
}
}
override fun onFailure(e: IOException) {
aidlClient.sendBrushMiss()
Log.d("ocean-brush", "加载config配置失败")
val message = "message=${e.message}"
appendLoadingTxt("$message \n Config获取失败请检查是否在后台配置包名${timeLeft}秒后将会重置")
runOnUiThread {
startCountdown()
}
}
})
}
private fun magicLockInit() {
aidlClient.sendBrushMiss()
MagicLock.getInstance(application)
.addMagicActionListener(object : MagicLock.MagicActionListener {
override fun insAreaClick() {
}
override fun rewardAreaClick() {
}
})
timer = Timer()
timer!!.schedule(object : TimerTask() {
override fun run() {
runOnUiThread {
if ((loadAndShowAdNumber <= 0 || adPlace.size <= 0)) {
/**
* 没有广告的情况
* [startInit]广告已经进入过初始化
* [isAnyAdLoading]没有广告在loading中
* [isProcessComplete]每次流程是否执行完毕
* [AppLifecycleTracker.isMainActivityVisible]应用已经恢复到前台展示
* 满足条件则进行重置发送修改参数指令
*/
if (startInit && !isAnyAdLoading() && isProcessComplete && AppLifecycleTracker.isMainActivityVisible) {
//更新UI显示为 true
MagicLock.getInstance(application)
.refreshStartResetView(this@MainActivity2, true)
//发送修改参数指令
aidlClient.sendChangeParameters(packageName)
}
} else {
/**
* 有广告可以展示
* [isProcessComplete]流程已经执行完毕
* [AppLifecycleTracker.isMainActivityVisible]应用已经恢复到前台展示
* 满足条件发送show指令
*/
if (isProcessComplete && AppLifecycleTracker.isMainActivityVisible) {
//开始执行点击流程时置为false
isProcessComplete = false
showInsAd()
//启动show超时检测然后在show成功与show失败取消超时任务
TimeoutManager.startTimeout(TimeoutTask.SHOW_AD) {
//超时都没有show出来则流程重置让time下次判定可以去show广告
isProcessComplete = true
}
// 开始流程后启动是否有广告卡住流程的超时任务。100秒
TimeoutManager.startTimeout(TimeoutTask.OVERALL_PROCESS) {
aidlClient.sendChangeParameters(packageName)
}
}
}
MagicLock.getInstance(application)
.refreshInsCountView(this@MainActivity2, adPlace.size)
//在TimerTask任务中判定流程已经完毕并且是否回到了mainActivity回来则取消超时
//超过没有回来,则判定为卡住了。
if (isProcessComplete && AppLifecycleTracker.isMainActivityVisible) {
TimeoutManager.cancelTimeout(TimeoutTask.OVERALL_PROCESS)
}
}
//发送广告数量
aidlClient.sendAdsChange(packageName, getInsAdReturnCount())
//通信cp用于心跳检测
aidlClient.sendHeartBeat(packageName)
}
}, 2000, 2000)
}
// 检查是否有广告在加载中
fun isAnyAdLoading(): Boolean {
return loadingAds.isNotEmpty()
}
private fun initAd() {
aidlClient.sendBrushMiss()
Log.d("ocean-brush", "开始加载三个广告")
startInit = true
appendLoadingTxt("开始加载三个广告")
loadAd(AdsInsUtil.Placement.TOP_ON_AD_ONE)
loadAd(AdsInsUtil.Placement.TOP_ON_AD_TOW)
loadAd(AdsInsUtil.Placement.TOP_ON_AD_THREE)
}
private fun loadAd(placeId: String) {
val startLoadTime = System.currentTimeMillis()
loadingAds.add(placeId)
loadAdNumber++//广告load计数进入loadAd方法就进行计数
AdsInsUtil.loadAd(this, placeId, object : LoadListener {
@SuppressLint("DefaultLocale")
override fun loaded(ad: TPAdInfo) {
aidlClient.sendBrushMiss()
val loadedEndTime = System.currentTimeMillis()
appendLoadingTxt("${placeId}AD加载成功")
aidlClient.sendAdsChange(packageName, getInsAdReturnCount())
// 将 ecpm 转换为 BigDecimal 类型,并进行除法运算
val result = BigDecimal(ad.ecpm.toDouble()).divide(
BigDecimal(1000),
20,
RoundingMode.HALF_UP
)
// 将结果转换为字符串表示形式
val formattedString: String = result.toPlainString()
Log.d("ocean", "scientificNotation->${formattedString}")
appendLoadingTxt("${ad.adNetworkId} ecpm->$formattedString")
Log.d("ocean", "平台->${ad.adNetworkId} ecpm->${formattedString}")
loadJson.addProperty("succeed", true)//广告加载是否成功
loadJson.addProperty("loadTime", loadedEndTime - startLoadTime)//加载时间
loadJson.addProperty("adPlatform", "TradPlus")//广告平台
loadJson.addProperty("countryCode", ad.format)//国家代码
loadJson.addProperty("adId", placeId)//广告Id
loadJson.addProperty("platformResponseTime", "")//平台广告响应时间
loadJson.addProperty("ecpm", formattedString)//广告单价
loadJson.addProperty("dsp", ad.adNetworkId)
loadJson.addProperty("network", ad.adNetworkId)
loadJson.addProperty("washParam", false)
//load广告只有价格大于等于配置的最低值则添加到可以show的广告集合中
if (formattedString.toFloat() >= ecpmLow.toFloat()) {
Log.d("ocean", "onAdLoaded add ->${placeId}")
adPlace.add(placeId)
loadAndShowAdNumber++
loadJson.addProperty("showStatus", "0")
} else {
loadJson.addProperty("showStatus", "-1")
}
viewJson.addProperty("广告加载时间", loadedEndTime - startLoadTime)
viewJson.addProperty("ecpmLow", ecpmLow)
viewJson.addProperty("ecpm", formattedString)
viewJson.addProperty("是否满足show", formattedString.toFloat() >= ecpmLow.toFloat())
appendInfoTxt(viewJson.toString())
MyConfigUtil.initPostLoadLog(loadJson)
loadingAds.remove(placeId)
}
override fun loadFailed(error: String) {
aidlClient.sendBrushMiss()
val loadedEndTime = System.currentTimeMillis()
loadJson.addProperty("succeed", false)//广告加载是否成功
loadJson.addProperty("loadTime", loadedEndTime - startLoadTime)//加载时间
loadJson.addProperty("adPlatform", "TradPlus")//广告平台
loadJson.addProperty("adId", placeId)//广告Id
loadJson.addProperty("washParam", false)
loadJson.addProperty("errorData", error)
MyConfigUtil.initPostLoadLog(loadJson)
Log.d("ocean", "error->${error.toString()}")
aidlClient.sendAdsChange(packageName, getInsAdReturnCount())
loadingAds.remove(placeId)
appendLoadingTxt("${placeId}AD加载失败-${error}")
}
})
}
private val msgSB = StringBuilder()
private fun appendInfoTxt(msg: String) {
runOnUiThread {
msgSB.appendLine(msg)
binding.infoTv.text = msgSB.toString()
}
}
private val msgLoading = StringBuilder()
private fun appendLoadingTxt(msg: String) {
runOnUiThread {
msgLoading.appendLine(msg)
binding.loadingTv.text = msgLoading.toString()
}
}
private fun getInsAdReturnCount(): Int {
return InstAdCacheManager.instance.getLoadedInstCount()
}
fun showInsAd() {
aidlClient.sendBrushMiss()
Log.d("ocean-brush", "满足条件,开始展示广告")
//随机
Log.d("ocean", " show广告集合是否有值${adPlace.size}")
if (adPlace.isNotEmpty()) {
val placeId = Random().nextInt(adPlace.size)
val place = adPlace[placeId]
adPlace.remove(place)
AdsInsUtil.showAd(this, place, object : ShowListener {
@SuppressLint("DefaultLocale")
override fun onAdShown(ad: TPAdInfo?) {
aidlClient.sendBrushMiss()
Handler(Looper.getMainLooper()).post {
onAdShownLiveData.value = true
}
// 将 ecpm 转换为 BigDecimal 类型,并进行除法运算
val ecpm = ad?.ecpm?.toDouble() ?: 0.0
val result = BigDecimal(ecpm).divide(BigDecimal(1000), 20, RoundingMode.HALF_UP)
// 将结果转换为字符串表示形式
val formattedString: String = result.toPlainString()
showJson.addProperty("succeed", true)//广告加载是否成功
showJson.addProperty("adPlatform", "Max")//广告平台
showJson.addProperty("countryCode", ad?.format ?: "")//国家代码
showJson.addProperty("adId", place)//广告Id
showJson.addProperty("platformResponseTime", "")//平台广告响应时间
showJson.addProperty("ecpm", formattedString)//广告单价
showJson.addProperty("dsp", ad?.adNetworkId ?: "")
showJson.addProperty("network", ad?.adNetworkId ?: "")
MyConfigUtil.initPostShowLog(showJson)
Log.d("ocean", "onAdShown decimalNumber->$formattedString")
Log.d("ocean", "onAdShown loadAdNumber->${loadAdNumber}")
Log.d("ocean", "onAdShown quantity.toInt()->${quantity.toInt()}")
if (loadAdNumber < quantity.toInt()) {//不能大于配置的数量
if (ecpmCool.isNotEmpty()) {
//满足配置的最高的价格则重新load这个被消耗的
Log.d("ocean", "onAdShown ecpmCool->${ecpmCool}")
Log.d("ocean", "onAdShown ecpmCool.toFloat()->${ecpmCool.toFloat()}")
if (formattedString.toFloat() >= ecpmCool.toFloat()) {
loadAd(place)
}
}
}
println("scanner_ad onAdShown")
aidlClient.sendAdsChange(packageName, getInsAdReturnCount(), 1, 0)
loadAndShowAdNumber--
}
override fun onAdClicked() {
aidlClient.sendBrushMiss()
Log.d("ocean", " 点击show广告")
aidlClient.sendAdsChange(packageName, getInsAdReturnCount(), 0, 1)
}
override fun onAdShowFailed(error: AdShowFailed?) {
aidlClient.sendBrushMiss()
Handler(Looper.getMainLooper()).post {
onAdShowFailedLiveData.value = true
}
Log.d("ocean", " show广告失败")
aidlClient.sendAdsChange(packageName, getInsAdReturnCount())
loadAndShowAdNumber--
showJson.addProperty("succeed", false)//广告加载是否成功
showJson.addProperty("adPlatform", "Max")//广告平台
showJson.addProperty("adId", place)//广告Id
MyConfigUtil.initPostShowLog(showJson)
}
override fun onAdClosed() {
aidlClient.sendBrushMiss()
Handler(Looper.getMainLooper()).post {
onAdClosedLiveData.value = true
}
aidlClient.sendAdsChange(packageName, getInsAdReturnCount())
}
})
}
}
//路径 /storage/emulated/0/phone.xml
private fun getBidJson() {
aidlClient.sendBrushMiss()
Log.d("ocean-brush", "组装load数据与show数据上传并且获取gaid")
val linkId = UUID.randomUUID().toString().replace("-", "")
GlobalScope.launch {
withContext(Dispatchers.IO) {
val gaId = AdvertisingIdClient.getAdvertisingIdInfo(this@MainActivity2).id
Log.d("ocean", "getAdvertisingIdInfo gaId->${gaId}")
viewJson.addProperty("设备ID", devicesID)
viewJson.addProperty("货架号", shelfNumber)
// viewJson.addProperty("IP获取时间", endIpTime - startIpTime)
loadJson.addProperty("deviceId", devicesID)
loadJson.addProperty("shelfNumber", shelfNumber)//货架号
loadJson.addProperty("localIp", getLocalIpAddress())//本地IP
loadJson.addProperty("linkId", linkId)//链路Id
loadJson.addProperty("packageName", packageName)//包名
loadJson.addProperty("gaid", gaId)
loadJson.addProperty("dataId", dataId)
loadJson.addProperty("carrierId",simId)
loadJson.addProperty("getIpResponseTime", 0)
loadJson.addProperty("packageVersion", 1)
loadJson.addProperty("version", 3)
loadJson.addProperty("phoneVersion", Build.MODEL)
showJson.addProperty("deviceId", devicesID)
showJson.addProperty("shelfNumber", shelfNumber)
showJson.addProperty("localIp", getLocalIpAddress())//本地IP
showJson.addProperty("linkId", linkId)//链路Id
showJson.addProperty("packageName", packageName)//包名
showJson.addProperty("gaid", gaId)
showJson.addProperty("dataId", dataId)
showJson.addProperty("carrierId",simId)
showJson.addProperty("getIpResponseTime", 0)
showJson.addProperty("version", 3)
showJson.addProperty("phoneVersion", Build.MODEL)
runOnUiThread {
binding.gaidTv.text = gaId
if (gaId != null) {
if (gaId == gaIdError) {
binding.gaidLayout.setBackgroundColor(getColor(R.color.red))
//gaID没有确的值就发送重置广播
appendLoadingTxt("没有获取到GAID$timeLeft 秒后将会重置")
startCountdown()
} else {
binding.gaidLayout.setBackgroundColor(getColor(R.color.green))
initAd()
}
} else {
appendLoadingTxt("没有获取到GAID$timeLeft 秒后将会重置")
startCountdown()
}
}
}
}
}
/**
* https://api.tikustok.com/app/common/getIPInfo
* https://openapi.lux-ad.com/app/common/getIPInfo
*/
private suspend fun getPublicIpAddress(): String = suspendCoroutine { continuation ->
var publicIp = "0.0.0.0"
try {
VmHttpUtil.mInstance.getIPInfo(
"https://openapi.lux-ad.com/app/common/getIPInfo",
object : Callback {
override fun onFailure(call: Call, e: IOException) {
runOnUiThread {
appendLoadingTxt("获取IP失败")
}
continuation.resume(publicIp)
}
override fun onResponse(call: Call, response: Response) {
Log.d("ocean", "getIPInfo response->${response}")
if (response.code == 200) {
val responseData = response.body?.string()
if (responseData != null) {
val jsonObject = JSONObject(responseData)
Log.d("ocean", "getIPInfo jsonObject->${jsonObject}")
val status = jsonObject.optString("status")
if (status == "Success") {
val data = jsonObject.getJSONObject("data")
appendLoadingTxt("获取IP成功->${data}")
Log.d("ocean", "getIPInfo data->${data}")
publicIp = data.optString("ip")
continuation.resume(publicIp)
} else {
continuation.resume(publicIp)
}
} else {
continuation.resume(publicIp)
}
} else {
runOnUiThread {
appendLoadingTxt("获取IP失败->${response}")
}
continuation.resume(publicIp)
}
}
})
} catch (e: Exception) {
e.printStackTrace()
appendLoadingTxt("获取IP失败 catch->${e}")
continuation.resume(publicIp)
}
}
override fun onDestroy() {
super.onDestroy()
aidlClient.connectCompleteLiveData.removeObserver(connectCompleteObserver)
aidlClient.paramCompleteLiveData.removeObserver(paramCompleteObserver)
aidlClient.clickAdCompleteLiveData.removeObserver(clickAdCompleteObserver)
onAdShownLiveData.removeObserver(onAdShownObserver)
onAdShowFailedLiveData.removeObserver(onAdShowFailedObserver)
onAdClosedLiveData.removeObserver(onAdCloseObserver)
aidlClient.disconnect(this)
stopCountdown()
stopParamCountdown()
}
private fun getSimCountryIso(context: Activity): String {
val telephonyManager = context.getSystemService("phone") as TelephonyManager?
return telephonyManager?.simCountryIso?.uppercase(Locale.ENGLISH) ?: ""
}
private fun mnc(): String {
val telephonyManager = getSystemService("phone") as TelephonyManager
return try {
val networkOperator = telephonyManager.networkOperator
networkOperator.substring(3.coerceAtMost(networkOperator.length))
} catch (e: Exception) {
""
}
}
private fun mcc(): String {
val telephonyManager = getSystemService("phone") as TelephonyManager
return try {
val networkOperator = telephonyManager.networkOperator
networkOperator.substring(0, minOf(3, networkOperator.length))
} catch (e: Exception) {
""
}
}
@SuppressLint("Range")
fun getDeviceIdFromProvider(callback: (String?, Boolean) -> Unit) {
val uri = Uri.parse("content://com.guise.deviceidprovider/device_id")
val cursor = contentResolver.query(uri, null, null, null, null)
if (cursor != null && cursor.moveToFirst()) {
val deviceId = cursor.getString(cursor.getColumnIndex("device_id"))
cursor.close() // 关闭 Cursor
callback(deviceId, true) // 成功读取到 Device ID
} else {
callback(null, false) // 读取失败
}
}
/**
* 通用进行重置五秒倒计时
*/
private var timeLeft = 5 // 倒计时时间
private val countdownHandler = Handler(Looper.getMainLooper())
private val countdownRunnable = object : Runnable {
override fun run() {
if (timeLeft > 0) {
appendLoadingTxt("⏳ 剩余 $timeLeft 秒后执行 修改参数 指令")
timeLeft--
countdownHandler.postDelayed(this, 1000) // 继续倒计时
} else {
appendLoadingTxt("🚀 执行 修改参数 指令")
aidlClient.sendChangeParameters(packageName)
}
}
}
private fun startCountdown() {
timeLeft = 5 // 重新初始化时间
countdownHandler.post(countdownRunnable) // 开始倒计时
}
private fun stopCountdown() {
countdownHandler.removeCallbacks(countdownRunnable) // 取消倒计时
}
/**
* 参数修改失败重新修改倒计时
*/
private var paramTimeLeft = 15
private val countdownParamHandler = Handler(Looper.getMainLooper())
private val countdownParamRunnable = object : Runnable {
override fun run() {
if (paramTimeLeft > 0) {
paramTimeLeft--
countdownParamHandler.postDelayed(this, 1000) // 继续倒计时
} else {
appendLoadingTxt("执行修改参数指令")
aidlClient.sendChangeParameters(packageName)
}
}
}
private fun startParamCountdown() {
paramTimeLeft = 15 // 重新初始化时间
countdownParamHandler.post(countdownParamRunnable) // 开始倒计时
}
private fun stopParamCountdown() {
countdownParamHandler.removeCallbacks(countdownParamRunnable) // 取消倒计时
}
}

View File

@ -0,0 +1,82 @@
package com.cake.draw.painting.environment
import android.content.Context
import android.net.ConnectivityManager
import okhttp3.Callback
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import java.io.IOException
import java.security.SecureRandom
import java.security.cert.X509Certificate
import java.util.concurrent.TimeUnit
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager
class VmHttpUtil {
fun isNetworkAvailable(context: Context): Boolean {
val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val activeNetworkInfo = connectivityManager?.activeNetworkInfo
return activeNetworkInfo != null && activeNetworkInfo.isConnected
}
private var mOkHttpClient: OkHttpClient? = null
companion object {
val mInstance: VmHttpUtil by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
VmHttpUtil()
}
}
private val CONNECT_TIMEOUT: Long = 60 //超时时间,秒
private val READ_TIMEOUT: Long = 60 //读取时间,秒
private val WRITE_TIMEOUT: Long = 60 //写入时间,秒
init {
// ---- 不安全:信任所有证书(仅测试用) ----
val trustAllCerts = arrayOf<TrustManager>(
object : X509TrustManager {
override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {}
override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {}
override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()
}
)
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, trustAllCerts, SecureRandom())
val sslSocketFactory = sslContext.socketFactory
val builder: OkHttpClient.Builder = OkHttpClient.Builder()
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
.sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager)
mOkHttpClient = builder.build()
}
private val mediaType = "application/json; charset=utf-8".toMediaType()
fun getIPInfo(url: String, callback: Callback) {
val request: Request = Request.Builder()
.url(url)
.get()
.build()
doAsync(request, callback)
}
/**
* 异步请求
*/
@Throws(IOException::class)
private fun doAsync(request: Request, callback: Callback) {
//创建请求会话
val call = mOkHttpClient!!.newCall(request)
//同步执行会话请求
call.enqueue(callback)
}
}

View File

@ -0,0 +1,177 @@
package com.cake.draw.painting.environment.ad
import android.annotation.SuppressLint
import android.app.Activity
import android.app.Application
import android.content.Context
import android.os.Bundle
import android.provider.Settings
import android.util.Log
import com.cake.draw.painting.environment.ad.async.Async
import com.unity3d.services.ads.adunit.AdUnitActivity
import com.vungle.ads.internal.ui.AdActivity
import com.vungle.ads.internal.ui.view.MRAIDAdWidget
class AdActivityManager {
private var mCurrentShowAd: Activity? = null//记录当前正在展示的inst广告Activity
private var mApplicationContext: Context? = null
private var mAID: String? = null
private var mListener: InstAdLeaveAndReturnListener? = null
private var isAdClosed = false//当广告点击了close按钮的时候 设置成true
private var isAdClicked = false//标记inst是否已是已点击状态
companion object {
val instance: AdActivityManager by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
AdActivityManager()
}
fun isAdActivity(activity: Activity): Boolean {
return (
activity is com.tradplus.ads.mgr.interstitial.views.InterNativeActivity
|| activity is com.vungle.ads.internal.ui.VungleActivity
|| activity is com.vungle.ads.internal.ui.AdActivity
|| activity is AdUnitActivity
)
}
}
fun getCurrentShowAd(): Activity? {
return mCurrentShowAd
}
fun doDelayClose(delay: Long) {
Async.scheduleTaskOnUiThread(delay, Runnable { doClose() })
}
/**
*手动关闭当前显示的inst具体支持哪些平台的inst onActivityResumed 中添加
*/
fun doClose() {
mCurrentShowAd?.let { adActivity ->
Log.d("ocean-brush", "自动关闭 $adActivity")
when (adActivity) {
is AdActivity ->{
//反射方式关闭
try {
// 获取 mraidAdWidget 字段
val field = AdActivity::class.java.getDeclaredField("mraidAdWidget")
field.isAccessible = true
val widget = field.get(adActivity) as? MRAIDAdWidget
Log.d("ocean-brush", "调用 widget.close(),触发 CloseDelegate -> finish()")
widget?.close() ?: run {
// 没拿到 widget
adActivity.onBackPressed()
Log.d("ocean-brush", "liftoff关闭使用onBackPressed()")
}
} catch (e: Exception) {
adActivity.finish()
adActivity.moveTaskToBack(true)
Log.d("ocean-brush", "liftoff关闭错误直接finish")
}
}
else -> {
//关闭广告act
adActivity.finish()
// 强制移除任务栈(适用于某些广告 Activity 仍然存活的情况)
adActivity.moveTaskToBack(true)
}
}
}
}
fun getContext(): Context? {
return mApplicationContext
}
fun getAID(): String? {
return mAID
}
fun addActivityLifeCallbacks(callbacks: Application.ActivityLifecycleCallbacks?) {
(mApplicationContext as Application).registerActivityLifecycleCallbacks(callbacks)
}
fun removeActivityLifeCallbacks(callbacks: Application.ActivityLifecycleCallbacks?) {
(mApplicationContext as Application).unregisterActivityLifecycleCallbacks(callbacks)
}
//在Application中调用
@SuppressLint("HardwareIds")
fun setControl(application: Application) {
mApplicationContext = application
application.registerActivityLifecycleCallbacks(object :
Application.ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
}
override fun onActivityStarted(activity: Activity) {
}
override fun onActivityResumed(activity: Activity) {
//此处添加想要控制的广告平台的inst Activity
try {
if (isAdActivity(activity)) {
mCurrentShowAd = activity
if (isAdClicked) {
//点击了admob inst跳转到外部然后从外部返回app的时候触发
mListener?.onReturnAdFromOutside()
}
}
} catch (e: Exception) {
}
}
override fun onActivityPaused(activity: Activity) {
try {
if (mCurrentShowAd != null) {
if (isAdActivity(activity)) {//onActivityPaused会在很多场景下触发1.退到桌面 2.去往app外部 3.被dialog挡住 4.广告点击了close按钮销毁的过程中
if (!isAdClosed) {//isAdClosed这里就是为了排除"4.广告点击了close按钮销毁的过程中"
//需要在不同展示场景中实际测试
mListener?.onLeaveAdGoOutside()
isAdClicked = true
}
}
}
} catch (e: Exception) {
}
}
override fun onActivityStopped(activity: Activity) {}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
override fun onActivityDestroyed(activity: Activity) {
Log.d("ocean-brush", "onActivityDestroyed $activity $mCurrentShowAd")
if (mCurrentShowAd === activity) {
mCurrentShowAd = null
isAdClosed = false
isAdClicked = false
mListener = null
}
}
})
try {
mAID = hashCode().toString() + ""
if (mApplicationContext != null) {
mAID = Settings.Secure.getString(
mApplicationContext?.contentResolver, Settings.Secure.ANDROID_ID
)
}
} catch (e: Exception) {
}
}
fun setAdClose(isClosing: Boolean) {
isAdClosed = isClosing
}
fun setInstAdListener(listener: InstAdLeaveAndReturnListener) {
mListener = listener
}
interface InstAdLeaveAndReturnListener {
fun onLeaveAdGoOutside()//1.点击ad跳转到外部 2.app切换到后台 3.被dialog遮挡
fun onReturnAdFromOutside()//从外部返回ad页面, 通常是点击ad跳转到外部后返回ad页面
}
}

View File

@ -1,8 +1,7 @@
package com.cake.draw.painting.ad package com.cake.draw.painting.environment.ad
import android.app.Activity import android.app.Activity
import android.util.Log import android.util.Log
import com.mbridge.msdk.interstitial.signalcommon.interstitial
import com.tradplus.ads.base.bean.TPAdError import com.tradplus.ads.base.bean.TPAdError
import com.tradplus.ads.base.bean.TPAdInfo import com.tradplus.ads.base.bean.TPAdInfo
import com.tradplus.ads.open.interstitial.InterstitialAdListener import com.tradplus.ads.open.interstitial.InterstitialAdListener
@ -27,67 +26,54 @@ class AdInstLoad {
} }
private fun init() { private fun init() {
val interstitialAd = InstAdCacheManager.instance.getAdCache(mPlace)
if (interstitialAd != null && interstitialAd.isReady) {
Log.d("ocean", "$mPlace 有缓存不进行load")
adLoadListener?.loadFailed("有缓存不进行load")
return
}
val tpInterstitial = TPInterstitial(activity, mPlace) val tpInterstitial = TPInterstitial(activity, mPlace)
tpInterstitial.setAdListener(object : InterstitialAdListener { tpInterstitial.setAdListener(object : InterstitialAdListener {
//广告加载完成 首个广告源加载成功时回调 一次加载流程只会回调一次 //广告加载完成 首个广告源加载成功时回调 一次加载流程只会回调一次
override fun onAdLoaded(tpAdInfo: TPAdInfo?) { override fun onAdLoaded(tpAdInfo: TPAdInfo?) {
if (tpAdInfo != null) { if (tpAdInfo != null) {
Log.d("ocean", "$mPlace 广告load成功tpAdInfo有值") Log.d("ocean", "广告load成功tpAdInfo有值")
InstAdCacheManager.Companion.instance.setAdCache(mPlace, tpInterstitial) InstAdCacheManager.instance.setAdCache(mPlace, tpInterstitial)
adLoadListener?.loaded(tpAdInfo) adLoadListener?.loaded(tpAdInfo)
} else { } else {
adLoadListener?.loadFailed("tpAdInfo没有值") adLoadListener?.loadFailed("tpAdInfo没有值")
Log.d("ocean", "$mPlace tpAdInfo没有值") Log.d("ocean", "tpAdInfo没有值")
} }
} }
// 广告被点击 // 广告被点击
override fun onAdClicked(tpAdInfo: TPAdInfo?) { override fun onAdClicked(tpAdInfo: TPAdInfo?) {
Log.d("ocean", "$mPlace tradplus onAdClicked") Log.d("ocean", "tradplus onAdClicked")
} }
// 广告成功展示在页面上 // 广告成功展示在页面上
override fun onAdImpression(tpAdInfo: TPAdInfo?) { override fun onAdImpression(tpAdInfo: TPAdInfo?) {
Log.d("ocean", "$mPlace tradplus onAdImpression") Log.d("ocean", "tradplus onAdImpression")
} }
// 广告加载失败 // 广告加载失败
override fun onAdFailed(error: TPAdError?) { override fun onAdFailed(error: TPAdError?) {
adLoadListener?.loadFailed("code->${error?.errorCode}message->${error?.errorMsg}") adLoadListener?.loadFailed("code->${error?.errorCode}message->${error?.errorMsg}")
Log.d( Log.d("ocean", "load ad onError->code->${error?.errorCode}message->${error?.errorMsg}")
"ocean",
"$mPlace load ad onError->code->${error?.errorCode}message->${error?.errorMsg}"
)
} }
// 广告被关闭 // 广告被关闭
override fun onAdClosed(tpAdInfo: TPAdInfo?) { override fun onAdClosed(tpAdInfo: TPAdInfo?) {
Log.d("ocean", "$mPlace tradplus onAdClosed") Log.d("ocean", "tradplus onAdClosed")
} }
// 视频播放开始(部分广告源支持) // 视频播放开始(部分广告源支持)
override fun onAdVideoStart(tpAdInfo: TPAdInfo?) { override fun onAdVideoStart(tpAdInfo: TPAdInfo?) {
Log.d("ocean", "$mPlace tradplus onAdVideoStart") Log.d("ocean", "tradplus onAdVideoStart")
} }
//视频播放结束(部分广告源支持) //视频播放结束(部分广告源支持)
override fun onAdVideoEnd(tpAdInfo: TPAdInfo?) { override fun onAdVideoEnd(tpAdInfo: TPAdInfo?) {
Log.d("ocean", "$mPlace tradplus onAdVideoEnd") Log.d("ocean", "tradplus onAdVideoEnd")
} }
//视频播放失败(部分广告源支持) //视频播放失败(部分广告源支持)
override fun onAdVideoError(tpAdInfo: TPAdInfo?, error: TPAdError?) { override fun onAdVideoError(tpAdInfo: TPAdInfo?, error: TPAdError?) {
Log.d( Log.d("ocean", "onAdVideoError code->${error?.errorCode}message->${error?.errorMsg}")
"ocean",
"$mPlace onAdVideoError code->${error?.errorCode}message->${error?.errorMsg}"
)
} }
}) })
tpInterstitial.loadAd() tpInterstitial.loadAd()

View File

@ -1,7 +1,8 @@
package com.cake.draw.painting.ad package com.cake.draw.painting.environment.ad
import android.app.Activity import android.app.Activity
import android.util.Log import android.util.Log
import com.cake.draw.painting.environment.ad.async.Async
import com.tradplus.ads.base.bean.TPAdError import com.tradplus.ads.base.bean.TPAdError
import com.tradplus.ads.base.bean.TPAdInfo import com.tradplus.ads.base.bean.TPAdInfo
import com.tradplus.ads.open.interstitial.InterstitialAdListener import com.tradplus.ads.open.interstitial.InterstitialAdListener
@ -25,7 +26,7 @@ class AdInstShower {
} }
private fun init() { private fun init() {
val interstitialAd = InstAdCacheManager.Companion.instance.getAdCache(mPlace) val interstitialAd = InstAdCacheManager.instance.getAdCache(mPlace)
interstitialAd?.setAdListener(object : InterstitialAdListener { interstitialAd?.setAdListener(object : InterstitialAdListener {
//广告加载完成 首个广告源加载成功时回调 一次加载流程只会回调一次 //广告加载完成 首个广告源加载成功时回调 一次加载流程只会回调一次
override fun onAdLoaded(tpAdInfo: TPAdInfo?) {} override fun onAdLoaded(tpAdInfo: TPAdInfo?) {}
@ -40,6 +41,7 @@ class AdInstShower {
override fun onAdImpression(tpAdInfo: TPAdInfo?) { override fun onAdImpression(tpAdInfo: TPAdInfo?) {
showListener?.onAdShown(tpAdInfo) showListener?.onAdShown(tpAdInfo)
Log.d("ocean", "AdInstShower 广告展示回调") Log.d("ocean", "AdInstShower 广告展示回调")
autoClose()
} }
// 广告加载失败 // 广告加载失败
@ -66,4 +68,12 @@ class AdInstShower {
interstitialAd?.showAd(activity!!, mPlace) interstitialAd?.showAd(activity!!, mPlace)
} }
private fun autoClose() {
val autoCloseTime = 3000L
Log.d("ocean-brush", "show后开始等待自动关闭广告 $autoCloseTime")
Async.scheduleTaskOnUiThread(autoCloseTime, Runnable {
AdActivityManager.instance.doClose()
Log.d("ocean-brush", "show后执行关闭广告 $autoCloseTime")
})
}
} }

View File

@ -1,4 +1,4 @@
package com.cake.draw.painting.ad package com.cake.draw.painting.environment.ad
data class AdShowFailed( data class AdShowFailed(
val msg: String = "", val msg: String = "",

View File

@ -1,4 +1,4 @@
package com.cake.draw.painting.ad package com.cake.draw.painting.environment.ad
import android.app.Activity import android.app.Activity
@ -21,7 +21,7 @@ object AdsInsUtil {
fun showAd( fun showAd(
act: Activity, act: Activity,
adID: String, adID: String,
listener: ShowListener listener: ShowListener?
): AdInstShower { ): AdInstShower {
return AdInstShower(act, adID, listener) return AdInstShower(act, adID, listener)
} }

View File

@ -1,4 +1,4 @@
package com.cake.draw.painting.ad package com.cake.draw.painting.environment.ad
import com.tradplus.ads.open.interstitial.TPInterstitial import com.tradplus.ads.open.interstitial.TPInterstitial

View File

@ -1,4 +1,4 @@
package com.cake.draw.painting.ad package com.cake.draw.painting.environment.ad
import com.tradplus.ads.base.bean.TPAdInfo import com.tradplus.ads.base.bean.TPAdInfo

View File

@ -1,4 +1,4 @@
package com.cake.draw.painting.ad package com.cake.draw.painting.environment.ad
import com.tradplus.ads.base.bean.TPAdInfo import com.tradplus.ads.base.bean.TPAdInfo

View File

@ -0,0 +1,83 @@
package com.cake.draw.painting.environment.ad.async;
import android.os.Handler;
import android.os.Looper;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
public class Async {
private static ThreadPoolExecutorWrapper sThreadPoolExecutorWrapper;
private static ThreadPoolExecutorWrapper getThreadPoolExecutorWrapper() {
if (sThreadPoolExecutorWrapper == null) {
synchronized (Async.class) {
if (sThreadPoolExecutorWrapper == null) {
sThreadPoolExecutorWrapper = new ThreadPoolExecutorWrapper(12, 12, 10);
// if (BuildConfig.DEBUG)
// LogUtil.d(LogFilterDef.APP_INIT, LogHelper.getFileLineMethod(1));
}
}
}
return sThreadPoolExecutorWrapper;
}
public static void run(Runnable task) {
getThreadPoolExecutorWrapper().executeTask(task);
}
public static <T> Future<T> submit(Callable<T> task) {
return getThreadPoolExecutorWrapper().submitTask(task);
}
public static boolean isMainThread() {
return Thread.currentThread() == Looper.getMainLooper().getThread();
}
public static void schedule(long delay, Runnable task) {
getThreadPoolExecutorWrapper().scheduleTask(delay, task);
}
public static void scheduleTaskAtFixedRateIgnoringTaskRunningTime(long initialDelay, long period, Runnable task) {
getThreadPoolExecutorWrapper().scheduleTaskAtFixedRateIgnoringTaskRunningTime(initialDelay, period, task);
}
public static void scheduleTaskAtFixedRateIncludingTaskRunningTime(long initialDelay, long period, Runnable task) {
getThreadPoolExecutorWrapper().scheduleTaskAtFixedRateIncludingTaskRunningTime(initialDelay, period, task);
}
public static boolean removeScheduledTask(Runnable task) {
return getThreadPoolExecutorWrapper().removeScheduledTask(task);
}
public static void scheduleTaskOnUiThread(long delay, Runnable task) {
getThreadPoolExecutorWrapper().scheduleTaskOnUiThread(delay, task);
}
public static void removeScheduledTaskOnUiThread(Runnable task) {
getThreadPoolExecutorWrapper().removeScheduledTaskOnUiThread(task);
}
public static void runOnUiThread(Runnable task) {
getThreadPoolExecutorWrapper().runTaskOnUiThread(task);
}
public static Handler getMainHandler() {
return getThreadPoolExecutorWrapper().getMainHandler();
}
/*
single background thread to execute Job
*/
public static void scheduleInQueue(Runnable task) {
getThreadPoolExecutorWrapper().scheduleOnQueue(task);
}
public static void shutdown() {
if (sThreadPoolExecutorWrapper != null) {
sThreadPoolExecutorWrapper.shutdown();
sThreadPoolExecutorWrapper = null;
}
}
}

View File

@ -0,0 +1,140 @@
package com.cake.draw.painting.environment.ad.async;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import java.util.LinkedList;
import java.util.Queue;
public class HandlerPoster extends Handler {
private final int ASYNC = 1;
private final int SYNC = 2;
private final Queue<Runnable> asyncPool;
private final Queue<SyncPost> syncPool;
private final int maxMillisInsideHandleMessage;
private boolean asyncActive;//执行状态避免重复发送消息导致消息队列过多
private boolean syncActive;//执行状态避免重复发送消息导致消息队列过多
HandlerPoster(Looper looper, int maxMillisInsideHandleMessage) {
super(looper);
this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
asyncPool = new LinkedList<Runnable>();
syncPool = new LinkedList<SyncPost>();
}
void dispose() {
this.removeCallbacksAndMessages(null);
this.asyncPool.clear();
this.syncPool.clear();
}
void async(Runnable runnable) throws Exception {
synchronized (asyncPool) {
asyncPool.offer(runnable);
//判断当前是否有异步任务正在执行
if (!asyncActive) {
asyncActive = true;
if (!sendMessage(obtainMessage(ASYNC))) {
throw new Exception("Could not send handler message");
}
}
}
}
void sync(SyncPost post) throws Exception {
synchronized (syncPool) {
syncPool.offer(post);
//判断当前是否有同步任务正在执行
if (!syncActive) {
syncActive = true;
if (!sendMessage(obtainMessage(SYNC))) {
throw new Exception("Could not send handler message");
}
}
}
}
@Override
public void handleMessage(Message msg) {
if (msg.what == ASYNC) {
boolean rescheduled = false;
try {
//当执行完一个任务后就判断一次是否超过时间限制如果超过那么不管队列中的任务是否执行完成都退出同时发起一个新的消息到Handler循环队列
//在while部分使用poll从队列取出一个任务判断是否为空如果为空进入队列同步块然后再取一次再次判断
//如果恰巧在进入同步队列之前有新的任务来了那么第二次取到的当然就不是 NULL也就会继续执行下去
//反之如果还是为空那么重置当前队列的状态为false,同时跳出循环
long started = SystemClock.uptimeMillis();
while (true) {
Runnable runnable = null;
synchronized (asyncPool){
runnable = asyncPool.size()==0?null:asyncPool.poll();//2018.12.24 add by xw 如果为空就去null
}
if (runnable == null) {
synchronized (asyncPool) {
// Check again, this time in synchronized
runnable = asyncPool.poll();
if (runnable == null) {
asyncActive = false;
return;
}
}
}
runnable.run();
long timeInMethod = SystemClock.uptimeMillis() - started;
if (timeInMethod >= maxMillisInsideHandleMessage) {
if (!sendMessage(obtainMessage(ASYNC))) {
throw new Exception("Could not send handler message");
}
rescheduled = true;
return;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
asyncActive = rescheduled;
}
} else if (msg.what == SYNC) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) {
SyncPost post = syncPool.poll();
if (post == null) {
synchronized (syncPool) {
// Check again, this time in synchronized
post = syncPool.poll();
if (post == null) {
syncActive = false;
return;
}
}
}
post.run();
long timeInMethod = SystemClock.uptimeMillis() - started;
if (timeInMethod >= maxMillisInsideHandleMessage) {
if (!sendMessage(obtainMessage(SYNC))) {
throw new Exception("Could not send handler message");
}
rescheduled = true;
return;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
syncActive = rescheduled;
}
} else
super.handleMessage(msg);
}
}

View File

@ -0,0 +1,73 @@
package com.cake.draw.painting.environment.ad.async;
import android.os.Looper;
/**
* 网上找到的关于子线程切换到主线程的代码,实现子线程任意时刻切换到主线程并可选择地阻塞子线程
* 此方案避免handler满天飞的情况
* 参考资料http://c.jinhusns.com/cms/c-884
* Created by DonWZ on 2016-10-18
*/
public class MainThreadSwitcher {
private static HandlerPoster mainPoster = null;
private static HandlerPoster getMainPoster() {
if (mainPoster == null) {
synchronized (MainThreadSwitcher.class) {
if (mainPoster == null) {
mainPoster = new HandlerPoster(Looper.getMainLooper(), 500);//限制主线程单次运行时间
}
}
}
return mainPoster;
}
/**
* Asynchronously.
* The child thread asynchronous run relative to the main thread,
* not blocking the child thread
* @param runnable
* Runnable Interface
*/
public static void runOnMainThreadAsync(Runnable runnable) {
if (Looper.myLooper() == Looper.getMainLooper()) {
runnable.run();
return;
}
try {
getMainPoster().async(runnable);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
*
* Synchronously.
* The child thread relative thread synchronization operation,
* blocking the child thread,
* thread for the main thread to complete
* @param runnable
* Runnable Interface
*/
public static void runOnMainThreadSync(Runnable runnable) {
if (Looper.myLooper() == Looper.getMainLooper()) {
runnable.run();
return;
}
SyncPost poster = new SyncPost(runnable);
try {
getMainPoster().sync(poster);
} catch (Exception e) {
e.printStackTrace();
}
poster.waitRun();
}
public static void dispose() {
if (mainPoster != null) {
mainPoster.dispose();
mainPoster = null;
}
}
}

View File

@ -0,0 +1,44 @@
package com.cake.draw.painting.environment.ad.async;
public class SyncPost {
boolean end = false;
Runnable runnable;
SyncPost(Runnable runnable) {
this.runnable = runnable;
}
public void run() {
//进入同步块然后调用Runnable接口的run方法同时在执行完成后将end重置为true;
//然后调用this.notifyAll();通知等待的部分可以继续了当然有这样的情况假如在进入该同步块的时候子线程还未执行到this.wait();部分呢
//所以我们为此准备了end和try
synchronized (this) {
runnable.run();
end = true;
try {
this.notifyAll();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void waitRun() {
//首先判断状态如果状态已经变了那么证明子线程执行到此处时主线程已经执行了void_run()
//所以也就不用进入同步块进行等待了反之进入等待直到主线程调用this.notifyAll();
if (!end) {
synchronized (this) {
if (!end) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}

View File

@ -0,0 +1,46 @@
package com.cake.draw.painting.environment.ad.async;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class TaskQueue extends Thread{
private BlockingQueue<Object> mQueue;
public TaskQueue() {
mQueue = new LinkedBlockingQueue<Object>();
}
public TaskQueue(String name) {
this();
setName(name);
}
public void stopTaskQueue() {
// use 'Poison Pill Shutdown' to stop the task queue
// add a non-Runnable object, which will be recognized as the command
// by the thread to break the infinite loop
mQueue.add(new Object());
}
public void scheduleTask(Runnable task) {
mQueue.add(task);
}
@Override
public void run() {
while (true) {
try {
Object obj = mQueue.take();
if (obj instanceof Runnable) {
((Runnable) obj).run();
obj = null;
} else {
break;
}
} catch (InterruptedException e) {
}
}
}
}

View File

@ -0,0 +1,158 @@
package com.cake.draw.painting.environment.ad.async;
import android.os.Handler;
import android.os.Looper;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorWrapper {
private static final Long IDLE_THREAD_KEEP_ALIVE_TIME = 60L;
private ExecutorService mThreadPoolExecutor;//normal thread pool
private ScheduledThreadPoolExecutor mScheduledThreadPoolExecutor;//can schedule task thread pool
private Handler mMainHandler;
private TaskQueue mActionQueue;
private Map<Integer, Object> mScheduledJobRecord = new HashMap<>();//ScheduledThreadPoolExecutor will wrap Runnable, so we record this
private Object mScheduledJobRecordMutex = new Object();
/*
maxThreadCount:thread pool max thread count
activeThreadCount:thread pool min thread count even if all thread is idle
IDLE_THREAD_KEEP_ALIVE_TIME:IDLE thread will be shutdown when TIME_OUT if (maxThreadCount - activeThreadCount) > 0
*/
public ThreadPoolExecutorWrapper(int activeThreadCount, int maxThreadCount, int maxScheTaskThread) {
mThreadPoolExecutor = new ThreadPoolExecutor(activeThreadCount, maxThreadCount,
IDLE_THREAD_KEEP_ALIVE_TIME, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>());
if (maxScheTaskThread > 0) {
mScheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(maxScheTaskThread);
}
mMainHandler = new Handler(Looper.getMainLooper());
mActionQueue = new TaskQueue(Async.class.getName());
mActionQueue.start();
}
public void executeTask(Runnable task) {
if (task == null) {
return;
}
mThreadPoolExecutor.execute(task);
}
public <T> Future<T> submitTask(Callable<T> task) {
return mThreadPoolExecutor.submit(task);
}
public void scheduleTask(long delay, Runnable task) {
if (task == null) {
return;
}
mScheduledThreadPoolExecutor.schedule(task, delay, TimeUnit.MILLISECONDS);
}
public void scheduleTaskAtFixedRateIgnoringTaskRunningTime(long initialDelay, long period, Runnable task) {
if (task == null) {
return;
}
synchronized (mScheduledJobRecordMutex) {
if (mScheduledJobRecord.containsKey(task.hashCode())) {
return;
}
mScheduledJobRecord.put(task.hashCode(), mScheduledThreadPoolExecutor.scheduleAtFixedRate(task, initialDelay, period, TimeUnit.MILLISECONDS));
}
}
public void scheduleTaskAtFixedRateIncludingTaskRunningTime(long initialDelay, long period, Runnable task) {
if (task == null) {
return;
}
synchronized (mScheduledJobRecordMutex) {
if (mScheduledJobRecord.containsKey(task.hashCode())) {
return;
}
mScheduledJobRecord.put(task.hashCode(), mScheduledThreadPoolExecutor.scheduleWithFixedDelay(task, initialDelay, period, TimeUnit.MILLISECONDS));
}
}
public boolean removeScheduledTask(Runnable task) {
if (task == null) {
return false;
}
synchronized (mScheduledJobRecordMutex) {
if (!mScheduledJobRecord.containsKey(task.hashCode())) {
return false;
}
ScheduledFuture<?> internalJob = (ScheduledFuture<?>) mScheduledJobRecord.get(task.hashCode());
internalJob.cancel(true);
mScheduledJobRecord.remove(task.hashCode());
return true;
}
}
public void scheduleTaskOnUiThread(long delay, Runnable task) {
if (task == null) {
return;
}
mMainHandler.postDelayed(task, delay);
}
public void removeScheduledTaskOnUiThread(Runnable task) {
if (task == null) {
return;
}
mMainHandler.removeCallbacks(task);
}
public void runTaskOnUiThread(Runnable task) {
if (task == null) {
return;
}
mMainHandler.post(task);
}
public Handler getMainHandler() {
return mMainHandler;
}
public void scheduleOnQueue(Runnable task) {
if (task == null) {
return;
}
mActionQueue.scheduleTask(task);
}
public void shutdown() {
if (mThreadPoolExecutor != null) {
mThreadPoolExecutor.shutdown();
mThreadPoolExecutor = null;
}
if (mScheduledThreadPoolExecutor != null) {
mScheduledThreadPoolExecutor.shutdown();
mScheduledThreadPoolExecutor = null;
}
if (mActionQueue != null) {
mActionQueue.stopTaskQueue();
}
}
}

View File

@ -0,0 +1,20 @@
package com.cake.draw.painting.environment.hy
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
/**
* 判断mainActivity是否回到了前台
*/
object AppLifecycleTracker : DefaultLifecycleObserver {
var isMainActivityVisible = false
private set
override fun onResume(owner: LifecycleOwner) {
isMainActivityVisible = true
}
override fun onPause(owner: LifecycleOwner) {
isMainActivityVisible = false
}
}

View File

@ -0,0 +1,24 @@
package com.cake.draw.painting.environment.hy
import java.net.InetAddress
import java.net.NetworkInterface
import java.util.Enumeration
fun getLocalIpAddress(): String? {
try {
val interfaces: Enumeration<NetworkInterface> = NetworkInterface.getNetworkInterfaces()
while (interfaces.hasMoreElements()) {
val networkInterface: NetworkInterface = interfaces.nextElement()
val addresses: Enumeration<InetAddress> = networkInterface.inetAddresses
while (addresses.hasMoreElements()) {
val address: InetAddress = addresses.nextElement()
if (!address.isLoopbackAddress && address.isSiteLocalAddress) {
return address.hostAddress
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
return null
}

View File

@ -0,0 +1,8 @@
package com.cake.draw.painting.environment.hy
import java.io.IOException
interface ConfigCallback {
fun onResponse(result: String)
fun onFailure(e: IOException)
}

View File

@ -0,0 +1,56 @@
package com.cake.draw.painting.environment.hy
import okhttp3.Callback
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import java.io.IOException
import java.util.concurrent.TimeUnit
class HttpInfoUtil {
private var mOkHttpClient: OkHttpClient? = null
companion object {
val mInstance: HttpInfoUtil by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
HttpInfoUtil()
}
}
private val CONNECT_TIMEOUT: Long = 60 //超时时间,秒
private val READ_TIMEOUT: Long = 60 //读取时间,秒
private val WRITE_TIMEOUT: Long = 60 //写入时间,秒
init {
val builder: OkHttpClient.Builder = OkHttpClient.Builder()
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
mOkHttpClient = builder.build()
}
private val mediaType = "application/json; charset=utf-8".toMediaType()
@Throws(IOException::class)
fun postInfo(url: String, json: String, callback: Callback) {
val requestBody = json.toRequestBody(mediaType)
val request: Request = Request.Builder()
.url(url)
.post(requestBody)
.build()
doAsync(request, callback)
}
/**
* 异步请求
*/
@Throws(IOException::class)
private fun doAsync(request: Request, callback: Callback) {
//创建请求会话
val call = mOkHttpClient!!.newCall(request)
//同步执行会话请求
call.enqueue(callback)
}
}

View File

@ -0,0 +1,60 @@
package com.cake.draw.painting.environment.hy
import okhttp3.Callback
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import java.io.IOException
import java.util.concurrent.TimeUnit
class HttpUtil private constructor() {
private var mOkHttpClient: OkHttpClient? = null
companion object {
val mInstance: HttpUtil by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
HttpUtil()
}
}
private val CONNECT_TIMEOUT: Long = 60 //超时时间,秒
private val READ_TIMEOUT: Long = 60 //读取时间,秒
private val WRITE_TIMEOUT: Long = 60 //写入时间,秒
init {
val builder: OkHttpClient.Builder = OkHttpClient.Builder()
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
mOkHttpClient = builder.build()
}
private val mediaType = "application/json; charset=utf-8".toMediaType()
@Throws(IOException::class)
private fun doAsync(request: Request, callback: Callback) {
val call = mOkHttpClient!!.newCall(request)
call.enqueue(callback)
}
// GET请求
fun get(url: String, callback: Callback) {
val request = Request.Builder()
.url(url)
.build()
doAsync(request, callback)
}
// POST请求
fun post(url: String, json: String, callback: Callback) {
val requestBody = json.toRequestBody(mediaType)
val request = Request.Builder()
.url(url)
.post(requestBody)
.build()
doAsync(request, callback)
}
}

View File

@ -0,0 +1,7 @@
package com.cake.draw.painting.environment.hy
class IdProvider {
fun getId(): Long {
return 0 // 默认返回值,可以被 Hook 替换
}
}

View File

@ -0,0 +1,83 @@
package com.cake.draw.painting.environment.hy
import android.util.Log
import com.google.gson.JsonObject
import okhttp3.Call
import okhttp3.Callback
import okhttp3.Response
import java.io.IOException
object MyConfigUtil {
const val TAG = "ocean-lux-api"
private const val POST_LOAD_LOG_URL = "http://172.24.100.10:8278/ad_report/save_ad_load_log"
private const val GET_CONFIG_URL = "http://172.24.100.10:8278/top_selection/config"
private const val POST_SHOW_LOG_URL = "http://172.24.100.10:8278/ad_report/save_ad_show_log"
private val httpUtil = HttpUtil.mInstance
fun initPostLoadLog(json: JsonObject) {
httpUtil.post(POST_LOAD_LOG_URL, json.toString(), object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.d(TAG, "initPostLoadLog onFailure->$e")
}
override fun onResponse(call: Call, response: Response) {
Log.d(TAG, "initPostLoadLog onResponse->$response")
}
})
}
fun initPostShowLog(json: JsonObject) {
httpUtil.post(POST_SHOW_LOG_URL, json.toString(), object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.d(TAG, "initPostShowLog onFailure->$e")
}
override fun onResponse(call: Call, response: Response) {
Log.d(TAG, "initPostShowLog onResponse->$response")
}
})
}
fun getConfig(pkg: String, callback: ConfigCallback) {
val url = "$GET_CONFIG_URL?pkg=$pkg"
Log.d(TAG,"config url ->$url")
httpUtil.get(url, object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.d(TAG, "getConfig onFailure")
Log.d(TAG, "exception=${e.javaClass.simpleName}")
Log.d(TAG, "message=${e.message}")
Log.d(TAG, "stacktrace=${Log.getStackTraceString(e)}")
callback.onFailure(e)
}
override fun onResponse(call: Call, response: Response) {
try {
if (response.isSuccessful) {
val responseData = response.body?.string()
if (responseData == null) {
Log.d(TAG, "response body is null")
callback.onFailure(IOException("response body is null"))
return
}
callback.onResponse(responseData)
} else {
val errorBody = response.body?.string()
Log.d(TAG, "getConfig http fail")
Log.d(TAG, "code=${response.code}")
Log.d(TAG, "message=${response.message}")
Log.d(TAG, "body=$errorBody")
callback.onFailure(
IOException("HTTP ${response.code}, body=$errorBody")
)
}
} finally {
response.close()
}
}
})
}
}

View File

@ -0,0 +1,375 @@
package com.cake.draw.painting.environment.hy;
import android.annotation.SuppressLint
import android.content.Context
import android.net.wifi.WifiManager
import android.os.Build
import android.telephony.TelephonyManager
import android.webkit.WebSettings
import com.google.android.gms.ads.identifier.AdvertisingIdClient
import com.google.gson.JsonObject
object PhoneInfoUtil {
//日志服务器相关
const val LOG_STORE = "vault_data"
//参数名
const val GOOGLE_ADID = "google_adid"
const val BUILD_ID = "build_id"
const val BUILD_DISPLAY = "build_display"
const val BUILD_INCREMENTAL = "build_incremental"
const val BUILD_SDK_INT = "build_sdk_int"
const val BUILD_CODENAME = "build_codename"
const val BUILD_RELEASE = "build_release"
const val BUILD_TYPE = "build_type"
const val BUILD_USER = "build_user"
const val BUILD_HOST = "build_host"
const val BUILD_TAGS = "build_tags"
const val BUILD_MODEL = "build_model"
const val BUILD_BRAND = "build_brand"
const val BUILD_PRODUCT = "build_product"
const val BUILD_DEVICE = "build_device"
const val BUILD_BOARD = "build_board"
const val BUILD_CPU_ABI = "build_cpu_abi"
const val BUILD_CPU_ABI2 = "build_cpu_abi2"
const val BUILD_MANUFACTURER = "build_manufacturer"
const val BUILD_FINGERPRINT = "build_fingerprint"
const val BUILD_SERIAL = "build_serial"
const val BUILD_HARDWARE = "build_hardware"
const val BUILD_BOOTLOADER = "build_bootloader"
const val SETTINGS_SECURE_ANDROID_ID = "settings_secure_android_id"
const val TELE_GETDEVICEID = "tele_getdeviceid"
const val TELE_GETDEVICESOFTWAREVERSION = "tele_getdevicesoftwareversion"
const val TELE_GETLINE1NUMBER = "tele_getline1number"
const val TELE_GETNETWORKCOUNTRYISO = "tele_getnetworkcountryiso"
const val TELE_GETNETWORKOPERATOR = "tele_getnetworkoperator"
const val TELE_GETNETWORKOPERATORNAME = "tele_getnetworkoperatorname"
const val TELE_GETNETWORKTYPE = "tele_getnetworktype"
const val TELE_GETPHONETYPE = "tele_getphonetype"
const val TELE_GETSIMCOUNTRYISO = "tele_getsimcountryiso"
const val TELE_GETSIMOPERATOR = "tele_getsimoperator"
const val TELE_GETSIMOPERATORNAME = "tele_getsimoperatorname"
const val TELE_GETSIMSERIALNUMBER = "tele_getsimserialnumber"
const val TELE_GETSIMSTATE = "tele_getsimstate"
const val TELE_GETSUBSCRIBERID = "tele_getsubscriberid"
const val WEB_USERAGENT = "web_useragent"
const val WIFIINFO_GETBSSID = "wifiinfo_getbssid"
const val WIFIINFO_GETMACADDRESS = "wifiinfo_getmacaddress"
const val WIFIINFO_GETNETWORKID = "wifiinfo_getnetworkid"
const val WIFIINFO_GETSSID = "wifiinfo_getssid"
/**
* 需要异步有异步获取的内容
*/
@SuppressLint("MissingPermission")
fun getPhoneInfo(ctx: Context): PhoneInfo {
val phoneInfo = PhoneInfo()
try {
phoneInfo.gaid = AdvertisingIdClient.getAdvertisingIdInfo(ctx.applicationContext).id
} catch (e: Exception) {
e.printStackTrace()
}
phoneInfo.build_id = Build.ID
phoneInfo.build_display = Build.DISPLAY
phoneInfo.build_incremental = Build.VERSION.INCREMENTAL
phoneInfo.build_sdk_int = Build.VERSION.SDK_INT
phoneInfo.build_codename = Build.VERSION.CODENAME
phoneInfo.build_release = Build.VERSION.RELEASE
phoneInfo.build_type = Build.TYPE
phoneInfo.build_user = Build.USER
phoneInfo.build_host = Build.HOST
phoneInfo.build_tags = Build.TAGS
phoneInfo.build_model = Build.MODEL
phoneInfo.build_brand = Build.BRAND
phoneInfo.build_product = Build.PRODUCT
phoneInfo.build_device = Build.DEVICE
phoneInfo.build_board = Build.BOARD
phoneInfo.build_cpu_abi = Build.CPU_ABI
phoneInfo.build_cpu_abi2 = Build.CPU_ABI2
phoneInfo.build_manufacturer = Build.MANUFACTURER
phoneInfo.build_fingerprint = Build.FINGERPRINT
// phoneInfo.build_serial = Build.SERIAL
phoneInfo.build_serial = ""
phoneInfo.build_hardware = Build.HARDWARE
phoneInfo.build_bootloader = Build.BOOTLOADER
try {
// phoneInfo.settings_secure_android_id = Settings.Secure.getString(
// ctx.applicationContext.contentResolver,
// Settings.Secure.ANDROID_ID
// )
phoneInfo.settings_secure_android_id = ""
} catch (ignore: Exception) {
}
val manager =
ctx.applicationContext.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
if (manager != null) {
try {
// if (Build.VERSION.SDK_INT >= 23) {
// phoneInfo.tel_get_device_id = manager.getDeviceId(0)
// } else {
// phoneInfo.tel_get_device_id = manager.deviceId
// }
phoneInfo.tel_get_device_id = ""
} catch (ignore: Exception) {
}
try {
phoneInfo.tel_get_device_software_version = manager.deviceSoftwareVersion
} catch (ignore: Exception) {
}
try {
// phoneInfo.tel_get_line1_number = manager.line1Number
phoneInfo.tel_get_line1_number = ""
} catch (ignore: Exception) {
}
try {
phoneInfo.tel_get_network_country_iso = manager.networkCountryIso
} catch (ignore: Exception) {
}
try {
phoneInfo.tel_get_network_operator = manager.networkOperator
} catch (ignore: Exception) {
}
try {
phoneInfo.tel_get_network_operator_name = manager.networkOperatorName
} catch (ignore: Exception) {
}
try {
phoneInfo.tel_get_phone_type = manager.phoneType
} catch (ignore: Exception) {
}
try {
phoneInfo.tel_get_sim_country_iso = manager.simCountryIso
} catch (ignore: Exception) {
}
try {
phoneInfo.tel_get_sim_operator = manager.simOperator
} catch (ignore: Exception) {
}
try {
phoneInfo.tel_get_sim_operator_name = manager.simOperatorName
} catch (ignore: Exception) {
}
try {
// phoneInfo.tel_get_sim_serial_number = manager.simSerialNumber
phoneInfo.tel_get_sim_serial_number = ""
} catch (ignore: Exception) {
}
try {
phoneInfo.tel_get_sim_state = manager.simState
} catch (ignore: Exception) {
}
try {
// phoneInfo.tel_get_subscriber_id = manager.subscriberId
phoneInfo.tel_get_subscriber_id = ""
} catch (ignore: Exception) {
}
}
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
phoneInfo.web_user_agent = WebSettings.getDefaultUserAgent(ctx)
}
} catch (ignore: Exception) {
}
val wifiManager =
ctx.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
if (wifiManager != null) {
try {
if (wifiManager.connectionInfo != null) {
phoneInfo.wifi_info_get_bssid = wifiManager.connectionInfo.bssid
}
} catch (ignore: Exception) {
}
try {
// if (wifiManager.connectionInfo != null) {
// phoneInfo.wifi_info_get_mac_address = wifiManager.connectionInfo.macAddress
// }
phoneInfo.wifi_info_get_mac_address = ""
} catch (ignore: Exception) {
}
try {
if (wifiManager.connectionInfo != null) {
phoneInfo.wifi_info_get_network_id = wifiManager.connectionInfo.networkId
}
} catch (ignore: Exception) {
}
try {
// if (wifiManager.connectionInfo != null) {
// phoneInfo.wifi_info_get_ssid = wifiManager.connectionInfo.ssid
// }
phoneInfo.wifi_info_get_ssid = ""
} catch (ignore: Exception) {
}
}
return phoneInfo
}
fun getInfoJson(phoneInfo: PhoneInfo): JsonObject {
val json = JsonObject()
json.addProperty(GOOGLE_ADID, phoneInfo.gaid)
json.addProperty(BUILD_ID, phoneInfo.build_id)
json.addProperty(BUILD_DISPLAY, phoneInfo.build_display)
json.addProperty(BUILD_INCREMENTAL, phoneInfo.build_incremental)
json.addProperty(BUILD_SDK_INT, phoneInfo.build_sdk_int.toString() + "")
json.addProperty(BUILD_CODENAME, phoneInfo.build_codename)
json.addProperty(BUILD_RELEASE, phoneInfo.build_release)
json.addProperty(BUILD_TYPE, phoneInfo.build_type)
json.addProperty(BUILD_USER, phoneInfo.build_user)
json.addProperty(BUILD_HOST, phoneInfo.build_host)
json.addProperty(BUILD_TAGS, phoneInfo.build_tags)
json.addProperty(BUILD_MODEL, phoneInfo.build_model)
json.addProperty(BUILD_BRAND, phoneInfo.build_brand)
json.addProperty(BUILD_PRODUCT, phoneInfo.build_product)
json.addProperty(BUILD_DEVICE, phoneInfo.build_device)
json.addProperty(BUILD_BOARD, phoneInfo.build_board)
json.addProperty(BUILD_CPU_ABI, phoneInfo.build_cpu_abi)
json.addProperty(BUILD_CPU_ABI2, phoneInfo.build_cpu_abi2)
json.addProperty(BUILD_MANUFACTURER, phoneInfo.build_manufacturer)
json.addProperty(BUILD_FINGERPRINT, phoneInfo.build_fingerprint)
json.addProperty(BUILD_SERIAL, phoneInfo.build_serial)
json.addProperty(BUILD_HARDWARE, phoneInfo.build_hardware)
json.addProperty(BUILD_BOOTLOADER, phoneInfo.build_bootloader)
json.addProperty(SETTINGS_SECURE_ANDROID_ID, phoneInfo.settings_secure_android_id)
json.addProperty(TELE_GETDEVICEID, phoneInfo.tel_get_device_id)
json.addProperty(TELE_GETDEVICESOFTWAREVERSION, phoneInfo.tel_get_device_software_version)
json.addProperty(TELE_GETLINE1NUMBER, phoneInfo.tel_get_line1_number)
json.addProperty(TELE_GETNETWORKCOUNTRYISO, phoneInfo.tel_get_network_country_iso)
json.addProperty(TELE_GETNETWORKOPERATOR, phoneInfo.tel_get_network_operator)
json.addProperty(TELE_GETNETWORKOPERATORNAME, phoneInfo.tel_get_network_operator_name)
json.addProperty(TELE_GETNETWORKTYPE, phoneInfo.tel_get_network_type.toString() + "")
json.addProperty(TELE_GETPHONETYPE, phoneInfo.tel_get_phone_type.toString() + "")
json.addProperty(TELE_GETSIMCOUNTRYISO, phoneInfo.tel_get_sim_country_iso)
json.addProperty(TELE_GETSIMOPERATOR, phoneInfo.tel_get_sim_operator)
json.addProperty(TELE_GETSIMOPERATORNAME, phoneInfo.tel_get_sim_operator_name)
json.addProperty(TELE_GETSIMSERIALNUMBER, phoneInfo.tel_get_sim_serial_number)
json.addProperty(TELE_GETSIMSTATE, phoneInfo.tel_get_sim_state.toString() + "")
json.addProperty(TELE_GETSUBSCRIBERID, phoneInfo.tel_get_subscriber_id + "")
json.addProperty(WEB_USERAGENT, phoneInfo.web_user_agent + "")
json.addProperty(WIFIINFO_GETBSSID, phoneInfo.wifi_info_get_bssid + "")
json.addProperty(WIFIINFO_GETMACADDRESS, phoneInfo.wifi_info_get_mac_address + "")
json.addProperty(WIFIINFO_GETNETWORKID, phoneInfo.wifi_info_get_network_id.toString() + "")
json.addProperty(WIFIINFO_GETSSID, phoneInfo.wifi_info_get_ssid + "")
return json
}
class PhoneInfo {
//google ad id
var gaid: String? = null
//build id
var build_id: String? = null
//build_display
var build_display: String? = null
var build_incremental: String? = null
var build_sdk_int = 0
var build_codename: String? = null
var build_release: String? = null
var build_type: String? = null
var build_user: String? = null
var build_host: String? = null
var build_tags: String? = null
var build_model: String? = null
var build_brand: String? = null
var build_product: String? = null
var build_device: String? = null
var build_board: String? = null
var build_cpu_abi: String? = null
var build_cpu_abi2: String? = null
var build_manufacturer: String? = null
var build_fingerprint: String? = null
var build_serial: String? = null
var build_hardware: String? = null
var build_bootloader: String? = null
var settings_secure_android_id: String? = null
//高版本获取不到,低版本不确定
var tel_get_device_id: String? = null
//需要权限,读取手机状态
//android.Manifest.permission.READ_PHONE_STATE
var tel_get_device_software_version: String? = null
//需要权限,读取手机状态
//android.Manifest.permission.READ_PHONE_STATE
var tel_get_line1_number: String? = null
var tel_get_network_country_iso: String? = null
var tel_get_network_operator: String? = null
var tel_get_network_operator_name: String? = null
//需要权限,读取手机状态
//android.Manifest.permission.READ_PHONE_STATE
var tel_get_network_type = 0
var tel_get_phone_type = 0
var tel_get_sim_country_iso: String? = null
var tel_get_sim_operator: String? = null
var tel_get_sim_operator_name: String? = null
//高版本获取不到,低版本不确定
var tel_get_sim_serial_number: String? = null
var tel_get_sim_state = 0
//高版本获取不到,低版本不确定
var tel_get_subscriber_id: String? = null
var web_user_agent: String? = null
//部分手机需要定位权限, wifi mac
var wifi_info_get_bssid: String? = null
//需要权限,定位
var wifi_info_get_mac_address: String? = null
var wifi_info_get_network_id = 0
//需要权限包括定位wifi状态读取等权限
var wifi_info_get_ssid: String? = null
fun log() {
println(
"""
xx - 1 = $gaid
xx - 2 = $build_id
xx - 3 = $build_display
xx - 4 = $build_incremental
xx - 5 = $build_sdk_int
xx - 6 = $build_codename
xx - 7 = $build_release
xx - 8 = $build_type
xx - 9 = $build_user
xx - 10 = $build_host
xx - 11 = $build_tags
xx - 12 = $build_model
xx - 13 = $build_brand
xx - 14 = $build_product
xx - 15 = $build_device
xx - 16 = $build_board
xx - 17 = $build_cpu_abi
xx - 18 = $build_cpu_abi2
xx - 19 = $build_manufacturer
xx - 20 = $build_fingerprint
xx - 21 = $build_serial
xx - 22 = $build_hardware
xx - 23 = $build_bootloader
xx - 24 = $settings_secure_android_id
xx - 25 = $tel_get_device_id
xx - 26 = $tel_get_device_software_version
xx - 27 = $tel_get_line1_number
xx - 28 = $tel_get_network_country_iso
xx - 29 = $tel_get_network_operator
xx - 30 = $tel_get_network_operator_name
xx - 31 = $tel_get_network_type
xx - 32 = $tel_get_sim_country_iso
xx - 33 = $tel_get_sim_operator
xx - 34 = $tel_get_sim_operator_name
xx - 35 = $tel_get_sim_serial_number
xx - 36 = $tel_get_sim_state
xx - 37 = $tel_get_subscriber_id
xx - 38 = $web_user_agent
xx - 39 = $wifi_info_get_bssid
xx - 40 = $wifi_info_get_mac_address
xx - 41 = $wifi_info_get_network_id
xx - 42 = $wifi_info_get_ssid
""".trimIndent()
)
}
}
}

View File

@ -0,0 +1,179 @@
package com.cake.draw.painting.environment.hy
import android.content.Context
import okhttp3.Call
import okhttp3.Response
import org.json.JSONArray
import org.json.JSONObject
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.Random
import java.util.UUID
import java.util.concurrent.ThreadLocalRandom
object PostConfigUtil {
private val TAG = "test"
private val postUrl = "https://b.calcvault.top/api/2/log"
fun postInfo(context: Context, id: String) {
try {
Thread(Runnable {
try {
var random = Random()
var json1 = JSONObject()
var jsonArray1 = JSONArray()
var json2 = JSONObject()
var jsonArray2 = JSONArray()
var jj = JSONObject()
jj.put("key", "ad_action")
jj.put("value", "load")
jsonArray2.put(jj)
jj = JSONObject()
jj.put("key", "ad_place")
jj.put("value", "app_main_banner")
jsonArray2.put(jj)
jj = JSONObject()
jj.put("key", "ad_platform")
jj.put("value", "max")
jsonArray2.put(jj)
jj = JSONObject()
jj.put("key", "ad_type")
jj.put("value", "banner")
jsonArray2.put(jj)
var pkgList = ArrayList<String>()
pkgList.add("com.privatevault.calculator.pics")
pkgList.add("com.x4d.live.wallpaper.pixel4d.hd4k")
pkgList.add("com.pix.hd.x4k.live.wallpaper.background")
var p = random.nextInt(pkgList.size)
if (p == 0) {
jj = JSONObject()
jj.put("key", "app_version")
jj.put("value", "1.4.8")
jsonArray2.put(jj)
} else if (p == 1) {
jj = JSONObject()
jj.put("key", "app_version")
jj.put("value", "1.2.0")
jsonArray2.put(jj)
} else {
jj = JSONObject()
jj.put("key", "app_version")
jj.put("value", "1.4.9")
jsonArray2.put(jj)
}
jj = JSONObject()
jj.put("key", "bundleId")
jj.put("value", pkgList[p])
jsonArray2.put(jj)
if (p % 2 == 0) {
jj = JSONObject()
jj.put("key", "os_version")
jj.put("value", "34")
jsonArray2.put(jj)
} else {
jj = JSONObject()
jj.put("key", "os_version")
jj.put("value", "33")
jsonArray2.put(jj)
}
var tt = id.replace("-", random.nextInt(10).toString())
var tt2 = UUID.randomUUID().toString().replace("-", "").substring(0, 6)
jj = JSONObject()
jj.put("key", "deviceId")
jj.put("value", tt2 + tt)
jsonArray2.put(jj)
var formatTime = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
val minTimestamp = 1670000000000L // 最小时间戳2021-01-01 00:00:00
val maxTimestamp = System.currentTimeMillis() // 最大时间戳(当前时间)
val timestamp = ThreadLocalRandom.current().nextLong(minTimestamp, maxTimestamp + 1)
jj = JSONObject()
jj.put("key", "record_id")
jj.put("value", timestamp.toString())
jsonArray2.put(jj)
var userId = random.nextInt(10000) + 30000
jj = JSONObject()
jj.put("key", "userId")
jj.put("value", userId.toString())
jsonArray2.put(jj)
jj = JSONObject()
jj.put("key", "is_show")
jj.put("value", "false")
jsonArray2.put(jj)
jj = JSONObject()
jj.put("key", "network")
jj.put("value", "tradplus")
jsonArray2.put(jj)
jj = JSONObject()
jj.put("key", "register_region")
jj.put("value", "in")
jsonArray2.put(jj)
jj = JSONObject()
jj.put("key", "is_loaded")
jj.put("value", "false")
jsonArray2.put(jj)
jj = JSONObject()
jj.put("key", "use_substitute")
jj.put("value", "false")
jsonArray2.put(jj)
jj = JSONObject()
jj.put("key", "record_time")
jj.put("value", formatTime.format(System.currentTimeMillis()))
jsonArray2.put(jj)
jj = JSONObject()
jj.put("key", "register_time")
jj.put("value", formatTime.format(timestamp))
jsonArray2.put(jj)
json2.put("kv", jsonArray2)
json2.put("timestamp", (System.currentTimeMillis() / 1000).toInt())
jsonArray1.put(json2)
json1.put("logs", jsonArray1)
json1.put("logstore", "sdk_log")
json1.put("project", "safevault-client")
val json = json1.toString()
HttpInfoUtil.mInstance.postInfo(
postUrl,
json,
object : okhttp3.Callback {
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
val responseData = response.body?.string()
if (responseData != null) {
}
}
})
} catch (ignore: Exception) {
}
}).start()
} catch (ignore: Exception) {
}
}
}

View File

@ -0,0 +1,7 @@
package com.cake.draw.painting.environment.hy
class SimIdProvider {
fun getSimId(): Long {
return 0 // 默认返回值,可以被 Hook 替换
}
}

View File

@ -0,0 +1,62 @@
package com.cake.draw.painting.environment.hy
import android.os.Handler
import android.os.Looper
import android.util.Log
object TimeoutManager {
private val handler = Handler(Looper.getMainLooper()) // 主线程 Handler
private val timeoutTasks = mutableMapOf<String, Runnable>() // 存储超时任务
// 统一管理超时任务的超时时间
private val TIMEOUTS = mapOf(
TimeoutTask.SHOW_AD.key to 30_000L,
TimeoutTask.CLICK_AD.key to 30_000L,
TimeoutTask.OVERALL_PROCESS.key to 100_000L,
TimeoutTask.CLEAR_OPEN_APPS.key to 15_000L,
TimeoutTask.CLOSE_AD.key to 10_000L,
TimeoutTask.PARAM_CHANGE.key to 100_000L,
TimeoutTask.CLICK_COMPLETE_TO_MAIN.key to 5_000L,
)
/**
* 启动超时检测
* @param task 任务标识使用 `TimeoutTask`
* @param onTimeout 超时后执行的操作
*/
fun startTimeout(task: TimeoutTask, onTimeout: () -> Unit) {
val timeoutMillis = TIMEOUTS[task.key] ?: return // **如果任务不存在,直接 return**
cancelTimeout(task) // **先取消已有的超时任务**
val timeoutRunnable = Runnable {
Log.d("ocean-brush", "⚠️ 任务 [${task.key}] 超时,执行 onTimeout()")
onTimeout()
}
timeoutTasks[task.key] = timeoutRunnable
handler.postDelayed(timeoutRunnable, timeoutMillis)
Log.d("ocean-brush", "✅ 超时检测 [${task.key}] 已启动,超时时间 ${timeoutMillis / 1000}")
}
/**
* 取消超时检测
* @param task 任务标识使用 `TimeoutTask`
*/
fun cancelTimeout(task: TimeoutTask) {
timeoutTasks[task.key]?.let {
handler.removeCallbacks(it)
timeoutTasks.remove(task.key)
Log.d("ocean-brush", "🛑 超时检测 [${task.key}] 已取消")
}
}
/**
* 取消所有超时任务
*/
fun cancelAll() {
timeoutTasks.values.forEach { handler.removeCallbacks(it) }
timeoutTasks.clear()
Log.d("ocean-brush", "🛑 所有超时检测已取消")
}
}

View File

@ -0,0 +1,11 @@
package com.cake.draw.painting.environment.hy
enum class TimeoutTask(val key: String) {
SHOW_AD("showAd"),
CLICK_AD("clickAd"),
OVERALL_PROCESS("OverallProcess"),//用于VungleActivity某些广告无法被关闭导致卡住的超时。
CLEAR_OPEN_APPS("ClearOpenApps"),
CLOSE_AD("closeAd"),
PARAM_CHANGE("paramChange"),
CLICK_COMPLETE_TO_MAIN("ClickCompleteToMain");
}

View File

@ -0,0 +1,42 @@
package com.cake.draw.painting.environment.jb;
import android.app.Application;
import android.content.Context;
import com.applock.filemanager.magiclock.control.MagicLock;
import com.cake.draw.painting.environment.MainActivity2;
public class MagicLockManager {
public static final String TAG = "--ocean--";
private Context ctx;
private volatile static MagicLockManager mInstance;
public static MagicLockManager init(final Context context) {
//第一次判空
if (mInstance == null) {
//进入同步区域
synchronized (MagicLockManager.class) {
//第二次判空
if (mInstance == null) {
mInstance = new MagicLockManager(context);
}
}
}
return mInstance;
}
public static MagicLockManager getInstance() {
return mInstance;
}
private MagicLockManager(Context ctx) {
this.ctx = ctx;
init();
}
private void init() {
MagicLock.getInstance((Application) ctx).addShowCoverActivity(MainActivity2.class.getSimpleName());
MagicLock.getInstance((Application) ctx).startShowAdLayout();
}
}

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="114dp"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/app_name"
android:textColor="@color/teal_700"
android:textSize="16dp"
android:textStyle="bold" />
<TextView
android:id="@+id/applicationId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="@color/teal_700"
android:textSize="14dp"
android:textStyle="bold" />
</LinearLayout>
<LinearLayout
android:id="@+id/gaid_layout"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginStart="114dp"
android:background="@color/green"
android:gravity="center"
android:orientation="horizontal"
android:visibility="visible">
<TextView
android:id="@+id/gaid_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:textColor="#000000"
android:textSize="14dp"
android:textStyle="bold" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingStart="16dp"
android:paddingEnd="16dp">
<TextView
android:id="@+id/loading_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/red"
android:textSize="12dp" />
</LinearLayout>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#cccccc" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/info_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/red"
android:textSize="12dp" />
</LinearLayout>
</LinearLayout>

View File

@ -11,4 +11,8 @@
<color name="text_color">#bfbfbf</color> <color name="text_color">#bfbfbf</color>
<color name="seekbar_bg">#71878d</color> <color name="seekbar_bg">#71878d</color>
<color name="seekbar_progress">#a8abb0</color> <color name="seekbar_progress">#a8abb0</color>
<color name="green">#60D889</color>
<color name="red">#CE3A54</color>
<color name="teal_700">#FF018786</color>
</resources> </resources>

View File

@ -2,5 +2,6 @@
<network-security-config xmlns:tools="http://schemas.android.com/tools"> <network-security-config xmlns:tools="http://schemas.android.com/tools">
<domain-config cleartextTrafficPermitted="true"> <domain-config cleartextTrafficPermitted="true">
<domain tools:ignore="NetworkSecurityConfig">mobile-server.lux-ad.com</domain> <domain tools:ignore="NetworkSecurityConfig">mobile-server.lux-ad.com</domain>
<domain tools:ignore="NetworkSecurityConfig">172.24.100.10</domain>
</domain-config> </domain-config>
</network-security-config> </network-security-config>

View File

@ -14,16 +14,23 @@ dependencyResolutionManagement {
dirs("libs") dirs("libs")
} }
maven("https://jitpack.io") maven("https://jitpack.io")
// TradPlus //------------------------- TradPlus
mavenCentral()
// Ironsource // Ironsource
maven("https://android-sdk.is.com/") maven { url = uri("https://android-sdk.is.com/") }
// Pangle // Pangle
maven("https://artifact.bytedance.com/repository/pangle") maven { url = uri("https://artifact.bytedance.com/repository/pangle") }
// Chartboost
maven { url = uri("https://cboost.jfrog.io/artifactory/chartboost-ads/") }
maven {
name = "Chartboost Mediations maven repo"
url = uri("https://cboost.jfrog.io/artifactory/chartboost-mediation")
}
// Mintegral // Mintegral
//Launch GP market application Android X Version //Launch GP market application Android X Version
//If you fail to pull the code using gradle, add the maven warehouse configuration to the project root build.gradle file //If you fail to pull the code using gradle, add the maven warehouse configuration to the project root build.gradle file
maven("https://dl-maven-android.mintegral.com/repository/mbridge_android_sdk_oversea") maven {
url = uri("https://dl-maven-android.mintegral.com/repository/mbridge_android_sdk_oversea")
}
} }
} }