diff --git a/aes/build.gradle b/aes/build.gradle index c9c1271c..2a780ce6 100644 --- a/aes/build.gradle +++ b/aes/build.gradle @@ -18,18 +18,22 @@ def genVersionName(def versionName){ } android { - compileSdkVersion 32 - buildToolsVersion "32.0.0" + + // 1. compileSdkVersion:必须 ≥ targetSdkVersion,建议直接等于 targetSdkVersion(30) + compileSdkVersion 30 + + // 2. buildToolsVersion:需匹配 compileSdkVersion,建议使用 30.x.x 最新稳定版(无需高于 compileSdkVersion) + buildToolsVersion "30.0.3" // 这是 30 对应的最新稳定版,避免使用 beta 版 defaultConfig { applicationId "cc.winboll.studio.aes" - minSdkVersion 24 + minSdkVersion 23 targetSdkVersion 30 versionCode 1 // versionName 更新后需要手动设置 // 项目模块目录的 build.gradle 文件的 stageCount=0 // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0" - versionName "15.10" + versionName "15.11" if(true) { versionName = genVersionName("${versionName}") } diff --git a/aes/build.properties b/aes/build.properties index 14e2b616..d1d3269d 100644 --- a/aes/build.properties +++ b/aes/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Mon Sep 29 13:04:18 HKT 2025 +#Wed Nov 19 08:36:25 HKT 2025 stageCount=3 libraryProject=libaes -baseVersion=15.10 -publishVersion=15.10.2 +baseVersion=15.11 +publishVersion=15.11.2 buildCount=0 -baseBetaVersion=15.10.3 +baseBetaVersion=15.11.3 diff --git a/aes/src/main/java/cc/winboll/studio/aes/App.java b/aes/src/main/java/cc/winboll/studio/aes/App.java index 9b0fb3a9..9a6674ca 100644 --- a/aes/src/main/java/cc/winboll/studio/aes/App.java +++ b/aes/src/main/java/cc/winboll/studio/aes/App.java @@ -8,8 +8,7 @@ package cc.winboll.studio.aes; import android.view.Gravity; import cc.winboll.studio.libaes.utils.WinBoLLActivityManager; import cc.winboll.studio.libappbase.GlobalApplication; -import com.hjq.toast.ToastUtils; -import com.hjq.toast.style.WhiteToastStyle; +import cc.winboll.studio.libappbase.ToastUtils; public class App extends GlobalApplication { @@ -19,15 +18,16 @@ public class App extends GlobalApplication { @Override public void onCreate() { super.onCreate(); + setIsDebugging(BuildConfig.DEBUG); WinBoLLActivityManager.init(this); // 初始化 Toast 框架 ToastUtils.init(this); - // 设置 Toast 布局样式 - //ToastUtils.setView(R.layout.view_toast); - ToastUtils.setStyle(new WhiteToastStyle()); - ToastUtils.setGravity(Gravity.BOTTOM, 0, 200); - } + @Override + public void onTerminate() { + super.onTerminate(); + ToastUtils.release(); + } } diff --git a/aes/src/main/java/cc/winboll/studio/aes/MainActivity.java b/aes/src/main/java/cc/winboll/studio/aes/MainActivity.java index 40a70dbf..53968fd8 100644 --- a/aes/src/main/java/cc/winboll/studio/aes/MainActivity.java +++ b/aes/src/main/java/cc/winboll/studio/aes/MainActivity.java @@ -91,8 +91,8 @@ public class MainActivity extends DrawerFragmentActivity implements IWinBoLLActi @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.toolbar_library, menu); - if(App.isDebuging()) { - getMenuInflater().inflate(cc.winboll.studio.libapputils.R.menu.toolbar_studio_debug, menu); + if(App.isDebugging()) { + getMenuInflater().inflate(cc.winboll.studio.libaes.R.menu.toolbar_studio_debug, menu); } return super.onCreateOptionsMenu(menu); } diff --git a/build.gradle b/build.gradle index 67994863..453f0841 100644 --- a/build.gradle +++ b/build.gradle @@ -5,6 +5,16 @@ buildscript { // 设置本地Maven仓库路径 url 'file:///sdcard/.m2/repository/' } + + //米盟通过maven接入时,要做如下配置 + maven { + url "https://repos.xiaomi.com/maven" + credentials { + username 'mimo-developer' + password 'AKCp8ih1PFG9tV8qaLyws67dLGZi8udFM39SfsHgihN15cgsiRvHuxj8JzFmuZjaViVeNawaA' + } + } + // Nexus Maven 库地址 // "WinBoLL Release" maven { url "https://nexus.winboll.cc/repository/maven-public/" } @@ -40,6 +50,15 @@ allprojects { // 设置本地Maven仓库路径 url 'file:///sdcard/.m2/repository/' } + + //米盟通过maven接入时,要做如下配置 + maven { + url "https://repos.xiaomi.com/maven" + credentials { + username 'mimo-developer' + password 'AKCp8ih1PFG9tV8qaLyws67dLGZi8udFM39SfsHgihN15cgsiRvHuxj8JzFmuZjaViVeNawaA' + } + } // Nexus Maven 库地址 // "WinBoLL Release" diff --git a/libaes/build.gradle b/libaes/build.gradle index 88f73bdd..6fa4929a 100644 --- a/libaes/build.gradle +++ b/libaes/build.gradle @@ -4,11 +4,15 @@ apply from: '../.winboll/winboll_lib_build.gradle' apply from: '../.winboll/winboll_lint_build.gradle' android { - compileSdkVersion 32 - buildToolsVersion "32.0.0" + + // 1. compileSdkVersion:必须 ≥ targetSdkVersion,建议直接等于 targetSdkVersion(30) + compileSdkVersion 30 + + // 2. buildToolsVersion:需匹配 compileSdkVersion,建议使用 30.x.x 最新稳定版(无需高于 compileSdkVersion) + buildToolsVersion "30.0.3" // 这是 30 对应的最新稳定版,避免使用 beta 版 defaultConfig { - minSdkVersion 24 + minSdkVersion 23 targetSdkVersion 30 } buildTypes { @@ -20,13 +24,6 @@ android { } dependencies { - api fileTree(dir: 'libs', include: ['*.jar']) - api 'cc.winboll.studio:libapputils:15.10.2' - api 'cc.winboll.studio:libappbase:15.10.9' - - // 吐司类库 - api 'com.github.getActivity:ToastUtils:10.5' - // 权限请求框架:https://github.com/getActivity/XXPermissions api 'com.github.getActivity:XXPermissions:18.63' // 下拉控件 @@ -52,4 +49,17 @@ dependencies { //api 'androidx.vectordrawable:vectordrawable:1.1.0' //api 'androidx.vectordrawable:vectordrawable-animated:1.1.0' //api 'androidx.fragment:fragment:1.1.0' + + // 米盟 + implementation 'com.miui.zeus:mimo-ad-sdk:5.3.+'//请使用最新版sdk + //注意:以下5个库必须要引入 + //implementation 'androidx.appcompat:appcompat:1.4.1' + implementation 'androidx.recyclerview:recyclerview:1.0.0' + implementation 'com.google.code.gson:gson:2.8.5' + implementation 'com.github.bumptech.glide:glide:4.9.0' + //annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0' + + api 'cc.winboll.studio:libappbase:15.11.0' + + api fileTree(dir: 'libs', include: ['*.jar']) } diff --git a/libaes/build.properties b/libaes/build.properties index 256a9063..23ebe3f3 100644 --- a/libaes/build.properties +++ b/libaes/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Mon Sep 29 13:04:07 HKT 2025 +#Wed Nov 19 08:36:14 HKT 2025 stageCount=3 libraryProject=libaes -baseVersion=15.10 -publishVersion=15.10.2 +baseVersion=15.11 +publishVersion=15.11.2 buildCount=0 -baseBetaVersion=15.10.3 +baseBetaVersion=15.11.3 diff --git a/libaes/src/main/AndroidManifest.xml b/libaes/src/main/AndroidManifest.xml index 7c90badf..2f221dae 100644 --- a/libaes/src/main/AndroidManifest.xml +++ b/libaes/src/main/AndroidManifest.xml @@ -1,9 +1,25 @@ - + + + + + + + + + + + + + + + + + + - \ No newline at end of file + diff --git a/libaes/src/main/java/cc/winboll/studio/libaes/activitys/DrawerFragmentActivity.java b/libaes/src/main/java/cc/winboll/studio/libaes/activitys/DrawerFragmentActivity.java index 812089e7..b6dfba12 100644 --- a/libaes/src/main/java/cc/winboll/studio/libaes/activitys/DrawerFragmentActivity.java +++ b/libaes/src/main/java/cc/winboll/studio/libaes/activitys/DrawerFragmentActivity.java @@ -31,6 +31,9 @@ import cc.winboll.studio.libappbase.LogUtils; import com.baoyz.widget.PullRefreshLayout; import java.util.ArrayList; import cc.winboll.studio.libaes.utils.WinBoLLActivityManager; +import cc.winboll.studio.libaes.views.ADsBannerView; +import cc.winboll.studio.libappbase.LogActivity; +import cc.winboll.studio.libappbase.ToastUtils; public abstract class DrawerFragmentActivity extends AppCompatActivity implements AdapterView.OnItemClickListener { @@ -71,17 +74,22 @@ public abstract class DrawerFragmentActivity extends AppCompatActivity implement @Override protected void onDestroy() { super.onDestroy(); + // 修复:释放广告资源,避免内存泄漏 + ADsBannerView adsBannerView = findViewById(R.id.adsbanner); + if (adsBannerView != null) { + adsBannerView.releaseAdResources(); + } } /*@Override - public Intent getIntent() { - // TODO: Implement this method - return super.getIntent(); - } + public Intent getIntent() { + // TODO: Implement this method + return super.getIntent(); + } - public Context getContext() { - return this.mContext; - }*/ + public Context getContext() { + return this.mContext; + }*/ @Override public MenuInflater getMenuInflater() { @@ -90,9 +98,9 @@ public abstract class DrawerFragmentActivity extends AppCompatActivity implement } /*public void setSubtitle(CharSequence context) { - // TODO: Implement this method - getSupportActionBar().setSubtitle(context); - }*/ + // TODO: Implement this method + getSupportActionBar().setSubtitle(context); + }*/ @Override public void recreate() { @@ -100,9 +108,9 @@ public abstract class DrawerFragmentActivity extends AppCompatActivity implement } /*@Override - public boolean moveTaskToBack(boolean nonRoot) { - return super.moveTaskToBack(nonRoot); - }*/ + public boolean moveTaskToBack(boolean nonRoot) { + return super.moveTaskToBack(nonRoot); + }*/ @Override public void startActivity(Intent intent) { @@ -115,24 +123,24 @@ public abstract class DrawerFragmentActivity extends AppCompatActivity implement } /*@Override - public FragmentManager getSupportFragmentManager() { - return super.getSupportFragmentManager(); - } + public FragmentManager getSupportFragmentManager() { + return super.getSupportFragmentManager(); + } - public void setSubtitle(int resId) { - // TODO: Implement this method - getSupportActionBar().setSubtitle(resId); - } + public void setSubtitle(int resId) { + // TODO: Implement this method + getSupportActionBar().setSubtitle(resId); + } - public void setTitle(CharSequence context) { - // TODO: Implement this method - getSupportActionBar().setTitle(context); - } + public void setTitle(CharSequence context) { + // TODO: Implement this method + getSupportActionBar().setTitle(context); + } - public void setTitle(int resId) { - // TODO: Implement this method - getSupportActionBar().setTitle(resId); - }*/ + public void setTitle(int resId) { + // TODO: Implement this method + getSupportActionBar().setTitle(resId); + }*/ @Override public SharedPreferences getSharedPreferences(String name, int mode) { @@ -175,6 +183,9 @@ public abstract class DrawerFragmentActivity extends AppCompatActivity implement for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) { getString(i); } + } else if (R.id.item_log == item.getItemId()) { + ToastUtils.show("Test"); + LogActivity.startLogActivity(this); } else if (R.id.item_about == item.getItemId()) { LogUtils.d(TAG, "onAbout"); } else if (android.R.id.home == item.getItemId()) { @@ -189,6 +200,11 @@ public abstract class DrawerFragmentActivity extends AppCompatActivity implement if (checkThemeStyleChange()) { recreate(); } + + ADsBannerView adsBannerView = findViewById(R.id.adsbanner); + if (adsBannerView != null) { + adsBannerView.resumeADs(); + } } void initRootView() { diff --git a/libaes/src/main/java/cc/winboll/studio/libaes/unittests/TestAButtonFragment.java b/libaes/src/main/java/cc/winboll/studio/libaes/unittests/TestAButtonFragment.java index 23f0deef..ce837aa9 100644 --- a/libaes/src/main/java/cc/winboll/studio/libaes/unittests/TestAButtonFragment.java +++ b/libaes/src/main/java/cc/winboll/studio/libaes/unittests/TestAButtonFragment.java @@ -13,7 +13,7 @@ import androidx.fragment.app.Fragment; import cc.winboll.studio.libaes.R; import cc.winboll.studio.libaes.views.AButton; import cc.winboll.studio.libappbase.LogUtils; -import com.hjq.toast.ToastUtils; +import cc.winboll.studio.libappbase.ToastUtils; public class TestAButtonFragment extends Fragment { diff --git a/libaes/src/main/java/cc/winboll/studio/libaes/unittests/TestViewPageFragment.java b/libaes/src/main/java/cc/winboll/studio/libaes/unittests/TestViewPageFragment.java index a4a33508..1d922fa1 100644 --- a/libaes/src/main/java/cc/winboll/studio/libaes/unittests/TestViewPageFragment.java +++ b/libaes/src/main/java/cc/winboll/studio/libaes/unittests/TestViewPageFragment.java @@ -12,14 +12,13 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.Toast; import androidx.fragment.app.Fragment; import androidx.viewpager.widget.ViewPager; import cc.winboll.studio.libaes.ImagePagerAdapter; import cc.winboll.studio.libaes.R; import cc.winboll.studio.libaes.views.AOHPCTCSeekBar; import cc.winboll.studio.libappbase.LogView; -import com.hjq.toast.ToastUtils; +import cc.winboll.studio.libappbase.ToastUtils; import java.util.ArrayList; import java.util.List; diff --git a/libaes/src/main/java/cc/winboll/studio/libaes/utils/MimoUtils.java b/libaes/src/main/java/cc/winboll/studio/libaes/utils/MimoUtils.java new file mode 100644 index 00000000..2c07337c --- /dev/null +++ b/libaes/src/main/java/cc/winboll/studio/libaes/utils/MimoUtils.java @@ -0,0 +1,33 @@ +package cc.winboll.studio.libaes.utils; + +import android.content.Context; +import android.util.DisplayMetrics; + +/** + * @Author ZhanGSKen&豆包大模型 + * @Date 2025/11/18 15:23 + * @Describe 米盟 MimoUtils + */ +public final class MimoUtils { + public static final String TAG = "Utils"; + + public static int dpToPx(Context context, float dp) { + DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); + return (int) (dp * displayMetrics.density + 0.5f); + } + + public static int pxToDp(Context context, float px) { + DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); + return (int) (px / displayMetrics.density + 0.5f); + } + + public static int pxToSp(Context context, float pxValue) { + DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); + return (int) (pxValue / displayMetrics.scaledDensity + 0.5f); + } + + public static int spToPx(Context context, float spValue) { + DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); + return (int) (spValue * displayMetrics.scaledDensity + 0.5f); + } +} diff --git a/libaes/src/main/java/cc/winboll/studio/libaes/utils/PrefUtils.java b/libaes/src/main/java/cc/winboll/studio/libaes/utils/PrefUtils.java new file mode 100644 index 00000000..751a9e56 --- /dev/null +++ b/libaes/src/main/java/cc/winboll/studio/libaes/utils/PrefUtils.java @@ -0,0 +1,33 @@ +package cc.winboll.studio.libaes.utils; + +import android.content.Context; +import android.content.SharedPreferences; + +/** + * @Author ZhanGSKen&豆包大模型 + * @Date 2025/11/13 06:50 + * @Describe 应用变量保存工具 + */ + +public class PrefUtils { + + public static final String TAG = "PrefUtils"; + + // + // 保存字符串到SharedPreferences的函数 + // + public static void saveString(Context context, String key, String value) { + SharedPreferences sharedPreferences = context.getSharedPreferences("myPrefs", Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putString(key, value); + editor.apply(); + } + + // + // 从SharedPreferences读取字符串的函数 + // + public static String getString(Context context, String key, String defaultValue) { + SharedPreferences sharedPreferences = context.getSharedPreferences("myPrefs", Context.MODE_PRIVATE); + return sharedPreferences.getString(key, defaultValue); + } +} diff --git a/libaes/src/main/java/cc/winboll/studio/libaes/views/ADsBannerView.java b/libaes/src/main/java/cc/winboll/studio/libaes/views/ADsBannerView.java new file mode 100644 index 00000000..44669aa5 --- /dev/null +++ b/libaes/src/main/java/cc/winboll/studio/libaes/views/ADsBannerView.java @@ -0,0 +1,489 @@ +package cc.winboll.studio.libaes.views; + +import android.app.Activity; +import android.content.Context; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.os.Handler; +import android.os.Looper; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.Display; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.Toast; +import androidx.appcompat.app.AlertDialog; +import cc.winboll.studio.libaes.R; +import cc.winboll.studio.libaes.utils.MimoUtils; +import cc.winboll.studio.libappbase.GlobalApplication; +import cc.winboll.studio.libappbase.LogUtils; +import cc.winboll.studio.libappbase.ToastUtils; +import com.miui.zeus.mimo.sdk.ADParams; +import com.miui.zeus.mimo.sdk.BannerAd; +import com.miui.zeus.mimo.sdk.MimoCustomController; +import com.miui.zeus.mimo.sdk.MimoLocation; +import com.miui.zeus.mimo.sdk.MimoSdk; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * @Author ZhanGSKen&豆包大模型 + * @Date 2025/11/18 14:41 + * @Describe WinBoLL 横幅广告类 + */ +public class ADsBannerView extends LinearLayout { + + public static final String TAG = "ADsBannerView"; + + private static final String PRIVACY_FILE = "privacy_pfs"; + private static final String PRIVACY_VALUE = "privacy_value";//0: 拒绝,1:赞同 + + private String BANNER_POS_ID = "802e356f1726f9ff39c69308bfd6f06a"; + private String BANNER_POS_ID_WINBOLL_BETA = "d129ee5a263911f981a6dc7a9802e3e7"; + private String BANNER_POS_ID_WINBOLL = "4ec30efdb32271765b9a4efac902828b"; + + /* + private String BANNER_POS_ID = "802e356f1726f9ff39c69308bfd6f06a"; + private String BANNER_POS_ID_WINBOLL_BETA = "802e356f1726f9ff39c69308bfd6f06a"; + private String BANNER_POS_ID_WINBOLL = "802e356f1726f9ff39c69308bfd6f06a"; + */ + + Context mContext; + View mMianView; + SharedPreferences mSharedPreferences; + ViewGroup mContainer; + BannerAd mBannerAd; + List mAllBanners = new ArrayList<>(); + // 新增:主线程Handler,确保广告操作在主线程执行 + private Handler mMainHandler; + + public ADsBannerView(Context context) { + super(context); + this.mContext = context; + initView(); + } + + public ADsBannerView(Context context, AttributeSet attrs) { + super(context, attrs); + this.mContext = context; + initView(); + } + + public ADsBannerView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + this.mContext = context; + initView(); + } + + public ADsBannerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + this.mContext = context; + initView(); + } + + void initView() { + + // 初始化主线程Handler(关键:确保广告操作在主线程执行) + mMainHandler = new Handler(Looper.getMainLooper()); + + // 米盟模块:隐私协议弹窗 + showPrivacy(); + + this.mMianView = inflate(this.mContext, R.layout.view_adsbanner, null); + mContainer = this.mMianView.findViewById(R.id.ads_container); + addView(this.mMianView); + } + + Activity getActivity() { + try { + Activity activity = (Activity)this.mContext; + return activity; + } catch (Exception ex) { + } + return null; + } + + public void resumeADs() { + // 修复:优化广告请求逻辑(添加生命周期判断 + 主线程执行) + if (getActivity() != null && !getActivity().isFinishing() && !getActivity().isDestroyed()) { + String privacyAgreeValue = getSharedPreferences().getString(PRIVACY_VALUE, null); + if (TextUtils.equals(privacyAgreeValue, String.valueOf(1))) { + LogUtils.i(TAG, "已同意隐私协议,开始播放米盟广告..."); + mMainHandler.postDelayed(new Runnable() { + @Override + public void run() { + //ToastUtils.show("ADs run"); + // 再次校验生命周期,避免延迟执行时Activity已销毁 + if (getActivity() != null && !getActivity().isFinishing() && !getActivity().isDestroyed()) { + fetchAd(); + } + } + }, 1000); // 延迟1秒请求广告,提升页面加载体验 + } + } + } + + /** + * 释放广告资源(关键:避免内存泄漏和空Context调用) + */ + public void releaseAdResources() { + LogUtils.d(TAG, "releaseAdResources()"); + + // 移除Handler回调 + if (mMainHandler != null) { + mMainHandler.removeCallbacksAndMessages(null); + } + + // 销毁所有广告实例 + if (mAllBanners != null && !mAllBanners.isEmpty()) { + for (BannerAd ad : mAllBanners) { + if (ad != null) { + ad.destroy(); + } + } + mAllBanners.clear(); + } + // 置空当前广告引用 + mBannerAd = null; + // 移除广告容器中的视图 + if (mContainer != null) { + mContainer.removeAllViews(); + } + } + + /** + * 显示广告(核心修复:传递安全的Context + 生命周期校验) + */ + private void showAd() { + LogUtils.d(TAG, "showAd()"); + // 1. 生命周期校验:避免Activity已销毁时操作UI + if (getActivity() == null || getActivity().isFinishing() || getActivity().isDestroyed()) { + LogUtils.e(TAG, "showAd: Activity is finishing or destroyed"); + return; + } + // 2. 非空校验:广告实例和容器 + if (mBannerAd == null || mContainer == null) { + LogUtils.e(TAG, "showAd: BannerAd or Container is null"); + return; + } + // 3. 创建广告容器(使用ApplicationContext避免内存泄漏) + final FrameLayout container = new FrameLayout(getActivity().getApplicationContext()); + container.setPadding(0, 0, 0, MimoUtils.dpToPx(getActivity(), 10)); + mContainer.addView(container, new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.WRAP_CONTENT + )); + +// if (mIsBiddingWin) { +// mBannerAd.setPrice(getPrice()); +// } + // 4. 显示广告:传递ApplicationContext,避免Activity Context失效 + mBannerAd.showAd(getActivity(), container, new BannerAd.BannerInteractionListener() { + @Override + public void onAdClick() { + LogUtils.d(TAG, "onAdClick"); + } + + @Override + public void onAdShow() { + LogUtils.d(TAG, "onAdShow"); + } + + @Override + public void onAdDismiss() { + LogUtils.d(TAG, "onAdDismiss"); + // 修复:移除容器时校验Activity状态 + if (getActivity() != null && !getActivity().isFinishing() && !getActivity().isDestroyed() && mContainer != null) { + mContainer.removeView(container); + } + } + + @Override + public void onRenderSuccess() { + LogUtils.d(TAG, "onRenderSuccess"); + } + + @Override + public void onRenderFail(int code, String msg) { + LogUtils.e(TAG, "onRenderFail errorCode " + code + " errorMsg " + msg); + // 修复:渲染失败时移除容器 + if (getActivity() != null && !getActivity().isFinishing() && !getActivity().isDestroyed() && mContainer != null) { + mContainer.removeView(container); + } + } + }); + } + + /** + * 请求广告(核心修复:Context安全校验 + 异常捕获 + 资源管理) + */ + private void fetchAd() { + LogUtils.d(TAG, "fetchAd()"); + // 1. 双重校验:Activity未销毁 + Context非空 + if (getActivity() == null || getActivity().isFinishing() || getActivity().isDestroyed() || getActivity().getApplicationContext() == null) { + LogUtils.e(TAG, "fetchAd: Invalid Context or Activity state"); + return; + } + // 2. 释放之前的广告资源,避免内存泄漏 + if (mBannerAd != null) { + mBannerAd.destroy(); + } + // 3. 初始化广告(使用ApplicationContext,避免Activity Context失效) + try { + mBannerAd = new BannerAd(); + mAllBanners.add(mBannerAd); + } catch (Exception e) { + LogUtils.e(TAG, "fetchAd: Init BannerAd failed", e); + return; + } + // 4. 设置下载监听 + mBannerAd.setDownLoadListener(new BannerAd.BannerDownloadListener() { + @Override + public void onDownloadStarted() { + LogUtils.d(TAG, "onDownloadStarted"); + } + + @Override + public void onDownloadPaused() { + LogUtils.d(TAG, "onDownloadPaused"); + } + + @Override + public void onDownloadFailed(int errorCode) { + String msg = "onDownloadFailed, errorCode = " + errorCode; + LogUtils.d(TAG, msg); + //ToastUtils.show(msg); + } + + @Override + public void onDownloadFinished() { + LogUtils.d(TAG, "onDownloadFinished"); + } + + @Override + public void onDownloadProgressUpdated(int progress) { + LogUtils.d(TAG, "onDownloadProgressUpdated " + progress + "%"); + } + + @Override + public void onInstallFailed(int errorCode) { + LogUtils.d(TAG, "onInstallFailed, errorCode = " + errorCode); + } + + @Override + public void onInstallStart() { + LogUtils.d(TAG, "onInstallStart"); + } + + @Override + public void onInstallSuccess() { + LogUtils.d(TAG, "onInstallSuccess"); + } + + @Override + public void onDownloadCancel() { + LogUtils.d(TAG, "onDownloadCancel"); + } + }); + + // 5. 构建广告参数并请求 + String currentAD_ID = getAD_ID(); + LogUtils.d(TAG, String.format("currentAD_ID = %s", currentAD_ID)); + ADParams params = new ADParams.Builder().setUpId(currentAD_ID).build(); + mBannerAd.loadAd(params, new BannerAd.BannerLoadListener() { + @Override + public void onBannerAdLoadSuccess() { + LogUtils.d(TAG, "onBannerAdLoadSuccess()"); + // 修复:广告加载成功后校验Activity状态 + if (getActivity() != null && !getActivity().isFinishing() && !getActivity().isDestroyed()) { + showAd(); + } + } + + @Override + public void onAdLoadFailed(int errorCode, String errorMsg) { + String msg = "onAdLoadFailed: errorCode = " + errorCode + ", errorMsg = " + errorMsg; + LogUtils.d(TAG, msg); + if (errorCode == 300219) { + // 如果是广告拉取错误就提示一下 + ToastUtils.show(String.format("米盟 SDK Error Code : %d", errorCode)); + } + removeAllBanners(); + } + }); + } + + void removeAllBanners() { + // 修复:加载失败时移除当前广告实例 + if (mAllBanners.contains(mBannerAd)) { + mAllBanners.remove(mBannerAd); + } + mBannerAd.destroy(); + mBannerAd = null; + } + + /** + * 根据当前秒数获取广告ID(原逻辑保留) + */ + private String getAD_ID() { + long currentSecond = System.currentTimeMillis() / 1000; + return (currentSecond % 2 == 0) ? BANNER_POS_ID : + (GlobalApplication.isDebugging() ? BANNER_POS_ID_WINBOLL_BETA : BANNER_POS_ID_WINBOLL); + } + + /** + * 获取广告价格(原逻辑保留,添加空指针校验) + */ + private long getPrice() { + if (mBannerAd == null) { + return 0; + } + Map map = mBannerAd.getMediaExtraInfo(); + if (map == null || map.isEmpty() || !map.containsKey("price")) { + LogUtils.w(TAG, "getPrice: media extra info is null or no price key"); + return 0; + } + Object priceObj = map.get("price"); + if (priceObj instanceof Long) { + return (Long) priceObj; + } else if (priceObj instanceof Integer) { + return ((Integer) priceObj).longValue(); + } else { + LogUtils.e(TAG, "getPrice: price type is invalid"); + return 0; + } + } + + /** + * 显示隐私协议弹窗(原逻辑保留,优化Context使用) + */ + private void showPrivacy() { + // 校验Activity状态,避免弹窗泄露 + if (getActivity() == null || getActivity().isFinishing() || getActivity().isDestroyed()) { + return; + } + String privacyAgreeValue = getSharedPreferences().getString(PRIVACY_VALUE, null); + if (TextUtils.equals(privacyAgreeValue, String.valueOf(0))) { + LogUtils.i(TAG, "已拒绝隐私协议,广告已处于不可用状态..."); + Toast.makeText(getActivity().getApplicationContext(), "已拒绝隐私协议,广告已处于不可用状态", Toast.LENGTH_SHORT).show(); + return; + } + if (TextUtils.equals(privacyAgreeValue, String.valueOf(1))) { + LogUtils.i(TAG, "已同意隐私协议,开始初始化米盟SDK..."); + initMimoSdk(); + return; + } + LogUtils.i(TAG, "开始弹出隐私协议..."); + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setTitle("用户须知"); + builder.setMessage("小米广告SDK隐私政策: https://dev.mi.com/distribute/doc/details?pId=1688, 请复制到浏览器查看"); + builder.setIcon(R.drawable.ic_launcher); + builder.setCancelable(false); // 点击对话框以外的区域不消失 + builder.setPositiveButton("同意", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + getSharedPreferences().edit() + .putString(PRIVACY_VALUE, String.valueOf(1)) + .apply(); + initMimoSdk(); + dialog.dismiss(); + } + }); + builder.setNegativeButton("拒绝", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + getSharedPreferences().edit() + .putString(PRIVACY_VALUE, String.valueOf(0)) + .apply(); + dialog.dismiss(); + } + }); + AlertDialog dialog = builder.create(); + + // 配置弹窗位置(底部全屏) + Window window = dialog.getWindow(); + if (window != null) { + window.setGravity(Gravity.BOTTOM); + WindowManager m = getActivity().getWindowManager(); + Display d = m.getDefaultDisplay(); + WindowManager.LayoutParams p = window.getAttributes(); + p.width = d.getWidth(); + window.setAttributes(p); + } + dialog.show(); + } + + /** + * 初始化米盟SDK(核心修复:传递ApplicationContext + 异常捕获) + */ + private void initMimoSdk() { + // 1. 安全获取ApplicationContext,避免Activity Context失效 + Context appContext = getActivity().getApplicationContext(); + if (appContext == null) { + LogUtils.e(TAG, "initMimoSdk: ApplicationContext is null"); + return; + } + // 2. 初始化SDK,捕获异常避免崩溃 + try { + MimoSdk.init(appContext, new MimoCustomController() { + @Override + public boolean isCanUseLocation() { + return true; + } + + @Override + public MimoLocation getMimoLocation() { + return null; + } + + @Override + public boolean isCanUseWifiState() { + return true; + } + + @Override + public boolean alist() { + return true; + } + }, new MimoSdk.InitCallback() { + @Override + public void success() { + LogUtils.d(TAG, "MimoSdk init success"); + } + + @Override + public void fail(int code, String msg) { + LogUtils.e(TAG, "MimoSdk init fail, code=" + code + ",msg=" + msg); + } + }); + MimoSdk.setDebugOn(true); + } catch (Exception e) { + LogUtils.e(TAG, "initMimoSdk: init failed", e); + } + } + + + /** + * 获取SharedPreferences实例(原逻辑保留,添加空指针校验) + */ + SharedPreferences getSharedPreferences() { + if (mSharedPreferences == null) { + // 修复:使用ApplicationContext获取SharedPreferences,避免Activity Context泄露 + Context appContext = getActivity().getApplicationContext(); + if (appContext != null) { + mSharedPreferences = appContext.getSharedPreferences(PRIVACY_FILE, Context.MODE_PRIVATE); + } else { + LogUtils.e(TAG, "getSharedPreferences: ApplicationContext is null"); + // 降级方案:若ApplicationContext为空,使用Activity Context(仅作兼容) + mSharedPreferences = getActivity().getSharedPreferences(PRIVACY_FILE, Context.MODE_PRIVATE); + } + } + return mSharedPreferences; + } +} diff --git a/libaes/src/main/java/cc/winboll/studio/libaes/views/AboutView.java b/libaes/src/main/java/cc/winboll/studio/libaes/views/AboutView.java index 41ddb8d0..fe6f076f 100644 --- a/libaes/src/main/java/cc/winboll/studio/libaes/views/AboutView.java +++ b/libaes/src/main/java/cc/winboll/studio/libaes/views/AboutView.java @@ -12,7 +12,6 @@ import android.content.res.TypedArray; import android.net.Uri; import android.os.Message; import android.util.AttributeSet; -import android.view.LayoutInflater; import android.view.View; import android.widget.EditText; import android.widget.LinearLayout; @@ -20,11 +19,11 @@ import cc.winboll.studio.libaes.R; import cc.winboll.studio.libaes.dialogs.YesNoAlertDialog; import cc.winboll.studio.libaes.models.APPInfo; import cc.winboll.studio.libaes.utils.AppVersionUtils; +import cc.winboll.studio.libaes.utils.PrefUtils; import cc.winboll.studio.libaes.utils.WinBoLLActivityManager; import cc.winboll.studio.libappbase.GlobalApplication; import cc.winboll.studio.libappbase.LogUtils; -import cc.winboll.studio.libapputils.utils.PrefUtils; -import com.hjq.toast.ToastUtils; +import cc.winboll.studio.libappbase.ToastUtils; import java.io.IOException; import mehdi.sakout.aboutpage.AboutPage; import mehdi.sakout.aboutpage.Element; @@ -108,7 +107,7 @@ public class AboutView extends LinearLayout { mszAppDescription = mAPPInfo.getAppDescription(); mnAppIcon = mAPPInfo.getAppIcon(); - mszWinBoLLServerHost = GlobalApplication.isDebuging() ? "https://yun-preivew.winboll.cc": "https://yun.winboll.cc"; + mszWinBoLLServerHost = GlobalApplication.isDebugging() ? "https://yun-preivew.winboll.cc": "https://yun.winboll.cc"; try { mszAppVersionName = _mContext.getPackageManager().getPackageInfo(_mContext.getPackageName(), 0).versionName; @@ -229,7 +228,7 @@ public class AboutView extends LinearLayout { // 定义应用调试按钮 // Element elementAppMode; - if (GlobalApplication.isDebuging()) { + if (GlobalApplication.isDebugging()) { elementAppMode = new Element(_mContext.getString(R.string.app_normal), R.drawable.ic_winboll); elementAppMode.setOnClickListener(mAppNormalOnClickListener); } else { @@ -263,8 +262,8 @@ public class AboutView extends LinearLayout { if (intent != null) { //intent.setAction(cc.winboll.studio.libapputils.intent.action.DEBUGVIEW); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - GlobalApplication.setIsDebuging(true); - GlobalApplication.saveDebugStatus(_mContext); + GlobalApplication.setIsDebugging(true); + GlobalApplication.saveDebugStatus((GlobalApplication)_mContext.getApplicationContext()); WinBoLLActivityManager.getInstance().finishAll(); context.startActivity(intent); @@ -275,8 +274,8 @@ public class AboutView extends LinearLayout { Intent intent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName()); if (intent != null) { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - GlobalApplication.setIsDebuging(false); - GlobalApplication.saveDebugStatus(_mContext); + GlobalApplication.setIsDebugging(false); + GlobalApplication.saveDebugStatus((GlobalApplication)_mContext.getApplicationContext()); WinBoLLActivityManager.getInstance().finishAll(); context.startActivity(intent); @@ -301,7 +300,7 @@ public class AboutView extends LinearLayout { String szUrl = mszWinBoLLServerHost + "/studio/details.php?app=" + mszAppAPKFolderName; // 构建包含认证信息的请求 String credential = ""; - if (GlobalApplication.isDebuging()) { + if (GlobalApplication.isDebugging()) { credential = Credentials.basic(metDevUserName.getText().toString(), metDevUserPassword.getText().toString()); PrefUtils.saveString(_mContext, "metDevUserName", metDevUserName.getText().toString()); PrefUtils.saveString(_mContext, "metDevUserPassword", metDevUserPassword.getText().toString()); diff --git a/libaes/src/main/res/layout/activity_drawerfragment.xml b/libaes/src/main/res/layout/activity_drawerfragment.xml index 8d976ab5..22ce6efb 100644 --- a/libaes/src/main/res/layout/activity_drawerfragment.xml +++ b/libaes/src/main/res/layout/activity_drawerfragment.xml @@ -10,7 +10,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/activitydrawerfragmentASupportToolbar1"/> - + + + + diff --git a/libaes/src/main/res/layout/view_adsbanner.xml b/libaes/src/main/res/layout/view_adsbanner.xml new file mode 100644 index 00000000..286d1f88 --- /dev/null +++ b/libaes/src/main/res/layout/view_adsbanner.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/libaes/src/main/res/menu/toolbar_studio_debug.xml b/libaes/src/main/res/menu/toolbar_studio_debug.xml new file mode 100644 index 00000000..4471301c --- /dev/null +++ b/libaes/src/main/res/menu/toolbar_studio_debug.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + diff --git a/libaes/src/main/res/values/strings.xml b/libaes/src/main/res/values/strings.xml index 5f94d4c4..f9f1d392 100644 --- a/libaes/src/main/res/values/strings.xml +++ b/libaes/src/main/res/values/strings.xml @@ -12,5 +12,10 @@ GoldenTheme MemorTheme TaoTheme - + + Click here is switch to Normal APP + Click here is switch to APP DEBUG + GITEA HOME + APP UPDATE + diff --git a/libaes/src/main/res/xml/file_paths.xml b/libaes/src/main/res/xml/file_paths.xml new file mode 100644 index 00000000..21bd1b8b --- /dev/null +++ b/libaes/src/main/res/xml/file_paths.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/libaes/src/main/res/xml/network_security_config.xml b/libaes/src/main/res/xml/network_security_config.xml new file mode 100644 index 00000000..ba3f407a --- /dev/null +++ b/libaes/src/main/res/xml/network_security_config.xml @@ -0,0 +1,16 @@ + + + + + winboll.cc + + + + + + + + + + +