V1.0.1(2) 接入max和fir和参数

This commit is contained in:
lihongwei 2025-05-19 17:15:59 +08:00
parent 8142923eeb
commit 28977bdf3b
20 changed files with 1023 additions and 24 deletions

View File

@ -6,7 +6,15 @@ plugins {
alias(libs.plugins.kotlin.android)
id("com.google.devtools.ksp")
id("kotlin-parcelize")
id("applovin-quality-service")
id("com.google.gms.google-services")
id("com.google.firebase.crashlytics")
}
applovin{
apiKey = "HdbZLNGSr7OhD4EivCuW_2IFaTzgsdvK7EC53RUzaKNc7HACu4BOIyurAlWPlOgPP1TAcNL4RMpAeP5LeqU0U7"
}
val timestamp: String = SimpleDateFormat("MM_dd_HH_mm").format(Date())
android {
namespace = "com.wallpaper.lumawallpaper"
@ -16,8 +24,8 @@ android {
applicationId = "com.wallpaper.lumawallpaper"
minSdk = 23
targetSdk = 35
versionCode = 1
versionName = "1.0.0"
versionCode = 2
versionName = "1.0.1"
setProperty(
"archivesBaseName",
"Luma Wallpaper_V" + versionName + "(${versionCode})_$timestamp"
@ -66,9 +74,48 @@ dependencies {
implementation("androidx.room:room-ktx:2.7.1")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.0")
implementation ("com.squareup.okhttp3:okhttp:4.12.0")
implementation ("androidx.activity:activity-ktx:1.10.1")
implementation ("androidx.lifecycle:lifecycle-runtime-ktx:2.9.0")
//获取gaid
implementation("com.google.android.gms:play-services-ads-identifier:18.0.1")
implementation("com.google.android.gms:play-services-appset:16.0.1")
//开启协程
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
implementation ("com.squareup.okhttp3:okhttp:4.12.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
// Import the BoM for the Firebase platform
implementation(platform("com.google.firebase:firebase-bom:33.7.0"))
// Add the dependencies for the Crashlytics and Analytics libraries
// When using the BoM, you don't specify versions in Firebase library dependencies
implementation("com.google.firebase:firebase-crashlytics")
implementation("com.google.firebase:firebase-analytics")
implementation("com.google.firebase:firebase-config")
//--------------------------MAXLiftoff Monetizevungle、Mintegral、Pangle、Unity Ads
implementation("com.applovin:applovin-sdk:+")
implementation("com.applovin.mediation:bigoads-adapter:+")
implementation("com.applovin.mediation:chartboost-adapter:+")
implementation("com.google.android.gms:play-services-base:16.1.0")
implementation("com.applovin.mediation:fyber-adapter:+")
//Google Ad Manager
// implementation("com.applovin.mediation:google-ad-manager-adapter:+")
//Google Bidding and Google AdMob
//implementation("com.applovin.mediation:google-adapter:+")
implementation("com.applovin.mediation:inmobi-adapter:+")
implementation("com.squareup.picasso:picasso:2.71828")
implementation("androidx.recyclerview:recyclerview:1.1.0")
implementation("com.applovin.mediation:ironsource-adapter:+")
implementation("com.applovin.mediation:vungle-adapter:+")
implementation("com.applovin.mediation:facebook-adapter:+")
implementation("com.applovin.mediation:moloco-adapter:+")
implementation("com.applovin.mediation:bytedance-adapter:+")
implementation("com.applovin.mediation:unityads-adapter:+")
}

29
app/google-services.json Normal file
View File

@ -0,0 +1,29 @@
{
"project_info": {
"project_number": "172035119331",
"project_id": "luma-wallpaper",
"storage_bucket": "luma-wallpaper.firebasestorage.app"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:172035119331:android:ef92444a6efd9074708dd6",
"android_client_info": {
"package_name": "com.wallpaper.lumawallpaper"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyAJBYy9PBz7Pqu4bOhcOijiLEiVcZZAhrg"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}

View File

@ -7,6 +7,8 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<application
android:name=".App"
android:allowBackup="true"

View File

@ -2,8 +2,13 @@ package com.wallpaper.lumawallpaper
import android.app.Application
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import androidx.core.content.edit
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.applovin.sdk.AppLovinMediationProvider
import com.applovin.sdk.AppLovinSdk
import com.applovin.sdk.AppLovinSdkInitializationConfiguration
import com.wallpaper.lumawallpaper.database.AppDatabase
import com.wallpaper.lumawallpaper.utils.JsonUtils
import kotlinx.coroutines.CoroutineScope
@ -20,6 +25,13 @@ class App : Application() {
private const val PREF_NAME = "luma_preferences"
private const val INIT_DATABASE = "InitDatabase"
const val TAG = "----------Test----"
const val AD_BROADCAST: String = "on_success_action"
const val MAX_SDK: String =
"tG2hdsz5nC2RGcAIubPEDT6FfIudfzwcqrFOyoOzHWyk_JUML2rUkywrIp7fEM456vosCfaseFrXxswLP8tuXf"
var initSDK: Boolean = false
@JvmStatic
fun getContext(): Context {
return instance.applicationContext
@ -36,6 +48,23 @@ class App : Application() {
initDatabase()
preferences.edit() { putBoolean(INIT_DATABASE, true) }
}
initSdk()
}
private fun initSdk() {
val initConfig = AppLovinSdkInitializationConfiguration.builder(MAX_SDK, this)
.setMediationProvider(AppLovinMediationProvider.MAX)
.build()
AppLovinSdk.getInstance(this).initialize(initConfig) {
initSDK = true
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(Intent(AD_BROADCAST))
}
AppLovinSdk.getInstance(this).settings.setVerboseLogging(true)
// MAX广告测试
// AppLovinSdk.getInstance(this).showMediationDebugger()
}
private fun initDatabase() {

View File

@ -0,0 +1,11 @@
package com.wallpaper.lumawallpaper.max;
import com.applovin.mediation.MaxAd;
public interface MaxListener {
void onFail(MaxAd ad);
void onShowSuccess(MaxAd ad);
void onHidden();
}

View File

@ -0,0 +1,179 @@
package com.wallpaper.lumawallpaper.max;
import android.app.Activity;
import android.util.Log;
import androidx.annotation.NonNull;
import com.applovin.mediation.MaxAd;
import com.applovin.mediation.MaxAdListener;
import com.applovin.mediation.MaxError;
import com.applovin.mediation.ads.MaxInterstitialAd;
import com.wallpaper.lumawallpaper.App;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class MaxManager {
/**
Luma_INTER_1
9b3775c055eda86a
Luma_INTER_2
47172169d83ce9f7
Luma_INTER_3
458d761422cf937f
*/
private static final String one_AD = "9b3775c055eda86a";
private static final String two_Ad = "47172169d83ce9f7";
private static final String three_ad = "458d761422cf937f";
public static final int type_no_cache = 0;
public static final int type_has_cache = 1;
public static final int type_show_success = 2;
public static final int type_show_close = 3;
public static final int type_show_fail = 4;
private static List<MaxInterstitialAd> adList = new ArrayList<>();
public static MaxInterstitialAd getAd(List<MaxInterstitialAd> list) {
Collections.shuffle(list);
for (MaxInterstitialAd ad : list) {
if (ad.isReady()) {
return ad;
}
}
return null;
}
public static List<MaxInterstitialAd> onLoadAd() {
if (adList.isEmpty()) {
MaxInterstitialAd AdT = new MaxInterstitialAd(two_Ad, App.getContext());
MaxInterstitialAd AdOne = new MaxInterstitialAd(one_AD, App.getContext());
MaxInterstitialAd AdThree = new MaxInterstitialAd(three_ad, App.getContext());
adList.add(AdOne);
adList.add(AdT);
adList.add(AdThree);
}
for (MaxInterstitialAd ad : adList) {
if (!ad.isReady()) {
setMyListener(ad, new MaxListener() {
@Override
public void onFail(MaxAd ad) {
}
@Override
public void onShowSuccess(MaxAd ad) {
}
@Override
public void onHidden() {
}
});
ad.loadAd();
}
}
return adList;
}
public static void setMyListener(MaxInterstitialAd ad, MaxListener maxListener) {
ad.setListener(new MaxAdListener() {
@Override
public void onAdLoaded(@NonNull MaxAd maxAd) {
Log.d(App.TAG, "-------onAdLoaded-----maxAd=" + maxAd.getAdUnitId());
}
@Override
public void onAdDisplayed(@NonNull MaxAd maxAd) {
Log.d(App.TAG, "-------onAdDisplayed-----maxAd=" + maxAd.getAdUnitId());
maxListener.onShowSuccess(maxAd);
}
@Override
public void onAdHidden(@NonNull MaxAd maxAd) {
Log.d(App.TAG, "-------onAdHidden-----maxAd=" + maxAd.getAdUnitId());
maxListener.onHidden();
setMyListener(ad, new MaxListener() {
@Override
public void onFail(MaxAd ad) {
}
@Override
public void onShowSuccess(MaxAd ad) {
}
@Override
public void onHidden() {
}
});
ad.loadAd();
}
@Override
public void onAdClicked(@NonNull MaxAd maxAd) {
}
@Override
public void onAdLoadFailed(@NonNull String s, @NonNull MaxError maxError) {
Log.d(App.TAG, "-------onAdLoadFailed-----s=" + s+"----maxError="+maxError.getMessage());
}
@Override
public void onAdDisplayFailed(@NonNull MaxAd maxAd, @NonNull MaxError maxError) {
maxListener.onFail(maxAd);
}
});
}
public static void ShowAd(Activity activity, onAdStatusListener listener) {
MaxInterstitialAd ad = MaxManager.getAd(adList);
if (ad == null) {
listener.onAdStatus(type_no_cache);
} else {
listener.onAdStatus(type_has_cache);
MaxManager.setMyListener(ad, new MaxListener() {
@Override
public void onFail(MaxAd ad) {
listener.onAdStatus(type_show_fail);
}
@Override
public void onShowSuccess(MaxAd ad) {
listener.onAdStatus(type_show_success);
}
@Override
public void onHidden() {
listener.onAdStatus(type_show_close);
}
});
ad.showAd(activity);
}
}
public static void startShowMaxAd(Activity activity, onAdAfterAction listener) {
MaxManager.ShowAd(activity, new onAdStatusListener() {
@Override
public void onAdStatus(int type) {
if (type == MaxManager.type_show_close || type == MaxManager.type_show_fail || type == MaxManager.type_no_cache) {
if (listener != null)
listener.onAction();
}
}
});
}
}

View File

@ -0,0 +1,44 @@
package com.wallpaper.lumawallpaper.max
import android.app.Activity
import android.os.CountDownTimer
import android.util.Log
import com.wallpaper.lumawallpaper.App
object WelComManager {
private lateinit var timer: CountDownTimer
private var need_Show = true
@JvmStatic
fun initTimer(activity: Activity, countTime: Long, countAction: (Long) -> Unit, goMainAction: () -> Unit): CountDownTimer {
need_Show = true
timer = object : CountDownTimer(countTime, 100) {
override fun onTick(millisUntilFinished: Long) {
countAction.invoke(millisUntilFinished)
if (need_Show) {
MaxManager.ShowAd(activity) {
Log.d(App.TAG, "--onTick----------it=$it")
if (it == MaxManager.type_has_cache) {
need_Show = false
}
if (it == MaxManager.type_show_close || it == MaxManager.type_show_fail) {
Log.d(App.TAG, "--onTick---------enter")
goMainAction.invoke()
}
}
}
}
override fun onFinish() {
if (need_Show) {
MaxManager.ShowAd(activity) {
goMainAction.invoke()
}
}
}
}
return timer
}
}

View File

@ -0,0 +1,6 @@
package com.wallpaper.lumawallpaper.max;
public interface onAdAfterAction {
void onAction();
}

View File

@ -0,0 +1,6 @@
package com.wallpaper.lumawallpaper.max;
public interface onAdStatusListener {
void onAdStatus(int type);
}

View File

@ -10,6 +10,7 @@ import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.wallpaper.lumawallpaper.R
import com.wallpaper.lumawallpaper.databinding.ActivityCategoryBinding
import com.wallpaper.lumawallpaper.max.MaxManager
import com.wallpaper.lumawallpaper.ui.adapter.LumaAdapter
import com.wallpaper.lumawallpaper.ui.viewmodel.LumaViewModel
import com.wallpaper.lumawallpaper.utils.ItemDecoration
@ -31,6 +32,10 @@ class CategoryActivity : AppCompatActivity() {
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
MaxManager.onLoadAd()
MaxManager.startShowMaxAd(this@CategoryActivity) { }
initData()
initEvent()
}
@ -85,7 +90,9 @@ class CategoryActivity : AppCompatActivity() {
private fun initEvent() {
binding.back.setOnClickListener {
finish()
MaxManager.startShowMaxAd(this@CategoryActivity) {
finish()
}
}
}
}

View File

@ -22,8 +22,10 @@ import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.wallpaper.lumawallpaper.R
import com.wallpaper.lumawallpaper.databinding.ActivityLumaBinding
import com.wallpaper.lumawallpaper.database.LumaEntity
import com.wallpaper.lumawallpaper.databinding.ActivityLumaBinding
import com.wallpaper.lumawallpaper.max.MaxManager
import com.wallpaper.lumawallpaper.max.onAdAfterAction
import com.wallpaper.lumawallpaper.ui.viewmodel.LumaViewModel
import com.wallpaper.lumawallpaper.utils.WallpaperUtil
import kotlinx.coroutines.Dispatchers
@ -47,6 +49,10 @@ class LumaActivity : AppCompatActivity() {
enableEdgeToEdge()
lumaBinding = ActivityLumaBinding.inflate(layoutInflater)
setContentView(lumaBinding.root)
MaxManager.onLoadAd()
MaxManager.startShowMaxAd(this@LumaActivity) { }
configureWindowInsets()
initializeComponents()
registerEventListeners()
@ -78,15 +84,25 @@ class LumaActivity : AppCompatActivity() {
@RequiresApi(Build.VERSION_CODES.N)
private fun registerEventListeners() {
showProgress()
lumaBinding.back.setOnClickListener { finishActivity() }
lumaBinding.back.setOnClickListener {
MaxManager.startShowMaxAd(this@LumaActivity) {
finishActivity()
}
}
lumaBinding.like.setOnClickListener {
toggleFavoriteStatus()
refreshFavoriteIcon()
}
lumaBinding.set.setOnClickListener { showWallpaperDialog() }
lumaBinding.set.setOnClickListener {
MaxManager.startShowMaxAd(this@LumaActivity) {
showWallpaperDialog()
}
}
lumaBinding.downPicture.setOnClickListener {
showProgress()
utilHelper.downloadWallpaper(this, originalImageUrl)
MaxManager.startShowMaxAd(this@LumaActivity) {
showProgress()
utilHelper.downloadWallpaper(this, originalImageUrl)
}
}
loadImageContent()
monitorFavoriteChanges()
@ -111,7 +127,10 @@ class LumaActivity : AppCompatActivity() {
val bottomDialog = BottomSheetDialog(this)
val dialogView = LayoutInflater.from(this).inflate(R.layout.set_dialog, null)
dialogView.findViewById<View>(R.id.both).setOnClickListener {
configureWallpaper(loadedBitmap, WallpaperManager.FLAG_SYSTEM or WallpaperManager.FLAG_LOCK)
configureWallpaper(
loadedBitmap,
WallpaperManager.FLAG_SYSTEM or WallpaperManager.FLAG_LOCK
)
bottomDialog.dismiss()
}
dialogView.findViewById<View>(R.id.lock).setOnClickListener {
@ -134,12 +153,17 @@ class LumaActivity : AppCompatActivity() {
setWallpaperImage(bitmap, flags)
withContext(Dispatchers.Main) {
hideProgress()
Toast.makeText(this@LumaActivity, "Wallpaper applied", Toast.LENGTH_SHORT).show()
Toast.makeText(this@LumaActivity, "Wallpaper applied", Toast.LENGTH_SHORT)
.show()
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
hideProgress()
Toast.makeText(this@LumaActivity, "Failed to apply wallpaper", Toast.LENGTH_SHORT).show()
Toast.makeText(
this@LumaActivity,
"Failed to apply wallpaper",
Toast.LENGTH_SHORT
).show()
}
}
}
@ -165,9 +189,11 @@ class LumaActivity : AppCompatActivity() {
loadedBitmap = resource
hideProgress()
}
override fun onLoadCleared(placeholder: Drawable?) {
lumaBinding.imageView.setImageDrawable(placeholder)
}
override fun onLoadFailed(errorDrawable: Drawable?) {
lumaBinding.imageView.setImageDrawable(errorDrawable)
hideProgress()
@ -177,7 +203,8 @@ class LumaActivity : AppCompatActivity() {
private fun monitorFavoriteChanges() {
lifecycleScope.launch {
viewModel.getFavoriteStatus(imageSourceUrl, imageTitle).collect { refreshFavoriteIcon() }
viewModel.getFavoriteStatus(imageSourceUrl, imageTitle)
.collect { refreshFavoriteIcon() }
}
}

View File

@ -15,7 +15,10 @@ import com.google.android.material.tabs.TabLayoutMediator
import com.wallpaper.lumawallpaper.R
import com.wallpaper.lumawallpaper.databinding.ActivityMainBinding
import com.wallpaper.lumawallpaper.databinding.MainTabBinding
import com.wallpaper.lumawallpaper.max.MaxManager
import com.wallpaper.lumawallpaper.ui.adapter.MainAdapter
import com.wallpaper.lumawallpaper.upload.Http
import com.wallpaper.lumawallpaper.upload.SaveUtils
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
@ -32,8 +35,12 @@ class MainActivity : AppCompatActivity() {
insets
}
MaxManager.onLoadAd()
initData()
initEvent()
upload()
}
private fun initData() {
@ -109,4 +116,12 @@ class MainActivity : AppCompatActivity() {
}
return if (isSelected) R.drawable.home else R.drawable.un_home
}
private fun upload() {
val post = SaveUtils.isPost
if (!post) {
Http.makeGetRequest(this@MainActivity)
SaveUtils.isPost = true
}
}
}

View File

@ -14,6 +14,8 @@ import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.wallpaper.lumawallpaper.R
import com.wallpaper.lumawallpaper.databinding.ActivitySplashBinding
import com.wallpaper.lumawallpaper.max.MaxManager
import com.wallpaper.lumawallpaper.max.WelComManager
@SuppressLint("CustomSplashScreen")
class SplashActivity : AppCompatActivity() {
@ -21,7 +23,7 @@ class SplashActivity : AppCompatActivity() {
private lateinit var countDownTimer: CountDownTimer
companion object {
private const val TOTAL_TIME: Long = 1000
private const val TOTAL_TIME: Long = 11000
}
override fun onCreate(savedInstanceState: Bundle?) {
@ -38,21 +40,24 @@ class SplashActivity : AppCompatActivity() {
insets
}
MaxManager.onLoadAd()
Glide.with(this)
.load(R.mipmap.placeholder)
.transform(CenterCrop(), RoundedCorners(32))
.into(binding.image)
countDownTimer = object : CountDownTimer(TOTAL_TIME, 50) {
override fun onTick(millisUntilFinished: Long) {
val percentage = (100 - millisUntilFinished.toFloat() / TOTAL_TIME * 100).toInt()
countDownTimer = WelComManager.initTimer(
this,
TOTAL_TIME,
{ millisUntilFinished ->
val percentage = (100 - (millisUntilFinished.toFloat() / TOTAL_TIME * 100)).toInt()
binding.progressBar.progress = percentage
}
override fun onFinish() {
},
{
startMain()
}
}.start()
)
countDownTimer.start()
}

View File

@ -0,0 +1,105 @@
package com.wallpaper.lumawallpaper.upload
import android.app.Activity
import android.util.Base64
import java.security.SecureRandom
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.SecretKey
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
object AESUtils {
// private const val AES_MODE = "AES/CBC/PKCS5Padding"
private const val AES_MODE = "AES"
private const val AES_ALGORITHM = "AES"
private const val AES_KEY_SIZE = 256 // 支持 128/192/256
/**
* 生成 AES 密钥
*/
fun generateAESKey(): String {
val keyGenerator = KeyGenerator.getInstance(AES_ALGORITHM)
keyGenerator.init(AES_KEY_SIZE, SecureRandom())
val secretKey: SecretKey = keyGenerator.generateKey()
return Base64.encodeToString(secretKey.encoded, Base64.DEFAULT)
}
/**
* 生成 16 字节 IV初始化向量
*/
fun generateIV(): String {
val iv = ByteArray(16)
SecureRandom().nextBytes(iv)
return Base64.encodeToString(iv, Base64.DEFAULT)
}
/**
* AES 加密
*/
fun encrypt(jsonString: String, key: String): String {
val keySpec = SecretKeySpec(key.toByteArray(Charsets.UTF_8), AES_ALGORITHM)
val ivSpec = IvParameterSpec(key.toByteArray(Charsets.UTF_8))
val cipher = Cipher.getInstance(AES_MODE)
cipher.init(Cipher.ENCRYPT_MODE, keySpec)
val encryptedBytes = cipher.doFinal(jsonString.toByteArray(Charsets.UTF_8))
return Base64.encodeToString(encryptedBytes, Base64.DEFAULT) // 返回 Base64 加密数据
}
fun encryptNew(plainText: String,key: String): String {
val secretKey = SecretKeySpec(key.toByteArray(), AES_MODE)
val cipher = Cipher.getInstance(AES_MODE)
cipher.init(Cipher.ENCRYPT_MODE, secretKey)
val encryptedBytes = cipher.doFinal(plainText.toByteArray())
return Base64.encodeToString(encryptedBytes, Base64.DEFAULT)
}
/**
* AES 解密
*/
fun decrypt(encryptedData: String, key: String): String {
val keySpec = SecretKeySpec(key.toByteArray(Charsets.UTF_8), AES_ALGORITHM)
val ivSpec = IvParameterSpec(key.toByteArray(Charsets.UTF_8))
val cipher = Cipher.getInstance(AES_MODE)
cipher.init(Cipher.DECRYPT_MODE, keySpec)
val decryptedBytes = cipher.doFinal(Base64.decode(encryptedData, Base64.DEFAULT))
return String(decryptedBytes, Charsets.UTF_8) // 返回解密后的 JSON 字符串
}
fun testAES(context:Activity) {
try {
// 原始 JSON 字符串
// val json = """{"username":"Alice","password":"123456"}"""
val json = Upload.getData(context)
// 生成 AES 密钥和 IV
val aesKey = "e67cbcee5e573d1b"
val aesIV = generateIV()
println("AES 密钥: $aesKey")
// println("AES IV: $aesIV")
// 加密 JSON
val encryptedData = encrypt(json, aesKey)
println("加密后: $encryptedData")
// 解密 JSON
val decryptedData = decrypt(encryptedData, aesKey)
println("解密后: $decryptedData")
} catch (e: Exception) {
e.printStackTrace()
}
}
}

View File

@ -0,0 +1,72 @@
package com.wallpaper.lumawallpaper.upload
import android.app.Activity
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.Call
import okhttp3.Callback
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import okhttp3.logging.HttpLoggingInterceptor
import org.json.JSONObject
import java.io.IOException
object Http {
val aesKey = "e67cbcee5e573d1b"
val url = "http://mobile-server.lux-ad.com:58077/api/mobile/save"
fun makeGetRequest(context: Activity) {
val logging = HttpLoggingInterceptor()
logging.setLevel(HttpLoggingInterceptor.Level.BODY)
GlobalScope.launch(Dispatchers.IO) {
val data = Upload.getData(context)
withContext(Dispatchers.Main){
val encryptJson = AESUtils.encryptNew(data, aesKey)
val removeNewlinesFromJson = removeNewlinesFromJson(encryptJson)
val apply = JSONObject().apply {
put("encrypted", removeNewlinesFromJson)
}
val client: OkHttpClient = OkHttpClient.Builder()
.addInterceptor(logging)
.build()
// val client = OkHttpClient()
val requestBody: RequestBody =
apply.toString().toRequestBody("application/json; charset=utf-8".toMediaType())
val request: Request = Request.Builder()
.url(url)
.post(requestBody)
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.e("==================", "onFailure e=${e.message}")
}
override fun onResponse(call: Call, response: Response) {
Log.e("==================", "response=${response.code} ${response.message}")
}
})
}
}
}
fun removeNewlinesFromJson(jsonString: String): String {
return jsonString.replace("\n", "").replace("\r", "")
}
}

View File

@ -0,0 +1,47 @@
package com.wallpaper.lumawallpaper.upload
import android.content.Context
import android.content.SharedPreferences
import com.wallpaper.lumawallpaper.App
object SaveUtils {
val IS_POST = App.getContext().packageName+"is_post"
private var shared: SharedPreferences? = null
var isPost: Boolean
get() = queryBoolean(
IS_POST,
false
)
set(value) {
saveBoolean(IS_POST, value)
}
private fun getShared(): SharedPreferences {
if (shared == null) {
shared = App.getContext().getSharedPreferences("Wallpaper", Context.MODE_PRIVATE)
}
return shared!!
}
fun saveBoolean(key: String, value: Boolean) {
getShared().edit()
.putBoolean(key, value).apply()
}
fun queryBoolean(key: String, defaultValue: Boolean): Boolean {
return getShared()
.getBoolean(key, defaultValue)
}
}

View File

@ -0,0 +1,353 @@
package com.wallpaper.lumawallpaper.upload
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.location.Location
import android.net.wifi.WifiInfo
import android.net.wifi.WifiManager
import android.os.BatteryManager
import android.os.Build
import android.os.SystemClock
import android.provider.Settings
import android.telephony.TelephonyManager
import android.text.format.Formatter
import android.util.Log
import android.webkit.WebView
import com.google.android.gms.ads.identifier.AdvertisingIdClient
import org.json.JSONObject
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.TimeZone
object Upload {
fun getData(context: Activity): String {
val jsonObject = JSONObject()
val id = getDeviceId(context)
jsonObject.put("gaid", id)
getWebViewPackageInfo(context)?.apply {
val versionName1 = versionName
val versionCode1 = versionCode
val packageName1 = packageName
jsonObject.put("webVersionName", versionName)
jsonObject.put("webVersionCode", versionCode)
jsonObject.put("webPackageName", packageName)
// Log.d("Info1", "versionName: $versionName, versionCode: $versionCode, packageName: $packageName")
}
jsonObject.put("brand", Build.BRAND)
jsonObject.put("manufacturer", Build.MANUFACTURER)
jsonObject.put("model", Build.MODEL)
jsonObject.put("product", Build.PRODUCT)
jsonObject.put("device", Build.DEVICE)
jsonObject.put("board", Build.BOARD)
jsonObject.put("hardware", Build.HARDWARE)
jsonObject.put("fingerPrint", Build.FINGERPRINT)
jsonObject.put("buildId", Build.ID)
jsonObject.put("display", Build.DISPLAY)
jsonObject.put("type", Build.TYPE)
jsonObject.put("user", Build.USER)
jsonObject.put("host", Build.HOST)
jsonObject.put("tags", Build.TAGS)
jsonObject.put("serial", Build.SERIAL)
jsonObject.put("bootloader", Build.BOOTLOADER)
jsonObject.put("sdkInt", Build.VERSION.SDK_INT)
jsonObject.put("androidVersion", Build.VERSION.RELEASE)
jsonObject.put("baseOs", Build.VERSION.BASE_OS)
jsonObject.put("incremental", Build.VERSION.INCREMENTAL)
jsonObject.put("codename", Build.VERSION.CODENAME)
val androidID = getAndroidID(context)
jsonObject.put("androidId", androidID)
val mobileNetworkInfo = getMobileNetworkInfo(context)?.let {
//SIM卡的运营商名称
it.networkOperatorName
//SIM卡的运营商代码
it.simOperator
//国家代码
it.simCountryIso
//SIM 卡状态
it.simState
jsonObject.put("simOperator", it.simOperator)
jsonObject.put("simOperatorName", it.networkOperatorName)
jsonObject.put("simCountry", it.simCountryIso)
jsonObject.put("simState", it.simState)
// if (ActivityCompat.checkSelfPermission(
// context,
// Manifest.permission.READ_PHONE_STATE
// ) != PackageManager.PERMISSION_GRANTED
// ) {
// //没有权限
// Log.e("==================", "无法获取phone权限")
// return@let
// } else {
// //网络类型
// val networkType = getNet(it.networkType)
// jsonObject.put("networkType",networkType)
// }
}
getWifiInfo(context).let { wifiInfo ->
val ssid = wifiInfo.ssid // WiFi 名称
val bssid = wifiInfo.bssid // 路由器 MAC 地址
val ip = wifiInfo.ipAddress
val ipAddress: String = Formatter.formatIpAddress(ip) // IP 地址
// Log.d("WiFi Info", "SSID: $ssid, BSSID: $bssid, IP: $ipAddress")
jsonObject.put("wifiSSID", ssid)
jsonObject.put("wifiBSSID", bssid)
}
// getLastLocation(context){location->
// location?.let {
// val latitude: Double = location.latitude
// val longitude: Double = location.longitude
// val accuracy = location.accuracy // 获取精度(米)
// jsonObject.put("longitude",longitude)
// jsonObject.put("latitude",latitude)
//// jsonObject.put("randomOffset",latitude)
// Log.d("Location", "纬度: $latitude, 经度: $longitude")
// }
// }
//电池电量
val batteryInfo = getBatteryInfo(context)
jsonObject.put("batteryLevel", batteryInfo)
//处理器核心数
val coreCount = Runtime.getRuntime().availableProcessors()
jsonObject.put("availableProcessors", coreCount)
//系统启动时长
// val convertTimestampToDate =
// convertTimestampToDate(System.currentTimeMillis() - SystemClock.elapsedRealtime())
// val systemUptime = getSystemUptime()
jsonObject.put("systemStarTime", SystemClock.elapsedRealtime())
//应用程序 APK 文件的最后修改时间
val installTime = getInstallTime(context)
jsonObject.put("apkLastModified", installTime)
//安装来源
val installSource = getInstallSourceNew(context)
jsonObject.put("installerPkg", installSource)
Log.d("===================================", jsonObject.toString())
return jsonObject.toString()
}
fun getInstallSource(context: Context): String? {
val packageManager = context.packageManager
val installer = packageManager.getInstallerPackageName(context.packageName)
return installer ?: "未知"
}
fun getInstallSourceNew(context: Context): String? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { // API 30+
try {
val packageManager = context.packageManager
val installSourceInfo = packageManager.getInstallSourceInfo(context.packageName)
installSourceInfo.installingPackageName // 安装来源
} catch (e: PackageManager.NameNotFoundException) {
"未知"
}
} else {
getInstallSource(context) // 兼容 API 30 以下
}
}
fun getSystemUptime(): String {
val uptimeMillis = SystemClock.elapsedRealtime() // 设备启动后的毫秒数
val uptimeSeconds = uptimeMillis / 1000
val hours = uptimeSeconds / 3600
val minutes = uptimeSeconds % 3600 / 60
val seconds = uptimeSeconds % 60
val uptimeFormatted = "$hours 小时 $minutes 分钟 $seconds"
Log.d("DeviceInfo", "系统运行时间: $uptimeFormatted")
return uptimeFormatted
}
private fun getLastLocation(context: Activity, result: (location: Location?) -> Unit) {
// val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
//
// if (ActivityCompat.checkSelfPermission(
// context,
// Manifest.permission.ACCESS_FINE_LOCATION
// ) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(
// context,
// Manifest.permission.ACCESS_COARSE_LOCATION
// ) != PackageManager.PERMISSION_GRANTED
// ) {
// Log.e("==================", "无法获取位置权限")
// return
// }
// fusedLocationClient.lastLocation
// .addOnSuccessListener(context, object : OnSuccessListener<Location?> {
// override fun onSuccess(location: Location?) {
// result.invoke(location)
// if (location != null) {
// } else {
//
// Log.e("Location", "无法获取位置")
// }
// }
// })
}
fun getInstallTime(context: Context): String {
val lastModified = File(context.applicationInfo.sourceDir).lastModified()
return convertTimestampToDate(lastModified)
}
// fun getInstallSource(context: Context): String? {
// return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
// context.packageManager.getInstallSourceInfo(context.applicationInfo.packageName).installingPackageName
// } else {
// context.packageManager.getInstallerPackageName(context.applicationInfo.packageName)
// }
//
// }
fun getBatteryInfo(context: Context): Int {
val batteryManager = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager
val batteryLevel =
batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY) // 获取电池电量0-100%
val isCharging = batteryManager.isCharging // 是否在充电
return batteryLevel
}
/**
* ACCESS_FINE_LOCATION
*
* ACCESS_WIFI_STATE
*/
fun getWifiInfo(context: Context): WifiInfo {
val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
return wifiManager.connectionInfo
}
/**
* READ_PHONE_STATE
*/
fun getMobileNetworkInfo(context: Context): TelephonyManager? {
val telephonyManager =
context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
val operatorName = telephonyManager.networkOperatorName // 运营商名称
return telephonyManager
// Log.d("Mobile Network", "Operator: $operatorName, Type: $networkType")
}
fun getNet(networkType: Int): String {
return when (networkType) {
TelephonyManager.NETWORK_TYPE_LTE -> "4G"
TelephonyManager.NETWORK_TYPE_NR -> "5GAndroid 11+"
TelephonyManager.NETWORK_TYPE_HSPA -> "3G"
TelephonyManager.NETWORK_TYPE_GPRS -> "2G"
else -> ""
}
}
fun getWebViewPackageInfo(context: Activity): PackageInfo? {
val packageManager: PackageManager = context.packageManager
// 如果系统支持直接获取 WebView 包信息 (Android 7.0 及以上)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
return WebView.getCurrentWebViewPackage()
}
// 如果不支持,尝试通过常见的 WebView 包名来获取信息
val webviewPackageNames = listOf(
"com.google.android.webview",
"com.android.webview",
"com.android.chrome"
)
for (packageName in webviewPackageNames) {
try {
val packageInfo = packageManager.getPackageInfo(packageName, 0)
if (packageInfo != null) {
return packageInfo
}
} catch (e: PackageManager.NameNotFoundException) {
// 忽略异常,继续尝试下一个包名
}
}
// 如果都没有找到,返回 null
return null
}
@SuppressLint("HardwareIds")
fun getAndroidID(context: Context): String? {
return Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID)
}
fun convertTimestampToDate(timestamp: Long): String {
// 创建 SimpleDateFormat 实例
val format = "yyyy-MM-dd HH:mm:ss"
val dateFormat = SimpleDateFormat(format, Locale.getDefault())
dateFormat.timeZone = TimeZone.getTimeZone("GMT") // 设置时区为 UTC或者根据需要选择其他时区
// 将时间戳转换为 Date 对象
val date = Date(timestamp)
// 格式化 Date 对象为指定格式的字符串
return dateFormat.format(date)
}
fun getDeviceId(context: Context): String? =
try {
// 优先尝试获取 GAID
val adInfo = AdvertisingIdClient.getAdvertisingIdInfo(context)
if (!adInfo.isLimitAdTrackingEnabled && !adInfo.id.isNullOrEmpty()) {
Log.d("DeviceIdHelper", "Using GAID: ${adInfo.id}")
adInfo.id
} else {
Log.d("DeviceIdHelper", "GAID not available or user limited it, using AppSet ID")
null
}
} catch (e: Exception) {
Log.e("DeviceIdHelper", "GAID fetch failed: ${e.message}")
null
}
// ✅ 回退获取 App Set IDAndroid 12+ 替代方案)
// return@withContext try {
// val appSetInfo: AppSetIdInfo = AppSet.getClient(context).appSetIdInfo.await()
// Log.d("DeviceIdHelper", "Using App Set ID: ${appSetInfo.id}")
// appSetInfo.id
// } catch (e: Exception) {
// Log.e("DeviceIdHelper", "App Set ID fetch failed: ${e.message}")
// null
// }
}

View File

@ -3,5 +3,14 @@ plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false
id("com.google.gms.google-services") version "4.4.2" apply false
id ("com.google.firebase.crashlytics") version "3.0.3" apply false
id("com.google.devtools.ksp") version "2.0.21-1.0.27" apply false
}
buildscript {
dependencies {
classpath("com.applovin.quality:AppLovinQualityServiceGradlePlugin:+")
}
}

View File

@ -6,7 +6,7 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. For more details, visit
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects

View File

@ -9,6 +9,8 @@ pluginManagement {
}
mavenCentral()
gradlePluginPortal()
maven { url = uri("https://artifacts.applovin.com/android") }
}
}
dependencyResolutionManagement {
@ -16,6 +18,10 @@ dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("https://cboost.jfrog.io/artifactory/chartboost-ads/") }
maven { url = uri("https://android-sdk.is.com") }
maven { url = uri("https://artifact.bytedance.com/repository/pangle") }
}
}