Browse Source

feat(android): improve received sms and status tracking reliability

pull/96/head
isra el 9 months ago
parent
commit
6886dd2f1a
  1. 2
      android/app/build.gradle
  2. 8
      android/app/src/main/AndroidManifest.xml
  3. 20
      android/app/src/main/java/com/vernu/sms/SMSGatewayApplication.java
  4. 33
      android/app/src/main/java/com/vernu/sms/receivers/SMSBroadcastReceiver.java
  5. 28
      android/app/src/main/java/com/vernu/sms/receivers/SMSStatusReceiver.java
  6. 106
      android/app/src/main/java/com/vernu/sms/workers/SMSReceivedWorker.java
  7. 105
      android/app/src/main/java/com/vernu/sms/workers/SMSStatusUpdateWorker.java

2
android/app/build.gradle

@ -54,6 +54,8 @@ dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.journeyapps:zxing-android-embedded:4.1.0'
implementation 'androidx.work:work-runtime:2.7.1'
// def room_version = "2.4.2"
// implementation "androidx.room:room-runtime:$room_version"

8
android/app/src/main/AndroidManifest.xml

@ -14,7 +14,9 @@
<uses-permission android:name="android.provider.Telephony.SMS_RECEIVED" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".SMSGatewayApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
@ -22,6 +24,12 @@
android:supportsRtl="true"
android:theme="@style/Theme.SMSGateway"
android:usesCleartextTraffic="true" >
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
<service
android:name=".services.FCMService"
android:exported="false"

20
android/app/src/main/java/com/vernu/sms/SMSGatewayApplication.java

@ -0,0 +1,20 @@
package com.vernu.sms;
import android.app.Application;
import androidx.work.Configuration;
import androidx.work.WorkManager;
public class SMSGatewayApplication extends Application implements Configuration.Provider {
@Override
public void onCreate() {
super.onCreate();
}
@Override
public Configuration getWorkManagerConfiguration() {
return new Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.INFO)
.build();
}
}

33
android/app/src/main/java/com/vernu/sms/receivers/SMSBroadcastReceiver.java

@ -6,17 +6,13 @@ import android.content.Intent;
import android.provider.Telephony;
import android.telephony.SmsMessage;
import android.util.Log;
import com.vernu.sms.ApiManager;
import com.vernu.sms.AppConstants;
import com.vernu.sms.dtos.SMSDTO;
import com.vernu.sms.dtos.SMSForwardResponseDTO;
import com.vernu.sms.helpers.SharedPreferenceHelper;
import com.vernu.sms.workers.SMSReceivedWorker;
import java.util.Date;
import java.util.Objects;
import retrofit2.Call;
import retrofit2.Response;
public class SMSBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "SMSBroadcastReceiver";
@ -64,32 +60,7 @@ public class SMSBroadcastReceiver extends BroadcastReceiver {
// receivedSMSDTO.setMessage(receivedSMS.getMessage());
// receivedSMSDTO.setReceivedAt(receivedSMS.getReceivedAt());
Call<SMSForwardResponseDTO> apiCall = ApiManager.getApiService().sendReceivedSMS(deviceId, apiKey, receivedSMSDTO);
apiCall.enqueue(new retrofit2.Callback<SMSForwardResponseDTO>() {
@Override
public void onResponse(Call<SMSForwardResponseDTO> call, Response<SMSForwardResponseDTO> response) {
// Date now = new Date();
if (response.isSuccessful()) {
Log.d(TAG, "SMS sent to server successfully");
// receivedSMS.setLastAcknowledgedRequestAt(now);
// receivedSMS.setServerAcknowledgedAt(now);
// updateLocalReceivedSMS(receivedSMS, context);
} else {
Log.e(TAG, "Failed to send SMS to server");
// receivedSMS.setServerAcknowledgedAt(null);
// receivedSMS.setLastAcknowledgedRequestAt(now);
// receivedSMS.setRetryCount(localReceivedSMS.getRetryCount() + 1);
// updateLocalReceivedSMS(receivedSMS, context);
}
}
@Override
public void onFailure(Call<SMSForwardResponseDTO> call, Throwable t) {
Log.e(TAG, "Failed to send SMS to server", t);
// receivedSMS.setServerAcknowledgedAt(null);
// receivedSMS.setLastAcknowledgedRequestAt(new Date());
// updateLocalReceivedSMS(receivedSMS, context);
}
});
SMSReceivedWorker.enqueueWork(context, deviceId, apiKey, receivedSMSDTO);
}
// private void updateLocalReceivedSMS(SMS localReceivedSMS, Context context) {

28
android/app/src/main/java/com/vernu/sms/receivers/SMSStatusReceiver.java

@ -7,16 +7,11 @@ import android.content.Intent;
import android.telephony.SmsManager;
import android.util.Log;
import com.vernu.sms.ApiManager;
import com.vernu.sms.AppConstants;
import com.vernu.sms.dtos.SMSDTO;
import com.vernu.sms.dtos.SMSForwardResponseDTO;
import com.vernu.sms.helpers.SharedPreferenceHelper;
import com.vernu.sms.services.GatewayApiService;
import com.vernu.sms.workers.SMSStatusUpdateWorker;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class SMSStatusReceiver extends BroadcastReceiver {
private static final String TAG = "SMSStatusReceiver";
@ -157,24 +152,7 @@ public class SMSStatusReceiver extends BroadcastReceiver {
Log.e(TAG, "Device ID or API key not found");
return;
}
GatewayApiService apiService = ApiManager.getApiService();
Call<SMSForwardResponseDTO> call = apiService.updateSMSStatus(deviceId, apiKey, smsDTO);
call.enqueue(new Callback<SMSForwardResponseDTO>() {
@Override
public void onResponse(Call<SMSForwardResponseDTO> call, Response<SMSForwardResponseDTO> response) {
if (response.isSuccessful()) {
Log.d(TAG, "SMS status updated successfully - ID: " + smsDTO.getSmsId() + ", Status: " + smsDTO.getStatus());
} else {
Log.e(TAG, "Failed to update SMS status. Response code: " + response.code());
}
}
@Override
public void onFailure(Call<SMSForwardResponseDTO> call, Throwable t) {
Log.e(TAG, "API call failed: " + t.getMessage());
}
});
SMSStatusUpdateWorker.enqueueWork(context, deviceId, apiKey, smsDTO);
}
}

106
android/app/src/main/java/com/vernu/sms/workers/SMSReceivedWorker.java

@ -0,0 +1,106 @@
package com.vernu.sms.workers;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.work.Data;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
import androidx.work.BackoffPolicy;
import androidx.work.Constraints;
import androidx.work.NetworkType;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;
import com.google.gson.Gson;
import com.vernu.sms.ApiManager;
import com.vernu.sms.dtos.SMSDTO;
import com.vernu.sms.dtos.SMSForwardResponseDTO;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import retrofit2.Call;
import retrofit2.Response;
public class SMSReceivedWorker extends Worker {
private static final String TAG = "SMSReceivedWorker";
private static final int MAX_RETRIES = 5;
public static final String KEY_DEVICE_ID = "device_id";
public static final String KEY_API_KEY = "api_key";
public static final String KEY_SMS_DTO = "sms_dto";
public static final String KEY_RETRY_COUNT = "retry_count";
public SMSReceivedWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@NonNull
@Override
public Result doWork() {
String deviceId = getInputData().getString(KEY_DEVICE_ID);
String apiKey = getInputData().getString(KEY_API_KEY);
String smsDtoJson = getInputData().getString(KEY_SMS_DTO);
int retryCount = getInputData().getInt(KEY_RETRY_COUNT, 0);
if (deviceId == null || apiKey == null || smsDtoJson == null) {
Log.e(TAG, "Missing required parameters");
return Result.failure();
}
// Check if we've exceeded the maximum retry count
if (retryCount >= MAX_RETRIES) {
Log.e(TAG, "Maximum retry count reached for received SMS");
return Result.failure();
}
SMSDTO smsDTO = new Gson().fromJson(smsDtoJson, SMSDTO.class);
try {
Call<SMSForwardResponseDTO> call = ApiManager.getApiService().sendReceivedSMS(deviceId, apiKey, smsDTO);
Response<SMSForwardResponseDTO> response = call.execute();
if (response.isSuccessful()) {
Log.d(TAG, "Received SMS sent to server successfully");
return Result.success();
} else {
Log.e(TAG, "Failed to send received SMS to server. Response code: " + response.code());
return Result.retry();
}
} catch (IOException e) {
Log.e(TAG, "API call failed: " + e.getMessage());
return Result.retry();
}
}
public static void enqueueWork(Context context, String deviceId, String apiKey, SMSDTO smsDTO) {
Data inputData = new Data.Builder()
.putString(KEY_DEVICE_ID, deviceId)
.putString(KEY_API_KEY, apiKey)
.putString(KEY_SMS_DTO, new Gson().toJson(smsDTO))
.putInt(KEY_RETRY_COUNT, 0)
.build();
Constraints constraints = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build();
OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(SMSReceivedWorker.class)
.setConstraints(constraints)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 10, TimeUnit.SECONDS)
.setInputData(inputData)
.addTag("sms_received")
.build();
String uniqueWorkName = "sms_received_" + System.currentTimeMillis();
WorkManager.getInstance(context)
.beginUniqueWork(uniqueWorkName,
androidx.work.ExistingWorkPolicy.APPEND_OR_REPLACE,
workRequest)
.enqueue();
Log.d(TAG, "Work enqueued for received SMS from: " + smsDTO.getSender());
}
}

105
android/app/src/main/java/com/vernu/sms/workers/SMSStatusUpdateWorker.java

@ -0,0 +1,105 @@
package com.vernu.sms.workers;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.work.Data;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
import androidx.work.BackoffPolicy;
import androidx.work.Constraints;
import androidx.work.NetworkType;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;
import com.google.gson.Gson;
import com.vernu.sms.ApiManager;
import com.vernu.sms.dtos.SMSDTO;
import com.vernu.sms.dtos.SMSForwardResponseDTO;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import retrofit2.Call;
import retrofit2.Response;
public class SMSStatusUpdateWorker extends Worker {
private static final String TAG = "SMSStatusUpdateWorker";
private static final int MAX_RETRIES = 5;
public static final String KEY_DEVICE_ID = "device_id";
public static final String KEY_API_KEY = "api_key";
public static final String KEY_SMS_DTO = "sms_dto";
public static final String KEY_RETRY_COUNT = "retry_count";
public SMSStatusUpdateWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@NonNull
@Override
public Result doWork() {
String deviceId = getInputData().getString(KEY_DEVICE_ID);
String apiKey = getInputData().getString(KEY_API_KEY);
String smsDtoJson = getInputData().getString(KEY_SMS_DTO);
int retryCount = getInputData().getInt(KEY_RETRY_COUNT, 0);
if (deviceId == null || apiKey == null || smsDtoJson == null) {
Log.e(TAG, "Missing required parameters");
return Result.failure();
}
// Check if we've exceeded the maximum retry count
if (retryCount >= MAX_RETRIES) {
Log.e(TAG, "Maximum retry count reached for SMS status update");
return Result.failure();
}
SMSDTO smsDTO = new Gson().fromJson(smsDtoJson, SMSDTO.class);
try {
Call<SMSForwardResponseDTO> call = ApiManager.getApiService().updateSMSStatus(deviceId, apiKey, smsDTO);
Response<SMSForwardResponseDTO> response = call.execute();
if (response.isSuccessful()) {
Log.d(TAG, "SMS status updated successfully - ID: " + smsDTO.getSmsId() + ", Status: " + smsDTO.getStatus());
return Result.success();
} else {
Log.e(TAG, "Failed to update SMS status. Response code: " + response.code());
return Result.retry();
}
} catch (IOException e) {
Log.e(TAG, "API call failed: " + e.getMessage());
return Result.retry();
}
}
public static void enqueueWork(Context context, String deviceId, String apiKey, SMSDTO smsDTO) {
Data inputData = new Data.Builder()
.putString(KEY_DEVICE_ID, deviceId)
.putString(KEY_API_KEY, apiKey)
.putString(KEY_SMS_DTO, new Gson().toJson(smsDTO))
.putInt(KEY_RETRY_COUNT, 0)
.build();
Constraints constraints = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build();
OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(SMSStatusUpdateWorker.class)
.setConstraints(constraints)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 10, TimeUnit.SECONDS)
.setInputData(inputData)
.build();
String uniqueWorkName = "sms_status_" + smsDTO.getStatus() + "_" + System.currentTimeMillis();
WorkManager.getInstance(context)
.beginUniqueWork(uniqueWorkName,
androidx.work.ExistingWorkPolicy.REPLACE,
workRequest)
.enqueue();
Log.d(TAG, "Work enqueued for SMS status update - ID: " + smsDTO.getSmsId());
}
}
Loading…
Cancel
Save