添加机房B面

This commit is contained in:
ocean 2026-01-15 10:57:55 +08:00
parent 6ed1de4f0c
commit 519a4a5873
42 changed files with 2720 additions and 23 deletions

1
.gitignore vendored
View File

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

View File

@ -56,6 +56,7 @@ android {
}
buildFeatures{
viewBinding = true
aidl = true
}
}
@ -90,7 +91,6 @@ dependencies {
androidTestImplementation(libs.androidx.espresso.core)
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")
@ -103,7 +103,7 @@ dependencies {
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/IronSourceLibrary_01_07_18_13-release.aar"))
//---------------------------ironSource

Binary file not shown.

View File

@ -31,3 +31,6 @@
-keepattributes Signature
-keepattributes AnnotationDefault,RuntimeVisibleAnnotations
-keep class com.gallery.free.wallpaper.environment.hy.IdProvider { *; }
-keep class com.gallery.free.wallpaper.environment.hy.SimIdProvider { *; }

View File

@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="DiscouragedApi,LockedOrientationActivity">
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
@ -15,6 +17,8 @@
<uses-permission android:name="android.permission.AD_ID" />
<uses-permission android:name="com.google.android.gms.permission.AD_ID" />
<application
android:name=".MyApplication"
android:allowBackup="true"
@ -24,19 +28,18 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:largeHeap="true"
android:networkSecurityConfig="@xml/net"
android:persistableMode="persistAcrossReboots"
android:requestLegacyExternalStorage="true"
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
android:supportsRtl="true"
tools:replace="networkSecurityConfig"
android:networkSecurityConfig="@xml/net"
android:taskAffinity=""
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
tools:replace="networkSecurityConfig"
tools:targetApi="30">
<activity
android:name=".LocalPreActivity"
android:exported="false"
@ -48,16 +51,28 @@
android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
<activity
android:name=".SplaActivity"
android:name="com.gallery.free.wallpaper.SplashActivity"
android:exported="true"
android:launchMode="singleTask"
android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="best.log" />
<category android:name="best.facebook.go.log" />
<action android:name="best.wash.param" />
<category android:name="best.wash.param.go" />
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.gallery.free.wallpaper.environment.MainActivity2"
android:exported="true"
android:launchMode="singleTask"
android:screenOrientation="portrait" />
<activity
android:name=".MainActivity"
android:exported="false"

View File

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

View File

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

View File

@ -14,7 +14,8 @@ import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.RequestOptions
import com.gallery.free.wallpaper.database.FavorWpManager
import com.up.uploadlibrary.UpLoadManager
import com.gallery.free.wallpaper.environment.ad.AdActivityManager
import com.gallery.free.wallpaper.environment.jb.MagicLockManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@ -76,7 +77,8 @@ class MyApplication : Application() {
// 验证固定分类文件
validateFixedCategories()
UpLoadManager.init(this,"ocean", callback = { _, _ -> })
AdActivityManager.instance.setControl(this)
MagicLockManager.init(this)
ISAdManager.init("-------WallpaperGallery--------",this,"24e146eb5","1x1dmfonwgx54zkn","3r21zexffllavwqj","4v2mbujbbk8wq7ed",false,{

View File

@ -7,8 +7,10 @@ import android.os.CountDownTimer
import androidx.appcompat.app.AppCompatActivity
import com.ad.ironsourcelibrary.ISAdManager
import com.gallery.free.wallpaper.databinding.ActivitySplaBinding
import com.gallery.free.wallpaper.environment.MainActivity2
import com.gallery.free.wallpaper.environment.hy.IdProvider
class SplaActivity : AppCompatActivity() {
class SplashActivity : AppCompatActivity() {
private var countDownTimer: CountDownTimer? = null
private var vb: ActivitySplaBinding? = null
private val totalTime: Long = 15000
@ -19,7 +21,7 @@ class SplaActivity : AppCompatActivity() {
vb = ActivitySplaBinding.inflate(layoutInflater)
Utils_com.initFull(this, true)
setContentView(vb?.getRoot())
if (IdProvider().getId() == 0L) {
countDownTimer = ISAdManager.showWelcomeAd(this, totalTime, { millisUntilFinished ->
val progressPercentage = ((100 * millisUntilFinished) / totalTime).toInt()
@ -28,11 +30,16 @@ class SplaActivity : AppCompatActivity() {
vb?.progressbar?.progress = countdownPercentage
}) {
vb?.progressbar?.progress = 100
val intent = Intent(this@SplaActivity, MainActivity::class.java)
val intent = Intent(this@SplashActivity, MainActivity::class.java)
startActivity(intent)
finish()
}
countDownTimer?.start()
} else {
val intent = Intent(this@SplashActivity, MainActivity2::class.java)
startActivity(intent)
finish()
}
}

View File

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

View File

@ -0,0 +1,740 @@
package com.gallery.free.wallpaper.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.core.graphics.toColorInt
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import com.applock.filemanager.magiclock.control.MagicLock
import com.google.android.gms.ads.identifier.AdvertisingIdClient
import com.google.gson.JsonObject
import com.unity3d.mediation.LevelPlay
import com.unity3d.mediation.LevelPlayAdInfo
import com.unity3d.mediation.LevelPlayConfiguration
import com.unity3d.mediation.LevelPlayInitError
import com.unity3d.mediation.LevelPlayInitListener
import com.unity3d.mediation.LevelPlayInitRequest
import com.gallery.free.wallpaper.R
import com.gallery.free.wallpaper.databinding.ActivityMain2Binding
import com.gallery.free.wallpaper.environment.ad.AdShowFailed
import com.gallery.free.wallpaper.environment.ad.AdsInsUtil
import com.gallery.free.wallpaper.environment.ad.InstAdCacheManager
import com.gallery.free.wallpaper.environment.ad.LoadListener
import com.gallery.free.wallpaper.environment.ad.ShowListener
import com.gallery.free.wallpaper.environment.hy.AppLifecycleTracker
import com.gallery.free.wallpaper.environment.hy.ConfigCallback
import com.gallery.free.wallpaper.environment.hy.IdProvider
import com.gallery.free.wallpaper.environment.hy.MyConfigUtil
import com.gallery.free.wallpaper.environment.hy.SimIdProvider
import com.gallery.free.wallpaper.environment.hy.TimeoutManager
import com.gallery.free.wallpaper.environment.hy.TimeoutTask
import com.gallery.free.wallpaper.environment.hy.getLocalIpAddress
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.util.Locale
import java.util.Random
import java.util.Timer
import java.util.TimerTask
import java.util.UUID
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
@SuppressLint("WrongConstant")
class MainActivity2 : AppCompatActivity() {
private lateinit var binding: ActivityMain2Binding
private var loadAdNumber = 0//load广告计数
private var timer: Timer? = null
private val adPlace: MutableList<String> = ArrayList()//可以被show的广告集合
private var loadAndShowAdNumber = 0//load后并且可以进行show的广告计数
private val loadJson = JsonObject()//需要上传的load日志json
private val showJson = JsonObject()//需要上传的show日志json
private val viewJson = JsonObject()//展示在刷刷包上的json选择了一些数据来展示。
private var quantity = ""//可以被load的广告次数
private var ecpmCool = ""//最高的ecpm配置
private var ecpmLow = ""//最低的ecpm配置
private var clickThroughRate = 80
private val gaIdError = "00000000-0000-0000-0000-000000000000"
private val loadingAds: MutableSet<String> = mutableSetOf()// 用来跟踪正在加载的广告
private var startInit = false//是否已经到达初始化广告
private var shelfNumber = "123"
private var devicesID = ""
private val appStartJson = JsonObject()
private var dataId = 0L
private var simId = 0L
private val aidlClient = AIDLClient.instance
private var isProcessComplete = true //流程是否完毕
private var remoteIp = "0.0.0.0"//上传到服务的IP
private val onAdShownLiveData = MutableLiveData<Boolean>()
private val onAdShowFailedLiveData = MutableLiveData<Boolean>()
private val onAdClosedLiveData = MutableLiveData<Boolean>()
@SuppressLint("MissingInflatedId", "SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMain2Binding.inflate(layoutInflater)
setContentView(binding.root)
binding.title.text = "Max(1月15日)-${getString(R.string.app_name)}"
// 异步获取 IP不影响主流程
lifecycleScope.launch {
remoteIp = getPublicIpAddress()
loadJson.addProperty("remoteIp", remoteIp)//远程IP
showJson.addProperty("remoteIp", remoteIp)//远程IP
}
val initRequest = LevelPlayInitRequest.Builder("24e146eb5")
.build()
LevelPlay.init(this, initRequest, object : LevelPlayInitListener {
override fun onInitFailed(error: LevelPlayInitError) {
//Recommended to initialize again
appendLoadingTxt("sdk初始化失败${timeLeft}秒后将会重置")
runOnUiThread {
startCountdown()
}
}
override fun onInitSuccess(configuration: LevelPlayConfiguration) {
//Create ad objects and load ads
appendLoadingTxt("sdk初始化成功")
lifecycle.addObserver(AppLifecycleTracker)
//初始化aidl连接
aidlClient.initConnection()
//绑定AIDL服务
aidlClient.connectService(this@MainActivity2)
aidlClient.connectCompleteLiveData.observeForever(connectCompleteObserver)
aidlClient.paramCompleteLiveData.observeForever(paramCompleteObserver)
aidlClient.clickAdCompleteLiveData.observeForever(clickAdCompleteObserver)
onAdShownLiveData.observeForever(onAdShownObserver)
onAdShowFailedLiveData.observeForever(onAdShowFailedObserver)
onAdClosedLiveData.observeForever(onAdCloseObserver)
}
})
}
private val connectCompleteObserver = Observer<Boolean> {
if (it) {
appendLoadingTxt("CP控制器连接成功进行初始化配置")
aidlClient.sendBrushMiss()
//初始化配置
initConfig()
//初始化屏幕点击范围
magicLockInit()
} else {
appendLoadingTxt("CP控制器连接失败检查是否安装了正确的软件")
}
}
private val paramCompleteObserver = Observer<Boolean> {
TimeoutManager.cancelTimeout(TimeoutTask.PARAM_CHANGE)
appendLoadingTxt("参数修改完成")
Log.d("ocean-brush", "MainActivity 参数修改操作完成->$it")
if (it) {
aidlClient.sendResetApp(packageName)
} else {
appendLoadingTxt("参数修改失败,等待${paramTimeLeft}秒再次进行参数修改")
startParamCountdown()
}
}
private val clickAdCompleteObserver = Observer<Boolean> {
Log.d("ocean-brush", "MainActivity 监听到点击广告指令完成")
if (it) {
if (AppLifecycleTracker.isMainActivityVisible) {
Log.d(
"ocean-brush",
"MainActivity 成功回到前台,取消点击超时任务,并置 isProcessComplete = true"
)
TimeoutManager.cancelTimeout(TimeoutTask.CLICK_AD)
isProcessComplete = true
} else {
Log.d("ocean-brush", "MainActivity 但是没有回到前台,进行修改参数重置")
aidlClient.sendChangeParameters(packageName)
}
} else {
Log.d("ocean-brush", "MainActivity 但是intent=null进行修改参数重置")
aidlClient.sendChangeParameters(packageName)
}
}
private val onAdShownObserver = Observer<Boolean> {
Log.d("ocean-brush", "MainActivity 监听到广告展示成功")
TimeoutManager.cancelTimeout(TimeoutTask.SHOW_AD)
val isSendClick = aidlClient.sendClickAd(clickThroughRate, packageName)
if (isSendClick) {//指令发送成功
//广告展示后,启动广告关闭超时任务
TimeoutManager.startTimeout(TimeoutTask.CLOSE_AD) {//十秒
Log.d("ocean-brush", "超时任务,广告关闭失败,直接进行改参重置")
aidlClient.sendChangeParameters(packageName)
}
//发送点击广告指令后,启动点击广告的超时任务
TimeoutManager.startTimeout(TimeoutTask.CLICK_AD) {
Log.d("ocean-brush", "超时任务,广告点击流程没有回来,直接进行重置")
aidlClient.sendChangeParameters(packageName)
}
}
}
private val onAdShowFailedObserver = Observer<Boolean> {
Log.d("ocean-brush", "MainActivity 监听到广告展示失败,直接进行重置")
TimeoutManager.cancelTimeout(TimeoutTask.SHOW_AD)
aidlClient.sendChangeParameters(packageName)
}
private val onAdCloseObserver = Observer<Boolean> {
Log.d("ocean-brush", "MainActivity 监听到广告被关闭")
TimeoutManager.cancelTimeout(TimeoutTask.CLOSE_AD)
}
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 \nConfig获取失败请检查是否在后台配置包名${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: LevelPlayAdInfo) {
aidlClient.sendBrushMiss()
val loadedEndTime = System.currentTimeMillis()
appendLoadingTxt("${placeId}AD加载成功")
aidlClient.sendAdsChange(packageName, getInsAdReturnCount())
val scientificNotation = ad.revenue
val formattedString: String = String.format("%.20f", scientificNotation)
appendLoadingTxt("${ad.adNetwork} ecpm->$formattedString")
Log.d("ocean", "平台->${ad.adNetwork} ecpm->${ad.revenue}")
loadJson.addProperty("succeed", true)//广告加载是否成功
loadJson.addProperty("loadTime", loadedEndTime - startLoadTime)//加载时间
loadJson.addProperty("adPlatform", "IronSource")//广告平台
loadJson.addProperty("countryCode", ad.country)//国家
loadJson.addProperty("adId", placeId)//广告Id
loadJson.addProperty("platformResponseTime", "")//平台广告响应时间
loadJson.addProperty("ecpm", formattedString)//广告单价
loadJson.addProperty("dsp", ad.adNetwork)
loadJson.addProperty("network", ad.adNetwork)
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", "IronSource")//广告平台
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: LevelPlayAdInfo) {
aidlClient.sendBrushMiss()
Handler(Looper.getMainLooper()).post {
onAdShownLiveData.value = true
}
val scientificNotation = ad.revenue
val formattedString: String = String.format("%.20f", scientificNotation)
showJson.addProperty("succeed", true)//广告加载是否成功
showJson.addProperty("adPlatform", "IronSource")//广告平台
showJson.addProperty("countryCode", ad.country)//国家代码
showJson.addProperty("adId", place)//广告Id
showJson.addProperty("platformResponseTime", "")//平台广告响应时间
showJson.addProperty("ecpm", formattedString)//广告单价
showJson.addProperty("dsp", ad.adNetwork)
showJson.addProperty("network", ad.adNetwork)
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", "IronSource")//广告平台
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("#CE3A54".toColorInt())
//gaID没有确的值就发送重置广播
appendLoadingTxt("没有获取到GAID$timeLeft 秒后将会重置")
Log.d("ocean", "没有获取到GAID")
startCountdown()
} else {
Log.d("ocean", "获取到GAID")
binding.gaidLayout.setBackgroundColor("#60D889".toColorInt())
initAd()
}
} else {
appendLoadingTxt("没有获取到GAID$timeLeft 秒后将会重置")
startCountdown()
}
}
}
}
}
/**
* https://api.tikustok.com/app/common/getIPInfo
* https://openapi.lux-ad.com/app/common/getIPInfo
*/
private suspend fun getPublicIpAddress(): String = suspendCoroutine { continuation ->
var publicIp = "0.0.0.0"
try {
VmHttpUtil.mInstance.getIPInfo(
"https://openapi.lux-ad.com/app/common/getIPInfo",
object : Callback {
override fun onFailure(call: Call, e: IOException) {
runOnUiThread {
appendLoadingTxt("获取IP失败")
}
continuation.resume(publicIp)
}
override fun onResponse(call: Call, response: Response) {
Log.d("ocean", "getIPInfo response->${response}")
if (response.code == 200) {
val responseData = response.body?.string()
if (responseData != null) {
val jsonObject = JSONObject(responseData)
Log.d("ocean", "getIPInfo jsonObject->${jsonObject}")
val status = jsonObject.optString("status")
if (status == "Success") {
val data = jsonObject.getJSONObject("data")
appendLoadingTxt("获取IP成功->${data}")
Log.d("ocean", "getIPInfo data->${data}")
publicIp = data.optString("ip")
continuation.resume(publicIp)
} else {
continuation.resume(publicIp)
}
} else {
continuation.resume(publicIp)
}
} else {
runOnUiThread {
appendLoadingTxt("获取IP失败->${response}")
}
continuation.resume(publicIp)
}
}
})
} catch (e: Exception) {
e.printStackTrace()
appendLoadingTxt("获取IP失败 catch->${e}")
continuation.resume(publicIp)
}
}
override fun onDestroy() {
super.onDestroy()
aidlClient.connectCompleteLiveData.removeObserver(connectCompleteObserver)
aidlClient.paramCompleteLiveData.removeObserver(paramCompleteObserver)
aidlClient.clickAdCompleteLiveData.removeObserver(clickAdCompleteObserver)
onAdShownLiveData.removeObserver(onAdShownObserver)
onAdShowFailedLiveData.removeObserver(onAdShowFailedObserver)
onAdClosedLiveData.removeObserver(onAdCloseObserver)
aidlClient.disconnect(this)
stopCountdown()
stopParamCountdown()
}
private fun getSimCountryIso(context: Activity): String {
val telephonyManager = context.getSystemService("phone") as TelephonyManager?
return telephonyManager?.simCountryIso?.uppercase(Locale.ENGLISH) ?: ""
}
private fun mnc(): String {
val telephonyManager = getSystemService("phone") as TelephonyManager
return try {
val networkOperator = telephonyManager.networkOperator
networkOperator.substring(3.coerceAtMost(networkOperator.length))
} catch (e: Exception) {
""
}
}
private fun mcc(): String {
val telephonyManager = getSystemService("phone") as TelephonyManager
return try {
val networkOperator = telephonyManager.networkOperator
networkOperator.substring(0, minOf(3, networkOperator.length))
} catch (e: Exception) {
""
}
}
@SuppressLint("Range")
fun getDeviceIdFromProvider(callback: (String?, Boolean) -> Unit) {
val uri = Uri.parse("content://com.guise.deviceidprovider/device_id")
val cursor = contentResolver.query(uri, null, null, null, null)
if (cursor != null && cursor.moveToFirst()) {
val deviceId = cursor.getString(cursor.getColumnIndex("device_id"))
cursor.close() // 关闭 Cursor
callback(deviceId, true) // 成功读取到 Device ID
} else {
callback(null, false) // 读取失败
}
}
/**
* 通用进行重置五秒倒计时
*/
private var timeLeft = 5 // 倒计时时间
private val countdownHandler = Handler(Looper.getMainLooper())
private val countdownRunnable = object : Runnable {
override fun run() {
if (timeLeft > 0) {
appendLoadingTxt("⏳ 剩余 $timeLeft 秒后执行 修改参数 指令")
timeLeft--
countdownHandler.postDelayed(this, 1000) // 继续倒计时
} else {
appendLoadingTxt("🚀 执行 修改参数 指令")
aidlClient.sendChangeParameters(packageName)
}
}
}
private fun startCountdown() {
timeLeft = 5 // 重新初始化时间
countdownHandler.post(countdownRunnable) // 开始倒计时
}
private fun stopCountdown() {
countdownHandler.removeCallbacks(countdownRunnable) // 取消倒计时
}
/**
* 参数修改失败重新修改倒计时
*/
private var paramTimeLeft = 15
private val countdownParamHandler = Handler(Looper.getMainLooper())
private val countdownParamRunnable = object : Runnable {
override fun run() {
if (paramTimeLeft > 0) {
paramTimeLeft--
countdownParamHandler.postDelayed(this, 1000) // 继续倒计时
} else {
appendLoadingTxt("执行修改参数指令")
aidlClient.sendChangeParameters(packageName)
}
}
}
private fun startParamCountdown() {
paramTimeLeft = 15 // 重新初始化时间
countdownParamHandler.post(countdownParamRunnable) // 开始倒计时
}
private fun stopParamCountdown() {
countdownParamHandler.removeCallbacks(countdownParamRunnable) // 取消倒计时
}
}

View File

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

View File

@ -0,0 +1,220 @@
package com.gallery.free.wallpaper.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 android.view.WindowManager
import com.vungle.ads.internal.ui.view.MRAIDAdWidget
import com.gallery.free.wallpaper.environment.ad.async.Async
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.ironsource.sdk.controller.ControllerActivity
|| activity is com.ironsource.sdk.controller.OpenUrlActivity
|| activity is com.unity3d.ironsourceads.internal.services.InlineStoreActivity
|| activity is com.vungle.ads.internal.ui.AdActivity
|| activity is com.mbridge.msdk.activity.MBCommonActivity
|| activity is com.mbridge.msdk.config.activity.MBRewardVideoActivity
|| activity is com.unity3d.services.ads.adunit.AdUnitActivity
|| activity is sg.bigo.ads.ad.splash.AdSplashActivity
|| activity is com.chartboost.sdk.internal.clickthrough.EmbeddedBrowserActivity
|| activity is com.fyber.inneractive.sdk.activities.InneractiveInternalBrowserActivity
|| activity is com.fyber.inneractive.sdk.activities.InneractiveFullscreenAdActivity
|| activity is com.fyber.inneractive.sdk.activities.InneractiveRichMediaVideoPlayerActivityCore
|| activity is com.fyber.inneractive.sdk.activities.InternalStoreWebpageActivity
|| activity is com.fyber.inneractive.sdk.activities.FyberReportAdActivity
|| activity is com.inmobi.ads.rendering.InMobiAdActivity
|| activity is com.bytedance.sdk.openadsdk.activity.TTBaseActivity
)
}
}
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")
/**
* 2025年10月15日尝试使用对应广告的act里面的关闭方法调用
*/
when (adActivity) {
is com.ironsource.sdk.controller.ControllerActivity -> {
adActivity.onCloseRequested()
}
is com.vungle.ads.internal.ui.AdActivity -> {
//反射方式关闭
try {
// 获取 mraidAdWidget 字段
val field =
com.vungle.ads.internal.ui.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")
}
}
is com.mbridge.msdk.activity.MBCommonActivity -> {
adActivity.finish()
}
is sg.bigo.ads.ad.splash.AdSplashActivity ->{
adActivity.finish()
}
is com.fyber.inneractive.sdk.activities.InneractiveInternalBrowserActivity->{
adActivity.finish()
}
is com.fyber.inneractive.sdk.activities.InneractiveFullscreenAdActivity->{
val closeButton = adActivity.closeButton
closeButton?.performClick() ?: adActivity.dismissAd(true)
}
else -> {
adActivity.finish()
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) {
Log.d("ocean-brush", "广告 Activity 正在销毁,但可能残留窗口")
removeVungleWindow(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页面
}
private fun removeVungleWindow(activity: Activity) {
try {
val windowManager = activity.getSystemService(Context.WINDOW_SERVICE) as WindowManager
val decorView = activity.window.decorView
windowManager.removeView(decorView)
Log.d("ocean-brush", "AdActivity 残留窗口已移除")
} catch (e: Exception) {
Log.e("ocean-brush", "移除 AdActivity 窗口失败", e)
}
}
}

View File

@ -0,0 +1,64 @@
package com.gallery.free.wallpaper.environment.ad
import android.app.Activity
import android.util.Log
import com.unity3d.mediation.LevelPlayAdError
import com.unity3d.mediation.LevelPlayAdInfo
import com.unity3d.mediation.interstitial.LevelPlayInterstitialAd
import com.unity3d.mediation.interstitial.LevelPlayInterstitialAdListener
class AdInstLoad {
private var mPlace: String
private var adLoadListener: LoadListener? = null
private var activity: Activity? = null
constructor(activity: Activity, place: String, listener: LoadListener?) {
this.mPlace = place
this.adLoadListener = listener
this.activity = activity
init()
}
constructor(place: String, listener: LoadListener?) {
this.mPlace = place
this.adLoadListener = listener
init()
}
private fun init() {
// Create the interstitial ad object
val mInterstitialAd = LevelPlayInterstitialAd(mPlace)
mInterstitialAd.setListener(object : LevelPlayInterstitialAdListener {
override fun onAdLoaded(levelPlayAdInfo: LevelPlayAdInfo) {
// Ad was loaded successfully
InstAdCacheManager.instance.setAdCache(mPlace, mInterstitialAd)
adLoadListener?.loaded(levelPlayAdInfo)
}
override fun onAdLoadFailed(levelPlayAdError: LevelPlayAdError) {
// Ad load failed
adLoadListener?.loadFailed("code->${levelPlayAdError.errorCode} message->${levelPlayAdError.errorMessage}")
Log.d("ocean", "load ad onError-> code->${levelPlayAdError.errorCode} message->${levelPlayAdError.errorMessage}")
}
override fun onAdDisplayed(levelPlayAdInfo: LevelPlayAdInfo) {
// Ad was displayed and visible on screen
}
override fun onAdDisplayFailed(levelPlayAdError: LevelPlayAdError, levelPlayAdInfo: LevelPlayAdInfo) {
// Ad fails to be displayed
// Optional
}
override fun onAdClicked(levelPlayAdInfo: LevelPlayAdInfo) {
// Ad was clicked
// Optional
}
override fun onAdClosed(levelPlayAdInfo: LevelPlayAdInfo) {
// Ad was closed
// Optional
}
override fun onAdInfoChanged(levelPlayAdInfo: LevelPlayAdInfo) {
// Called after the ad info is updated. Available when another interstitial ad has loaded, and includes a higher CPM/Rate
// Optional
}
})
mInterstitialAd.loadAd()
}
}

View File

@ -0,0 +1,74 @@
package com.gallery.free.wallpaper.environment.ad
import android.app.Activity
import android.util.Log
import com.unity3d.mediation.LevelPlayAdError
import com.unity3d.mediation.LevelPlayAdInfo
import com.unity3d.mediation.interstitial.LevelPlayInterstitialAdListener
import com.gallery.free.wallpaper.environment.ad.async.Async
class AdInstShower {
private var mPlace: String
private var showListener: ShowListener? = null
private var activity: Activity? = null
constructor(activity: Activity, place: String, showListener: ShowListener?) {
this.mPlace = place
this.showListener = showListener
this.activity = activity
init()
}
constructor(place: String, showListener: ShowListener?) {
this.mPlace = place
this.showListener = showListener
init()
}
private fun init() {
val interstitialAd = InstAdCacheManager.instance.getAdCache(mPlace)
interstitialAd?.setListener(object : LevelPlayInterstitialAdListener{
override fun onAdLoaded(levelPlayAdInfo: LevelPlayAdInfo) {
// Ad was loaded successfully
}
override fun onAdLoadFailed(levelPlayAdError: LevelPlayAdError) {
// Ad load failed
}
override fun onAdDisplayed(levelPlayAdInfo: LevelPlayAdInfo) {
// Ad was displayed and visible on screen
showListener?.onAdShown(levelPlayAdInfo)
Log.d("ocean", "onAdDisplayed 广告展示回调")
autoClose()
}
override fun onAdDisplayFailed(levelPlayAdError: LevelPlayAdError, levelPlayAdInfo: LevelPlayAdInfo) {
// Ad fails to be displayed
Log.d("ocean", "AdInstShower 视频广告播放失败回调->${levelPlayAdError.errorCode} ${levelPlayAdError.errorMessage}")
showListener?.onAdShowFailed(AdShowFailed("${levelPlayAdError.errorCode} ${levelPlayAdError.errorMessage}"))
}
override fun onAdClicked(levelPlayAdInfo: LevelPlayAdInfo) {
// Ad was clicked
showListener?.onAdClicked()
Log.d("ocean", "onAdClicked 广告点击回调")
}
override fun onAdClosed(levelPlayAdInfo: LevelPlayAdInfo) {
// Ad was closed
showListener?.onAdClosed()
Log.d("ocean", "onAdClosed 广告关闭回调")
}
override fun onAdInfoChanged(levelPlayAdInfo: LevelPlayAdInfo) {
// Called after the ad info is updated. Available when another interstitial ad has loaded, and includes a higher CPM/Rate
// Optional
}
})
interstitialAd?.showAd(activity!!)
}
private fun autoClose() {
val autoCloseTime = 3000L
Log.d("ocean-brush","show后开始等待自动关闭广告 $autoCloseTime")
Async.scheduleTaskOnUiThread(autoCloseTime, Runnable {
AdActivityManager.instance.doClose()
Log.d("ocean-brush","show后执行关闭广告 $autoCloseTime")
})
}
}

View File

@ -0,0 +1,5 @@
package com.gallery.free.wallpaper.environment.ad
data class AdShowFailed(
val msg: String = "",
)

View File

@ -0,0 +1,28 @@
package com.gallery.free.wallpaper.environment.ad
import android.app.Activity
object AdsInsUtil {
object Placement {
const val TOP_ON_AD_ONE = "1x1dmfonwgx54zkn"
const val TOP_ON_AD_TOW = "3r21zexffllavwqj"
const val TOP_ON_AD_THREE = "4v2mbujbbk8wq7ed"
}
fun loadAd(
act: Activity,
adID: String,
loadListener: LoadListener?
): AdInstLoad {
return AdInstLoad(act, adID, loadListener)
}
fun showAd(
act: Activity,
adID: String,
listener: ShowListener?
): AdInstShower {
return AdInstShower(act, adID, listener)
}
}

View File

@ -0,0 +1,36 @@
package com.gallery.free.wallpaper.environment.ad
import com.unity3d.mediation.interstitial.LevelPlayInterstitialAd
class InstAdCacheManager {
private val mAdCacheDict: MutableMap<String, LevelPlayInterstitialAd> = mutableMapOf()
companion object {
val instance: InstAdCacheManager by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
InstAdCacheManager()
}
}
fun setAdCache(place: String, adCache: LevelPlayInterstitialAd) {
mAdCacheDict[place] = adCache
}
fun getAdCache(place: String): LevelPlayInterstitialAd? {
return mAdCacheDict[place]
}
fun getLoadedInstCount(): Int {
var count = 0
try {
mAdCacheDict.forEach { (key, value) ->
if (value.isAdReady) {
count += 1
}
}
} catch (_: Exception) {
}
return count
}
}

View File

@ -0,0 +1,8 @@
package com.gallery.free.wallpaper.environment.ad
import com.unity3d.mediation.LevelPlayAdInfo
interface LoadListener {
fun loadFailed(error: String) {}
fun loaded(ad: LevelPlayAdInfo) {}
}

View File

@ -0,0 +1,10 @@
package com.gallery.free.wallpaper.environment.ad
import com.unity3d.mediation.LevelPlayAdInfo
interface ShowListener {
fun onAdShown(ad: LevelPlayAdInfo) {}
fun onAdShowFailed(error: AdShowFailed?) {}
fun onAdClosed() {}
fun onAdClicked() {}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,181 @@
package com.gallery.free.wallpaper.environment.hy;
import android.util.Log;
import com.google.gson.JsonObject;
import java.io.IOException;
import androidx.annotation.NonNull;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class ConfigUtil {
public static final String TAG = "--ocean--";
public static final String POST_BID = "http://mobile.jeanell.vip/data/dataAnalysis";//上传gaid价格等地址
public static final String POST_LOAD_LOG_URL = "http://192.168.40.190/top_selection/save_ad_load_log";
// public static final String GET_CONFIG_URL = "http://mobile.jeanell.vip/exhibit/config";//获取配置地址
public static final String GET_CONFIG_URL = "http://192.168.40.190/top_selection/config";
public static final String POST_APP_START = "http://192.168.40.190/top_selection/save_app_start_log";
public static final String POST_SHOW_LOG_URL = "http://192.168.40.190/top_selection/save_ad_show_log";
public static final MediaType JSONMediaType = MediaType.parse("application/json; charset=utf-8");
public static void initPostBid(JsonObject json) {
Thread mThread = new Thread(new Runnable() {
@Override
public void run() {
postBid(json);
}
});
mThread.start();
}
private static void postBid(JsonObject json) {
Log.d("ocean", "json->" + json);
OkHttpClient client = new OkHttpClient();
RequestBody requestBody = RequestBody.create(JSONMediaType, json.toString());
Request request = new Request.Builder()
.url(ConfigUtil.POST_BID)
.post(requestBody)
.build();
Response response = null;
try {
response = client.newCall(request).execute();
String requestJson = response.body().string();
Log.d(TAG, "requestJson" + requestJson);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void initPostLoadLog(JsonObject json) {
Thread mThread = new Thread(new Runnable() {
@Override
public void run() {
postLoadLog(json);
}
});
mThread.start();
}
private static void postLoadLog(JsonObject json) {
Log.d("ocean", "postLoadLog json->" + json);
OkHttpClient client = new OkHttpClient();
RequestBody requestBody = RequestBody.create(JSONMediaType, json.toString());
Request request = new Request.Builder()
.url(ConfigUtil.POST_LOAD_LOG_URL)
.post(requestBody)
.build();
Response response = null;
try {
response = client.newCall(request).execute();
String requestJson = response.body().string();
Log.d(TAG, "postLoadLog requestJson" + requestJson);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void initPostShowLog(JsonObject json) {
Thread mThread = new Thread(new Runnable() {
@Override
public void run() {
postShowLog(json);
}
});
mThread.start();
}
private static void postShowLog(JsonObject json) {
Log.d("ocean", "show json->" + json);
OkHttpClient client = new OkHttpClient();
RequestBody requestBody = RequestBody.create(JSONMediaType, json.toString());
Request request = new Request.Builder()
.url(ConfigUtil.POST_SHOW_LOG_URL)
.post(requestBody)
.build();
Response response = null;
try {
response = client.newCall(request).execute();
String requestJson = response.body().string();
Log.d(TAG, "show requestJson" + requestJson);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void getConfig(String pkg, final MyCallback callback) {
String url = GET_CONFIG_URL + "?pkg=" + pkg;
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.build();
// 发送异步GET请求
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
// 请求失败时回调onFailure方法
callback.onFailure(e);
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
// 请求成功时回调onResponse方法
if (response.isSuccessful()) {
final String responseData = response.body().string();
callback.onResponse(responseData);
} else {
// 处理请求失败的情况
callback.onFailure(new IOException("Unexpected code " + response));
}
// 记得关闭响应释放资源
response.close();
}
});
}
// 定义一个接口用于回调请求结果
public interface MyCallback {
void onResponse(String result);
void onFailure(IOException e);
}
public static void initPostAppStart(JsonObject json) {
Thread mThread = new Thread(new Runnable() {
@Override
public void run() {
postAppStart(json);
}
});
mThread.start();
}
private static void postAppStart(JsonObject json) {
Log.d("ocean", "POST_APP_START json->" + json);
OkHttpClient client = new OkHttpClient();
RequestBody requestBody = RequestBody.create(JSONMediaType, json.toString());
Request request = new Request.Builder()
.url(ConfigUtil.POST_APP_START)
.post(requestBody)
.build();
Response response = null;
try {
response = client.newCall(request).execute();
String requestJson = response.body().string();
Log.d(TAG, "POST_APP_START requestJson" + requestJson);
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,7 +5,7 @@
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.gallery.free.wallpaper.SplaActivity">
tools:context="com.gallery.free.wallpaper.SplashActivity">
<RelativeLayout
android:layout_width="match_parent"

View File

@ -9,4 +9,6 @@
<color name="light_purple">#CCCCFF</color>
<color name="light_gray">#D3D3D3</color>
<color name="transparent">#00000000</color>
<color name="green">#60D889</color>
<color name="red">#CE3A54</color>
</resources>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config xmlns:tools="http://schemas.android.com/tools">
<domain-config cleartextTrafficPermitted="true">
<domain tools:ignore="NetworkSecurityConfig">mobile-server.lux-ad.com</domain>
<domain tools:ignore="NetworkSecurityConfig">172.24.100.10</domain>
</domain-config>
</network-security-config>