feat: 新增SMSRecycle2Activity自由模式回收站与刻度全局同步

- 新增SMSRecycle2Activity窗口,每项使用ProtectModeTextView显示短信内容
- 顶部添加示例ProtectModeTextView,刻度值通过SP全局同步到列表所有项
- AppSettingsActivity新增回收站模式RadioGroup:简洁模式/自由模式
- MainActivity回收站菜单根据配置路由到对应Activity
- AppConfigBean新增recycleBinClass字段持久化模式选择
- ProtectModeTextView新增OnScaleChangedListener与setContentTextWithScale
This commit is contained in:
2026-05-08 20:46:27 +08:00
parent 3b60a3b713
commit 63580b111c
13 changed files with 411 additions and 9 deletions

0
gradlew vendored Normal file → Executable file
View File

View File

@@ -18,16 +18,19 @@ def genVersionName(def versionName){
}
android {
// 关键:改为你已安装的 SDK 32≥ targetSdkVersion 30兼容已安装环境
compileSdkVersion 32
// 直接使用已安装的构建工具 33.0.3(无需修改)
buildToolsVersion "33.0.3"
compileSdkVersion 30
buildToolsVersion "30.0.3"
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
defaultConfig {
applicationId "cc.winboll.studio.mymessagemanager"
minSdkVersion 23
minSdkVersion 26
targetSdkVersion 30
versionCode 8
// versionName 更新后需要手动设置

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Fri May 08 11:33:06 GMT 2026
#Fri May 08 20:38:36 CST 2026
stageCount=8
libraryProject=
baseVersion=15.12
publishVersion=15.12.7
buildCount=9
buildCount=18
baseBetaVersion=15.12.8

View File

@@ -216,6 +216,8 @@
<activity android:name="cc.winboll.studio.mymessagemanager.activitys.SMSRecycleActivity"/>
<activity android:name="cc.winboll.studio.mymessagemanager.activitys.SMSRecycle2Activity"/>
<activity android:name="cc.winboll.studio.mymessagemanager.unittest.UnitTestActivity"/>
<activity android:name="cc.winboll.studio.mymessagemanager.activitys.TTSFloatSettingsActivity"/>

View File

@@ -12,6 +12,8 @@ import android.os.Bundle;
import android.provider.Settings;
import android.view.View;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Switch;
import android.widget.Toast;
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
@@ -40,6 +42,7 @@ public class AppSettingsActivity extends WinBoLLActivity implements IWinBoLLActi
//EditText metProtectModerRefuseChars;
EditText metProtectModerReplaceChars;
String mszProtectModerRefuseChars = "";
RadioGroup mRadioGroupRecycleBin;
@Override
public Activity getActivity() {
@@ -83,6 +86,13 @@ public class AppSettingsActivity extends WinBoLLActivity implements IWinBoLLActi
metProtectModerReplaceChars = findViewById(R.id.activityappsettingsEditText4);
metProtectModerReplaceChars.setText(mAppConfigUtil.mAppConfigBean.getProtectModerReplaceChars());
mRadioGroupRecycleBin = findViewById(R.id.activityappsettingsRadioGroup1);
if (mAppConfigUtil.mAppConfigBean.getRecycleBinClass().equals("SMSRecycle2Activity")) {
mRadioGroupRecycleBin.check(R.id.activityappsettingsRadioButton2);
} else {
mRadioGroupRecycleBin.check(R.id.activityappsettingsRadioButton1);
}
mAOHPCTCSeekBar = findViewById(R.id.activityappsettingsAOHPCTCSeekBar1);
mAOHPCTCSeekBar.setThumb(getDrawable(R.drawable.cursor_pointer));
mAOHPCTCSeekBar.setThumbOffset(0);
@@ -92,6 +102,11 @@ public class AppSettingsActivity extends WinBoLLActivity implements IWinBoLLActi
public void onOHPCommit() {
mAppConfigUtil.reLoadConfig();
mAppConfigUtil.mAppConfigBean.setIsSMSRecycleProtectMode(mswSMSRecycleProtectMode.isChecked());
if (mRadioGroupRecycleBin.getCheckedRadioButtonId() == R.id.activityappsettingsRadioButton2) {
mAppConfigUtil.mAppConfigBean.setRecycleBinClass("SMSRecycle2Activity");
} else {
mAppConfigUtil.mAppConfigBean.setRecycleBinClass("SMSRecycleActivity");
}
//mAppConfigUtil.mAppConfigBean.setProtectModerRefuseChars(metProtectModerRefuseChars.getText().toString());
mAppConfigUtil.mAppConfigBean.setProtectModerRefuseChars(mszProtectModerRefuseChars);
mAppConfigUtil.mAppConfigBean.setProtectModerReplaceChars(metProtectModerReplaceChars.getText().toString());

View File

@@ -329,7 +329,13 @@ public class MainActivity extends WinBoLLActivity {
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
} else if (nItemId == R.id.app_smsrecycle) {
Intent i = new Intent(MainActivity.this, SMSRecycleActivity.class);
Class<?> recycleClass;
if (mAppConfigUtil.mAppConfigBean.getRecycleBinClass().equals("SMSRecycle2Activity")) {
recycleClass = SMSRecycle2Activity.class;
} else {
recycleClass = SMSRecycleActivity.class;
}
Intent i = new Intent(MainActivity.this, recycleClass);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
}

View File

@@ -0,0 +1,120 @@
package cc.winboll.studio.mymessagemanager.activitys;
import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import cc.winboll.studio.libaes.dialogs.YesNoAlertDialog;
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
import cc.winboll.studio.mymessagemanager.R;
import cc.winboll.studio.mymessagemanager.adapters.SMSRecycle2Adapter;
import cc.winboll.studio.mymessagemanager.utils.SMSRecycleUtil;
import cc.winboll.studio.mymessagemanager.views.ProtectModeTextView;
import com.baoyz.widget.PullRefreshLayout;
import java.io.File;
public class SMSRecycle2Activity extends WinBoLLActivity implements IWinBoLLActivity {
public static final String TAG = "SMSRecycle2Activity";
private static final String SP_NAME = "smsrecycle2_config";
private static final String KEY_SCALE = "recycle2_scale";
Toolbar mToolbar;
RecyclerView mRecyclerView;
SMSRecycle2Adapter mSMSRecycle2Adapter;
ProtectModeTextView mSampleProtectModeTextView;
SharedPreferences mSP;
@Override
public Activity getActivity() {
return this;
}
@Override
public String getTag() {
return TAG;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_smsrecycle2);
mToolbar = findViewById(R.id.activitysmsrecycle2ASupportToolbar1);
mToolbar.setSubtitle(getString(R.string.activity_name_about));
setSupportActionBar(mToolbar);
mSP = getSharedPreferences(SP_NAME, MODE_PRIVATE);
mSampleProtectModeTextView = findViewById(R.id.activitysmsrecycle2SampleProtectModeTextView);
mSampleProtectModeTextView.setContentTextWithScale(
"调节本短信下方刻度滑条,可预览文本打乱效果;同时该进度条数值将作为回收站短信全局默认初始值。\n"
+ "刻度0 = 保持原文不打乱;\n"
+ "刻度数值越小,字符分组越细碎,文本打乱混乱程度越大;\n"
+ "刻度数值越大,字符连串分组越长,文本打乱混乱程度越小。",
mSP.getInt(KEY_SCALE, 0));
mSampleProtectModeTextView.setOnScaleChangedListener(new ProtectModeTextView.OnScaleChangedListener() {
@Override
public void onScaleChanged(int progress) {
mSP.edit().putInt(KEY_SCALE, progress).apply();
mSMSRecycle2Adapter.setScaleProgress(progress);
mSMSRecycle2Adapter.notifyDataSetChanged();
}
});
initView();
}
void initView() {
mRecyclerView = findViewById(R.id.activitysmsrecycle2RecyclerView1);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(layoutManager);
mSMSRecycle2Adapter = new SMSRecycle2Adapter(this, mSP.getInt(KEY_SCALE, 0));
mRecyclerView.setAdapter(mSMSRecycle2Adapter);
final PullRefreshLayout pullRefreshLayout = findViewById(R.id.activitysmsrecycle2PullRefreshLayout1);
pullRefreshLayout.setOnRefreshListener(new PullRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
mSMSRecycle2Adapter.loadSMSRecycleList();
mSMSRecycle2Adapter.notifyDataSetChanged();
pullRefreshLayout.setRefreshing(false);
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.toolbar_smsrecycle, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int nItemId = item.getItemId();
if (nItemId == R.id.item_cleansmsrecycle) {
YesNoAlertDialog.show(this, "回收站清空确认", "是否清空回收站", mDeleteListener);
}
return true;
}
YesNoAlertDialog.OnDialogResultListener mDeleteListener = new YesNoAlertDialog.OnDialogResultListener() {
@Override
public void onNo() {
}
@Override
public void onYes() {
File file = new File(SMSRecycleUtil.getSMSRecycleListDataPath(SMSRecycle2Activity.this));
file.delete();
mSMSRecycle2Adapter.loadSMSRecycleList();
mSMSRecycle2Adapter.notifyDataSetChanged();
}
};
}

View File

@@ -0,0 +1,78 @@
package cc.winboll.studio.mymessagemanager.adapters;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import cc.winboll.studio.mymessagemanager.R;
import cc.winboll.studio.mymessagemanager.beans.SMSRecycleBean;
import cc.winboll.studio.mymessagemanager.utils.AddressUtils;
import cc.winboll.studio.mymessagemanager.utils.SMSRecycleUtil;
import cc.winboll.studio.mymessagemanager.views.DateAgoTextView;
import cc.winboll.studio.mymessagemanager.views.ProtectModeTextView;
import java.util.ArrayList;
public class SMSRecycle2Adapter extends RecyclerView.Adapter<SMSRecycle2Adapter.ViewHolder> {
public static final String TAG = "SMSRecycle2Adapter";
Context mContext;
ArrayList<SMSRecycleBean> mDataList;
String mszSMSRecycleListDataPath;
int mScaleProgress;
public SMSRecycle2Adapter(Context context, int scaleProgress) {
mContext = context;
mScaleProgress = scaleProgress;
mszSMSRecycleListDataPath = SMSRecycleUtil.getSMSRecycleListDataPath(mContext);
mDataList = new ArrayList<SMSRecycleBean>();
mDataList = loadSMSRecycleList();
}
public void setScaleProgress(int scaleProgress) {
mScaleProgress = scaleProgress;
}
public ArrayList<SMSRecycleBean> loadSMSRecycleList() {
ArrayList<SMSRecycleBean> list = new ArrayList<SMSRecycleBean>();
SMSRecycleBean.loadBeanListFromFile(mszSMSRecycleListDataPath, list, SMSRecycleBean.class);
SMSRecycleBean.sortSMSByDeleteDateDesc(list, true);
mDataList.clear();
mDataList.addAll(list);
return mDataList;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.listview_smsrecycle2, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
SMSRecycleBean item = mDataList.get(position);
holder.mtvAddress.setText(AddressUtils.getFormattedAddress(item.getAddress()));
holder.mdatvDeleteDate.setDate(item.getDeleteDate());
holder.mProtectModeTextView.setContentTextWithScale(item.getBody(), mScaleProgress);
}
@Override
public int getItemCount() {
return mDataList.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView mtvAddress;
DateAgoTextView mdatvDeleteDate;
ProtectModeTextView mProtectModeTextView;
ViewHolder(View itemView) {
super(itemView);
mtvAddress = itemView.findViewById(R.id.listviewsmsrecycle2TextViewAddress);
mdatvDeleteDate = itemView.findViewById(R.id.listviewsmsrecycle2DateAgoTextViewDelete);
mProtectModeTextView = itemView.findViewById(R.id.listviewsmsrecycle2ProtectModeTextView);
}
}
}

View File

@@ -29,6 +29,8 @@ public class AppConfigBean extends BaseBean {
String protectModerRefuseChars = "设定被和谐的字符";
// 保护式预览拒绝显示的字符集的替代字符
String protectModerReplaceChars = "当前替代显示字符";
// 回收站打开的窗口类名 SMSRecycleActivity / SMSRecycle2Activity
String recycleBinClass = "SMSRecycleActivity";
//int appThemeID = ThemeUtil.getThemeID(ThemeUtil.BaseTheme.DEFAULT);
public void setProtectModerRefuseChars(String protectModerRefuseChars) {
@@ -47,6 +49,14 @@ public class AppConfigBean extends BaseBean {
return protectModerReplaceChars;
}
public void setRecycleBinClass(String recycleBinClass) {
this.recycleBinClass = recycleBinClass;
}
public String getRecycleBinClass() {
return recycleBinClass;
}
public void setIsSMSRecycleProtectMode(boolean isSMSRecycleProtectMode) {
this.isSMSRecycleProtectMode = isSMSRecycleProtectMode;
}
@@ -142,6 +152,7 @@ public class AppConfigBean extends BaseBean {
jsonWriter.name("isSMSRecycleProtectMode").value(bean.isSMSRecycleProtectMode());
jsonWriter.name("protectModerRefuseChars").value(bean.getProtectModerRefuseChars());
jsonWriter.name("protectModerReplaceChars").value(bean.getProtectModerReplaceChars());
jsonWriter.name("recycleBinClass").value(bean.getRecycleBinClass());
//jsonWriter.name("appThemeID").value(bean.getAppThemeID());
}
@@ -171,6 +182,8 @@ public class AppConfigBean extends BaseBean {
bean.setProtectModerRefuseChars(jsonReader.nextString());
} else if (name.equals("protectModerReplaceChars")) {
bean.setProtectModerReplaceChars(jsonReader.nextString());
} else if (name.equals("recycleBinClass")) {
bean.setRecycleBinClass(jsonReader.nextString());
} /*else if (name.equals("appThemeID")) {
bean.setAppThemeID(jsonReader.nextInt());
}*/ else {

View File

@@ -24,11 +24,16 @@ import java.util.Random;
*/
public class ProtectModeTextView extends LinearLayout {
public interface OnScaleChangedListener {
void onScaleChanged(int progress);
}
private TextView tvContent;
private SeekBar seekBarScale;
private String originText;
private List<Character> charAllList;
private final Random random = new Random();
private OnScaleChangedListener mOnScaleChangedListener;
public ProtectModeTextView(Context context) {
super(context);
@@ -60,6 +65,9 @@ public class ProtectModeTextView extends LinearLayout {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
handleTextLogic(progress);
if (fromUser && mOnScaleChangedListener != null) {
mOnScaleChangedListener.onScaleChanged(progress);
}
}
@Override
@@ -69,6 +77,17 @@ public class ProtectModeTextView extends LinearLayout {
});
}
public void setOnScaleChangedListener(OnScaleChangedListener listener) {
mOnScaleChangedListener = listener;
}
public void setContentTextWithScale(String text, int scaleProgress) {
this.originText = text;
convertToCharList(text);
seekBarScale.setProgress(scaleProgress);
handleTextLogic(scaleProgress);
}
public void setContentText(String text) {
this.originText = text;
convertToCharList(text);

View File

@@ -119,6 +119,54 @@
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_frame"
android:padding="12dp"
android:layout_marginBottom="8dp">
<TextView
android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="回收站模式设置:"
android:paddingLeft="5dp"
android:layout_marginBottom="8dp"/>
<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:background="#999999"
android:layout_marginBottom="8dp"/>
<RadioGroup
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/activityappsettingsRadioGroup1"
android:paddingLeft="5dp">
<RadioButton
android:layout_width="match_parent"
android:layout_height="48dp"
android:text="简洁模式SMSRecycleActivity"
android:id="@+id/activityappsettingsRadioButton1"
android:gravity="center_vertical"
android:textSize="14sp"/>
<RadioButton
android:layout_width="match_parent"
android:layout_height="48dp"
android:text="自由模式SMSRecycle2Activity"
android:id="@+id/activityappsettingsRadioButton2"
android:gravity="center_vertical"
android:textSize="14sp"/>
</RadioGroup>
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<cc.winboll.studio.libaes.views.ASupportToolbar
android:layout_width="match_parent"
android:layout_height="@dimen/toolbar_height"
android:id="@+id/activitysmsrecycle2ASupportToolbar1"/>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_frame"
android:padding="8dp"
android:layout_margin="4dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="刻度值调节示例(对所有列表项生效):"
android:textSize="12sp"
android:paddingLeft="4dp"
android:paddingBottom="4dp"/>
<cc.winboll.studio.mymessagemanager.views.ProtectModeTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/activitysmsrecycle2SampleProtectModeTextView"/>
</LinearLayout>
<com.baoyz.widget.PullRefreshLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.0"
android:id="@+id/activitysmsrecycle2PullRefreshLayout1">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:isScrollContainer="true"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:reverseLayout="false"
android:id="@+id/activitysmsrecycle2RecyclerView1"/>
</com.baoyz.widget.PullRefreshLayout>
</LinearLayout>

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_frame"
android:padding="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/listviewsmsrecycle2TextViewAddress"
android:padding="5dp"
android:textSize="16sp"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:padding="5dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text_smsdeletetime"/>
<cc.winboll.studio.mymessagemanager.views.DateAgoTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Text"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:id="@+id/listviewsmsrecycle2DateAgoTextViewDelete"/>
</LinearLayout>
<cc.winboll.studio.mymessagemanager.views.ProtectModeTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/listviewsmsrecycle2ProtectModeTextView"/>
</LinearLayout>