diff --git a/aes/build.gradle b/aes/build.gradle
index d65a9c5..49d532b 100644
--- a/aes/build.gradle
+++ b/aes/build.gradle
@@ -38,6 +38,11 @@ android {
versionName = genVersionName("${versionName}")
}
}
+
+ packagingOptions {
+ exclude 'META-INF/NOTICE.md'
+ exclude 'META-INF/LICENSE.md'
+ }
}
diff --git a/aes/build.properties b/aes/build.properties
index 768b8eb..be636bd 100644
--- a/aes/build.properties
+++ b/aes/build.properties
@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
-#Sat Jul 11 20:07:00 HKT 2026
-stageCount=19
+#Fri Jul 17 15:03:05 HKT 2026
+stageCount=20
libraryProject=libaes
baseVersion=15.20
-publishVersion=15.20.18
+publishVersion=15.20.19
buildCount=0
-baseBetaVersion=15.20.19
+baseBetaVersion=15.20.20
diff --git a/aes/src/main/AndroidManifest.xml b/aes/src/main/AndroidManifest.xml
index c732afa..e4f770c 100644
--- a/aes/src/main/AndroidManifest.xml
+++ b/aes/src/main/AndroidManifest.xml
@@ -40,6 +40,8 @@
+
+
\ No newline at end of file
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 30be2c0..133564c 100644
--- a/aes/src/main/java/cc/winboll/studio/aes/MainActivity.java
+++ b/aes/src/main/java/cc/winboll/studio/aes/MainActivity.java
@@ -183,6 +183,8 @@ public class MainActivity extends DrawerFragmentActivity {
} else if (nItemId == R.id.item_settings) {
Intent intent = new Intent(this, SettingsActivity.class);
startActivity(intent);
+ } else if (nItemId == R.id.item_smtp_settings) {
+ WinBoLLActivityManager.getInstance().startWinBoLLActivity(this, SMTPSettingsActivity.class);
} else if (nItemId == R.id.item_about) {
// Intent intent = new Intent(this, AboutActivity.class);
// startActivity(intent);
diff --git a/aes/src/main/java/cc/winboll/studio/aes/SMTPSettingsActivity.java b/aes/src/main/java/cc/winboll/studio/aes/SMTPSettingsActivity.java
new file mode 100644
index 0000000..72e3728
--- /dev/null
+++ b/aes/src/main/java/cc/winboll/studio/aes/SMTPSettingsActivity.java
@@ -0,0 +1,51 @@
+package cc.winboll.studio.aes;
+
+import android.os.Bundle;
+import android.view.View;
+import androidx.appcompat.widget.Toolbar;
+import cc.winboll.studio.libappbase.LogUtils;
+
+/**
+ * @Author ZhanGSKen
+ * @Date 2026/07/16
+ * @Describe SMTP邮件设置窗口,使用SMTPConfigView控件进行配置操作
+ */
+public class SMTPSettingsActivity extends BaseWinBoLLActivity {
+
+ public static final String TAG = "SMTPSettingsActivity";
+
+ private Toolbar mToolbar;
+
+ @Override
+ public String getTag() {
+ return TAG;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_smtp_settings);
+
+ initToolbar();
+ }
+
+ private void initToolbar() {
+ LogUtils.d(TAG, "initToolbar() 开始初始化");
+ mToolbar = findViewById(R.id.toolbar);
+ if (mToolbar == null) {
+ LogUtils.e(TAG, "initToolbar() | Toolbar未找到");
+ return;
+ }
+ setSupportActionBar(mToolbar);
+ mToolbar.setSubtitle(getTag());
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ LogUtils.d(TAG, "导航栏 点击返回按钮");
+ finish();
+ }
+ });
+ LogUtils.d(TAG, "initToolbar() 配置完成");
+ }
+}
diff --git a/aes/src/main/res/layout/activity_smtp_settings.xml b/aes/src/main/res/layout/activity_smtp_settings.xml
new file mode 100644
index 0000000..39c4f81
--- /dev/null
+++ b/aes/src/main/res/layout/activity_smtp_settings.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aes/src/main/res/menu/toolbar_main.xml b/aes/src/main/res/menu/toolbar_main.xml
index b87657a..da2fefb 100644
--- a/aes/src/main/res/menu/toolbar_main.xml
+++ b/aes/src/main/res/menu/toolbar_main.xml
@@ -35,6 +35,9 @@
+
diff --git a/libaes/build.gradle b/libaes/build.gradle
index e75211e..75f0bf4 100644
--- a/libaes/build.gradle
+++ b/libaes/build.gradle
@@ -66,5 +66,9 @@ dependencies {
// 备用库 jitpack.io 地址
//api 'com.github.ZhanGSKen:libappbase:appbase-v15.20.33'
+ // JavaMail SMTP (Android兼容版)
+ api 'com.sun.mail:android-mail:1.6.7'
+ api 'com.sun.mail:android-activation:1.6.7'
+
api fileTree(dir: 'libs', include: ['*.jar'])
}
diff --git a/libaes/build.properties b/libaes/build.properties
index 768b8eb..be636bd 100644
--- a/libaes/build.properties
+++ b/libaes/build.properties
@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
-#Sat Jul 11 20:07:00 HKT 2026
-stageCount=19
+#Fri Jul 17 15:03:05 HKT 2026
+stageCount=20
libraryProject=libaes
baseVersion=15.20
-publishVersion=15.20.18
+publishVersion=15.20.19
buildCount=0
-baseBetaVersion=15.20.19
+baseBetaVersion=15.20.20
diff --git a/libaes/src/main/AndroidManifest.xml b/libaes/src/main/AndroidManifest.xml
index d4325dc..c62f5c7 100644
--- a/libaes/src/main/AndroidManifest.xml
+++ b/libaes/src/main/AndroidManifest.xml
@@ -5,6 +5,8 @@
package="cc.winboll.studio.libaes">
+
+
+ * @Date 2026/07/16
+ * @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 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) {
+ 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);
+ return sp.getString(KEY_SMTP_SERVER, DEFAULT_SMTP_SERVER);
+ }
+
+ public static String getPort(Context context) {
+ SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
+ return sp.getString(KEY_SMTP_PORT, DEFAULT_SMTP_PORT);
+ }
+
+ public static String getSender(Context context) {
+ SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
+ return sp.getString(KEY_SMTP_SENDER, "");
+ }
+
+ public static String getAuthCode(Context context) {
+ SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
+ return sp.getString(KEY_SMTP_AUTH_CODE, "");
+ }
+
+ public static String getRecipient(Context context) {
+ SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
+ return sp.getString(KEY_SMTP_RECIPIENT, "");
+ }
+
+ //
+ // 发送邮件(异步,在后台线程执行)
+ //
+ public static void sendMail(final Context context, final String subject,
+ final String content, final String recipients) {
+ final String server = getServer(context);
+ final String port = getPort(context);
+ final String sender = getSender(context);
+ final String authCode = getAuthCode(context);
+
+ if (sender == null || sender.length() == 0) {
+ showToast(context, "请先配置发件人邮箱");
+ return;
+ }
+ if (authCode == null || authCode.length() == 0) {
+ showToast(context, "请先配置邮箱授权码");
+ return;
+ }
+ if (recipients == null || recipients.length() == 0) {
+ showToast(context, "收件人不能为空");
+ return;
+ }
+
+ showToast(context, "正在发送邮件...");
+
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ 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");
+
+ Session session = Session.getInstance(props, new Authenticator() {
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(sender, authCode);
+ }
+ });
+
+ MimeMessage message = new MimeMessage(session);
+ message.setFrom(new InternetAddress(sender));
+ message.setRecipients(Message.RecipientType.TO,
+ InternetAddress.parse(recipients));
+ message.setSubject(subject);
+ message.setText(content);
+
+ Transport.send(message);
+
+ LogUtils.d(TAG, "sendMail: 邮件发送成功");
+ showToast(context, "邮件发送成功");
+ } catch (Exception e) {
+ LogUtils.e(TAG, "sendMail: 邮件发送失败", e);
+ showToast(context, "邮件发送失败: " + e.getMessage());
+ }
+ }
+ }).start();
+ }
+
+ //
+ // 在主线程显示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/libaes/src/main/java/cc/winboll/studio/libaes/views/SMTPConfigView.java b/libaes/src/main/java/cc/winboll/studio/libaes/views/SMTPConfigView.java
new file mode 100644
index 0000000..f39c6a9
--- /dev/null
+++ b/libaes/src/main/java/cc/winboll/studio/libaes/views/SMTPConfigView.java
@@ -0,0 +1,101 @@
+package cc.winboll.studio.libaes.views;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.Toast;
+import cc.winboll.studio.libaes.R;
+import cc.winboll.studio.libaes.utils.APPMSGMailUtils;
+import cc.winboll.studio.libappbase.LogUtils;
+
+/**
+ * @Author ZhanGSKen
+ * @Date 2026/07/16
+ * @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;
+
+ 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);
+
+ // 加载已保存的配置
+ 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));
+ 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/libaes/src/main/res/layout/view_smtp_config.xml b/libaes/src/main/res/layout/view_smtp_config.xml
new file mode 100644
index 0000000..c9326f8
--- /dev/null
+++ b/libaes/src/main/res/layout/view_smtp_config.xml
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+