diff --git a/app/src/androidTest/java/com/smsreceive/app/SmsProviderInstrumentedTest.java b/app/src/androidTest/java/com/smsreceive/app/sms/SmsProviderInstrumentedTest.java similarity index 95% rename from app/src/androidTest/java/com/smsreceive/app/SmsProviderInstrumentedTest.java rename to app/src/androidTest/java/com/smsreceive/app/sms/SmsProviderInstrumentedTest.java index 0d9761d..b460c18 100644 --- a/app/src/androidTest/java/com/smsreceive/app/SmsProviderInstrumentedTest.java +++ b/app/src/androidTest/java/com/smsreceive/app/sms/SmsProviderInstrumentedTest.java @@ -1,4 +1,4 @@ -package com.smsreceive.app; +package com.smsreceive.app.sms; import android.content.Context; import android.util.Log; diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c3a4ad5..1300a6f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,7 +15,7 @@ android:supportsRtl="true" android:theme="@style/AppTheme"> @@ -25,7 +25,7 @@ @@ -35,7 +35,7 @@ @@ -46,12 +46,12 @@ diff --git a/app/src/main/java/com/smsreceive/app/FeishuWebhookPushResult.java b/app/src/main/java/com/smsreceive/app/FeishuWebhookPushResult.java deleted file mode 100644 index 0d66fdf..0000000 --- a/app/src/main/java/com/smsreceive/app/FeishuWebhookPushResult.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.smsreceive.app; - -final class FeishuWebhookPushResult { - static final String STATUS_SUCCESS = "success"; - static final String STATUS_DISABLED = "disabled"; - static final String STATUS_MISSING_CONFIG = "missing_config"; - static final String STATUS_SIGN_ERROR = "sign_error"; - static final String STATUS_NETWORK_ERROR = "network_error"; - static final String STATUS_TIMEOUT = "timeout"; - static final String STATUS_HTTP_ERROR = "http_error"; - static final String STATUS_INVALID_JSON = "invalid_json"; - static final String STATUS_API_ERROR = "api_error"; - - final boolean success; - final String status; - final String message; - final int httpStatus; - final int apiCode; - final long timeMillis; - - private FeishuWebhookPushResult( - boolean success, - String status, - String message, - int httpStatus, - int apiCode, - long timeMillis) { - this.success = success; - this.status = status == null ? "" : status; - this.message = message == null ? "" : message; - this.httpStatus = httpStatus; - this.apiCode = apiCode; - this.timeMillis = timeMillis; - } - - static FeishuWebhookPushResult success(String message) { - return new FeishuWebhookPushResult(true, STATUS_SUCCESS, message, 200, 0, System.currentTimeMillis()); - } - - static FeishuWebhookPushResult failure(String status, String message) { - return failure(status, message, 0, 0); - } - - static FeishuWebhookPushResult failure(String status, String message, int httpStatus, int apiCode) { - return new FeishuWebhookPushResult(false, status, message, httpStatus, apiCode, System.currentTimeMillis()); - } -} diff --git a/app/src/main/java/com/smsreceive/app/FeishuWebhookClient.java b/app/src/main/java/com/smsreceive/app/feishu/FeishuWebhookClient.java similarity index 93% rename from app/src/main/java/com/smsreceive/app/FeishuWebhookClient.java rename to app/src/main/java/com/smsreceive/app/feishu/FeishuWebhookClient.java index ab29327..7282305 100644 --- a/app/src/main/java/com/smsreceive/app/FeishuWebhookClient.java +++ b/app/src/main/java/com/smsreceive/app/feishu/FeishuWebhookClient.java @@ -1,4 +1,4 @@ -package com.smsreceive.app; +package com.smsreceive.app.feishu; import android.content.Context; import android.content.Intent; @@ -11,6 +11,8 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import com.smsreceive.app.sms.CaptureResult; + import java.io.IOException; import java.net.SocketTimeoutException; import java.nio.charset.StandardCharsets; @@ -29,7 +31,7 @@ import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; -final class FeishuWebhookClient { +public final class FeishuWebhookClient { private static final String TAG = "[SMS]SmsReceive"; private static final String WEBHOOK_URL_PREFIX = "https://open.feishu.cn/open-apis/bot/v2/hook/"; private static final char[] BASE64_TABLE = @@ -47,7 +49,7 @@ final class FeishuWebhookClient { private FeishuWebhookClient() { } - static void pushCaptureResultAsync(Context context, CaptureResult result) { + public static void pushCaptureResultAsync(Context context, CaptureResult result) { if (context == null || result == null) { return; } @@ -76,7 +78,7 @@ final class FeishuWebhookClient { pushMarkdownAsync(appContext, markdown, result); } - static void pushMarkdownAsync(Context context, String markdownContent) { + public static void pushMarkdownAsync(Context context, String markdownContent) { pushMarkdownAsync(context, markdownContent, null); } @@ -98,7 +100,7 @@ final class FeishuWebhookClient { }); } - static FeishuWebhookPushResult pushMarkdown( + public static FeishuWebhookPushResult pushMarkdown( FeishuWebhookConfigStore.Config config, String markdownContent, long timestampSeconds) { @@ -157,14 +159,14 @@ final class FeishuWebhookClient { } } - static String generateSign(String secret, long timestampSeconds) throws GeneralSecurityException { + public static String generateSign(String secret, long timestampSeconds) throws GeneralSecurityException { String stringToSign = timestampSeconds + "\n" + (secret == null ? "" : secret); Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(stringToSign.getBytes(StandardCharsets.UTF_8), "HmacSHA256")); return base64EncodeNoWrap(mac.doFinal(new byte[0])); } - static String buildRequestJson(String markdownContent, long timestampSeconds, String sign) throws JSONException { + public static String buildRequestJson(String markdownContent, long timestampSeconds, String sign) throws JSONException { JSONObject markdown = new JSONObject() .put("tag", "markdown") .put("content", markdownContent == null ? "" : markdownContent); @@ -178,7 +180,7 @@ final class FeishuWebhookClient { .toString(); } - static FeishuWebhookPushResult parseResponse(String responseBody) { + public static FeishuWebhookPushResult parseResponse(String responseBody) { try { JSONObject json = new JSONObject(responseBody == null ? "" : responseBody); int code = json.optInt("code", Integer.MIN_VALUE); @@ -198,15 +200,15 @@ final class FeishuWebhookClient { } } - static String buildWebhookUrl(String webhookId) { + public static String buildWebhookUrl(String webhookId) { return WEBHOOK_URL_PREFIX + (webhookId == null ? "" : webhookId.trim()); } - static String buildMarkdownFromCapture(CaptureResult result) { + public static String buildMarkdownFromCapture(CaptureResult result) { return buildMarkdownFromCapture(result, new FeishuWebhookConfigStore.Config(false, "", "", false, false)); } - static String buildMarkdownFromCapture(CaptureResult result, FeishuWebhookConfigStore.Config config) { + public static String buildMarkdownFromCapture(CaptureResult result, FeishuWebhookConfigStore.Config config) { boolean filterCode = config != null && config.filterVerificationCode; boolean includeFullBody = config == null || !filterCode || config.sendFullBodyDebug; StringBuilder builder = new StringBuilder(); diff --git a/app/src/main/java/com/smsreceive/app/FeishuWebhookConfigStore.java b/app/src/main/java/com/smsreceive/app/feishu/FeishuWebhookConfigStore.java similarity index 89% rename from app/src/main/java/com/smsreceive/app/FeishuWebhookConfigStore.java rename to app/src/main/java/com/smsreceive/app/feishu/FeishuWebhookConfigStore.java index 2774e1f..fc1975d 100644 --- a/app/src/main/java/com/smsreceive/app/FeishuWebhookConfigStore.java +++ b/app/src/main/java/com/smsreceive/app/feishu/FeishuWebhookConfigStore.java @@ -1,9 +1,11 @@ -package com.smsreceive.app; +package com.smsreceive.app.feishu; import android.content.Context; import android.content.SharedPreferences; import android.util.Log; +import com.smsreceive.app.sms.CaptureResult; + import org.json.JSONException; import org.json.JSONObject; @@ -16,8 +18,8 @@ import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; -final class FeishuWebhookConfigStore { - static final String ACTION_PUSH_UPDATED = "com.smsreceive.app.ACTION_FEISHU_PUSH_UPDATED"; +public final class FeishuWebhookConfigStore { + public static final String ACTION_PUSH_UPDATED = "com.smsreceive.app.ACTION_FEISHU_PUSH_UPDATED"; private static final String TAG = "[SMS]SmsReceive"; private static final String PREFS = "feishu_webhook"; @@ -43,7 +45,7 @@ final class FeishuWebhookConfigStore { private FeishuWebhookConfigStore() { } - static Config loadConfig(Context context) { + public static Config loadConfig(Context context) { ensureDefaultConfigFile(context); File file = configFile(context); if (!file.exists()) { @@ -59,7 +61,7 @@ final class FeishuWebhookConfigStore { } } - static void saveConfig( + public static void saveConfig( Context context, boolean enabled, String webhookId, @@ -82,7 +84,7 @@ final class FeishuWebhookConfigStore { } } - static void saveLastResult(Context context, FeishuWebhookPushResult result) { + public static void saveLastResult(Context context, FeishuWebhookPushResult result) { preferences(context).edit() .putLong(KEY_LAST_TIME, result.timeMillis) .putBoolean(KEY_LAST_SUCCESS, result.success) @@ -136,7 +138,7 @@ final class FeishuWebhookConfigStore { + ", smsKey=" + smsKey); } - static LastResult loadLastResult(Context context) { + public static LastResult loadLastResult(Context context) { SharedPreferences prefs = preferences(context); return new LastResult( prefs.getLong(KEY_LAST_TIME, 0L), @@ -147,14 +149,14 @@ final class FeishuWebhookConfigStore { prefs.getInt(KEY_LAST_API_CODE, 0)); } - static LastPushedSms loadLastPushedSms(Context context) { + public static LastPushedSms loadLastPushedSms(Context context) { SharedPreferences prefs = preferences(context); return new LastPushedSms( prefs.getLong(KEY_LAST_PUSHED_SMS_RECEIVED_SECOND, 0L), prefs.getString(KEY_LAST_PUSHED_SMS_KEY, "")); } - static String maskSecret(String secret) { + public static String maskSecret(String secret) { if (isEmpty(secret)) { return ""; } @@ -164,15 +166,15 @@ final class FeishuWebhookConfigStore { return secret.substring(0, 3) + "***" + secret.substring(secret.length() - 3); } - static String configPath(Context context) { + public static String configPath(Context context) { return configFile(context).getAbsolutePath(); } - static String defaultConfigPath(Context context) { + public static String defaultConfigPath(Context context) { return defaultConfigFile(context).getAbsolutePath(); } - static String configTemplate() { + public static String configTemplate() { try { return configToJson(defaultConfig()).toString(2); } catch (JSONException e) { @@ -289,14 +291,14 @@ final class FeishuWebhookConfigStore { + Integer.toHexString((result.body == null ? "" : result.body).hashCode()); } - static final class Config { - final boolean enabled; - final String webhookId; - final String secret; - final boolean sendFullBodyDebug; - final boolean filterVerificationCode; + public static final class Config { + public final boolean enabled; + public final String webhookId; + public final String secret; + public final boolean sendFullBodyDebug; + public final boolean filterVerificationCode; - Config( + public Config( boolean enabled, String webhookId, String secret, @@ -309,22 +311,22 @@ final class FeishuWebhookConfigStore { this.filterVerificationCode = filterVerificationCode; } - boolean hasWebhookId() { + public boolean hasWebhookId() { return !isEmpty(webhookId); } - boolean hasSecret() { + public boolean hasSecret() { return !isEmpty(secret); } } - static final class LastResult { - final long timeMillis; - final boolean success; - final String status; - final String message; - final int httpStatus; - final int apiCode; + public static final class LastResult { + public final long timeMillis; + public final boolean success; + public final String status; + public final String message; + public final int httpStatus; + public final int apiCode; LastResult(long timeMillis, boolean success, String status, String message, int httpStatus, int apiCode) { this.timeMillis = timeMillis; @@ -336,9 +338,9 @@ final class FeishuWebhookConfigStore { } } - static final class LastPushedSms { - final long receivedSecond; - final String smsKey; + public static final class LastPushedSms { + public final long receivedSecond; + public final String smsKey; LastPushedSms(long receivedSecond, String smsKey) { this.receivedSecond = receivedSecond; diff --git a/app/src/main/java/com/smsreceive/app/feishu/FeishuWebhookPushResult.java b/app/src/main/java/com/smsreceive/app/feishu/FeishuWebhookPushResult.java new file mode 100644 index 0000000..232685a --- /dev/null +++ b/app/src/main/java/com/smsreceive/app/feishu/FeishuWebhookPushResult.java @@ -0,0 +1,47 @@ +package com.smsreceive.app.feishu; + +public final class FeishuWebhookPushResult { + public static final String STATUS_SUCCESS = "success"; + public static final String STATUS_DISABLED = "disabled"; + public static final String STATUS_MISSING_CONFIG = "missing_config"; + public static final String STATUS_SIGN_ERROR = "sign_error"; + public static final String STATUS_NETWORK_ERROR = "network_error"; + public static final String STATUS_TIMEOUT = "timeout"; + public static final String STATUS_HTTP_ERROR = "http_error"; + public static final String STATUS_INVALID_JSON = "invalid_json"; + public static final String STATUS_API_ERROR = "api_error"; + + public final boolean success; + public final String status; + public final String message; + public final int httpStatus; + public final int apiCode; + public final long timeMillis; + + private FeishuWebhookPushResult( + boolean success, + String status, + String message, + int httpStatus, + int apiCode, + long timeMillis) { + this.success = success; + this.status = status == null ? "" : status; + this.message = message == null ? "" : message; + this.httpStatus = httpStatus; + this.apiCode = apiCode; + this.timeMillis = timeMillis; + } + + public static FeishuWebhookPushResult success(String message) { + return new FeishuWebhookPushResult(true, STATUS_SUCCESS, message, 200, 0, System.currentTimeMillis()); + } + + public static FeishuWebhookPushResult failure(String status, String message) { + return failure(status, message, 0, 0); + } + + public static FeishuWebhookPushResult failure(String status, String message, int httpStatus, int apiCode) { + return new FeishuWebhookPushResult(false, status, message, httpStatus, apiCode, System.currentTimeMillis()); + } +} diff --git a/app/src/main/java/com/smsreceive/app/BootReceiver.java b/app/src/main/java/com/smsreceive/app/keepalive/BootReceiver.java similarity index 98% rename from app/src/main/java/com/smsreceive/app/BootReceiver.java rename to app/src/main/java/com/smsreceive/app/keepalive/BootReceiver.java index 39108b7..db9d459 100644 --- a/app/src/main/java/com/smsreceive/app/BootReceiver.java +++ b/app/src/main/java/com/smsreceive/app/keepalive/BootReceiver.java @@ -1,4 +1,4 @@ -package com.smsreceive.app; +package com.smsreceive.app.keepalive; import android.content.BroadcastReceiver; import android.content.Context; diff --git a/app/src/main/java/com/smsreceive/app/KeepAliveDatabase.java b/app/src/main/java/com/smsreceive/app/keepalive/KeepAliveDatabase.java similarity index 94% rename from app/src/main/java/com/smsreceive/app/KeepAliveDatabase.java rename to app/src/main/java/com/smsreceive/app/keepalive/KeepAliveDatabase.java index d4fab6e..3ed49a2 100644 --- a/app/src/main/java/com/smsreceive/app/KeepAliveDatabase.java +++ b/app/src/main/java/com/smsreceive/app/keepalive/KeepAliveDatabase.java @@ -1,4 +1,4 @@ -package com.smsreceive.app; +package com.smsreceive.app.keepalive; import android.content.ContentValues; import android.content.Context; @@ -11,7 +11,7 @@ import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; -final class KeepAliveDatabase { +public final class KeepAliveDatabase { private static final String TAG = "[SMS]SmsReceive"; private static final String DATABASE_NAME = "sms_keep_alive.db"; private static final int DATABASE_VERSION = 1; @@ -23,7 +23,7 @@ final class KeepAliveDatabase { private KeepAliveDatabase() { } - static long writeLastActiveTime(Context context) { + public static long writeLastActiveTime(Context context) { long now = System.currentTimeMillis(); SQLiteDatabase database = helper(context).getWritableDatabase(); ContentValues values = new ContentValues(); @@ -35,7 +35,7 @@ final class KeepAliveDatabase { return now; } - static long readLastActiveTime(Context context) { + public static long readLastActiveTime(Context context) { SQLiteDatabase database = helper(context).getReadableDatabase(); try (Cursor cursor = database.query( TABLE_META, diff --git a/app/src/main/java/com/smsreceive/app/KeepAliveNotification.java b/app/src/main/java/com/smsreceive/app/keepalive/KeepAliveNotification.java similarity index 96% rename from app/src/main/java/com/smsreceive/app/KeepAliveNotification.java rename to app/src/main/java/com/smsreceive/app/keepalive/KeepAliveNotification.java index 4d44b08..7e5dedd 100644 --- a/app/src/main/java/com/smsreceive/app/KeepAliveNotification.java +++ b/app/src/main/java/com/smsreceive/app/keepalive/KeepAliveNotification.java @@ -1,4 +1,4 @@ -package com.smsreceive.app; +package com.smsreceive.app.keepalive; import android.app.Notification; import android.app.NotificationChannel; @@ -9,6 +9,8 @@ import android.content.Intent; import android.os.Build; import android.util.Log; +import com.smsreceive.app.ui.MainActivity; + final class KeepAliveNotification { static final int NOTIFICATION_ID = 2101; private static final String TAG = "[SMS]SmsReceive"; diff --git a/app/src/main/java/com/smsreceive/app/KeepAliveStateStore.java b/app/src/main/java/com/smsreceive/app/keepalive/KeepAliveStateStore.java similarity index 79% rename from app/src/main/java/com/smsreceive/app/KeepAliveStateStore.java rename to app/src/main/java/com/smsreceive/app/keepalive/KeepAliveStateStore.java index a1d75c0..74de526 100644 --- a/app/src/main/java/com/smsreceive/app/KeepAliveStateStore.java +++ b/app/src/main/java/com/smsreceive/app/keepalive/KeepAliveStateStore.java @@ -1,11 +1,11 @@ -package com.smsreceive.app; +package com.smsreceive.app.keepalive; import android.content.Context; import android.content.SharedPreferences; import android.text.TextUtils; import android.util.Log; -final class KeepAliveStateStore { +public final class KeepAliveStateStore { private static final String TAG = "[SMS]SmsReceive"; private static final String PREFS = "sms_keep_alive"; @@ -23,14 +23,14 @@ final class KeepAliveStateStore { private KeepAliveStateStore() { } - static void setEnabledByUser(Context context, boolean enabled) { + public static void setEnabledByUser(Context context, boolean enabled) { Log.d(TAG, "KeepAliveStateStore.setEnabledByUser enabled=" + enabled); preferences(context).edit() .putBoolean(KEY_ENABLED_BY_USER, enabled) .apply(); } - static void recordServiceStarted(Context context) { + public static void recordServiceStarted(Context context) { long now = System.currentTimeMillis(); Log.d(TAG, "KeepAliveStateStore.recordServiceStarted time=" + now); preferences(context).edit() @@ -40,7 +40,7 @@ final class KeepAliveStateStore { .apply(); } - static void recordServiceStopped(Context context, String reason) { + public static void recordServiceStopped(Context context, String reason) { Log.d(TAG, "KeepAliveStateStore.recordServiceStopped reason=" + reason); preferences(context).edit() .putBoolean(KEY_SERVICE_RUNNING, false) @@ -48,7 +48,7 @@ final class KeepAliveStateStore { .apply(); } - static void recordHeartbeat(Context context) { + public static void recordHeartbeat(Context context) { Log.d(TAG, "KeepAliveStateStore.recordHeartbeat"); preferences(context).edit() .putBoolean(KEY_SERVICE_RUNNING, true) @@ -56,7 +56,7 @@ final class KeepAliveStateStore { .apply(); } - static void recordBootEvent(Context context, String action) { + public static void recordBootEvent(Context context, String action) { long now = System.currentTimeMillis(); Log.d(TAG, "KeepAliveStateStore.recordBootEvent action=" + action + ", time=" + now); preferences(context).edit() @@ -65,7 +65,7 @@ final class KeepAliveStateStore { .apply(); } - static void recordServiceStartFailure(Context context, String reason) { + public static void recordServiceStartFailure(Context context, String reason) { Log.w(TAG, "KeepAliveStateStore.recordServiceStartFailure reason=" + reason); preferences(context).edit() .putBoolean(KEY_SERVICE_RUNNING, false) @@ -73,39 +73,39 @@ final class KeepAliveStateStore { .apply(); } - static void setManualAutostartConfirmed(Context context, boolean confirmed) { + public static void setManualAutostartConfirmed(Context context, boolean confirmed) { Log.d(TAG, "KeepAliveStateStore.setManualAutostartConfirmed confirmed=" + confirmed); preferences(context).edit() .putBoolean(KEY_MANUAL_AUTOSTART_CONFIRMED, confirmed) .apply(); } - static void setManualBatteryUnrestrictedConfirmed(Context context, boolean confirmed) { + public static void setManualBatteryUnrestrictedConfirmed(Context context, boolean confirmed) { Log.d(TAG, "KeepAliveStateStore.setManualBatteryUnrestrictedConfirmed confirmed=" + confirmed); preferences(context).edit() .putBoolean(KEY_MANUAL_BATTERY_UNRESTRICTED_CONFIRMED, confirmed) .apply(); } - static void setBatteryOptimizationIgnored(Context context, boolean ignored) { + public static void setBatteryOptimizationIgnored(Context context, boolean ignored) { Log.d(TAG, "KeepAliveStateStore.setBatteryOptimizationIgnored ignored=" + ignored); preferences(context).edit() .putBoolean(KEY_BATTERY_OPTIMIZATION_IGNORED, ignored) .apply(); } - static void setToastOnDatabaseWrite(Context context, boolean enabled) { + public static void setToastOnDatabaseWrite(Context context, boolean enabled) { Log.d(TAG, "KeepAliveStateStore.setToastOnDatabaseWrite enabled=" + enabled); preferences(context).edit() .putBoolean(KEY_TOAST_ON_DATABASE_WRITE, enabled) .apply(); } - static boolean isToastOnDatabaseWriteEnabled(Context context) { + public static boolean isToastOnDatabaseWriteEnabled(Context context) { return preferences(context).getBoolean(KEY_TOAST_ON_DATABASE_WRITE, false); } - static State load(Context context) { + public static State load(Context context) { SharedPreferences prefs = preferences(context); return new State( prefs.getBoolean(KEY_ENABLED_BY_USER, false), @@ -128,17 +128,17 @@ final class KeepAliveStateStore { return TextUtils.isEmpty(value) ? "" : value; } - static final class State { - final boolean enabledByUser; - final boolean serviceRunning; - final long lastHeartbeatMillis; - final String lastBootEvent; - final long lastBootTimeMillis; - final String lastServiceStartFailure; - final boolean manualAutostartConfirmed; - final boolean manualBatteryUnrestrictedConfirmed; - final boolean batteryOptimizationIgnored; - final boolean toastOnDatabaseWrite; + public static final class State { + public final boolean enabledByUser; + public final boolean serviceRunning; + public final long lastHeartbeatMillis; + public final String lastBootEvent; + public final long lastBootTimeMillis; + public final String lastServiceStartFailure; + public final boolean manualAutostartConfirmed; + public final boolean manualBatteryUnrestrictedConfirmed; + public final boolean batteryOptimizationIgnored; + public final boolean toastOnDatabaseWrite; State( boolean enabledByUser, diff --git a/app/src/main/java/com/smsreceive/app/SmsKeepAliveService.java b/app/src/main/java/com/smsreceive/app/keepalive/SmsKeepAliveService.java similarity index 95% rename from app/src/main/java/com/smsreceive/app/SmsKeepAliveService.java rename to app/src/main/java/com/smsreceive/app/keepalive/SmsKeepAliveService.java index 8ec1138..0ebaa68 100644 --- a/app/src/main/java/com/smsreceive/app/SmsKeepAliveService.java +++ b/app/src/main/java/com/smsreceive/app/keepalive/SmsKeepAliveService.java @@ -1,4 +1,4 @@ -package com.smsreceive.app; +package com.smsreceive.app.keepalive; import android.app.Service; import android.content.Context; @@ -10,6 +10,8 @@ import android.os.Looper; import android.util.Log; import android.widget.Toast; +import com.smsreceive.app.sms.SmsCaptureStore; + import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; @@ -41,7 +43,7 @@ public final class SmsKeepAliveService extends Service { } }; - static void start(Context context) { + public static void start(Context context) { Log.d(TAG, "SmsKeepAliveService.start requested sdk=" + Build.VERSION.SDK_INT); Intent intent = new Intent(context, SmsKeepAliveService.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -51,7 +53,7 @@ public final class SmsKeepAliveService extends Service { } } - static void stop(Context context) { + public static void stop(Context context) { Log.d(TAG, "SmsKeepAliveService.stop requested"); context.stopService(new Intent(context, SmsKeepAliveService.class)); } diff --git a/app/src/main/java/com/smsreceive/app/SmsPollingService.java b/app/src/main/java/com/smsreceive/app/keepalive/SmsPollingService.java similarity index 94% rename from app/src/main/java/com/smsreceive/app/SmsPollingService.java rename to app/src/main/java/com/smsreceive/app/keepalive/SmsPollingService.java index 0ec0aa9..d285abb 100644 --- a/app/src/main/java/com/smsreceive/app/SmsPollingService.java +++ b/app/src/main/java/com/smsreceive/app/keepalive/SmsPollingService.java @@ -1,4 +1,4 @@ -package com.smsreceive.app; +package com.smsreceive.app.keepalive; import android.Manifest; import android.app.Service; @@ -12,6 +12,11 @@ import android.os.Looper; import android.util.Log; import android.widget.Toast; +import com.smsreceive.app.feishu.FeishuWebhookClient; +import com.smsreceive.app.sms.CaptureResult; +import com.smsreceive.app.sms.SmsCaptureStore; +import com.smsreceive.app.sms.SmsInboxReader; + public final class SmsPollingService extends Service { private static final String TAG = "[SMS]SmsReceive"; private static final String SOURCE_INBOX_POLLING = "sms_inbox_polling"; @@ -29,7 +34,7 @@ public final class SmsPollingService extends Service { } }; - static void start(Context context) { + public static void start(Context context) { Log.d(TAG, "SmsPollingService.start requested sdk=" + Build.VERSION.SDK_INT); long startTimeMillis = System.currentTimeMillis() - 2_000L; SmsPollingStateStore.recordStarted(context, startTimeMillis); @@ -42,7 +47,7 @@ public final class SmsPollingService extends Service { } } - static void stop(Context context) { + public static void stop(Context context) { Log.d(TAG, "SmsPollingService.stop requested"); SmsPollingStateStore.recordStopped(context, "用户停止轮询"); context.stopService(new Intent(context, SmsPollingService.class)); diff --git a/app/src/main/java/com/smsreceive/app/SmsPollingStateStore.java b/app/src/main/java/com/smsreceive/app/keepalive/SmsPollingStateStore.java similarity index 81% rename from app/src/main/java/com/smsreceive/app/SmsPollingStateStore.java rename to app/src/main/java/com/smsreceive/app/keepalive/SmsPollingStateStore.java index 6cc23db..daa5521 100644 --- a/app/src/main/java/com/smsreceive/app/SmsPollingStateStore.java +++ b/app/src/main/java/com/smsreceive/app/keepalive/SmsPollingStateStore.java @@ -1,11 +1,11 @@ -package com.smsreceive.app; +package com.smsreceive.app.keepalive; import android.content.Context; import android.content.SharedPreferences; import android.text.TextUtils; import android.util.Log; -final class SmsPollingStateStore { +public final class SmsPollingStateStore { private static final String TAG = "[SMS]SmsReceive"; private static final String PREFS = "sms_polling"; private static final String KEY_ENABLED_BY_USER = "enabled_by_user"; @@ -22,7 +22,7 @@ final class SmsPollingStateStore { private SmsPollingStateStore() { } - static void recordStarted(Context context, long startTimeMillis) { + public static void recordStarted(Context context, long startTimeMillis) { Log.d(TAG, "SmsPollingStateStore.recordStarted startTime=" + startTimeMillis); preferences(context).edit() .putBoolean(KEY_ENABLED_BY_USER, true) @@ -32,7 +32,7 @@ final class SmsPollingStateStore { .apply(); } - static void recordStopped(Context context, String reason) { + public static void recordStopped(Context context, String reason) { Log.d(TAG, "SmsPollingStateStore.recordStopped reason=" + reason); preferences(context).edit() .putBoolean(KEY_ENABLED_BY_USER, false) @@ -41,7 +41,7 @@ final class SmsPollingStateStore { .apply(); } - static void recordServiceStopped(Context context, String reason) { + public static void recordServiceStopped(Context context, String reason) { Log.d(TAG, "SmsPollingStateStore.recordServiceStopped reason=" + reason); preferences(context).edit() .putBoolean(KEY_RUNNING, false) @@ -49,13 +49,13 @@ final class SmsPollingStateStore { .apply(); } - static void recordServiceRunning(Context context) { + public static void recordServiceRunning(Context context) { preferences(context).edit() .putBoolean(KEY_RUNNING, true) .apply(); } - static void recordHit(Context context, long smsId, long hitTimeMillis) { + public static void recordHit(Context context, long smsId, long hitTimeMillis) { Log.d(TAG, "SmsPollingStateStore.recordHit id=" + smsId + ", time=" + hitTimeMillis); preferences(context).edit() .putLong(KEY_LAST_HIT_ID, smsId) @@ -64,7 +64,7 @@ final class SmsPollingStateStore { .apply(); } - static void setIntervalSeconds(Context context, int seconds) { + public static void setIntervalSeconds(Context context, int seconds) { int safeSeconds = clampIntervalSeconds(seconds); Log.d(TAG, "SmsPollingStateStore.setIntervalSeconds seconds=" + safeSeconds); preferences(context).edit() @@ -72,11 +72,11 @@ final class SmsPollingStateStore { .apply(); } - static int getIntervalSeconds(Context context) { + public static int getIntervalSeconds(Context context) { return clampIntervalSeconds(preferences(context).getInt(KEY_INTERVAL_SECONDS, DEFAULT_INTERVAL_SECONDS)); } - static State load(Context context) { + public static State load(Context context) { SharedPreferences prefs = preferences(context); return new State( prefs.getBoolean(KEY_ENABLED_BY_USER, false), @@ -100,14 +100,14 @@ final class SmsPollingStateStore { return Math.max(MIN_INTERVAL_SECONDS, Math.min(seconds, MAX_INTERVAL_SECONDS)); } - static final class State { - final boolean enabledByUser; - final boolean running; - final long startTimeMillis; - final long lastHitId; - final long lastHitTimeMillis; - final String lastFailure; - final int intervalSeconds; + public static final class State { + public final boolean enabledByUser; + public final boolean running; + public final long startTimeMillis; + public final long lastHitId; + public final long lastHitTimeMillis; + public final String lastFailure; + public final int intervalSeconds; State( boolean enabledByUser, diff --git a/app/src/main/java/com/smsreceive/app/CaptureResult.java b/app/src/main/java/com/smsreceive/app/sms/CaptureResult.java similarity index 77% rename from app/src/main/java/com/smsreceive/app/CaptureResult.java rename to app/src/main/java/com/smsreceive/app/sms/CaptureResult.java index 39eb183..a2f64e8 100644 --- a/app/src/main/java/com/smsreceive/app/CaptureResult.java +++ b/app/src/main/java/com/smsreceive/app/sms/CaptureResult.java @@ -1,15 +1,15 @@ -package com.smsreceive.app; +package com.smsreceive.app.sms; -final class CaptureResult { - static final long UNKNOWN_SMS_PROVIDER_ID = -1L; +public final class CaptureResult { + public static final long UNKNOWN_SMS_PROVIDER_ID = -1L; - final long receivedAtMillis; - final long smsProviderId; - final String sender; - final String body; - final VerificationCodeParser.ParseResult parseResult; - final String source; - final String failureReason; + public final long receivedAtMillis; + public final long smsProviderId; + public final String sender; + public final String body; + public final VerificationCodeParser.ParseResult parseResult; + public final String source; + public final String failureReason; private CaptureResult( long receivedAtMillis, @@ -28,7 +28,7 @@ final class CaptureResult { this.failureReason = failureReason == null ? "" : failureReason; } - static CaptureResult success( + public static CaptureResult success( long receivedAtMillis, String sender, String body, @@ -37,7 +37,7 @@ final class CaptureResult { return success(receivedAtMillis, UNKNOWN_SMS_PROVIDER_ID, sender, body, parseResult, source); } - static CaptureResult success( + public static CaptureResult success( long receivedAtMillis, long smsProviderId, String sender, @@ -47,7 +47,7 @@ final class CaptureResult { return new CaptureResult(receivedAtMillis, smsProviderId, sender, body, parseResult, source, ""); } - static CaptureResult failure( + public static CaptureResult failure( long receivedAtMillis, String sender, String body, @@ -56,7 +56,7 @@ final class CaptureResult { return failure(receivedAtMillis, UNKNOWN_SMS_PROVIDER_ID, sender, body, source, failureReason); } - static CaptureResult failure( + public static CaptureResult failure( long receivedAtMillis, long smsProviderId, String sender, diff --git a/app/src/main/java/com/smsreceive/app/SmsCaptureStore.java b/app/src/main/java/com/smsreceive/app/sms/SmsCaptureStore.java similarity index 83% rename from app/src/main/java/com/smsreceive/app/SmsCaptureStore.java rename to app/src/main/java/com/smsreceive/app/sms/SmsCaptureStore.java index 4b2c111..2aaa797 100644 --- a/app/src/main/java/com/smsreceive/app/SmsCaptureStore.java +++ b/app/src/main/java/com/smsreceive/app/sms/SmsCaptureStore.java @@ -1,12 +1,12 @@ -package com.smsreceive.app; +package com.smsreceive.app.sms; import android.content.Context; import android.content.SharedPreferences; import android.text.TextUtils; import android.util.Log; -final class SmsCaptureStore { - static final String ACTION_CAPTURE_UPDATED = "com.smsreceive.app.ACTION_CAPTURE_UPDATED"; +public final class SmsCaptureStore { + public static final String ACTION_CAPTURE_UPDATED = "com.smsreceive.app.ACTION_CAPTURE_UPDATED"; private static final String TAG = "[SMS]SmsReceive"; private static final String PREFS = "sms_capture"; @@ -25,7 +25,7 @@ final class SmsCaptureStore { private SmsCaptureStore() { } - static void save(Context context, CaptureResult result) { + public static void save(Context context, CaptureResult result) { VerificationCodeParser.ParseResult parse = result.parseResult; Log.d(TAG, "SmsCaptureStore.save source=" + result.source + ", success=" + parse.success @@ -53,7 +53,7 @@ final class SmsCaptureStore { editor.apply(); } - static StoredCapture load(Context context) { + public static StoredCapture load(Context context) { SharedPreferences prefs = preferences(context); return new StoredCapture( prefs.getLong(KEY_TIME, 0L), @@ -66,12 +66,12 @@ final class SmsCaptureStore { prefs.getString(KEY_BODY_PREVIEW, "")); } - static void clear(Context context) { + public static void clear(Context context) { Log.d(TAG, "SmsCaptureStore.clear"); preferences(context).edit().clear().apply(); } - static DeliveryDiagnostics loadDeliveryDiagnostics(Context context) { + public static DeliveryDiagnostics loadDeliveryDiagnostics(Context context) { SharedPreferences prefs = preferences(context); return new DeliveryDiagnostics( prefs.getLong(KEY_LAST_BROADCAST_TIME, 0L), @@ -101,15 +101,15 @@ final class SmsCaptureStore { return normalized.length() <= 48 ? normalized : normalized.substring(0, 48) + "..."; } - static final class StoredCapture { - final long timeMillis; - final String sender; - final String code; - final String strategy; - final int confidence; - final String source; - final String failure; - final String bodyPreview; + public static final class StoredCapture { + public final long timeMillis; + public final String sender; + public final String code; + public final String strategy; + public final int confidence; + public final String source; + public final String failure; + public final String bodyPreview; StoredCapture( long timeMillis, @@ -131,10 +131,10 @@ final class SmsCaptureStore { } } - static final class DeliveryDiagnostics { - final long lastBroadcastTimeMillis; - final long lastInboxTimeMillis; - final String lastInboxSource; + public static final class DeliveryDiagnostics { + public final long lastBroadcastTimeMillis; + public final long lastInboxTimeMillis; + public final String lastInboxSource; DeliveryDiagnostics(long lastBroadcastTimeMillis, long lastInboxTimeMillis, String lastInboxSource) { this.lastBroadcastTimeMillis = lastBroadcastTimeMillis; @@ -142,7 +142,7 @@ final class SmsCaptureStore { this.lastInboxSource = lastInboxSource == null ? "" : lastInboxSource; } - boolean inboxNewerThanBroadcast() { + public boolean inboxNewerThanBroadcast() { return lastInboxTimeMillis > 0L && lastInboxTimeMillis > lastBroadcastTimeMillis; } } diff --git a/app/src/main/java/com/smsreceive/app/SmsInboxReader.java b/app/src/main/java/com/smsreceive/app/sms/SmsInboxReader.java similarity index 92% rename from app/src/main/java/com/smsreceive/app/SmsInboxReader.java rename to app/src/main/java/com/smsreceive/app/sms/SmsInboxReader.java index 9b12186..9adee69 100644 --- a/app/src/main/java/com/smsreceive/app/SmsInboxReader.java +++ b/app/src/main/java/com/smsreceive/app/sms/SmsInboxReader.java @@ -1,4 +1,4 @@ -package com.smsreceive.app; +package com.smsreceive.app.sms; import android.content.Context; import android.database.Cursor; @@ -11,14 +11,14 @@ import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; -final class SmsInboxReader { +public final class SmsInboxReader { private static final String TAG = "[SMS]SmsReceive"; private static final Uri SMS_INBOX_URI = Uri.parse("content://sms/inbox"); private SmsInboxReader() { } - static InboxResult readLatest(Context context) { + public static InboxResult readLatest(Context context) { String[] projection = { Telephony.Sms._ID, Telephony.Sms.ADDRESS, @@ -64,7 +64,7 @@ final class SmsInboxReader { } } - static int logRecentMessages(Context context, int limit) { + public static int logRecentMessages(Context context, int limit) { Uri uri = Telephony.Sms.CONTENT_URI; String[] projection = { Telephony.Sms._ID, @@ -116,11 +116,11 @@ final class SmsInboxReader { } } - static RecentCodeResult findLatestVerificationCode(Context context, int limit) { + public static RecentCodeResult findLatestVerificationCode(Context context, int limit) { return findLatestVerificationCode(context, limit, 0L); } - static RecentCodeResult findLatestVerificationCode(Context context, int limit, long minDateMillis) { + public static RecentCodeResult findLatestVerificationCode(Context context, int limit, long minDateMillis) { Uri uri = Telephony.Sms.CONTENT_URI; String[] projection = { Telephony.Sms._ID, @@ -231,13 +231,13 @@ final class SmsInboxReader { } } - static final class InboxResult { - final boolean success; - final long id; - final String sender; - final String body; - final long dateMillis; - final String failureReason; + public static final class InboxResult { + public final boolean success; + public final long id; + public final String sender; + public final String body; + public final long dateMillis; + public final String failureReason; private InboxResult(boolean success, long id, String sender, String body, long dateMillis, String failureReason) { this.success = success; @@ -257,15 +257,15 @@ final class SmsInboxReader { } } - static final class RecentCodeResult { - final boolean success; - final long id; - final String sender; - final String body; - final long dateMillis; - final VerificationCodeParser.ParseResult parseResult; - final int scannedCount; - final String failureReason; + public static final class RecentCodeResult { + public final boolean success; + public final long id; + public final String sender; + public final String body; + public final long dateMillis; + public final VerificationCodeParser.ParseResult parseResult; + public final int scannedCount; + public final String failureReason; private RecentCodeResult( boolean success, diff --git a/app/src/main/java/com/smsreceive/app/SmsMessageReader.java b/app/src/main/java/com/smsreceive/app/sms/SmsMessageReader.java similarity index 98% rename from app/src/main/java/com/smsreceive/app/SmsMessageReader.java rename to app/src/main/java/com/smsreceive/app/sms/SmsMessageReader.java index e28864a..dcac92e 100644 --- a/app/src/main/java/com/smsreceive/app/SmsMessageReader.java +++ b/app/src/main/java/com/smsreceive/app/sms/SmsMessageReader.java @@ -1,4 +1,4 @@ -package com.smsreceive.app; +package com.smsreceive.app.sms; import android.content.Intent; import android.provider.Telephony; diff --git a/app/src/main/java/com/smsreceive/app/SmsReceiver.java b/app/src/main/java/com/smsreceive/app/sms/SmsReceiver.java similarity index 97% rename from app/src/main/java/com/smsreceive/app/SmsReceiver.java rename to app/src/main/java/com/smsreceive/app/sms/SmsReceiver.java index 78c3540..d1a4b6a 100644 --- a/app/src/main/java/com/smsreceive/app/SmsReceiver.java +++ b/app/src/main/java/com/smsreceive/app/sms/SmsReceiver.java @@ -1,4 +1,4 @@ -package com.smsreceive.app; +package com.smsreceive.app.sms; import android.content.BroadcastReceiver; import android.content.Context; @@ -7,6 +7,8 @@ import android.provider.Telephony; import android.util.Log; import android.widget.Toast; +import com.smsreceive.app.feishu.FeishuWebhookClient; + public final class SmsReceiver extends BroadcastReceiver { private static final String TAG = "[SMS]SmsReceive"; private static final String SOURCE_SYSTEM_BROADCAST = "system_sms_broadcast"; diff --git a/app/src/main/java/com/smsreceive/app/VerificationCodeParser.java b/app/src/main/java/com/smsreceive/app/sms/VerificationCodeParser.java similarity index 99% rename from app/src/main/java/com/smsreceive/app/VerificationCodeParser.java rename to app/src/main/java/com/smsreceive/app/sms/VerificationCodeParser.java index a3f5d55..249c063 100644 --- a/app/src/main/java/com/smsreceive/app/VerificationCodeParser.java +++ b/app/src/main/java/com/smsreceive/app/sms/VerificationCodeParser.java @@ -1,4 +1,4 @@ -package com.smsreceive.app; +package com.smsreceive.app.sms; import java.util.ArrayList; import java.util.List; diff --git a/app/src/main/java/com/smsreceive/app/MainActivity.java b/app/src/main/java/com/smsreceive/app/ui/MainActivity.java similarity index 98% rename from app/src/main/java/com/smsreceive/app/MainActivity.java rename to app/src/main/java/com/smsreceive/app/ui/MainActivity.java index 00c5f30..b504616 100644 --- a/app/src/main/java/com/smsreceive/app/MainActivity.java +++ b/app/src/main/java/com/smsreceive/app/ui/MainActivity.java @@ -1,4 +1,4 @@ -package com.smsreceive.app; +package com.smsreceive.app.ui; import android.Manifest; import android.app.Activity; @@ -35,6 +35,19 @@ import android.widget.Switch; import android.widget.TextView; import android.widget.Toast; +import com.smsreceive.app.feishu.FeishuWebhookClient; +import com.smsreceive.app.feishu.FeishuWebhookConfigStore; +import com.smsreceive.app.feishu.FeishuWebhookPushResult; +import com.smsreceive.app.keepalive.KeepAliveDatabase; +import com.smsreceive.app.keepalive.KeepAliveStateStore; +import com.smsreceive.app.keepalive.SmsKeepAliveService; +import com.smsreceive.app.keepalive.SmsPollingService; +import com.smsreceive.app.keepalive.SmsPollingStateStore; +import com.smsreceive.app.sms.CaptureResult; +import com.smsreceive.app.sms.SmsCaptureStore; +import com.smsreceive.app.sms.SmsInboxReader; +import com.smsreceive.app.sms.VerificationCodeParser; + import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; diff --git a/app/src/test/java/com/smsreceive/app/FeishuWebhookClientTest.java b/app/src/test/java/com/smsreceive/app/feishu/FeishuWebhookClientTest.java similarity index 98% rename from app/src/test/java/com/smsreceive/app/FeishuWebhookClientTest.java rename to app/src/test/java/com/smsreceive/app/feishu/FeishuWebhookClientTest.java index 54270d1..5ddcd12 100644 --- a/app/src/test/java/com/smsreceive/app/FeishuWebhookClientTest.java +++ b/app/src/test/java/com/smsreceive/app/feishu/FeishuWebhookClientTest.java @@ -1,4 +1,4 @@ -package com.smsreceive.app; +package com.smsreceive.app.feishu; import org.json.JSONObject; import org.junit.Test; diff --git a/app/src/test/java/com/smsreceive/app/VerificationCodeParserTest.java b/app/src/test/java/com/smsreceive/app/sms/VerificationCodeParserTest.java similarity index 98% rename from app/src/test/java/com/smsreceive/app/VerificationCodeParserTest.java rename to app/src/test/java/com/smsreceive/app/sms/VerificationCodeParserTest.java index d13e32f..6c2cde4 100644 --- a/app/src/test/java/com/smsreceive/app/VerificationCodeParserTest.java +++ b/app/src/test/java/com/smsreceive/app/sms/VerificationCodeParserTest.java @@ -1,4 +1,4 @@ -package com.smsreceive.app; +package com.smsreceive.app.sms; import org.junit.Test; diff --git a/gradle.properties b/gradle.properties index f86ba92..dfc6ee8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,4 +2,3 @@ org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 android.useAndroidX=true android.enableJetifier=false android.injected.testOnly=false -android.aapt2FromMavenOverride=/Users/zouchao/Library/Android/sdk/build-tools/30.0.3/aapt2