新增用户系统功能 Activity

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
wchino
2026-04-05 22:29:10 +08:00
parent 1951b76a7c
commit 9c2c5d6f5f
5 changed files with 639 additions and 0 deletions

View File

@@ -0,0 +1,134 @@
package com.miraclegarden.smsmessage.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.Nullable;
import com.miraclegarden.library.app.MiracleGardenActivity;
import com.miraclegarden.smsmessage.databinding.ActivityBankEditBinding;
import com.miraclegarden.smsmessage.model.ApiError;
import com.miraclegarden.smsmessage.model.BankInfo;
import com.miraclegarden.smsmessage.network.ApiService;
import com.miraclegarden.smsmessage.network.TokenManager;
public class BankEditActivity extends MiracleGardenActivity<ActivityBankEditBinding> {
private ApiService apiService;
private TokenManager tokenManager;
private String bankId;
private boolean isEditMode = false;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
tokenManager = TokenManager.getInstance(this);
if (!tokenManager.isLoggedIn()) {
startActivity(new Intent(this, LoginActivity.class));
finish();
return;
}
apiService = new ApiService(this);
initData();
initView();
}
private void initData() {
bankId = getIntent().getStringExtra("bank_id");
isEditMode = bankId != null;
if (isEditMode) {
binding.tvTitle.setText("编辑银行");
binding.etBankName.setText(getIntent().getStringExtra("bank_name"));
binding.etAccount.setText(getIntent().getStringExtra("account"));
binding.etRemark.setText(getIntent().getStringExtra("remark"));
} else {
binding.tvTitle.setText("添加银行");
}
}
private void initView() {
String username = tokenManager.getUsername();
if (username != null) {
binding.tvUsername.setText(username);
}
binding.backIv.setOnClickListener(v -> finish());
binding.btnSave.setOnClickListener(v -> attemptSave());
}
private void attemptSave() {
String bankName = binding.etBankName.getText().toString().trim();
String account = binding.etAccount.getText().toString().trim();
String remark = binding.etRemark.getText().toString().trim();
if (TextUtils.isEmpty(bankName)) {
binding.etBankName.setError("请输入银行名称");
binding.etBankName.requestFocus();
return;
}
if (TextUtils.isEmpty(account)) {
binding.etAccount.setError("请输入账户");
binding.etAccount.requestFocus();
return;
}
BankInfo bankInfo = new BankInfo(bankName, account, remark);
showLoading(true);
if (isEditMode) {
apiService.updateBank(bankId, bankInfo, new ApiService.ApiCallback<BankInfo>() {
@Override
public void onSuccess(BankInfo result) {
runOnUiThread(() -> {
showLoading(false);
Toast.makeText(BankEditActivity.this, "修改成功", Toast.LENGTH_SHORT).show();
finish();
});
}
@Override
public void onError(ApiError error) {
runOnUiThread(() -> {
showLoading(false);
Toast.makeText(BankEditActivity.this,
"修改失败: " + error.getMessage(), Toast.LENGTH_LONG).show();
});
}
});
} else {
apiService.createBank(bankInfo, new ApiService.ApiCallback<BankInfo>() {
@Override
public void onSuccess(BankInfo result) {
runOnUiThread(() -> {
showLoading(false);
Toast.makeText(BankEditActivity.this, "添加成功", Toast.LENGTH_SHORT).show();
finish();
});
}
@Override
public void onError(ApiError error) {
runOnUiThread(() -> {
showLoading(false);
Toast.makeText(BankEditActivity.this,
"添加失败: " + error.getMessage(), Toast.LENGTH_LONG).show();
});
}
});
}
}
private void showLoading(boolean show) {
binding.progressBar.setVisibility(show ? View.VISIBLE : View.GONE);
binding.btnSave.setEnabled(!show);
}
}

View File

@@ -0,0 +1,139 @@
package com.miraclegarden.smsmessage.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.miraclegarden.library.app.MiracleGardenActivity;
import com.miraclegarden.smsmessage.R;
import com.miraclegarden.smsmessage.comm.CommonAdapter;
import com.miraclegarden.smsmessage.comm.ViewHolder;
import com.miraclegarden.smsmessage.databinding.ActivityBankListBinding;
import com.miraclegarden.smsmessage.model.ApiError;
import com.miraclegarden.smsmessage.model.BankInfo;
import com.miraclegarden.smsmessage.network.ApiService;
import com.miraclegarden.smsmessage.network.TokenManager;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
public class BankListActivity extends MiracleGardenActivity<ActivityBankListBinding> {
private ApiService apiService;
private TokenManager tokenManager;
private List<BankInfo> bankList = new ArrayList<>();
private CommonAdapter<BankInfo> adapter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
tokenManager = TokenManager.getInstance(this);
if (!tokenManager.isLoggedIn()) {
startActivity(new Intent(this, LoginActivity.class));
finish();
return;
}
apiService = new ApiService(this);
initView();
loadBankList();
}
private void initView() {
String username = tokenManager.getUsername();
if (username != null) {
binding.tvUsername.setText(username);
}
binding.backIv.setOnClickListener(v -> finish());
binding.recyclerview.setLayoutManager(new LinearLayoutManager(this));
adapter = new CommonAdapter<BankInfo>(this, R.layout.item_bank, bankList) {
@Override
public void convert(ViewHolder holder, BankInfo bankInfo, int index) {
holder.setText(R.id.tv_bank_name, bankInfo.getBankName());
holder.setText(R.id.tv_account, bankInfo.getAccount());
if (bankInfo.getRemark() != null && !bankInfo.getRemark().isEmpty()) {
holder.setText(R.id.tv_remark, bankInfo.getRemark());
holder.getView(R.id.tv_remark).setVisibility(View.VISIBLE);
} else {
holder.getView(R.id.tv_remark).setVisibility(View.GONE);
}
String createdAt = formatTime(bankInfo.getCreatedAt());
String updatedAt = formatTime(bankInfo.getUpdatedAt());
holder.setText(R.id.tv_created_at, "创建: " + createdAt);
holder.setText(R.id.tv_updated_at, "更新: " + updatedAt);
}
};
binding.recyclerview.setAdapter(adapter);
}
private String formatTime(String isoTime) {
if (isoTime == null || isoTime.isEmpty()) {
return "--";
}
try {
SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault());
inputFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = inputFormat.parse(isoTime);
SimpleDateFormat outputFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault());
outputFormat.setTimeZone(TimeZone.getDefault());
return outputFormat.format(date);
} catch (Exception e) {
return isoTime.substring(0, Math.min(16, isoTime.length()));
}
}
private void loadBankList() {
binding.loadingLy.setVisibility(View.VISIBLE);
binding.emptyLy.setVisibility(View.GONE);
apiService.getBankList(new ApiService.ApiCallback<List<BankInfo>>() {
@Override
public void onSuccess(List<BankInfo> result) {
runOnUiThread(() -> {
binding.loadingLy.setVisibility(View.GONE);
bankList.clear();
if (result != null) {
bankList.addAll(result);
}
adapter.notifyDataSetChanged();
if (bankList.isEmpty()) {
binding.emptyLy.setVisibility(View.VISIBLE);
} else {
binding.emptyLy.setVisibility(View.GONE);
}
});
}
@Override
public void onError(ApiError error) {
runOnUiThread(() -> {
binding.loadingLy.setVisibility(View.GONE);
Toast.makeText(BankListActivity.this,
"加载失败: " + error.getMessage(), Toast.LENGTH_LONG).show();
});
}
});
}
@Override
protected void onResume() {
super.onResume();
loadBankList();
}
}

View File

@@ -0,0 +1,108 @@
package com.miraclegarden.smsmessage.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.Nullable;
import com.miraclegarden.library.app.MiracleGardenActivity;
import com.miraclegarden.smsmessage.databinding.ActivityChangePasswordBinding;
import com.miraclegarden.smsmessage.model.ApiError;
import com.miraclegarden.smsmessage.network.ApiService;
import com.miraclegarden.smsmessage.network.TokenManager;
/**
* 修改密码页面(首登强制修改)
*/
public class ChangePasswordActivity extends MiracleGardenActivity<ActivityChangePasswordBinding> {
private ApiService apiService;
private TokenManager tokenManager;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
apiService = new ApiService(this);
tokenManager = TokenManager.getInstance(this);
initView();
}
private void initView() {
binding.btnSubmit.setOnClickListener(v -> attemptChangePassword());
}
private void attemptChangePassword() {
String oldPassword = binding.etOldPassword.getText().toString().trim();
String newPassword = binding.etNewPassword.getText().toString().trim();
String confirmPassword = binding.etConfirmPassword.getText().toString().trim();
if (TextUtils.isEmpty(oldPassword)) {
binding.etOldPassword.setError("请输入旧密码");
binding.etOldPassword.requestFocus();
return;
}
if (TextUtils.isEmpty(newPassword)) {
binding.etNewPassword.setError("请输入新密码");
binding.etNewPassword.requestFocus();
return;
}
if (newPassword.length() < 6) {
binding.etNewPassword.setError("新密码至少6位");
binding.etNewPassword.requestFocus();
return;
}
if (!newPassword.equals(confirmPassword)) {
binding.etConfirmPassword.setError("两次输入的密码不一致");
binding.etConfirmPassword.requestFocus();
return;
}
showLoading(true);
apiService.changePassword(oldPassword, newPassword, new ApiService.ApiCallback<Void>() {
@Override
public void onSuccess(Void result) {
runOnUiThread(() -> {
showLoading(false);
tokenManager.setFirstLoginComplete();
Toast.makeText(ChangePasswordActivity.this, "密码修改成功", Toast.LENGTH_SHORT).show();
navigateToMain();
});
}
@Override
public void onError(ApiError error) {
runOnUiThread(() -> {
showLoading(false);
Toast.makeText(ChangePasswordActivity.this,
"修改失败: " + error.getMessage(), Toast.LENGTH_LONG).show();
});
}
});
}
private void showLoading(boolean show) {
binding.progressBar.setVisibility(show ? View.VISIBLE : View.GONE);
binding.btnSubmit.setEnabled(!show);
}
private void navigateToMain() {
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();
}
@Override
public void onBackPressed() {
Toast.makeText(this, "请先修改密码", Toast.LENGTH_SHORT).show();
}
}

View File

@@ -0,0 +1,120 @@
package com.miraclegarden.smsmessage.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.Nullable;
import com.miraclegarden.library.app.MiracleGardenActivity;
import com.miraclegarden.smsmessage.databinding.ActivityLoginBinding;
import com.miraclegarden.smsmessage.model.ApiError;
import com.miraclegarden.smsmessage.model.LoginResponse;
import com.miraclegarden.smsmessage.network.ApiService;
import com.miraclegarden.smsmessage.network.TokenManager;
/**
* 登录页面
*/
public class LoginActivity extends MiracleGardenActivity<ActivityLoginBinding> {
private ApiService apiService;
private TokenManager tokenManager;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
apiService = new ApiService(this);
tokenManager = TokenManager.getInstance(this);
if (tokenManager.isLoggedIn()) {
navigateToMain();
return;
}
initView();
}
private void initView() {
binding.btnLogin.setOnClickListener(v -> attemptLogin());
}
private void attemptLogin() {
String username = binding.etUsername.getText().toString().trim();
String password = binding.etPassword.getText().toString().trim();
if (TextUtils.isEmpty(username)) {
binding.etUsername.setError("请输入用户名");
binding.etUsername.requestFocus();
return;
}
if (TextUtils.isEmpty(password)) {
binding.etPassword.setError("请输入密码");
binding.etPassword.requestFocus();
return;
}
showLoading(true);
apiService.login(username, password, new ApiService.ApiCallback<LoginResponse>() {
@Override
public void onSuccess(LoginResponse result) {
runOnUiThread(() -> {
showLoading(false);
tokenManager.saveLoginData(result);
if (result.isFirstLogin()) {
navigateToChangePassword();
} else {
Toast.makeText(LoginActivity.this, "登录成功", Toast.LENGTH_SHORT).show();
navigateToMain();
}
});
}
@Override
public void onError(ApiError error) {
runOnUiThread(() -> {
showLoading(false);
String msg;
switch (error.getStatusCode()) {
case 400:
msg = "用户名和密码不能为空";
break;
case 401:
msg = "用户名或密码错误";
break;
case 403:
msg = "账户已被停用,请联系管理员";
break;
default:
msg = "登录失败: " + error.getMessage();
}
Toast.makeText(LoginActivity.this, msg, Toast.LENGTH_LONG).show();
});
}
});
}
private void showLoading(boolean show) {
binding.progressBar.setVisibility(show ? View.VISIBLE : View.GONE);
binding.btnLogin.setEnabled(!show);
}
private void navigateToMain() {
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();
}
private void navigateToChangePassword() {
Intent intent = new Intent(this, ChangePasswordActivity.class);
startActivity(intent);
finish();
}
}

View File

@@ -0,0 +1,138 @@
package com.miraclegarden.smsmessage.Activity;
import android.Manifest;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import com.miraclegarden.library.app.MiracleGardenActivity;
import com.miraclegarden.smsmessage.databinding.ActivityPermissionBinding;
import com.miraclegarden.smsmessage.network.TokenManager;
import com.miraclegarden.smsmessage.util.OEMBackgroundHelper;
public class PermissionActivity extends MiracleGardenActivity<ActivityPermissionBinding> {
private TokenManager tokenManager;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
tokenManager = TokenManager.getInstance(this);
if (!tokenManager.isLoggedIn()) {
startActivity(new Intent(this, LoginActivity.class));
finish();
return;
}
initView();
}
@Override
protected void onResume() {
super.onResume();
updatePermissionStatus();
}
private void initView() {
String username = tokenManager.getUsername();
if (username != null) {
binding.tvUsername.setText(username);
}
binding.ivBack.setOnClickListener(v -> finish());
binding.layoutNotificationAccess.setOnClickListener(v -> {
startActivity(new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS));
});
binding.layoutPostNotifications.setOnClickListener(v -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
!= android.content.pm.PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.POST_NOTIFICATIONS}, 100);
}
}
});
binding.layoutBattery.setOnClickListener(v -> {
requestBatteryOptimization();
});
binding.btnAutostart.setOnClickListener(v -> {
OEMBackgroundHelper.openAutoStartSettings(this);
});
binding.btnBatteryOptimization.setOnClickListener(v -> {
OEMBackgroundHelper.requestBatteryOptimizationExemption(this);
});
binding.tvDeviceBrand.setText("设备: " + OEMBackgroundHelper.getManufacturer());
}
private void updatePermissionStatus() {
boolean notificationAccessEnabled = isNotificationListenerEnabled();
updatePermissionItem(binding.iconNotificationAccess, binding.statusNotificationAccess,
notificationAccessEnabled, notificationAccessEnabled ? "已授权" : "未授权");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
boolean postNotificationsGranted = ContextCompat.checkSelfPermission(this,
Manifest.permission.POST_NOTIFICATIONS) == android.content.pm.PackageManager.PERMISSION_GRANTED;
updatePermissionItem(binding.iconPostNotifications, binding.statusPostNotifications,
postNotificationsGranted, postNotificationsGranted ? "已授权" : "未授权");
binding.layoutPostNotifications.setVisibility(View.VISIBLE);
} else {
binding.layoutPostNotifications.setVisibility(View.GONE);
}
boolean batteryOptimized = isBatteryOptimizationEnabled();
updatePermissionItem(binding.iconBattery, binding.statusBattery,
!batteryOptimized, batteryOptimized ? "未授权" : "已授权");
}
private void updatePermissionItem(ImageView icon, TextView status, boolean granted, String statusText) {
if (granted) {
icon.setImageResource(android.R.drawable.checkbox_on_background);
icon.setColorFilter(ContextCompat.getColor(this, android.R.color.holo_green_dark));
} else {
icon.setImageResource(android.R.drawable.ic_dialog_info);
icon.setColorFilter(ContextCompat.getColor(this, android.R.color.holo_red_dark));
}
status.setText(statusText);
}
private boolean isNotificationListenerEnabled() {
String flat = Settings.Secure.getString(getContentResolver(), "enabled_notification_listeners");
return flat != null && flat.contains(getPackageName());
}
private boolean isBatteryOptimizationEnabled() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
return pm != null && !pm.isIgnoringBatteryOptimizations(getPackageName());
}
return false;
}
private void requestBatteryOptimization() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
try {
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivity(intent);
} catch (Exception e) {
startActivity(new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS));
}
}
}
}