Skip to content

Commit 215eb61

Browse files
committed
add dark mode support
support debug mode
1 parent f84aa61 commit 215eb61

17 files changed

Lines changed: 550 additions & 144 deletions

File tree

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
name: add-dependency
3+
description: Use this skill when adding a new library or dependency to the project. It ensures the dependency is added to the correct build.gradle file and the project is synced.
4+
---
5+
6+
# Add Gradle Dependency
7+
8+
## When to use this skill
9+
- When the user asks to install a library (e.g., Gson, Retrofit).
10+
- When you need to add a new `implementation` or `api` line to `build.gradle`.
11+
12+
## Instructions
13+
14+
### 1. Identify the correct build.gradle
15+
- Usually `app/build.gradle` for app-level dependencies.
16+
- `build.gradle` (root) for project-wide configurations (less common for dependencies).
17+
18+
### 2. Check for conflicts
19+
- Search existing dependencies to avoid duplicates or version conflicts.
20+
- Use `gradle dependencies` command if unsure about transitive dependencies (optional).
21+
22+
### 3. Add the dependency
23+
- Add the line inside the `dependencies { ... }` block.
24+
- Examples:
25+
- `implementation 'com.google.code.gson:gson:2.10.1'`
26+
- `testImplementation 'junit:junit:4.13.2'`
27+
28+
### 4. Sync/Build
29+
- Run `./gradlew assembleDebug` (or `gradlew.bat` on Windows) to verify the dependency resolves and the app builds.
30+
- **Critical**: If the build fails, revert the change and inform the user or try a different version.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
name: create-activity
3+
description: Use this skill when the user asks to create a new Android Activity or screen. It ensures consistency across Java code, XML layouts, and Manifest registration.
4+
---
5+
6+
# Create Android Activity
7+
8+
## When to use this skill
9+
- When the user asks to create a new screen, page, or activity.
10+
- When creating a new `Activity` class.
11+
12+
## Instructions
13+
Follow these steps to ensure a complete and compiling implementation.
14+
15+
### 1. Plan the Activity
16+
- Determine the Activity name (e.g., `SettingsActivity`).
17+
- Determine the layout name (e.g., `activity_settings.xml`).
18+
- Identify the package path (usually `top.yztz.msggo.activities` or similar).
19+
20+
### 2. Create the Layout XML
21+
- Create the file in `app/src/main/res/layout/`.
22+
- Use a root layout (e.g., `LinearLayout`, `ConstraintLayout`) appropriate for the content.
23+
- Add necessary UI elements with unique IDs.
24+
25+
### 3. Create the Java Class
26+
- Create the file in the appropriate package directory.
27+
- Extend `AppCompatActivity` or `Activity`.
28+
- Override `onCreate`.
29+
- Call `setContentView(R.layout.your_layout_name)`.
30+
- Initialize views using `findViewById`.
31+
32+
### 4. Register in AndroidManifest.xml
33+
- Open `app/src/main/AndroidManifest.xml`.
34+
- Add an `<activity>` tag inside the `<application>` tag.
35+
- Specify the `android:name` (using relative path like `.activities.NewActivity`).
36+
- Add any necessary intent filters or attributes (e.g., `android:label`).
37+
38+
### 5. Verification
39+
- Ensure the project builds.
40+
- Check that the new Activity can be launched via Intent from elsewhere in the app (if applicable).

app/src/main/java/top/yztz/msggo/activities/ChooserActivity.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444

4545
import java.io.File;
4646
import java.util.ArrayList;
47+
import java.util.Collections;
4748
import java.util.List;
4849
import java.util.Locale;
4950
import java.util.Map;
@@ -56,6 +57,7 @@
5657
import top.yztz.msggo.services.SMSSender;
5758

5859
import top.yztz.msggo.util.FileUtil;
60+
import top.yztz.msggo.util.SensitiveWordUtil;
5961
import top.yztz.msggo.util.TextParser;
6062
import top.yztz.msggo.util.ToastUtil;
6163
import top.yztz.msggo.adapters.CheckboxAdapter;
@@ -231,8 +233,7 @@ private void setupTableHeader() {
231233
tv.setPadding(0, 16, 0, 16);
232234
tv.setMaxLines(1);
233235
tv.setEllipsize(TextUtils.TruncateAt.END);
234-
// Use Material 3 text appearance
235-
tv.setTextAppearance(com.google.android.material.R.style.TextAppearance_Material3_LabelLarge);
236+
tv.setTextAppearance(R.style.TextAppearance_Material3_LabelLarge);
236237
layoutHeader.addView(tv);
237238
}
238239

@@ -313,7 +314,8 @@ private void checkAndSendMessages(List<Message> messages, int index) {
313314
}
314315

315316
Message message = messages.get(index);
316-
List<String> sensitiveWords = top.yztz.msggo.util.SensitiveWordUtil.findAll(message.getContent());
317+
List<String> sensitiveWords = SettingManager.isSensitiveWordFilterEnabled() ?
318+
SensitiveWordUtil.findAll(message.getContent()) : Collections.EMPTY_LIST;
317319

318320
if (sensitiveWords.isEmpty()) {
319321
// No sensitive words, proceed to next message

app/src/main/java/top/yztz/msggo/activities/MainActivity.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import androidx.activity.result.contract.ActivityResultContracts;
2222
import androidx.annotation.NonNull;
2323
import androidx.appcompat.app.AppCompatActivity;
24+
import androidx.appcompat.app.AppCompatDelegate;
2425
import androidx.core.app.ActivityCompat;
2526
import androidx.core.content.ContextCompat;
2627
import androidx.core.content.IntentCompat;
@@ -61,7 +62,7 @@
6162
import com.google.android.material.progressindicator.LinearProgressIndicator;
6263

6364
import io.noties.markwon.Markwon;
64-
import io.noties.markwon.ext.tables.TablePlugin;
65+
import top.yztz.msggo.util.MarkwonFactory;
6566
import java.util.ArrayList;
6667
import java.util.List;
6768

@@ -225,6 +226,7 @@ protected void onCreate(Bundle savedInstanceState) {
225226
super.onCreate(savedInstanceState);
226227
Log.d(TAG, "onCreate: ");
227228
SettingManager.init(this);
229+
AppCompatDelegate.setDefaultNightMode(SettingManager.getDarkMode());
228230

229231
LocaleUtils.applyLocale();
230232
excelPickerLauncher = registerForActivityResult(
@@ -277,13 +279,11 @@ private void showLawDialog(String title, int res_id, Runnable onAgree) {
277279
TextView textView = new TextView(this);
278280
int padding = (int) (16 * getResources().getDisplayMetrics().density);
279281
textView.setPadding(padding, padding / 2, padding, padding);
280-
textView.setTextAppearance(com.google.android.material.R.style.TextAppearance_Material3_BodySmall);
282+
textView.setTextAppearance(R.style.TextAppearance_Material3_BodySmall);
281283
textView.setLineSpacing(0, 1.2f);
282284
scrollView.addView(textView);
283285

284-
Markwon markwon = Markwon.builder(this)
285-
.usePlugin(TablePlugin.create(this))
286-
.build();
286+
Markwon markwon = MarkwonFactory.create(this);
287287

288288
String content = FileUtil.loadFromRaw(this, res_id);
289289
// content = content.replaceFirst("(?m)^#\\s.*(?:\\r?\\n)?", "");

app/src/main/java/top/yztz/msggo/activities/MarkdownActivity.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@
2727
import androidx.appcompat.widget.Toolbar;
2828

2929
import io.noties.markwon.Markwon;
30-
import io.noties.markwon.ext.tables.TablePlugin;
3130
import top.yztz.msggo.R;
3231
import top.yztz.msggo.util.FileUtil;
32+
import top.yztz.msggo.util.MarkwonFactory;
3333

3434
import java.io.BufferedReader;
3535
import java.io.InputStream;
@@ -91,9 +91,7 @@ protected void onCreate(Bundle savedInstanceState) {
9191
toolbar.setNavigationOnClickListener(v -> finish());
9292

9393
TextView tvContent = findViewById(R.id.tv_markdown_content);
94-
Markwon markwon = Markwon.builder(this)
95-
.usePlugin(TablePlugin.create(this))
96-
.build();
94+
Markwon markwon = MarkwonFactory.create(this);
9795

9896
String finalContent;
9997
if (!TextUtils.isEmpty(content)) {

app/src/main/java/top/yztz/msggo/data/SettingManager.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import android.content.SharedPreferences;
2222
import android.util.Log;
2323

24+
import androidx.appcompat.app.AppCompatDelegate;
25+
2426
import java.util.HashMap;
2527

2628
public class SettingManager {
@@ -37,6 +39,15 @@ public class SettingManager {
3739
private static final String LANGUAGE_KEY = "language_v1";
3840
private static final String PRIVACY_ACCEPTED_KEY = "privacy_accepted";
3941
private static final String DISCLAIMER_ACCEPTED_KEY = "disclaimer_accepted";
42+
private static final String DARK_MODE_KEY = "dark_mode_v1";
43+
private static final String SENSITIVE_WORD_FILTER_KEY = "sensitive_word_filter_v1";
44+
45+
/** 深色模式值常量:跟随系统 */
46+
public static final int DARK_MODE_SYSTEM = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM;
47+
/** 深色模式值常量:始终浅色 */
48+
public static final int DARK_MODE_LIGHT = AppCompatDelegate.MODE_NIGHT_NO;
49+
/** 深色模式值常量:始终深色 */
50+
public static final int DARK_MODE_DARK = AppCompatDelegate.MODE_NIGHT_YES;
4051

4152

4253
// Default values
@@ -48,6 +59,8 @@ public class SettingManager {
4859
DefaultPropMap.put(LANGUAGE_KEY, Settings.LANGUAGE_DEFAULT);
4960
DefaultPropMap.put(PRIVACY_ACCEPTED_KEY, Settings.PRIVACY_ACCEPTED_DEFAULT);
5061
DefaultPropMap.put(DISCLAIMER_ACCEPTED_KEY, Settings.DISCLAIMER_ACCEPTED_DEFAULT);
62+
DefaultPropMap.put(DARK_MODE_KEY, DARK_MODE_SYSTEM);
63+
DefaultPropMap.put(SENSITIVE_WORD_FILTER_KEY, true);
5164
}
5265

5366
public static void init(Context context) {
@@ -126,4 +139,20 @@ public static void setRandomizeDelay(boolean flag) {
126139
mEditor.putBoolean(SEND_DELAY_RANDOMIZATION_KEY, flag).apply();
127140
}
128141

142+
public static int getDarkMode() {
143+
return mSp.getInt(DARK_MODE_KEY, DARK_MODE_SYSTEM);
144+
}
145+
146+
public static void setDarkMode(int mode) {
147+
mEditor.putInt(DARK_MODE_KEY, mode).apply();
148+
}
149+
150+
public static boolean isSensitiveWordFilterEnabled() {
151+
return mSp.getBoolean(SENSITIVE_WORD_FILTER_KEY, true);
152+
}
153+
154+
public static void setSensitiveWordFilterEnabled(boolean enabled) {
155+
mEditor.putBoolean(SENSITIVE_WORD_FILTER_KEY, enabled).apply();
156+
}
157+
129158
}

app/src/main/java/top/yztz/msggo/fragments/SettingFrag.java

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import com.google.android.material.card.MaterialCardView;
4747
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
4848
import com.google.android.material.materialswitch.MaterialSwitch;
49+
import androidx.appcompat.app.AppCompatDelegate;
4950
import com.google.android.material.slider.Slider;
5051
import com.google.android.material.textfield.TextInputLayout;
5152
import com.google.android.material.transition.MaterialSharedAxis;
@@ -62,10 +63,10 @@
6263
public class SettingFrag extends Fragment {
6364
private static final String TAG = "SettingFrag";
6465
private Context context;
65-
private MaterialSwitch mSwitchAutoEditor, mSwitchRandomizeDelay;
66+
private MaterialSwitch mSwitchAutoEditor, mSwitchRandomizeDelay, mSwitchSensitiveWord;
6667
private MaterialCardView mCardClearCache;
67-
private View mRowExportLog, mRowAboutApp, mRowLanguage, mRowCheckUpdate;
68-
private TextView mTvCache, mTvDelayValue, mTvSmsRateValue, mTvLanguage;
68+
private View mRowExportLog, mRowAboutApp, mRowLanguage, mRowCheckUpdate, mRowDarkMode;
69+
private TextView mTvCache, mTvDelayValue, mTvSmsRateValue, mTvLanguage, mTvDarkModeSummary;
6970
private LinearLayout mCardSmsRate;
7071
private boolean isUpdatingUI = false;
7172
private Slider mSliderDelay;
@@ -100,6 +101,9 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
100101
mRowLanguage = view.findViewById(R.id.row_language);
101102
mTvLanguage = view.findViewById(R.id.tv_language);
102103
mRowCheckUpdate = view.findViewById(R.id.row_check_update);
104+
mRowDarkMode = view.findViewById(R.id.row_dark_mode);
105+
mTvDarkModeSummary = view.findViewById(R.id.tv_dark_mode_summary);
106+
mSwitchSensitiveWord = view.findViewById(R.id.switch_sensitive_word);
103107

104108
mSliderDelay = view.findViewById(R.id.slider_delay);
105109
mSliderDelay.setValueFrom(Settings.SEND_DELAY_MIN);
@@ -138,6 +142,56 @@ private void setupListeners() {
138142
return String.format(Locale.getDefault(), "%.1fs", seconds);
139143
});
140144

145+
// Sensitive Word Filter
146+
mSwitchSensitiveWord.setOnCheckedChangeListener((buttonView, isChecked) -> {
147+
if (isUpdatingUI) return;
148+
if (!isChecked) {
149+
// 如果用户试图关闭,弹窗警告
150+
new MaterialAlertDialogBuilder(context)
151+
.setTitle(getString(R.string.sensitive_word_filter_dev_title))
152+
.setMessage(getString(R.string.sensitive_word_filter_dev_msg))
153+
.setPositiveButton(getString(R.string.disable), (dialog, which) -> {
154+
SettingManager.setSensitiveWordFilterEnabled(false);
155+
showInfo();
156+
})
157+
.setNegativeButton(getString(R.string.cancel), (dialog, which) -> {
158+
// 还原开关状态
159+
isUpdatingUI = true;
160+
mSwitchSensitiveWord.setChecked(true);
161+
isUpdatingUI = false;
162+
})
163+
.setCancelable(false)
164+
.show();
165+
} else {
166+
SettingManager.setSensitiveWordFilterEnabled(true);
167+
showInfo();
168+
}
169+
});
170+
171+
// Dark Mode
172+
mRowDarkMode.setOnClickListener(v -> {
173+
String[] options = {
174+
getString(R.string.dark_mode_option_system),
175+
getString(R.string.dark_mode_option_off),
176+
getString(R.string.dark_mode_option_on)
177+
};
178+
int checkedItem = SettingManager.getDarkMode();
179+
new MaterialAlertDialogBuilder(context)
180+
.setTitle(getString(R.string.dark_mode))
181+
.setSingleChoiceItems(options, checkedItem, (dialog, which) -> {
182+
SettingManager.setDarkMode(which);
183+
int mode = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM;
184+
if (which == SettingManager.DARK_MODE_LIGHT) mode = AppCompatDelegate.MODE_NIGHT_NO;
185+
else if (which == SettingManager.DARK_MODE_DARK) mode = AppCompatDelegate.MODE_NIGHT_YES;
186+
AppCompatDelegate.setDefaultNightMode(mode);
187+
// getActivity().recreate();
188+
dialog.dismiss();
189+
showInfo();
190+
})
191+
.setNegativeButton(getString(R.string.cancel), null)
192+
.show();
193+
});
194+
141195
// SMS Rate
142196
mCardSmsRate.setOnClickListener(v -> {
143197
View dialogView = LayoutInflater.from(context).inflate(R.layout.dialog_edit_text, null);
@@ -304,6 +358,17 @@ public void showInfo() {
304358
// Set switches
305359
mSwitchAutoEditor.setChecked(SettingManager.autoEnterEditor());
306360
mSwitchRandomizeDelay.setChecked(SettingManager.isRandomizeDelay());
361+
mSwitchSensitiveWord.setChecked(SettingManager.isSensitiveWordFilterEnabled());
362+
363+
// Display dark mode summary
364+
int darkMode = SettingManager.getDarkMode();
365+
if (darkMode == SettingManager.DARK_MODE_LIGHT) {
366+
mTvDarkModeSummary.setText(getString(R.string.dark_mode_summary_off));
367+
} else if (darkMode == SettingManager.DARK_MODE_DARK) {
368+
mTvDarkModeSummary.setText(getString(R.string.dark_mode_summary_on));
369+
} else {
370+
mTvDarkModeSummary.setText(getString(R.string.dark_mode_summary_system));
371+
}
307372

308373
// Display number column
309374
// mTvNumberColumn.setText(TextUtils.isEmpty(numberColumn) ? "未选择" : numberColumn);

0 commit comments

Comments
 (0)