From 9b12f95b8b4ba548d49a4d97ea6011c59dad6667 Mon Sep 17 00:00:00 2001 From: qinglong Date: Thu, 23 Jul 2026 22:00:02 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=88=E5=B9=B6=20APPBase=20=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- appbase/build.gradle | 4 - appbase/build.properties | 10 +- .../java/cc/winboll/studio/appbase/App.java | 65 ++---- .../APPExcetionMailSettingsActivity.java | 2 +- .../activity_appexcetionmailsettings.xml | 2 +- libappbase/build.gradle | 2 + libappbase/build.properties | 10 +- .../libappbase/utils/APPMSGMailUtils.java | 216 ++++++++++++++++++ .../libappbase/views/SMTPConfigView.java | 113 +++++++++ .../src/main/res/layout/view_smtp_config.xml | 110 +++++++++ 10 files changed, 469 insertions(+), 65 deletions(-) create mode 100644 libappbase/src/main/java/cc/winboll/studio/libappbase/utils/APPMSGMailUtils.java create mode 100644 libappbase/src/main/java/cc/winboll/studio/libappbase/views/SMTPConfigView.java create mode 100644 libappbase/src/main/res/layout/view_smtp_config.xml diff --git a/appbase/build.gradle b/appbase/build.gradle index 5b6b64e..e2d5f41 100644 --- a/appbase/build.gradle +++ b/appbase/build.gradle @@ -55,10 +55,6 @@ android { dependencies { api project(':libappbase') - // WinBoLL库 nexus.winboll.cc 地址 - //api 'cc.winboll.studio:libappbase:15.20.+' - api 'cc.winboll.studio:libaes:15.20.21' - api 'androidx.appcompat:appcompat:1.3.1' // JavaMail SMTP (Android兼容版) api 'com.sun.mail:android-mail:1.6.7' api 'com.sun.mail:android-activation:1.6.7' diff --git a/appbase/build.properties b/appbase/build.properties index 1bef60a..a3a0769 100644 --- a/appbase/build.properties +++ b/appbase/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Tue Jul 21 18:04:08 HKT 2026 -stageCount=4 +#Thu Jul 23 19:08:15 HKT 2026 +stageCount=5 libraryProject=libappbase baseVersion=15.21 -publishVersion=15.21.3 -buildCount=15 -baseBetaVersion=15.21.4 +publishVersion=15.21.4 +buildCount=0 +baseBetaVersion=15.21.5 diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/App.java b/appbase/src/main/java/cc/winboll/studio/appbase/App.java index 3895eab..39c7e3c 100644 --- a/appbase/src/main/java/cc/winboll/studio/appbase/App.java +++ b/appbase/src/main/java/cc/winboll/studio/appbase/App.java @@ -1,13 +1,11 @@ package cc.winboll.studio.appbase; -import android.content.SharedPreferences; - import cc.winboll.studio.libappbase.CrashActivity; import cc.winboll.studio.libappbase.GlobalApplication; import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.ToastUtils; +import cc.winboll.studio.libappbase.utils.APPMSGMailUtils; import cc.winboll.studio.libappbase.utils.CrashHandleNotifyUtils; -import cc.winboll.studio.libappbase.utils.SMTPUtils; import java.io.PrintWriter; import java.io.StringWriter; @@ -33,22 +31,32 @@ public class App extends GlobalApplication { public void onCreate() { try { super.onCreate(); - + // 初始化 Toast 工具类(传入应用全局上下文,确保 Toast 可在任意地方调用) ToastUtils.init(getApplicationContext()); + + // 调试异常捕获 + final String errorMsg = "初始化异常捕获调试信息,这个是调试异常捕获的测试数据。"; + LogUtils.e(TAG, errorMsg); + throw new IllegalArgumentException(errorMsg); + } catch (Throwable e) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); pw.close(); String stackTraceStr = sw.toString(); + + // 发送异常信息邮箱。 + sendExceptionMail(stackTraceStr); + + // 前台通知栏提醒。 CrashHandleNotifyUtils.handleUncaughtException( this, getPackageName(), stackTraceStr, CrashActivity.class ); - sendExceptionMail(stackTraceStr); } } @@ -58,50 +66,9 @@ public class App extends GlobalApplication { * @param stackTraceStr 异常堆栈信息 */ private void sendExceptionMail(final String stackTraceStr) { - SharedPreferences sp = getSharedPreferences(PREF_NAME, MODE_PRIVATE); - final String host = sp.getString("smtp_server", ""); - final String portStr = sp.getString("smtp_port", ""); - final String sender = sp.getString("sender_email", ""); - final String auth = sp.getString("auth_code", ""); - final String recipient = sp.getString("recipient_email", ""); - - if (host.isEmpty() || portStr.isEmpty() || sender.isEmpty() - || auth.isEmpty() || recipient.isEmpty()) { - LogUtils.d(TAG, "sendExceptionMail skipped, SMTP config incomplete"); - return; - } - - final int port; - try { - port = Integer.parseInt(portStr); - } catch (NumberFormatException e) { - LogUtils.e(TAG, "sendExceptionMail failed, port format error: " + portStr); - return; - } - - new Thread(new Runnable() { - @Override - public void run() { - String deviceInfo = "\n\n--- Device Info ---\n" - + "Package: " + getPackageName() + "\n" - + "Time: " + System.currentTimeMillis() + "\n"; - - boolean success = SMTPUtils.sendTextMail( - recipient, - "APPBase 异常报告", - stackTraceStr + deviceInfo, - sender, - auth, - host, - port); - - if (success) { - LogUtils.d(TAG, "sendExceptionMail success"); - } else { - LogUtils.e(TAG, "sendExceptionMail failed"); - } - } - }).start(); + String subject = "Exception Report - " + getPackageName(); + String recipients = APPMSGMailUtils.getRecipient(this); + APPMSGMailUtils.sendMail(this, subject, stackTraceStr, recipients); } /** diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/develop/APPExcetionMailSettingsActivity.java b/appbase/src/main/java/cc/winboll/studio/appbase/develop/APPExcetionMailSettingsActivity.java index 8a982d0..60e5796 100644 --- a/appbase/src/main/java/cc/winboll/studio/appbase/develop/APPExcetionMailSettingsActivity.java +++ b/appbase/src/main/java/cc/winboll/studio/appbase/develop/APPExcetionMailSettingsActivity.java @@ -3,7 +3,7 @@ package cc.winboll.studio.appbase.develop; import android.app.Activity; import android.os.Bundle; import cc.winboll.studio.appbase.R; -import cc.winboll.studio.libaes.views.SMTPConfigView; +import cc.winboll.studio.libappbase.views.SMTPConfigView; /** * @Author 豆包&BigPickle&MiMo&ZhanGSKen diff --git a/appbase/src/main/res/layout/activity_appexcetionmailsettings.xml b/appbase/src/main/res/layout/activity_appexcetionmailsettings.xml index 9f38164..f5e6525 100644 --- a/appbase/src/main/res/layout/activity_appexcetionmailsettings.xml +++ b/appbase/src/main/res/layout/activity_appexcetionmailsettings.xml @@ -5,7 +5,7 @@ android:layout_height="match_parent" android:background="#FAFAFA"> - diff --git a/libappbase/build.gradle b/libappbase/build.gradle index 876e8b7..8eb4888 100644 --- a/libappbase/build.gradle +++ b/libappbase/build.gradle @@ -26,4 +26,6 @@ android { dependencies { api fileTree(dir: 'libs', include: ['*.jar']) + api 'com.sun.mail:android-mail:1.6.7' + api 'com.sun.mail:android-activation:1.6.7' } diff --git a/libappbase/build.properties b/libappbase/build.properties index 1bef60a..94493dc 100644 --- a/libappbase/build.properties +++ b/libappbase/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Tue Jul 21 18:04:08 HKT 2026 -stageCount=4 +#Thu Jul 23 19:07:39 HKT 2026 +stageCount=5 libraryProject=libappbase baseVersion=15.21 -publishVersion=15.21.3 -buildCount=15 -baseBetaVersion=15.21.4 +publishVersion=15.21.4 +buildCount=0 +baseBetaVersion=15.21.5 diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/utils/APPMSGMailUtils.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/utils/APPMSGMailUtils.java new file mode 100644 index 0000000..3794292 --- /dev/null +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/utils/APPMSGMailUtils.java @@ -0,0 +1,216 @@ +package cc.winboll.studio.libappbase.utils; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.widget.Toast; +import cc.winboll.studio.libappbase.LogUtils; +import java.util.Properties; +import javax.mail.Authenticator; +import javax.mail.MessagingException; +import javax.mail.PasswordAuthentication; +import javax.mail.Session; +import javax.mail.Transport; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeMessage; +/** + * @Author 豆包&BigPickle&MiMo&ZhanGSKen + * @Date 2026/07/23 17:36 + * @Describe APPMSG邮件发送工具类,使用QQ邮件SMTP服务发送邮件 + */ +public class APPMSGMailUtils { + + public static final String TAG = "APPMSGMailUtils"; + + // + // SharedPreferences 配置 + // + private static final String SP_NAME = "appmsg_mail_config"; + private static final String KEY_SMTP_SERVER = "smtp_server"; + private static final String KEY_SMTP_PORT = "smtp_port"; + private static final String KEY_SMTP_SENDER = "smtp_sender"; + private static final String KEY_SMTP_AUTH_CODE = "smtp_auth_code"; + private static final String KEY_SMTP_RECIPIENT = "smtp_recipient"; + private static final String KEY_SMTP_ENABLED = "smtp_enabled"; + + private static final String DEFAULT_SMTP_SERVER = "smtp.qq.com"; + private static final String DEFAULT_SMTP_PORT = "465"; + + // + // 保存SMTP配置到SharedPreferences + // + public static void saveConfig(Context context, String server, String port, + String sender, String authCode, String recipient) { + LogUtils.d(TAG, "saveConfig: 开始保存SMTP配置"); + LogUtils.d(TAG, "saveConfig: server=" + server + ", port=" + port); + LogUtils.d(TAG, "saveConfig: sender=" + sender + ", authCode长度=" + (authCode != null ? authCode.length() : 0)); + LogUtils.d(TAG, "saveConfig: recipient=" + recipient); + SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sp.edit(); + editor.putString(KEY_SMTP_SERVER, server); + editor.putString(KEY_SMTP_PORT, port); + editor.putString(KEY_SMTP_SENDER, sender); + editor.putString(KEY_SMTP_AUTH_CODE, authCode); + editor.putString(KEY_SMTP_RECIPIENT, recipient); + editor.apply(); + LogUtils.d(TAG, "saveConfig: SMTP配置已保存完成"); + } + + // + // 从SharedPreferences读取SMTP配置 + // + public static String getServer(Context context) { + SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); + String value = sp.getString(KEY_SMTP_SERVER, DEFAULT_SMTP_SERVER); + LogUtils.d(TAG, "getServer: " + value); + return value; + } + + public static String getPort(Context context) { + SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); + String value = sp.getString(KEY_SMTP_PORT, DEFAULT_SMTP_PORT); + LogUtils.d(TAG, "getPort: " + value); + return value; + } + + public static String getSender(Context context) { + SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); + String value = sp.getString(KEY_SMTP_SENDER, ""); + LogUtils.d(TAG, "getSender: " + value); + return value; + } + + public static String getAuthCode(Context context) { + SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); + String value = sp.getString(KEY_SMTP_AUTH_CODE, ""); + LogUtils.d(TAG, "getAuthCode: 长度=" + (value != null ? value.length() : 0)); + return value; + } + + public static String getRecipient(Context context) { + SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); + String value = sp.getString(KEY_SMTP_RECIPIENT, ""); + LogUtils.d(TAG, "getRecipient: " + value); + return value; + } + + // + // SMTP邮件发送开关 + // + public static void setEnabled(Context context, boolean enabled) { + SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sp.edit(); + editor.putBoolean(KEY_SMTP_ENABLED, enabled); + editor.apply(); + LogUtils.d(TAG, "setEnabled: " + enabled); + } + + public static boolean isEnabled(Context context) { + SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); + return sp.getBoolean(KEY_SMTP_ENABLED, false); + } + + // + // 发送邮件(异步,在后台线程执行) + // + public static void sendMail(final Context context, final String subject, + final String content, final String recipients) { + LogUtils.d(TAG, "sendMail: ====== 开始调用sendMail ======"); + LogUtils.d(TAG, "sendMail: subject=" + subject); + LogUtils.d(TAG, "sendMail: content长度=" + (content != null ? content.length() : 0)); + LogUtils.d(TAG, "sendMail: recipients=" + recipients); + boolean enabled = isEnabled(context); + LogUtils.d(TAG, "sendMail: isEnabled=" + enabled); + if (!enabled) { + LogUtils.d(TAG, "sendMail: SMTP邮件发送已禁用,跳过操作"); + return; + } + final String server = getServer(context); + final String port = getPort(context); + final String sender = getSender(context); + final String authCode = getAuthCode(context); + LogUtils.d(TAG, "sendMail: server=" + server + ", port=" + port); + LogUtils.d(TAG, "sendMail: sender=" + sender + ", authCode长度=" + (authCode != null ? authCode.length() : 0)); + LogUtils.d(TAG, "sendMail: recipients=" + recipients); + + if (sender == null || sender.length() == 0) { + LogUtils.w(TAG, "sendMail: 请先配置发件人邮箱"); + showToast(context, "请先配置发件人邮箱"); + return; + } + if (authCode == null || authCode.length() == 0) { + LogUtils.w(TAG, "sendMail: 请先配置邮箱授权码"); + showToast(context, "请先配置邮箱授权码"); + return; + } + if (recipients == null || recipients.length() == 0) { + LogUtils.w(TAG, "sendMail: 收件人不能为空"); + showToast(context, "收件人不能为空"); + return; + } + + LogUtils.d(TAG, "sendMail: 参数校验通过,准备启动发送线程"); + showToast(context, "正在发送邮件..."); + + new Thread(new Runnable() { + @Override + public void run() { + LogUtils.d(TAG, "sendMail: 发送线程已启动"); + try { + LogUtils.d(TAG, "sendMail: 创建邮件属性配置"); + Properties props = new Properties(); + props.put("mail.smtp.host", server); + props.put("mail.smtp.port", port); + props.put("mail.smtp.auth", "true"); + props.put("mail.smtp.socketFactory.port", port); + props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); + + LogUtils.d(TAG, "sendMail: 创建邮件会话"); + Session session = Session.getInstance(props, new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + LogUtils.d(TAG, "sendMail: 身份验证回调触发"); + return new PasswordAuthentication(sender, authCode); + } + }); + + LogUtils.d(TAG, "sendMail: 创建MimeMessage"); + MimeMessage message = new MimeMessage(session); + message.setFrom(new InternetAddress(sender)); + message.setRecipients(javax.mail.Message.RecipientType.TO, + InternetAddress.parse(recipients)); + message.setSubject(subject); + message.setText(content); + + LogUtils.d(TAG, "sendMail: 调用Transport.send开始发送"); + Transport.send(message); + + LogUtils.d(TAG, "sendMail: 邮件发送成功"); + showToast(context, "邮件发送成功"); + } catch (Exception e) { + LogUtils.e(TAG, "sendMail: 邮件发送异常", e); + showToast(context, "邮件发送失败: " + e.getMessage()); + } + } + }).start(); + LogUtils.d(TAG, "sendMail: ====== sendMail调用结束 ======"); + } + + // + // 在主线程显示Toast提示 + // + private static void showToast(final Context context, final String message) { + if (context == null || message == null) { + return; + } + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + Toast.makeText(context.getApplicationContext(), + message, Toast.LENGTH_SHORT).show(); + } + }); + } +} diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/views/SMTPConfigView.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/views/SMTPConfigView.java new file mode 100644 index 0000000..4ee382e --- /dev/null +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/views/SMTPConfigView.java @@ -0,0 +1,113 @@ +package cc.winboll.studio.libappbase.views; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.Toast; +import cc.winboll.studio.libappbase.R; +import cc.winboll.studio.libappbase.utils.APPMSGMailUtils; +import cc.winboll.studio.libappbase.LogUtils; + +/** + * @Author 豆包&BigPickle&MiMo&ZhanGSKen + * @Date 2026/07/23 17:35 + * @Describe SMTP邮件配置视图控件,用于设置QQ邮件SMTP服务参数 + */ +public class SMTPConfigView extends LinearLayout { + + public static final String TAG = "SMTPConfigView"; + + private EditText mEtServer; + private EditText mEtPort; + private EditText mEtSender; + private EditText mEtAuthCode; + private EditText mEtRecipient; + private CheckBox mCbEnabled; + + public SMTPConfigView(Context context) { + super(context); + initView(context); + } + + public SMTPConfigView(Context context, AttributeSet attrs) { + super(context, attrs); + initView(context); + } + + private void initView(Context context) { + LogUtils.d(TAG, "initView: 初始化SMTP配置视图"); + View.inflate(context, R.layout.view_smtp_config, this); + + mEtServer = (EditText) findViewById(R.id.et_smtp_server); + mEtPort = (EditText) findViewById(R.id.et_smtp_port); + mEtSender = (EditText) findViewById(R.id.et_smtp_sender); + mEtAuthCode = (EditText) findViewById(R.id.et_smtp_auth_code); + mEtRecipient = (EditText) findViewById(R.id.et_smtp_recipient); + mCbEnabled = (CheckBox) findViewById(R.id.cb_smtp_enabled); + + mCbEnabled.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + APPMSGMailUtils.setEnabled(getContext(), isChecked); + } + }); + + // 加载已保存的配置 + loadConfig(); + + // 保存按钮点击事件 + findViewById(R.id.btn_smtp_save).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + saveConfig(); + } + }); + + // 发送测试邮件按钮点击事件 + findViewById(R.id.btn_smtp_test_send).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + saveConfig(); + Context ctx = getContext(); + String recipient = mEtRecipient.getText().toString().trim(); + APPMSGMailUtils.sendMail(ctx, "SMTP测试邮件", + "这是一封SMTP配置测试邮件。如果您收到此邮件,说明SMTP配置正确。", + recipient); + } + }); + } + + // + // 从SharedPreferences加载已保存的SMTP配置 + // + private void loadConfig() { + Context context = getContext(); + mEtServer.setText(APPMSGMailUtils.getServer(context)); + mEtPort.setText(APPMSGMailUtils.getPort(context)); + mEtSender.setText(APPMSGMailUtils.getSender(context)); + mEtAuthCode.setText(APPMSGMailUtils.getAuthCode(context)); + mEtRecipient.setText(APPMSGMailUtils.getRecipient(context)); + mCbEnabled.setChecked(APPMSGMailUtils.isEnabled(context)); + LogUtils.d(TAG, "loadConfig: SMTP配置已加载"); + } + + // + // 将当前输入的SMTP配置保存到SharedPreferences + // + private void saveConfig() { + Context context = getContext(); + String server = mEtServer.getText().toString().trim(); + String port = mEtPort.getText().toString().trim(); + String sender = mEtSender.getText().toString().trim(); + String authCode = mEtAuthCode.getText().toString().trim(); + String recipient = mEtRecipient.getText().toString().trim(); + + APPMSGMailUtils.saveConfig(context, server, port, sender, authCode, recipient); + Toast.makeText(context, "SMTP配置已保存", Toast.LENGTH_SHORT).show(); + LogUtils.d(TAG, "saveConfig: SMTP配置已保存"); + } +} diff --git a/libappbase/src/main/res/layout/view_smtp_config.xml b/libappbase/src/main/res/layout/view_smtp_config.xml new file mode 100644 index 0000000..e00119b --- /dev/null +++ b/libappbase/src/main/res/layout/view_smtp_config.xml @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + +