合并 APPBase 项目

This commit is contained in:
qinglong
2026-07-23 22:00:02 +08:00
parent a57b48ddba
commit 9b12f95b8b
10 changed files with 469 additions and 65 deletions
-4
View File
@@ -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'
+5 -5
View File
@@ -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
@@ -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);
}
/**
@@ -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<zhangsken@qq.com>
@@ -5,7 +5,7 @@
android:layout_height="match_parent"
android:background="#FAFAFA">
<cc.winboll.studio.libaes.views.SMTPConfigView
<cc.winboll.studio.libappbase.views.SMTPConfigView
android:id="@+id/smtp_config_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
+2
View File
@@ -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'
}
+5 -5
View File
@@ -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
@@ -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<zhangsken@qq.com>
* @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();
}
});
}
}
@@ -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<zhangsken@qq.com>
* @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配置已保存");
}
}
@@ -0,0 +1,110 @@
<?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="wrap_content"
android:orientation="vertical"
android:padding="16dp"
android:background="@drawable/bg_container_border">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SMTP邮件配置"
android:textSize="16sp"
android:textStyle="bold"
android:layout_marginBottom="12dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SMTP服务器"
android:textSize="14sp"
android:layout_marginTop="8dp"/>
<EditText
android:id="@+id/et_smtp_server"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="smtp.qq.com"
android:inputType="textUri"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="端口"
android:textSize="14sp"
android:layout_marginTop="8dp"/>
<EditText
android:id="@+id/et_smtp_port"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="465"
android:inputType="number"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="发件人邮箱"
android:textSize="14sp"
android:layout_marginTop="8dp"/>
<EditText
android:id="@+id/et_smtp_sender"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="your@qq.com"
android:inputType="textEmailAddress"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="授权码"
android:textSize="14sp"
android:layout_marginTop="8dp"/>
<EditText
android:id="@+id/et_smtp_auth_code"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="QQ邮箱授权码"
android:inputType="textPassword"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="默认收件人"
android:textSize="14sp"
android:layout_marginTop="8dp"/>
<EditText
android:id="@+id/et_smtp_recipient"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="recipient@example.com"
android:inputType="textEmailAddress"/>
<CheckBox
android:id="@+id/cb_smtp_enabled"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="启用SMTP邮件发送"
android:textSize="14sp"
android:layout_marginTop="12dp"/>
<Button
android:id="@+id/btn_smtp_save"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="保存配置"
android:layout_marginTop="16dp"/>
<Button
android:id="@+id/btn_smtp_test_send"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="发送测试邮件"
android:layout_marginTop="8dp"/>
</LinearLayout>