mirror of
https://gitea.winboll.cc/Studio/WinBoLL.git
synced 2026-08-14 05:27:10 +08:00
refactor(libappbase+appbase): 提取SMTPConfigView控件并添加异常邮件发送
libappbase: - 新增SMTPConfigView自定义控件,封装SMTP配置输入/保存/测试发送 - SMTPUtils添加支持自定义host/port的重载方法,sendMail参数化 appbase: - APPExcetionMailSettingsActivity改用SMTPConfigView,删除全部旧控件 - App.onCreate catch块添加sendExceptionMail(),子线程发送异常堆栈邮件
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
#Created by .winboll/winboll_app_build.gradle
|
#Created by .winboll/winboll_app_build.gradle
|
||||||
#Tue Jul 21 17:05:50 HKT 2026
|
#Tue Jul 21 17:33:50 HKT 2026
|
||||||
stageCount=4
|
stageCount=4
|
||||||
libraryProject=libappbase
|
libraryProject=libappbase
|
||||||
baseVersion=15.21
|
baseVersion=15.21
|
||||||
publishVersion=15.21.3
|
publishVersion=15.21.3
|
||||||
buildCount=6
|
buildCount=9
|
||||||
baseBetaVersion=15.21.4
|
baseBetaVersion=15.21.4
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
package cc.winboll.studio.appbase;
|
package cc.winboll.studio.appbase;
|
||||||
|
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
import cc.winboll.studio.libappbase.CrashActivity;
|
import cc.winboll.studio.libappbase.CrashActivity;
|
||||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||||
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
import cc.winboll.studio.libappbase.ToastUtils;
|
import cc.winboll.studio.libappbase.ToastUtils;
|
||||||
import cc.winboll.studio.libappbase.utils.CrashHandleNotifyUtils;
|
import cc.winboll.studio.libappbase.utils.CrashHandleNotifyUtils;
|
||||||
|
import cc.winboll.studio.libappbase.utils.SMTPUtils;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
|
||||||
@@ -18,6 +22,9 @@ public class App extends GlobalApplication {
|
|||||||
/** 当前应用类的日志 TAG(用于调试输出,标识日志来源) */
|
/** 当前应用类的日志 TAG(用于调试输出,标识日志来源) */
|
||||||
public static final String TAG = "App";
|
public static final String TAG = "App";
|
||||||
|
|
||||||
|
/** SMTP配置 SharedPreferences 名称 */
|
||||||
|
private static final String PREF_NAME = "smtp_config";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 应用创建时回调(全局初始化入口)
|
* 应用创建时回调(全局初始化入口)
|
||||||
* 在应用进程启动时执行,仅调用一次,用于初始化全局工具类、第三方库等
|
* 在应用进程启动时执行,仅调用一次,用于初始化全局工具类、第三方库等
|
||||||
@@ -41,9 +48,62 @@ public class App extends GlobalApplication {
|
|||||||
stackTraceStr,
|
stackTraceStr,
|
||||||
CrashActivity.class
|
CrashActivity.class
|
||||||
);
|
);
|
||||||
|
sendExceptionMail(stackTraceStr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送异常报告邮件
|
||||||
|
* 读取SMTP配置,若配置完整则在子线程发送异常堆栈邮件
|
||||||
|
* @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();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 应用终止时回调(资源释放入口)
|
* 应用终止时回调(资源释放入口)
|
||||||
* 仅在模拟环境(如 Android Studio 模拟器)中可靠触发,真机上可能因系统回收进程不执行
|
* 仅在模拟环境(如 Android Studio 模拟器)中可靠触发,真机上可能因系统回收进程不执行
|
||||||
@@ -56,4 +116,3 @@ public class App extends GlobalApplication {
|
|||||||
ToastUtils.release();
|
ToastUtils.release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-139
@@ -1,162 +1,27 @@
|
|||||||
package cc.winboll.studio.appbase.develop;
|
package cc.winboll.studio.appbase.develop;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.Toast;
|
|
||||||
import cc.winboll.studio.appbase.R;
|
import cc.winboll.studio.appbase.R;
|
||||||
import cc.winboll.studio.libappbase.LogUtils;
|
import cc.winboll.studio.libappbase.widget.SMTPConfigView;
|
||||||
import cc.winboll.studio.libappbase.utils.SMTPUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author 豆包&BigPickle&MiMo&ZhanGSKen<zhangsken@qq.com>
|
* @Author 豆包&BigPickle&MiMo&ZhanGSKen<zhangsken@qq.com>
|
||||||
* @Date 2026/07/21 12:44
|
* @Date 2026/07/21 12:44
|
||||||
* @Describe 应用异常信息邮件接收账号设置
|
* @Describe 应用异常信息邮件接收账号设置
|
||||||
* 配置SMTP服务器参数,用于通过邮件发送异常报告
|
* 使用SMTPConfigView控件配置SMTP服务器参数
|
||||||
*/
|
*/
|
||||||
public class APPExcetionMailSettingsActivity extends Activity {
|
public class APPExcetionMailSettingsActivity extends Activity {
|
||||||
|
|
||||||
public static final String TAG = "APPExcetionMailSettingsActivity";
|
public static final String TAG = "APPExcetionMailSettingsActivity";
|
||||||
|
|
||||||
private static final String PREF_NAME = "smtp_config";
|
private SMTPConfigView mSMTPConfigView;
|
||||||
private static final String KEY_SMTP_SERVER = "smtp_server";
|
|
||||||
private static final String KEY_SMTP_PORT = "smtp_port";
|
|
||||||
private static final String KEY_SENDER_EMAIL = "sender_email";
|
|
||||||
private static final String KEY_AUTH_CODE = "auth_code";
|
|
||||||
private static final String KEY_RECIPIENT_EMAIL = "recipient_email";
|
|
||||||
|
|
||||||
private EditText mEtSmtpServer;
|
|
||||||
private EditText mEtSmtpPort;
|
|
||||||
private EditText mEtSenderEmail;
|
|
||||||
private EditText mEtAuthCode;
|
|
||||||
private EditText mEtRecipientEmail;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_appexcetionmailsettings);
|
setContentView(R.layout.activity_appexcetionmailsettings);
|
||||||
|
|
||||||
mEtSmtpServer = (EditText) findViewById(R.id.et_smtp_server);
|
mSMTPConfigView = (SMTPConfigView) findViewById(R.id.smtp_config_view);
|
||||||
mEtSmtpPort = (EditText) findViewById(R.id.et_smtp_port);
|
|
||||||
mEtSenderEmail = (EditText) findViewById(R.id.et_sender_email);
|
|
||||||
mEtAuthCode = (EditText) findViewById(R.id.et_auth_code);
|
|
||||||
mEtRecipientEmail = (EditText) findViewById(R.id.et_recipient_email);
|
|
||||||
|
|
||||||
loadConfig();
|
|
||||||
|
|
||||||
Button btnSave = (Button) findViewById(R.id.btn_save);
|
|
||||||
btnSave.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
saveConfig();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Button btnTest = (Button) findViewById(R.id.btn_test);
|
|
||||||
btnTest.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
testSendMail();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadConfig() {
|
|
||||||
SharedPreferences sp = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
|
|
||||||
String server = sp.getString(KEY_SMTP_SERVER, "smtp.qq.com");
|
|
||||||
String port = sp.getString(KEY_SMTP_PORT, "465");
|
|
||||||
String sender = sp.getString(KEY_SENDER_EMAIL, "");
|
|
||||||
String auth = sp.getString(KEY_AUTH_CODE, "");
|
|
||||||
String recipient = sp.getString(KEY_RECIPIENT_EMAIL, "");
|
|
||||||
|
|
||||||
mEtSmtpServer.setText(server);
|
|
||||||
mEtSmtpPort.setText(port);
|
|
||||||
mEtSenderEmail.setText(sender);
|
|
||||||
mEtAuthCode.setText(auth);
|
|
||||||
mEtRecipientEmail.setText(recipient);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveConfig() {
|
|
||||||
String server = mEtSmtpServer.getText().toString().trim();
|
|
||||||
String port = mEtSmtpPort.getText().toString().trim();
|
|
||||||
String sender = mEtSenderEmail.getText().toString().trim();
|
|
||||||
String auth = mEtAuthCode.getText().toString().trim();
|
|
||||||
String recipient = mEtRecipientEmail.getText().toString().trim();
|
|
||||||
|
|
||||||
SharedPreferences.Editor editor = getSharedPreferences(PREF_NAME, MODE_PRIVATE).edit();
|
|
||||||
editor.putString(KEY_SMTP_SERVER, server);
|
|
||||||
editor.putString(KEY_SMTP_PORT, port);
|
|
||||||
editor.putString(KEY_SENDER_EMAIL, sender);
|
|
||||||
editor.putString(KEY_AUTH_CODE, auth);
|
|
||||||
editor.putString(KEY_RECIPIENT_EMAIL, recipient);
|
|
||||||
editor.apply();
|
|
||||||
|
|
||||||
Toast.makeText(this, "设置已保存", Toast.LENGTH_SHORT).show();
|
|
||||||
LogUtils.d(TAG, "saveConfig success, server=" + server + ", port=" + port + ", sender=" + sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void testSendMail() {
|
|
||||||
String server = mEtSmtpServer.getText().toString().trim();
|
|
||||||
String portStr = mEtSmtpPort.getText().toString().trim();
|
|
||||||
String sender = mEtSenderEmail.getText().toString().trim();
|
|
||||||
String auth = mEtAuthCode.getText().toString().trim();
|
|
||||||
String recipient = mEtRecipientEmail.getText().toString().trim();
|
|
||||||
|
|
||||||
if (server.isEmpty()) {
|
|
||||||
Toast.makeText(this, "请输入SMTP服务器地址", Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (portStr.isEmpty()) {
|
|
||||||
Toast.makeText(this, "请输入SMTP端口", Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (sender.isEmpty()) {
|
|
||||||
Toast.makeText(this, "请输入发件人邮箱", Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (auth.isEmpty()) {
|
|
||||||
Toast.makeText(this, "请输入邮箱授权码", Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (recipient.isEmpty()) {
|
|
||||||
Toast.makeText(this, "请输入收件人邮箱", Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Toast.makeText(this, "正在发送测试邮件...", Toast.LENGTH_SHORT).show();
|
|
||||||
|
|
||||||
final String finalRecipient = recipient;
|
|
||||||
final String finalSender = sender;
|
|
||||||
final String finalAuth = auth;
|
|
||||||
|
|
||||||
new Thread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
final boolean success = SMTPUtils.sendTextMail(
|
|
||||||
finalRecipient,
|
|
||||||
"APPBase 测试邮件",
|
|
||||||
"这是一封来自APPBase的测试邮件。\n\n发送时间: " + System.currentTimeMillis(),
|
|
||||||
finalSender,
|
|
||||||
finalAuth);
|
|
||||||
|
|
||||||
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (success) {
|
|
||||||
Toast.makeText(APPExcetionMailSettingsActivity.this,
|
|
||||||
"测试邮件发送成功", Toast.LENGTH_SHORT).show();
|
|
||||||
} else {
|
|
||||||
Toast.makeText(APPExcetionMailSettingsActivity.this,
|
|
||||||
"测试邮件发送失败,请检查设置", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,148 +3,11 @@
|
|||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="#FAFAFA"
|
android:background="#FAFAFA">
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
<cc.winboll.studio.libappbase.widget.SMTPConfigView
|
||||||
|
android:id="@+id/smtp_config_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"/>
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="SMTP 邮件发送设置"
|
|
||||||
android:textSize="18sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:textColor="#333333"
|
|
||||||
android:layout_marginBottom="16dp"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="SMTP 服务器"
|
|
||||||
android:textSize="13sp"
|
|
||||||
android:textColor="#666666"
|
|
||||||
android:layout_marginBottom="4dp"/>
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/et_smtp_server"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:inputType="textUri"
|
|
||||||
android:hint="smtp.qq.com"
|
|
||||||
android:textSize="15sp"
|
|
||||||
android:textColor="#000000"
|
|
||||||
android:textColorHint="#999999"
|
|
||||||
android:background="#FFFFFF"
|
|
||||||
android:padding="12dp"
|
|
||||||
android:layout_marginBottom="12dp"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="SMTP 端口"
|
|
||||||
android:textSize="13sp"
|
|
||||||
android:textColor="#666666"
|
|
||||||
android:layout_marginBottom="4dp"/>
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/et_smtp_port"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:inputType="number"
|
|
||||||
android:hint="465"
|
|
||||||
android:textSize="15sp"
|
|
||||||
android:textColor="#000000"
|
|
||||||
android:textColorHint="#999999"
|
|
||||||
android:background="#FFFFFF"
|
|
||||||
android:padding="12dp"
|
|
||||||
android:layout_marginBottom="12dp"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="发件人邮箱(QQ邮箱地址)"
|
|
||||||
android:textSize="13sp"
|
|
||||||
android:textColor="#666666"
|
|
||||||
android:layout_marginBottom="4dp"/>
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/et_sender_email"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:inputType="textEmailAddress"
|
|
||||||
android:hint="123456@qq.com"
|
|
||||||
android:textSize="15sp"
|
|
||||||
android:textColor="#000000"
|
|
||||||
android:textColorHint="#999999"
|
|
||||||
android:background="#FFFFFF"
|
|
||||||
android:padding="12dp"
|
|
||||||
android:layout_marginBottom="12dp"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="邮箱授权码(非QQ密码,在QQ邮箱设置中获取)"
|
|
||||||
android:textSize="13sp"
|
|
||||||
android:textColor="#666666"
|
|
||||||
android:layout_marginBottom="4dp"/>
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/et_auth_code"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:inputType="textPassword"
|
|
||||||
android:hint="请输入授权码"
|
|
||||||
android:textSize="15sp"
|
|
||||||
android:textColor="#000000"
|
|
||||||
android:textColorHint="#999999"
|
|
||||||
android:background="#FFFFFF"
|
|
||||||
android:padding="12dp"
|
|
||||||
android:layout_marginBottom="12dp"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="收件人邮箱"
|
|
||||||
android:textSize="13sp"
|
|
||||||
android:textColor="#666666"
|
|
||||||
android:layout_marginBottom="4dp"/>
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/et_recipient_email"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:inputType="textEmailAddress"
|
|
||||||
android:hint="123456@qq.com"
|
|
||||||
android:textSize="15sp"
|
|
||||||
android:textColor="#000000"
|
|
||||||
android:textColorHint="#999999"
|
|
||||||
android:background="#FFFFFF"
|
|
||||||
android:padding="12dp"
|
|
||||||
android:layout_marginBottom="24dp"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/btn_save"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="保存设置"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:textColor="#FFFFFF"
|
|
||||||
android:background="#007AFF"
|
|
||||||
android:padding="14dp"
|
|
||||||
android:layout_marginBottom="12dp"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/btn_test"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="发送测试邮件"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:textColor="#007AFF"
|
|
||||||
android:background="#FFFFFF"
|
|
||||||
android:padding="14dp"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#Created by .winboll/winboll_app_build.gradle
|
#Created by .winboll/winboll_app_build.gradle
|
||||||
#Tue Jul 21 17:05:50 HKT 2026
|
#Tue Jul 21 17:33:50 HKT 2026
|
||||||
stageCount=4
|
stageCount=4
|
||||||
libraryProject=libappbase
|
libraryProject=libappbase
|
||||||
baseVersion=15.21
|
baseVersion=15.21
|
||||||
publishVersion=15.21.3
|
publishVersion=15.21.3
|
||||||
buildCount=6
|
buildCount=9
|
||||||
baseBetaVersion=15.21.4
|
baseBetaVersion=15.21.4
|
||||||
|
|||||||
@@ -47,7 +47,30 @@ public final class SMTPUtils {
|
|||||||
final String body,
|
final String body,
|
||||||
final String from,
|
final String from,
|
||||||
final String authCode) {
|
final String authCode) {
|
||||||
return sendMail(to, subject, body, from, authCode, false);
|
return sendMail(to, subject, body, from, authCode, false,
|
||||||
|
DEFAULT_SMTP_HOST, DEFAULT_SMTP_PORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送纯文本邮件(自定义SMTP服务器)
|
||||||
|
* @param to 收件人邮箱地址
|
||||||
|
* @param subject 邮件主题
|
||||||
|
* @param body 邮件正文(纯文本)
|
||||||
|
* @param from 发件人邮箱地址
|
||||||
|
* @param authCode 邮箱授权码
|
||||||
|
* @param host SMTP服务器地址
|
||||||
|
* @param port SMTP端口
|
||||||
|
* @return true=发送成功,false=发送失败
|
||||||
|
*/
|
||||||
|
public static boolean sendTextMail(
|
||||||
|
final String to,
|
||||||
|
final String subject,
|
||||||
|
final String body,
|
||||||
|
final String from,
|
||||||
|
final String authCode,
|
||||||
|
final String host,
|
||||||
|
final int port) {
|
||||||
|
return sendMail(to, subject, body, from, authCode, false, host, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,7 +88,30 @@ public final class SMTPUtils {
|
|||||||
final String body,
|
final String body,
|
||||||
final String from,
|
final String from,
|
||||||
final String authCode) {
|
final String authCode) {
|
||||||
return sendMail(to, subject, body, from, authCode, true);
|
return sendMail(to, subject, body, from, authCode, true,
|
||||||
|
DEFAULT_SMTP_HOST, DEFAULT_SMTP_PORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送HTML邮件(自定义SMTP服务器)
|
||||||
|
* @param to 收件人邮箱地址
|
||||||
|
* @param subject 邮件主题
|
||||||
|
* @param body 邮件正文(HTML格式)
|
||||||
|
* @param from 发件人邮箱地址
|
||||||
|
* @param authCode 邮箱授权码
|
||||||
|
* @param host SMTP服务器地址
|
||||||
|
* @param port SMTP端口
|
||||||
|
* @return true=发送成功,false=发送失败
|
||||||
|
*/
|
||||||
|
public static boolean sendHtmlMail(
|
||||||
|
final String to,
|
||||||
|
final String subject,
|
||||||
|
final String body,
|
||||||
|
final String from,
|
||||||
|
final String authCode,
|
||||||
|
final String host,
|
||||||
|
final int port) {
|
||||||
|
return sendMail(to, subject, body, from, authCode, true, host, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,9 +130,12 @@ public final class SMTPUtils {
|
|||||||
final String body,
|
final String body,
|
||||||
final String from,
|
final String from,
|
||||||
final String authCode,
|
final String authCode,
|
||||||
final boolean isHtml) {
|
final boolean isHtml,
|
||||||
|
final String host,
|
||||||
|
final int port) {
|
||||||
|
|
||||||
LogUtils.d(TAG, "sendMail invoke, to=" + to + ", subject=" + subject + ", isHtml=" + isHtml);
|
LogUtils.d(TAG, "sendMail invoke, to=" + to + ", subject=" + subject
|
||||||
|
+ ", isHtml=" + isHtml + ", host=" + host + ":" + port);
|
||||||
|
|
||||||
// 参数校验
|
// 参数校验
|
||||||
if (to == null || to.isEmpty()) {
|
if (to == null || to.isEmpty()) {
|
||||||
@@ -108,9 +157,9 @@ public final class SMTPUtils {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. 创建SSL连接
|
// 1. 创建SSL连接
|
||||||
LogUtils.d(TAG, " connecting to " + DEFAULT_SMTP_HOST + ":" + DEFAULT_SMTP_PORT);
|
LogUtils.d(TAG, " connecting to " + host + ":" + port);
|
||||||
SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
|
SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
|
||||||
socket = factory.createSocket(DEFAULT_SMTP_HOST, DEFAULT_SMTP_PORT);
|
socket = factory.createSocket(host, port);
|
||||||
socket.setSoTimeout(10000);
|
socket.setSoTimeout(10000);
|
||||||
|
|
||||||
reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
|
reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
|
||||||
|
|||||||
@@ -0,0 +1,299 @@
|
|||||||
|
package cc.winboll.studio.libappbase.widget;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
import cc.winboll.studio.libappbase.utils.SMTPUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author 豆包&BigPickle&MiMo&ZhanGSKen<zhangsken@qq.com>
|
||||||
|
* @CreateDate 2026/07/21
|
||||||
|
* @LastEditDate 2026/07/21
|
||||||
|
* @Describe SMTP邮件配置自定义控件
|
||||||
|
* 封装SMTP服务器参数的输入、保存、测试发送功能
|
||||||
|
* 可在任意Activity中直接嵌入使用
|
||||||
|
*/
|
||||||
|
public class SMTPConfigView extends LinearLayout {
|
||||||
|
|
||||||
|
private static final String TAG = "SMTPConfigView";
|
||||||
|
|
||||||
|
private static final String DEFAULT_PREF_NAME = "smtp_config";
|
||||||
|
private static final String KEY_SMTP_SERVER = "smtp_server";
|
||||||
|
private static final String KEY_SMTP_PORT = "smtp_port";
|
||||||
|
private static final String KEY_SENDER_EMAIL = "sender_email";
|
||||||
|
private static final String KEY_AUTH_CODE = "auth_code";
|
||||||
|
private static final String KEY_RECIPIENT_EMAIL = "recipient_email";
|
||||||
|
|
||||||
|
private EditText mEtSmtpServer;
|
||||||
|
private EditText mEtSmtpPort;
|
||||||
|
private EditText mEtSenderEmail;
|
||||||
|
private EditText mEtAuthCode;
|
||||||
|
private EditText mEtRecipientEmail;
|
||||||
|
|
||||||
|
private String mPrefName = DEFAULT_PREF_NAME;
|
||||||
|
|
||||||
|
private OnTestSendListener mTestSendListener;
|
||||||
|
|
||||||
|
public interface OnTestSendListener {
|
||||||
|
void onTestSendStart();
|
||||||
|
void onTestSendResult(boolean success);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SMTPConfigView(Context context) {
|
||||||
|
super(context);
|
||||||
|
init(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SMTPConfigView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
init(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SMTPConfigView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
init(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrefName(String prefName) {
|
||||||
|
mPrefName = prefName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnTestSendListener(OnTestSendListener listener) {
|
||||||
|
mTestSendListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init(Context context) {
|
||||||
|
setOrientation(VERTICAL);
|
||||||
|
setLayoutParams(new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
|
||||||
|
int padH = dp2px(context, 16);
|
||||||
|
int padV = dp2px(context, 8);
|
||||||
|
setPadding(padH, padV, padH, padV);
|
||||||
|
|
||||||
|
mEtSmtpServer = createEditText(context, "smtp.qq.com", "textUri");
|
||||||
|
mEtSmtpPort = createEditText(context, "465", "number");
|
||||||
|
mEtSenderEmail = createEditText(context, "123456@qq.com", "textEmailAddress");
|
||||||
|
mEtAuthCode = createEditText(context, "请输入授权码", "textPassword");
|
||||||
|
mEtRecipientEmail = createEditText(context, "123456@qq.com", "textEmailAddress");
|
||||||
|
|
||||||
|
addField(context, "SMTP 服务器", mEtSmtpServer);
|
||||||
|
addField(context, "SMTP 端口", mEtSmtpPort);
|
||||||
|
addField(context, "发件人邮箱(QQ邮箱地址)", mEtSenderEmail);
|
||||||
|
addField(context, "邮箱授权码(非QQ密码,在QQ邮箱设置中获取)", mEtAuthCode);
|
||||||
|
addField(context, "收件人邮箱", mEtRecipientEmail);
|
||||||
|
|
||||||
|
Button btnSave = createButton(context, "保存设置", "#007AFF", "#FFFFFF");
|
||||||
|
addView(btnSave);
|
||||||
|
addVerticalSpacing(context, 12);
|
||||||
|
|
||||||
|
Button btnTest = createButton(context, "发送测试邮件", "#FFFFFF", "#007AFF");
|
||||||
|
addView(btnTest);
|
||||||
|
addVerticalSpacing(context, 12);
|
||||||
|
|
||||||
|
btnSave.setOnClickListener(new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
saveConfig();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
btnTest.setOnClickListener(new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
testSendMail();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
loadConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addField(Context context, String label, EditText editText) {
|
||||||
|
android.widget.TextView tv = new android.widget.TextView(context);
|
||||||
|
tv.setText(label);
|
||||||
|
tv.setTextSize(13);
|
||||||
|
tv.setTextColor(0xFF666666);
|
||||||
|
LayoutParams tvLp = new LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
tvLp.bottomMargin = dp2px(context, 4);
|
||||||
|
addView(tv, tvLp);
|
||||||
|
|
||||||
|
LayoutParams etLp = new LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
etLp.bottomMargin = dp2px(context, 12);
|
||||||
|
addView(editText, etLp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private EditText createEditText(Context context, String hint, String inputType) {
|
||||||
|
EditText et = new EditText(context);
|
||||||
|
et.setHint(hint);
|
||||||
|
et.setTextSize(15);
|
||||||
|
et.setTextColor(0xFF000000);
|
||||||
|
et.setHintTextColor(0xFF999999);
|
||||||
|
et.setBackgroundColor(0xFFFFFFFF);
|
||||||
|
int pad = dp2px(context, 12);
|
||||||
|
et.setPadding(pad, pad, pad, pad);
|
||||||
|
et.setInputType(getInputType(inputType));
|
||||||
|
return et;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getInputType(String type) {
|
||||||
|
if ("number".equals(type)) return android.text.InputType.TYPE_CLASS_NUMBER;
|
||||||
|
if ("textUri".equals(type)) return android.text.InputType.TYPE_CLASS_TEXT
|
||||||
|
| android.text.InputType.TYPE_TEXT_VARIATION_URI;
|
||||||
|
if ("textEmailAddress".equals(type)) return android.text.InputType.TYPE_CLASS_TEXT
|
||||||
|
| android.text.InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
|
||||||
|
if ("textPassword".equals(type)) return android.text.InputType.TYPE_CLASS_TEXT
|
||||||
|
| android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD;
|
||||||
|
return android.text.InputType.TYPE_CLASS_TEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Button createButton(Context context, String text, String bgColor, String textColor) {
|
||||||
|
Button btn = new Button(context);
|
||||||
|
btn.setText(text);
|
||||||
|
btn.setTextSize(16);
|
||||||
|
btn.setTextColor(android.graphics.Color.parseColor(textColor));
|
||||||
|
btn.setBackgroundColor(android.graphics.Color.parseColor(bgColor));
|
||||||
|
int pad = dp2px(context, 14);
|
||||||
|
btn.setPadding(pad, pad, pad, pad);
|
||||||
|
return btn;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addVerticalSpacing(Context context, int dp) {
|
||||||
|
View spacer = new View(context);
|
||||||
|
LayoutParams lp = new LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT, dp2px(context, dp));
|
||||||
|
addView(spacer, lp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int dp2px(Context context, int dp) {
|
||||||
|
float density = context.getResources().getDisplayMetrics().density;
|
||||||
|
return (int) (dp * density + 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadConfig() {
|
||||||
|
SharedPreferences sp = getContext().getSharedPreferences(mPrefName, Context.MODE_PRIVATE);
|
||||||
|
mEtSmtpServer.setText(sp.getString(KEY_SMTP_SERVER, "smtp.qq.com"));
|
||||||
|
mEtSmtpPort.setText(sp.getString(KEY_SMTP_PORT, "465"));
|
||||||
|
mEtSenderEmail.setText(sp.getString(KEY_SENDER_EMAIL, ""));
|
||||||
|
mEtAuthCode.setText(sp.getString(KEY_AUTH_CODE, ""));
|
||||||
|
mEtRecipientEmail.setText(sp.getString(KEY_RECIPIENT_EMAIL, ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveConfig() {
|
||||||
|
String server = mEtSmtpServer.getText().toString().trim();
|
||||||
|
String port = mEtSmtpPort.getText().toString().trim();
|
||||||
|
String sender = mEtSenderEmail.getText().toString().trim();
|
||||||
|
String auth = mEtAuthCode.getText().toString().trim();
|
||||||
|
String recipient = mEtRecipientEmail.getText().toString().trim();
|
||||||
|
|
||||||
|
SharedPreferences.Editor editor = getContext().getSharedPreferences(
|
||||||
|
mPrefName, Context.MODE_PRIVATE).edit();
|
||||||
|
editor.putString(KEY_SMTP_SERVER, server);
|
||||||
|
editor.putString(KEY_SMTP_PORT, port);
|
||||||
|
editor.putString(KEY_SENDER_EMAIL, sender);
|
||||||
|
editor.putString(KEY_AUTH_CODE, auth);
|
||||||
|
editor.putString(KEY_RECIPIENT_EMAIL, recipient);
|
||||||
|
editor.apply();
|
||||||
|
|
||||||
|
Toast.makeText(getContext(), "设置已保存", Toast.LENGTH_SHORT).show();
|
||||||
|
LogUtils.d(TAG, "saveConfig success, server=" + server + ", port=" + port);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSmtpServer() {
|
||||||
|
return mEtSmtpServer.getText().toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSmtpPort() {
|
||||||
|
return mEtSmtpPort.getText().toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSenderEmail() {
|
||||||
|
return mEtSenderEmail.getText().toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAuthCode() {
|
||||||
|
return mEtAuthCode.getText().toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRecipientEmail() {
|
||||||
|
return mEtRecipientEmail.getText().toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConfigValid() {
|
||||||
|
return !getSmtpServer().isEmpty()
|
||||||
|
&& !getSmtpPort().isEmpty()
|
||||||
|
&& !getSenderEmail().isEmpty()
|
||||||
|
&& !getAuthCode().isEmpty()
|
||||||
|
&& !getRecipientEmail().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSendMail() {
|
||||||
|
if (!isConfigValid()) {
|
||||||
|
Toast.makeText(getContext(), "请填写所有配置项", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mTestSendListener != null) {
|
||||||
|
mTestSendListener.onTestSendStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.makeText(getContext(), "正在发送测试邮件...", Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
|
final String recipient = getRecipientEmail();
|
||||||
|
final String sender = getSenderEmail();
|
||||||
|
final String auth = getAuthCode();
|
||||||
|
final String host = getSmtpServer();
|
||||||
|
final int port;
|
||||||
|
try {
|
||||||
|
port = Integer.parseInt(getSmtpPort());
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Toast.makeText(getContext(), "端口号格式错误", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
final boolean success = SMTPUtils.sendTextMail(
|
||||||
|
recipient,
|
||||||
|
"SMTPConfigView 测试邮件",
|
||||||
|
"这是一封SMTPConfigView的测试邮件。\n\n发送时间: "
|
||||||
|
+ System.currentTimeMillis(),
|
||||||
|
sender,
|
||||||
|
auth,
|
||||||
|
host,
|
||||||
|
port);
|
||||||
|
|
||||||
|
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (success) {
|
||||||
|
Toast.makeText(getContext(),
|
||||||
|
"测试邮件发送成功", Toast.LENGTH_SHORT).show();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getContext(),
|
||||||
|
"测试邮件发送失败,请检查设置", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
if (mTestSendListener != null) {
|
||||||
|
mTestSendListener.onTestSendResult(success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user