main #1
@ -1,4 +1,4 @@
|
|||||||
package com.smsreceive.app;
|
package com.smsreceive.app.sms;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@ -15,7 +15,7 @@
|
|||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".ui.MainActivity"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
@ -25,7 +25,7 @@
|
|||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".SmsReceiver"
|
android:name=".sms.SmsReceiver"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:permission="android.permission.BROADCAST_SMS">
|
android:permission="android.permission.BROADCAST_SMS">
|
||||||
@ -35,7 +35,7 @@
|
|||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".BootReceiver"
|
android:name=".keepalive.BootReceiver"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
@ -46,12 +46,12 @@
|
|||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".SmsKeepAliveService"
|
android:name=".keepalive.SmsKeepAliveService"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".SmsPollingService"
|
android:name=".keepalive.SmsPollingService"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
</application>
|
</application>
|
||||||
|
|||||||
@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.smsreceive.app;
|
package com.smsreceive.app.feishu;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@ -11,6 +11,8 @@ import org.json.JSONArray;
|
|||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import com.smsreceive.app.sms.CaptureResult;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
@ -29,7 +31,7 @@ import okhttp3.Request;
|
|||||||
import okhttp3.RequestBody;
|
import okhttp3.RequestBody;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
|
|
||||||
final class FeishuWebhookClient {
|
public final class FeishuWebhookClient {
|
||||||
private static final String TAG = "[SMS]SmsReceive";
|
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 String WEBHOOK_URL_PREFIX = "https://open.feishu.cn/open-apis/bot/v2/hook/";
|
||||||
private static final char[] BASE64_TABLE =
|
private static final char[] BASE64_TABLE =
|
||||||
@ -47,7 +49,7 @@ final class FeishuWebhookClient {
|
|||||||
private FeishuWebhookClient() {
|
private FeishuWebhookClient() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pushCaptureResultAsync(Context context, CaptureResult result) {
|
public static void pushCaptureResultAsync(Context context, CaptureResult result) {
|
||||||
if (context == null || result == null) {
|
if (context == null || result == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -76,7 +78,7 @@ final class FeishuWebhookClient {
|
|||||||
pushMarkdownAsync(appContext, markdown, result);
|
pushMarkdownAsync(appContext, markdown, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pushMarkdownAsync(Context context, String markdownContent) {
|
public static void pushMarkdownAsync(Context context, String markdownContent) {
|
||||||
pushMarkdownAsync(context, markdownContent, null);
|
pushMarkdownAsync(context, markdownContent, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +100,7 @@ final class FeishuWebhookClient {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static FeishuWebhookPushResult pushMarkdown(
|
public static FeishuWebhookPushResult pushMarkdown(
|
||||||
FeishuWebhookConfigStore.Config config,
|
FeishuWebhookConfigStore.Config config,
|
||||||
String markdownContent,
|
String markdownContent,
|
||||||
long timestampSeconds) {
|
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);
|
String stringToSign = timestampSeconds + "\n" + (secret == null ? "" : secret);
|
||||||
Mac mac = Mac.getInstance("HmacSHA256");
|
Mac mac = Mac.getInstance("HmacSHA256");
|
||||||
mac.init(new SecretKeySpec(stringToSign.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
|
mac.init(new SecretKeySpec(stringToSign.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
|
||||||
return base64EncodeNoWrap(mac.doFinal(new byte[0]));
|
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()
|
JSONObject markdown = new JSONObject()
|
||||||
.put("tag", "markdown")
|
.put("tag", "markdown")
|
||||||
.put("content", markdownContent == null ? "" : markdownContent);
|
.put("content", markdownContent == null ? "" : markdownContent);
|
||||||
@ -178,7 +180,7 @@ final class FeishuWebhookClient {
|
|||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
static FeishuWebhookPushResult parseResponse(String responseBody) {
|
public static FeishuWebhookPushResult parseResponse(String responseBody) {
|
||||||
try {
|
try {
|
||||||
JSONObject json = new JSONObject(responseBody == null ? "" : responseBody);
|
JSONObject json = new JSONObject(responseBody == null ? "" : responseBody);
|
||||||
int code = json.optInt("code", Integer.MIN_VALUE);
|
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());
|
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));
|
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 filterCode = config != null && config.filterVerificationCode;
|
||||||
boolean includeFullBody = config == null || !filterCode || config.sendFullBodyDebug;
|
boolean includeFullBody = config == null || !filterCode || config.sendFullBodyDebug;
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
@ -1,9 +1,11 @@
|
|||||||
package com.smsreceive.app;
|
package com.smsreceive.app.feishu;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.smsreceive.app.sms.CaptureResult;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
@ -16,8 +18,8 @@ import java.io.InputStreamReader;
|
|||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
final class FeishuWebhookConfigStore {
|
public final class FeishuWebhookConfigStore {
|
||||||
static final String ACTION_PUSH_UPDATED = "com.smsreceive.app.ACTION_FEISHU_PUSH_UPDATED";
|
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 TAG = "[SMS]SmsReceive";
|
||||||
private static final String PREFS = "feishu_webhook";
|
private static final String PREFS = "feishu_webhook";
|
||||||
@ -43,7 +45,7 @@ final class FeishuWebhookConfigStore {
|
|||||||
private FeishuWebhookConfigStore() {
|
private FeishuWebhookConfigStore() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static Config loadConfig(Context context) {
|
public static Config loadConfig(Context context) {
|
||||||
ensureDefaultConfigFile(context);
|
ensureDefaultConfigFile(context);
|
||||||
File file = configFile(context);
|
File file = configFile(context);
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
@ -59,7 +61,7 @@ final class FeishuWebhookConfigStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void saveConfig(
|
public static void saveConfig(
|
||||||
Context context,
|
Context context,
|
||||||
boolean enabled,
|
boolean enabled,
|
||||||
String webhookId,
|
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()
|
preferences(context).edit()
|
||||||
.putLong(KEY_LAST_TIME, result.timeMillis)
|
.putLong(KEY_LAST_TIME, result.timeMillis)
|
||||||
.putBoolean(KEY_LAST_SUCCESS, result.success)
|
.putBoolean(KEY_LAST_SUCCESS, result.success)
|
||||||
@ -136,7 +138,7 @@ final class FeishuWebhookConfigStore {
|
|||||||
+ ", smsKey=" + smsKey);
|
+ ", smsKey=" + smsKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
static LastResult loadLastResult(Context context) {
|
public static LastResult loadLastResult(Context context) {
|
||||||
SharedPreferences prefs = preferences(context);
|
SharedPreferences prefs = preferences(context);
|
||||||
return new LastResult(
|
return new LastResult(
|
||||||
prefs.getLong(KEY_LAST_TIME, 0L),
|
prefs.getLong(KEY_LAST_TIME, 0L),
|
||||||
@ -147,14 +149,14 @@ final class FeishuWebhookConfigStore {
|
|||||||
prefs.getInt(KEY_LAST_API_CODE, 0));
|
prefs.getInt(KEY_LAST_API_CODE, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
static LastPushedSms loadLastPushedSms(Context context) {
|
public static LastPushedSms loadLastPushedSms(Context context) {
|
||||||
SharedPreferences prefs = preferences(context);
|
SharedPreferences prefs = preferences(context);
|
||||||
return new LastPushedSms(
|
return new LastPushedSms(
|
||||||
prefs.getLong(KEY_LAST_PUSHED_SMS_RECEIVED_SECOND, 0L),
|
prefs.getLong(KEY_LAST_PUSHED_SMS_RECEIVED_SECOND, 0L),
|
||||||
prefs.getString(KEY_LAST_PUSHED_SMS_KEY, ""));
|
prefs.getString(KEY_LAST_PUSHED_SMS_KEY, ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
static String maskSecret(String secret) {
|
public static String maskSecret(String secret) {
|
||||||
if (isEmpty(secret)) {
|
if (isEmpty(secret)) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@ -164,15 +166,15 @@ final class FeishuWebhookConfigStore {
|
|||||||
return secret.substring(0, 3) + "***" + secret.substring(secret.length() - 3);
|
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();
|
return configFile(context).getAbsolutePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
static String defaultConfigPath(Context context) {
|
public static String defaultConfigPath(Context context) {
|
||||||
return defaultConfigFile(context).getAbsolutePath();
|
return defaultConfigFile(context).getAbsolutePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
static String configTemplate() {
|
public static String configTemplate() {
|
||||||
try {
|
try {
|
||||||
return configToJson(defaultConfig()).toString(2);
|
return configToJson(defaultConfig()).toString(2);
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
@ -289,14 +291,14 @@ final class FeishuWebhookConfigStore {
|
|||||||
+ Integer.toHexString((result.body == null ? "" : result.body).hashCode());
|
+ Integer.toHexString((result.body == null ? "" : result.body).hashCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class Config {
|
public static final class Config {
|
||||||
final boolean enabled;
|
public final boolean enabled;
|
||||||
final String webhookId;
|
public final String webhookId;
|
||||||
final String secret;
|
public final String secret;
|
||||||
final boolean sendFullBodyDebug;
|
public final boolean sendFullBodyDebug;
|
||||||
final boolean filterVerificationCode;
|
public final boolean filterVerificationCode;
|
||||||
|
|
||||||
Config(
|
public Config(
|
||||||
boolean enabled,
|
boolean enabled,
|
||||||
String webhookId,
|
String webhookId,
|
||||||
String secret,
|
String secret,
|
||||||
@ -309,22 +311,22 @@ final class FeishuWebhookConfigStore {
|
|||||||
this.filterVerificationCode = filterVerificationCode;
|
this.filterVerificationCode = filterVerificationCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean hasWebhookId() {
|
public boolean hasWebhookId() {
|
||||||
return !isEmpty(webhookId);
|
return !isEmpty(webhookId);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean hasSecret() {
|
public boolean hasSecret() {
|
||||||
return !isEmpty(secret);
|
return !isEmpty(secret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class LastResult {
|
public static final class LastResult {
|
||||||
final long timeMillis;
|
public final long timeMillis;
|
||||||
final boolean success;
|
public final boolean success;
|
||||||
final String status;
|
public final String status;
|
||||||
final String message;
|
public final String message;
|
||||||
final int httpStatus;
|
public final int httpStatus;
|
||||||
final int apiCode;
|
public final int apiCode;
|
||||||
|
|
||||||
LastResult(long timeMillis, boolean success, String status, String message, int httpStatus, int apiCode) {
|
LastResult(long timeMillis, boolean success, String status, String message, int httpStatus, int apiCode) {
|
||||||
this.timeMillis = timeMillis;
|
this.timeMillis = timeMillis;
|
||||||
@ -336,9 +338,9 @@ final class FeishuWebhookConfigStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class LastPushedSms {
|
public static final class LastPushedSms {
|
||||||
final long receivedSecond;
|
public final long receivedSecond;
|
||||||
final String smsKey;
|
public final String smsKey;
|
||||||
|
|
||||||
LastPushedSms(long receivedSecond, String smsKey) {
|
LastPushedSms(long receivedSecond, String smsKey) {
|
||||||
this.receivedSecond = receivedSecond;
|
this.receivedSecond = receivedSecond;
|
||||||
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.smsreceive.app;
|
package com.smsreceive.app.keepalive;
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.smsreceive.app;
|
package com.smsreceive.app.keepalive;
|
||||||
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@ -11,7 +11,7 @@ import java.text.SimpleDateFormat;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
final class KeepAliveDatabase {
|
public final class KeepAliveDatabase {
|
||||||
private static final String TAG = "[SMS]SmsReceive";
|
private static final String TAG = "[SMS]SmsReceive";
|
||||||
private static final String DATABASE_NAME = "sms_keep_alive.db";
|
private static final String DATABASE_NAME = "sms_keep_alive.db";
|
||||||
private static final int DATABASE_VERSION = 1;
|
private static final int DATABASE_VERSION = 1;
|
||||||
@ -23,7 +23,7 @@ final class KeepAliveDatabase {
|
|||||||
private KeepAliveDatabase() {
|
private KeepAliveDatabase() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static long writeLastActiveTime(Context context) {
|
public static long writeLastActiveTime(Context context) {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
SQLiteDatabase database = helper(context).getWritableDatabase();
|
SQLiteDatabase database = helper(context).getWritableDatabase();
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
@ -35,7 +35,7 @@ final class KeepAliveDatabase {
|
|||||||
return now;
|
return now;
|
||||||
}
|
}
|
||||||
|
|
||||||
static long readLastActiveTime(Context context) {
|
public static long readLastActiveTime(Context context) {
|
||||||
SQLiteDatabase database = helper(context).getReadableDatabase();
|
SQLiteDatabase database = helper(context).getReadableDatabase();
|
||||||
try (Cursor cursor = database.query(
|
try (Cursor cursor = database.query(
|
||||||
TABLE_META,
|
TABLE_META,
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.smsreceive.app;
|
package com.smsreceive.app.keepalive;
|
||||||
|
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationChannel;
|
import android.app.NotificationChannel;
|
||||||
@ -9,6 +9,8 @@ import android.content.Intent;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.smsreceive.app.ui.MainActivity;
|
||||||
|
|
||||||
final class KeepAliveNotification {
|
final class KeepAliveNotification {
|
||||||
static final int NOTIFICATION_ID = 2101;
|
static final int NOTIFICATION_ID = 2101;
|
||||||
private static final String TAG = "[SMS]SmsReceive";
|
private static final String TAG = "[SMS]SmsReceive";
|
||||||
@ -1,11 +1,11 @@
|
|||||||
package com.smsreceive.app;
|
package com.smsreceive.app.keepalive;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
final class KeepAliveStateStore {
|
public final class KeepAliveStateStore {
|
||||||
private static final String TAG = "[SMS]SmsReceive";
|
private static final String TAG = "[SMS]SmsReceive";
|
||||||
private static final String PREFS = "sms_keep_alive";
|
private static final String PREFS = "sms_keep_alive";
|
||||||
|
|
||||||
@ -23,14 +23,14 @@ final class KeepAliveStateStore {
|
|||||||
private 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);
|
Log.d(TAG, "KeepAliveStateStore.setEnabledByUser enabled=" + enabled);
|
||||||
preferences(context).edit()
|
preferences(context).edit()
|
||||||
.putBoolean(KEY_ENABLED_BY_USER, enabled)
|
.putBoolean(KEY_ENABLED_BY_USER, enabled)
|
||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void recordServiceStarted(Context context) {
|
public static void recordServiceStarted(Context context) {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
Log.d(TAG, "KeepAliveStateStore.recordServiceStarted time=" + now);
|
Log.d(TAG, "KeepAliveStateStore.recordServiceStarted time=" + now);
|
||||||
preferences(context).edit()
|
preferences(context).edit()
|
||||||
@ -40,7 +40,7 @@ final class KeepAliveStateStore {
|
|||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void recordServiceStopped(Context context, String reason) {
|
public static void recordServiceStopped(Context context, String reason) {
|
||||||
Log.d(TAG, "KeepAliveStateStore.recordServiceStopped reason=" + reason);
|
Log.d(TAG, "KeepAliveStateStore.recordServiceStopped reason=" + reason);
|
||||||
preferences(context).edit()
|
preferences(context).edit()
|
||||||
.putBoolean(KEY_SERVICE_RUNNING, false)
|
.putBoolean(KEY_SERVICE_RUNNING, false)
|
||||||
@ -48,7 +48,7 @@ final class KeepAliveStateStore {
|
|||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void recordHeartbeat(Context context) {
|
public static void recordHeartbeat(Context context) {
|
||||||
Log.d(TAG, "KeepAliveStateStore.recordHeartbeat");
|
Log.d(TAG, "KeepAliveStateStore.recordHeartbeat");
|
||||||
preferences(context).edit()
|
preferences(context).edit()
|
||||||
.putBoolean(KEY_SERVICE_RUNNING, true)
|
.putBoolean(KEY_SERVICE_RUNNING, true)
|
||||||
@ -56,7 +56,7 @@ final class KeepAliveStateStore {
|
|||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void recordBootEvent(Context context, String action) {
|
public static void recordBootEvent(Context context, String action) {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
Log.d(TAG, "KeepAliveStateStore.recordBootEvent action=" + action + ", time=" + now);
|
Log.d(TAG, "KeepAliveStateStore.recordBootEvent action=" + action + ", time=" + now);
|
||||||
preferences(context).edit()
|
preferences(context).edit()
|
||||||
@ -65,7 +65,7 @@ final class KeepAliveStateStore {
|
|||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void recordServiceStartFailure(Context context, String reason) {
|
public static void recordServiceStartFailure(Context context, String reason) {
|
||||||
Log.w(TAG, "KeepAliveStateStore.recordServiceStartFailure reason=" + reason);
|
Log.w(TAG, "KeepAliveStateStore.recordServiceStartFailure reason=" + reason);
|
||||||
preferences(context).edit()
|
preferences(context).edit()
|
||||||
.putBoolean(KEY_SERVICE_RUNNING, false)
|
.putBoolean(KEY_SERVICE_RUNNING, false)
|
||||||
@ -73,39 +73,39 @@ final class KeepAliveStateStore {
|
|||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setManualAutostartConfirmed(Context context, boolean confirmed) {
|
public static void setManualAutostartConfirmed(Context context, boolean confirmed) {
|
||||||
Log.d(TAG, "KeepAliveStateStore.setManualAutostartConfirmed confirmed=" + confirmed);
|
Log.d(TAG, "KeepAliveStateStore.setManualAutostartConfirmed confirmed=" + confirmed);
|
||||||
preferences(context).edit()
|
preferences(context).edit()
|
||||||
.putBoolean(KEY_MANUAL_AUTOSTART_CONFIRMED, confirmed)
|
.putBoolean(KEY_MANUAL_AUTOSTART_CONFIRMED, confirmed)
|
||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setManualBatteryUnrestrictedConfirmed(Context context, boolean confirmed) {
|
public static void setManualBatteryUnrestrictedConfirmed(Context context, boolean confirmed) {
|
||||||
Log.d(TAG, "KeepAliveStateStore.setManualBatteryUnrestrictedConfirmed confirmed=" + confirmed);
|
Log.d(TAG, "KeepAliveStateStore.setManualBatteryUnrestrictedConfirmed confirmed=" + confirmed);
|
||||||
preferences(context).edit()
|
preferences(context).edit()
|
||||||
.putBoolean(KEY_MANUAL_BATTERY_UNRESTRICTED_CONFIRMED, confirmed)
|
.putBoolean(KEY_MANUAL_BATTERY_UNRESTRICTED_CONFIRMED, confirmed)
|
||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setBatteryOptimizationIgnored(Context context, boolean ignored) {
|
public static void setBatteryOptimizationIgnored(Context context, boolean ignored) {
|
||||||
Log.d(TAG, "KeepAliveStateStore.setBatteryOptimizationIgnored ignored=" + ignored);
|
Log.d(TAG, "KeepAliveStateStore.setBatteryOptimizationIgnored ignored=" + ignored);
|
||||||
preferences(context).edit()
|
preferences(context).edit()
|
||||||
.putBoolean(KEY_BATTERY_OPTIMIZATION_IGNORED, ignored)
|
.putBoolean(KEY_BATTERY_OPTIMIZATION_IGNORED, ignored)
|
||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setToastOnDatabaseWrite(Context context, boolean enabled) {
|
public static void setToastOnDatabaseWrite(Context context, boolean enabled) {
|
||||||
Log.d(TAG, "KeepAliveStateStore.setToastOnDatabaseWrite enabled=" + enabled);
|
Log.d(TAG, "KeepAliveStateStore.setToastOnDatabaseWrite enabled=" + enabled);
|
||||||
preferences(context).edit()
|
preferences(context).edit()
|
||||||
.putBoolean(KEY_TOAST_ON_DATABASE_WRITE, enabled)
|
.putBoolean(KEY_TOAST_ON_DATABASE_WRITE, enabled)
|
||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isToastOnDatabaseWriteEnabled(Context context) {
|
public static boolean isToastOnDatabaseWriteEnabled(Context context) {
|
||||||
return preferences(context).getBoolean(KEY_TOAST_ON_DATABASE_WRITE, false);
|
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);
|
SharedPreferences prefs = preferences(context);
|
||||||
return new State(
|
return new State(
|
||||||
prefs.getBoolean(KEY_ENABLED_BY_USER, false),
|
prefs.getBoolean(KEY_ENABLED_BY_USER, false),
|
||||||
@ -128,17 +128,17 @@ final class KeepAliveStateStore {
|
|||||||
return TextUtils.isEmpty(value) ? "" : value;
|
return TextUtils.isEmpty(value) ? "" : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class State {
|
public static final class State {
|
||||||
final boolean enabledByUser;
|
public final boolean enabledByUser;
|
||||||
final boolean serviceRunning;
|
public final boolean serviceRunning;
|
||||||
final long lastHeartbeatMillis;
|
public final long lastHeartbeatMillis;
|
||||||
final String lastBootEvent;
|
public final String lastBootEvent;
|
||||||
final long lastBootTimeMillis;
|
public final long lastBootTimeMillis;
|
||||||
final String lastServiceStartFailure;
|
public final String lastServiceStartFailure;
|
||||||
final boolean manualAutostartConfirmed;
|
public final boolean manualAutostartConfirmed;
|
||||||
final boolean manualBatteryUnrestrictedConfirmed;
|
public final boolean manualBatteryUnrestrictedConfirmed;
|
||||||
final boolean batteryOptimizationIgnored;
|
public final boolean batteryOptimizationIgnored;
|
||||||
final boolean toastOnDatabaseWrite;
|
public final boolean toastOnDatabaseWrite;
|
||||||
|
|
||||||
State(
|
State(
|
||||||
boolean enabledByUser,
|
boolean enabledByUser,
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.smsreceive.app;
|
package com.smsreceive.app.keepalive;
|
||||||
|
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@ -10,6 +10,8 @@ import android.os.Looper;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.smsreceive.app.sms.SmsCaptureStore;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Locale;
|
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);
|
Log.d(TAG, "SmsKeepAliveService.start requested sdk=" + Build.VERSION.SDK_INT);
|
||||||
Intent intent = new Intent(context, SmsKeepAliveService.class);
|
Intent intent = new Intent(context, SmsKeepAliveService.class);
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
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");
|
Log.d(TAG, "SmsKeepAliveService.stop requested");
|
||||||
context.stopService(new Intent(context, SmsKeepAliveService.class));
|
context.stopService(new Intent(context, SmsKeepAliveService.class));
|
||||||
}
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.smsreceive.app;
|
package com.smsreceive.app.keepalive;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
@ -12,6 +12,11 @@ import android.os.Looper;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.Toast;
|
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 {
|
public final class SmsPollingService extends Service {
|
||||||
private static final String TAG = "[SMS]SmsReceive";
|
private static final String TAG = "[SMS]SmsReceive";
|
||||||
private static final String SOURCE_INBOX_POLLING = "sms_inbox_polling";
|
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);
|
Log.d(TAG, "SmsPollingService.start requested sdk=" + Build.VERSION.SDK_INT);
|
||||||
long startTimeMillis = System.currentTimeMillis() - 2_000L;
|
long startTimeMillis = System.currentTimeMillis() - 2_000L;
|
||||||
SmsPollingStateStore.recordStarted(context, startTimeMillis);
|
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");
|
Log.d(TAG, "SmsPollingService.stop requested");
|
||||||
SmsPollingStateStore.recordStopped(context, "用户停止轮询");
|
SmsPollingStateStore.recordStopped(context, "用户停止轮询");
|
||||||
context.stopService(new Intent(context, SmsPollingService.class));
|
context.stopService(new Intent(context, SmsPollingService.class));
|
||||||
@ -1,11 +1,11 @@
|
|||||||
package com.smsreceive.app;
|
package com.smsreceive.app.keepalive;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
final class SmsPollingStateStore {
|
public final class SmsPollingStateStore {
|
||||||
private static final String TAG = "[SMS]SmsReceive";
|
private static final String TAG = "[SMS]SmsReceive";
|
||||||
private static final String PREFS = "sms_polling";
|
private static final String PREFS = "sms_polling";
|
||||||
private static final String KEY_ENABLED_BY_USER = "enabled_by_user";
|
private static final String KEY_ENABLED_BY_USER = "enabled_by_user";
|
||||||
@ -22,7 +22,7 @@ final class SmsPollingStateStore {
|
|||||||
private 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);
|
Log.d(TAG, "SmsPollingStateStore.recordStarted startTime=" + startTimeMillis);
|
||||||
preferences(context).edit()
|
preferences(context).edit()
|
||||||
.putBoolean(KEY_ENABLED_BY_USER, true)
|
.putBoolean(KEY_ENABLED_BY_USER, true)
|
||||||
@ -32,7 +32,7 @@ final class SmsPollingStateStore {
|
|||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void recordStopped(Context context, String reason) {
|
public static void recordStopped(Context context, String reason) {
|
||||||
Log.d(TAG, "SmsPollingStateStore.recordStopped reason=" + reason);
|
Log.d(TAG, "SmsPollingStateStore.recordStopped reason=" + reason);
|
||||||
preferences(context).edit()
|
preferences(context).edit()
|
||||||
.putBoolean(KEY_ENABLED_BY_USER, false)
|
.putBoolean(KEY_ENABLED_BY_USER, false)
|
||||||
@ -41,7 +41,7 @@ final class SmsPollingStateStore {
|
|||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void recordServiceStopped(Context context, String reason) {
|
public static void recordServiceStopped(Context context, String reason) {
|
||||||
Log.d(TAG, "SmsPollingStateStore.recordServiceStopped reason=" + reason);
|
Log.d(TAG, "SmsPollingStateStore.recordServiceStopped reason=" + reason);
|
||||||
preferences(context).edit()
|
preferences(context).edit()
|
||||||
.putBoolean(KEY_RUNNING, false)
|
.putBoolean(KEY_RUNNING, false)
|
||||||
@ -49,13 +49,13 @@ final class SmsPollingStateStore {
|
|||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void recordServiceRunning(Context context) {
|
public static void recordServiceRunning(Context context) {
|
||||||
preferences(context).edit()
|
preferences(context).edit()
|
||||||
.putBoolean(KEY_RUNNING, true)
|
.putBoolean(KEY_RUNNING, true)
|
||||||
.apply();
|
.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);
|
Log.d(TAG, "SmsPollingStateStore.recordHit id=" + smsId + ", time=" + hitTimeMillis);
|
||||||
preferences(context).edit()
|
preferences(context).edit()
|
||||||
.putLong(KEY_LAST_HIT_ID, smsId)
|
.putLong(KEY_LAST_HIT_ID, smsId)
|
||||||
@ -64,7 +64,7 @@ final class SmsPollingStateStore {
|
|||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setIntervalSeconds(Context context, int seconds) {
|
public static void setIntervalSeconds(Context context, int seconds) {
|
||||||
int safeSeconds = clampIntervalSeconds(seconds);
|
int safeSeconds = clampIntervalSeconds(seconds);
|
||||||
Log.d(TAG, "SmsPollingStateStore.setIntervalSeconds seconds=" + safeSeconds);
|
Log.d(TAG, "SmsPollingStateStore.setIntervalSeconds seconds=" + safeSeconds);
|
||||||
preferences(context).edit()
|
preferences(context).edit()
|
||||||
@ -72,11 +72,11 @@ final class SmsPollingStateStore {
|
|||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
static int getIntervalSeconds(Context context) {
|
public static int getIntervalSeconds(Context context) {
|
||||||
return clampIntervalSeconds(preferences(context).getInt(KEY_INTERVAL_SECONDS, DEFAULT_INTERVAL_SECONDS));
|
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);
|
SharedPreferences prefs = preferences(context);
|
||||||
return new State(
|
return new State(
|
||||||
prefs.getBoolean(KEY_ENABLED_BY_USER, false),
|
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));
|
return Math.max(MIN_INTERVAL_SECONDS, Math.min(seconds, MAX_INTERVAL_SECONDS));
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class State {
|
public static final class State {
|
||||||
final boolean enabledByUser;
|
public final boolean enabledByUser;
|
||||||
final boolean running;
|
public final boolean running;
|
||||||
final long startTimeMillis;
|
public final long startTimeMillis;
|
||||||
final long lastHitId;
|
public final long lastHitId;
|
||||||
final long lastHitTimeMillis;
|
public final long lastHitTimeMillis;
|
||||||
final String lastFailure;
|
public final String lastFailure;
|
||||||
final int intervalSeconds;
|
public final int intervalSeconds;
|
||||||
|
|
||||||
State(
|
State(
|
||||||
boolean enabledByUser,
|
boolean enabledByUser,
|
||||||
@ -1,15 +1,15 @@
|
|||||||
package com.smsreceive.app;
|
package com.smsreceive.app.sms;
|
||||||
|
|
||||||
final class CaptureResult {
|
public final class CaptureResult {
|
||||||
static final long UNKNOWN_SMS_PROVIDER_ID = -1L;
|
public static final long UNKNOWN_SMS_PROVIDER_ID = -1L;
|
||||||
|
|
||||||
final long receivedAtMillis;
|
public final long receivedAtMillis;
|
||||||
final long smsProviderId;
|
public final long smsProviderId;
|
||||||
final String sender;
|
public final String sender;
|
||||||
final String body;
|
public final String body;
|
||||||
final VerificationCodeParser.ParseResult parseResult;
|
public final VerificationCodeParser.ParseResult parseResult;
|
||||||
final String source;
|
public final String source;
|
||||||
final String failureReason;
|
public final String failureReason;
|
||||||
|
|
||||||
private CaptureResult(
|
private CaptureResult(
|
||||||
long receivedAtMillis,
|
long receivedAtMillis,
|
||||||
@ -28,7 +28,7 @@ final class CaptureResult {
|
|||||||
this.failureReason = failureReason == null ? "" : failureReason;
|
this.failureReason = failureReason == null ? "" : failureReason;
|
||||||
}
|
}
|
||||||
|
|
||||||
static CaptureResult success(
|
public static CaptureResult success(
|
||||||
long receivedAtMillis,
|
long receivedAtMillis,
|
||||||
String sender,
|
String sender,
|
||||||
String body,
|
String body,
|
||||||
@ -37,7 +37,7 @@ final class CaptureResult {
|
|||||||
return success(receivedAtMillis, UNKNOWN_SMS_PROVIDER_ID, sender, body, parseResult, source);
|
return success(receivedAtMillis, UNKNOWN_SMS_PROVIDER_ID, sender, body, parseResult, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
static CaptureResult success(
|
public static CaptureResult success(
|
||||||
long receivedAtMillis,
|
long receivedAtMillis,
|
||||||
long smsProviderId,
|
long smsProviderId,
|
||||||
String sender,
|
String sender,
|
||||||
@ -47,7 +47,7 @@ final class CaptureResult {
|
|||||||
return new CaptureResult(receivedAtMillis, smsProviderId, sender, body, parseResult, source, "");
|
return new CaptureResult(receivedAtMillis, smsProviderId, sender, body, parseResult, source, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
static CaptureResult failure(
|
public static CaptureResult failure(
|
||||||
long receivedAtMillis,
|
long receivedAtMillis,
|
||||||
String sender,
|
String sender,
|
||||||
String body,
|
String body,
|
||||||
@ -56,7 +56,7 @@ final class CaptureResult {
|
|||||||
return failure(receivedAtMillis, UNKNOWN_SMS_PROVIDER_ID, sender, body, source, failureReason);
|
return failure(receivedAtMillis, UNKNOWN_SMS_PROVIDER_ID, sender, body, source, failureReason);
|
||||||
}
|
}
|
||||||
|
|
||||||
static CaptureResult failure(
|
public static CaptureResult failure(
|
||||||
long receivedAtMillis,
|
long receivedAtMillis,
|
||||||
long smsProviderId,
|
long smsProviderId,
|
||||||
String sender,
|
String sender,
|
||||||
@ -1,12 +1,12 @@
|
|||||||
package com.smsreceive.app;
|
package com.smsreceive.app.sms;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
final class SmsCaptureStore {
|
public final class SmsCaptureStore {
|
||||||
static final String ACTION_CAPTURE_UPDATED = "com.smsreceive.app.ACTION_CAPTURE_UPDATED";
|
public static final String ACTION_CAPTURE_UPDATED = "com.smsreceive.app.ACTION_CAPTURE_UPDATED";
|
||||||
private static final String TAG = "[SMS]SmsReceive";
|
private static final String TAG = "[SMS]SmsReceive";
|
||||||
|
|
||||||
private static final String PREFS = "sms_capture";
|
private static final String PREFS = "sms_capture";
|
||||||
@ -25,7 +25,7 @@ final class SmsCaptureStore {
|
|||||||
private SmsCaptureStore() {
|
private SmsCaptureStore() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void save(Context context, CaptureResult result) {
|
public static void save(Context context, CaptureResult result) {
|
||||||
VerificationCodeParser.ParseResult parse = result.parseResult;
|
VerificationCodeParser.ParseResult parse = result.parseResult;
|
||||||
Log.d(TAG, "SmsCaptureStore.save source=" + result.source
|
Log.d(TAG, "SmsCaptureStore.save source=" + result.source
|
||||||
+ ", success=" + parse.success
|
+ ", success=" + parse.success
|
||||||
@ -53,7 +53,7 @@ final class SmsCaptureStore {
|
|||||||
editor.apply();
|
editor.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
static StoredCapture load(Context context) {
|
public static StoredCapture load(Context context) {
|
||||||
SharedPreferences prefs = preferences(context);
|
SharedPreferences prefs = preferences(context);
|
||||||
return new StoredCapture(
|
return new StoredCapture(
|
||||||
prefs.getLong(KEY_TIME, 0L),
|
prefs.getLong(KEY_TIME, 0L),
|
||||||
@ -66,12 +66,12 @@ final class SmsCaptureStore {
|
|||||||
prefs.getString(KEY_BODY_PREVIEW, ""));
|
prefs.getString(KEY_BODY_PREVIEW, ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void clear(Context context) {
|
public static void clear(Context context) {
|
||||||
Log.d(TAG, "SmsCaptureStore.clear");
|
Log.d(TAG, "SmsCaptureStore.clear");
|
||||||
preferences(context).edit().clear().apply();
|
preferences(context).edit().clear().apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
static DeliveryDiagnostics loadDeliveryDiagnostics(Context context) {
|
public static DeliveryDiagnostics loadDeliveryDiagnostics(Context context) {
|
||||||
SharedPreferences prefs = preferences(context);
|
SharedPreferences prefs = preferences(context);
|
||||||
return new DeliveryDiagnostics(
|
return new DeliveryDiagnostics(
|
||||||
prefs.getLong(KEY_LAST_BROADCAST_TIME, 0L),
|
prefs.getLong(KEY_LAST_BROADCAST_TIME, 0L),
|
||||||
@ -101,15 +101,15 @@ final class SmsCaptureStore {
|
|||||||
return normalized.length() <= 48 ? normalized : normalized.substring(0, 48) + "...";
|
return normalized.length() <= 48 ? normalized : normalized.substring(0, 48) + "...";
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class StoredCapture {
|
public static final class StoredCapture {
|
||||||
final long timeMillis;
|
public final long timeMillis;
|
||||||
final String sender;
|
public final String sender;
|
||||||
final String code;
|
public final String code;
|
||||||
final String strategy;
|
public final String strategy;
|
||||||
final int confidence;
|
public final int confidence;
|
||||||
final String source;
|
public final String source;
|
||||||
final String failure;
|
public final String failure;
|
||||||
final String bodyPreview;
|
public final String bodyPreview;
|
||||||
|
|
||||||
StoredCapture(
|
StoredCapture(
|
||||||
long timeMillis,
|
long timeMillis,
|
||||||
@ -131,10 +131,10 @@ final class SmsCaptureStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class DeliveryDiagnostics {
|
public static final class DeliveryDiagnostics {
|
||||||
final long lastBroadcastTimeMillis;
|
public final long lastBroadcastTimeMillis;
|
||||||
final long lastInboxTimeMillis;
|
public final long lastInboxTimeMillis;
|
||||||
final String lastInboxSource;
|
public final String lastInboxSource;
|
||||||
|
|
||||||
DeliveryDiagnostics(long lastBroadcastTimeMillis, long lastInboxTimeMillis, String lastInboxSource) {
|
DeliveryDiagnostics(long lastBroadcastTimeMillis, long lastInboxTimeMillis, String lastInboxSource) {
|
||||||
this.lastBroadcastTimeMillis = lastBroadcastTimeMillis;
|
this.lastBroadcastTimeMillis = lastBroadcastTimeMillis;
|
||||||
@ -142,7 +142,7 @@ final class SmsCaptureStore {
|
|||||||
this.lastInboxSource = lastInboxSource == null ? "" : lastInboxSource;
|
this.lastInboxSource = lastInboxSource == null ? "" : lastInboxSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean inboxNewerThanBroadcast() {
|
public boolean inboxNewerThanBroadcast() {
|
||||||
return lastInboxTimeMillis > 0L && lastInboxTimeMillis > lastBroadcastTimeMillis;
|
return lastInboxTimeMillis > 0L && lastInboxTimeMillis > lastBroadcastTimeMillis;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.smsreceive.app;
|
package com.smsreceive.app.sms;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
@ -11,14 +11,14 @@ import java.text.SimpleDateFormat;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
final class SmsInboxReader {
|
public final class SmsInboxReader {
|
||||||
private static final String TAG = "[SMS]SmsReceive";
|
private static final String TAG = "[SMS]SmsReceive";
|
||||||
private static final Uri SMS_INBOX_URI = Uri.parse("content://sms/inbox");
|
private static final Uri SMS_INBOX_URI = Uri.parse("content://sms/inbox");
|
||||||
|
|
||||||
private SmsInboxReader() {
|
private SmsInboxReader() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static InboxResult readLatest(Context context) {
|
public static InboxResult readLatest(Context context) {
|
||||||
String[] projection = {
|
String[] projection = {
|
||||||
Telephony.Sms._ID,
|
Telephony.Sms._ID,
|
||||||
Telephony.Sms.ADDRESS,
|
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;
|
Uri uri = Telephony.Sms.CONTENT_URI;
|
||||||
String[] projection = {
|
String[] projection = {
|
||||||
Telephony.Sms._ID,
|
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);
|
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;
|
Uri uri = Telephony.Sms.CONTENT_URI;
|
||||||
String[] projection = {
|
String[] projection = {
|
||||||
Telephony.Sms._ID,
|
Telephony.Sms._ID,
|
||||||
@ -231,13 +231,13 @@ final class SmsInboxReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class InboxResult {
|
public static final class InboxResult {
|
||||||
final boolean success;
|
public final boolean success;
|
||||||
final long id;
|
public final long id;
|
||||||
final String sender;
|
public final String sender;
|
||||||
final String body;
|
public final String body;
|
||||||
final long dateMillis;
|
public final long dateMillis;
|
||||||
final String failureReason;
|
public final String failureReason;
|
||||||
|
|
||||||
private InboxResult(boolean success, long id, String sender, String body, long dateMillis, String failureReason) {
|
private InboxResult(boolean success, long id, String sender, String body, long dateMillis, String failureReason) {
|
||||||
this.success = success;
|
this.success = success;
|
||||||
@ -257,15 +257,15 @@ final class SmsInboxReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class RecentCodeResult {
|
public static final class RecentCodeResult {
|
||||||
final boolean success;
|
public final boolean success;
|
||||||
final long id;
|
public final long id;
|
||||||
final String sender;
|
public final String sender;
|
||||||
final String body;
|
public final String body;
|
||||||
final long dateMillis;
|
public final long dateMillis;
|
||||||
final VerificationCodeParser.ParseResult parseResult;
|
public final VerificationCodeParser.ParseResult parseResult;
|
||||||
final int scannedCount;
|
public final int scannedCount;
|
||||||
final String failureReason;
|
public final String failureReason;
|
||||||
|
|
||||||
private RecentCodeResult(
|
private RecentCodeResult(
|
||||||
boolean success,
|
boolean success,
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.smsreceive.app;
|
package com.smsreceive.app.sms;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.provider.Telephony;
|
import android.provider.Telephony;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.smsreceive.app;
|
package com.smsreceive.app.sms;
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@ -7,6 +7,8 @@ import android.provider.Telephony;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.smsreceive.app.feishu.FeishuWebhookClient;
|
||||||
|
|
||||||
public final class SmsReceiver extends BroadcastReceiver {
|
public final class SmsReceiver extends BroadcastReceiver {
|
||||||
private static final String TAG = "[SMS]SmsReceive";
|
private static final String TAG = "[SMS]SmsReceive";
|
||||||
private static final String SOURCE_SYSTEM_BROADCAST = "system_sms_broadcast";
|
private static final String SOURCE_SYSTEM_BROADCAST = "system_sms_broadcast";
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.smsreceive.app;
|
package com.smsreceive.app.sms;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.smsreceive.app;
|
package com.smsreceive.app.ui;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
@ -35,6 +35,19 @@ import android.widget.Switch;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
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.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.smsreceive.app;
|
package com.smsreceive.app.feishu;
|
||||||
|
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.smsreceive.app;
|
package com.smsreceive.app.sms;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@ -2,4 +2,3 @@ org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8
|
|||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.enableJetifier=false
|
android.enableJetifier=false
|
||||||
android.injected.testOnly=false
|
android.injected.testOnly=false
|
||||||
android.aapt2FromMavenOverride=/Users/zouchao/Library/Android/sdk/build-tools/30.0.3/aapt2
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user