Skip to content

Commit d613217

Browse files
committed
Merge branch 'main' of github.com:vernu/textbee
2 parents 598d697 + 5ae010f commit d613217

15 files changed

Lines changed: 404 additions & 129 deletions

File tree

android/app/src/main/java/com/vernu/sms/AppConstants.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,7 @@ public class AppConstants {
2424
public static final String SHARED_PREFS_HEARTBEAT_INTERVAL_MINUTES_KEY = "HEARTBEAT_INTERVAL_MINUTES";
2525
public static final String SHARED_PREFS_SMS_FILTER_CONFIG_KEY = "SMS_FILTER_CONFIG";
2626
public static final String SHARED_PREFS_DEVICE_NAME_KEY = "DEVICE_NAME";
27+
public static final String SHARED_PREFS_SMS_SEND_DELAY_SECONDS_KEY = "SMS_SEND_DELAY_SECONDS";
28+
/** Default delay between SMS sends (seconds). 5s helps avoid carrier/device throttling. */
29+
public static final int DEFAULT_SMS_SEND_DELAY_SECONDS = 5;
2730
}

android/app/src/main/java/com/vernu/sms/TextBeeUtils.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -199,19 +199,35 @@ public static List<SimInfoDTO> collectSimInfo(Context context) {
199199
Log.d(TAG, "Could not get SIM slot index for subscription " + subscriptionInfo.getSubscriptionId());
200200
}
201201

202-
// Get MCC
202+
// Get MCC (getMccString() is API 29+; use getMcc() on older devices)
203203
try {
204-
String mcc = subscriptionInfo.getMccString();
204+
String mcc = null;
205+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
206+
mcc = subscriptionInfo.getMccString();
207+
} else {
208+
int mccInt = subscriptionInfo.getMcc();
209+
if (mccInt != Integer.MAX_VALUE) {
210+
mcc = String.format("%03d", mccInt);
211+
}
212+
}
205213
if (mcc != null && !mcc.isEmpty()) {
206214
simInfo.setMcc(mcc);
207215
}
208216
} catch (Exception e) {
209217
Log.d(TAG, "Could not get MCC for subscription " + subscriptionInfo.getSubscriptionId());
210218
}
211219

212-
// Get MNC
220+
// Get MNC (getMncString() is API 29+; use getMnc() on older devices)
213221
try {
214-
String mnc = subscriptionInfo.getMncString();
222+
String mnc = null;
223+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
224+
mnc = subscriptionInfo.getMncString();
225+
} else {
226+
int mncInt = subscriptionInfo.getMnc();
227+
if (mncInt != Integer.MAX_VALUE) {
228+
mnc = String.valueOf(mncInt);
229+
}
230+
}
215231
if (mnc != null && !mnc.isEmpty()) {
216232
simInfo.setMnc(mnc);
217233
}

android/app/src/main/java/com/vernu/sms/activities/MainActivity.java

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
import com.vernu.sms.activities.SMSFilterActivity;
1111
import android.os.Build;
1212
import android.os.Bundle;
13+
import android.os.Handler;
14+
import android.os.Looper;
15+
import android.text.Editable;
16+
import android.text.TextWatcher;
1317
import android.util.Log;
1418
import android.view.View;
1519
import android.widget.Button;
@@ -49,13 +53,16 @@ public class MainActivity extends AppCompatActivity {
4953

5054
private Context mContext;
5155
private Switch gatewaySwitch, receiveSMSSwitch, stickyNotificationSwitch;
52-
private EditText apiKeyEditText, fcmTokenEditText, deviceIdEditText, deviceNameEditText;
56+
private EditText apiKeyEditText, fcmTokenEditText, deviceIdEditText, deviceNameEditText, smsSendDelayEditText;
5357
private Button registerDeviceBtn, grantSMSPermissionBtn, scanQRBtn, checkUpdatesBtn, configureFilterBtn;
5458
private ImageButton copyDeviceIdImgBtn;
5559
private TextView deviceBrandAndModelTxt, deviceIdTxt, appVersionNameTxt, appVersionCodeTxt;
5660
private RadioGroup defaultSimSlotRadioGroup;
5761
private static final int SCAN_QR_REQUEST_CODE = 49374;
5862
private static final int PERMISSION_REQUEST_CODE = 0;
63+
private static final long SMS_DELAY_SAVE_DEBOUNCE_MS = 3000L;
64+
private final Handler smsDelaySaveHandler = new Handler(Looper.getMainLooper());
65+
private Runnable smsDelaySaveRunnable;
5966
private String deviceId = null;
6067
private static final String TAG = "MainActivity";
6168

@@ -84,6 +91,7 @@ protected void onCreate(Bundle savedInstanceState) {
8491
appVersionCodeTxt = findViewById(R.id.appVersionCodeTxt);
8592
checkUpdatesBtn = findViewById(R.id.checkUpdatesBtn);
8693
configureFilterBtn = findViewById(R.id.configureFilterBtn);
94+
smsSendDelayEditText = findViewById(R.id.smsSendDelayEditText);
8795

8896
deviceIdTxt.setText(deviceId);
8997
deviceIdEditText.setText(deviceId);
@@ -255,6 +263,63 @@ public void onFailure(Call<RegisterDeviceResponseDTO> call, Throwable t) {
255263
Intent filterIntent = new Intent(MainActivity.this, SMSFilterActivity.class);
256264
startActivity(filterIntent);
257265
});
266+
267+
// SMS Send Delay setting: save 3 seconds after user stops typing
268+
int currentDelay = SharedPreferenceHelper.getSharedPreferenceInt(
269+
mContext, AppConstants.SHARED_PREFS_SMS_SEND_DELAY_SECONDS_KEY, AppConstants.DEFAULT_SMS_SEND_DELAY_SECONDS);
270+
smsSendDelayEditText.setText(String.valueOf(currentDelay));
271+
smsDelaySaveRunnable = this::saveSendDelay;
272+
smsSendDelayEditText.addTextChangedListener(new TextWatcher() {
273+
@Override
274+
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
275+
276+
@Override
277+
public void onTextChanged(CharSequence s, int start, int before, int count) {}
278+
279+
@Override
280+
public void afterTextChanged(Editable s) {
281+
smsDelaySaveHandler.removeCallbacks(smsDelaySaveRunnable);
282+
smsDelaySaveHandler.postDelayed(smsDelaySaveRunnable, SMS_DELAY_SAVE_DEBOUNCE_MS);
283+
}
284+
});
285+
smsSendDelayEditText.setOnEditorActionListener((v, actionId, event) -> {
286+
smsDelaySaveHandler.removeCallbacks(smsDelaySaveRunnable);
287+
saveSendDelay();
288+
return false;
289+
});
290+
}
291+
292+
private void saveSendDelay() {
293+
String text = smsSendDelayEditText.getText().toString().trim();
294+
if (text.isEmpty()) {
295+
int defaultDelay = AppConstants.DEFAULT_SMS_SEND_DELAY_SECONDS;
296+
smsSendDelayEditText.setText(String.valueOf(defaultDelay));
297+
SharedPreferenceHelper.setSharedPreferenceInt(mContext, AppConstants.SHARED_PREFS_SMS_SEND_DELAY_SECONDS_KEY, defaultDelay);
298+
Snackbar.make(smsSendDelayEditText, "SMS send delay saved (" + defaultDelay + " sec)", Snackbar.LENGTH_SHORT).show();
299+
return;
300+
}
301+
try {
302+
int value = Integer.parseInt(text);
303+
if (value < 0) {
304+
value = 0;
305+
smsSendDelayEditText.setText("0");
306+
SharedPreferenceHelper.setSharedPreferenceInt(mContext, AppConstants.SHARED_PREFS_SMS_SEND_DELAY_SECONDS_KEY, 0);
307+
Snackbar.make(smsSendDelayEditText, "Minimum delay is 0 seconds. Saved.", Snackbar.LENGTH_SHORT).show();
308+
} else if (value > 3600) {
309+
value = 3600;
310+
smsSendDelayEditText.setText("3600");
311+
SharedPreferenceHelper.setSharedPreferenceInt(mContext, AppConstants.SHARED_PREFS_SMS_SEND_DELAY_SECONDS_KEY, 3600);
312+
Snackbar.make(smsSendDelayEditText, "Maximum delay is 3600 seconds. Saved.", Snackbar.LENGTH_SHORT).show();
313+
} else {
314+
SharedPreferenceHelper.setSharedPreferenceInt(mContext, AppConstants.SHARED_PREFS_SMS_SEND_DELAY_SECONDS_KEY, value);
315+
Snackbar.make(smsSendDelayEditText, "SMS send delay saved (" + value + " sec)", Snackbar.LENGTH_SHORT).show();
316+
}
317+
} catch (NumberFormatException e) {
318+
int defaultDelay = AppConstants.DEFAULT_SMS_SEND_DELAY_SECONDS;
319+
smsSendDelayEditText.setText(String.valueOf(defaultDelay));
320+
SharedPreferenceHelper.setSharedPreferenceInt(mContext, AppConstants.SHARED_PREFS_SMS_SEND_DELAY_SECONDS_KEY, defaultDelay);
321+
Snackbar.make(smsSendDelayEditText, "Invalid value. Reset to " + defaultDelay + " sec.", Snackbar.LENGTH_SHORT).show();
322+
}
258323
}
259324

260325
private void renderAvailableSimOptions() {

android/app/src/main/java/com/vernu/sms/dtos/HeartbeatInputDTO.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public class HeartbeatInputDTO {
1616
private String timezone;
1717
private String locale;
1818
private Boolean receiveSMSEnabled;
19+
private Integer smsSendDelaySeconds;
1920
private SimInfoCollectionDTO simInfo;
2021

2122
public HeartbeatInputDTO() {
@@ -141,6 +142,14 @@ public void setReceiveSMSEnabled(Boolean receiveSMSEnabled) {
141142
this.receiveSMSEnabled = receiveSMSEnabled;
142143
}
143144

145+
public Integer getSmsSendDelaySeconds() {
146+
return smsSendDelaySeconds;
147+
}
148+
149+
public void setSmsSendDelaySeconds(Integer smsSendDelaySeconds) {
150+
this.smsSendDelaySeconds = smsSendDelaySeconds;
151+
}
152+
144153
public SimInfoCollectionDTO getSimInfo() {
145154
return simInfo;
146155
}

android/app/src/main/java/com/vernu/sms/helpers/HeartbeatHelper.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,14 @@ public static boolean sendHeartbeat(Context context, String deviceId, String api
138138
);
139139
heartbeatInput.setReceiveSMSEnabled(receiveSMSEnabled);
140140

141+
// SMS send delay (device queue)
142+
int smsSendDelaySeconds = SharedPreferenceHelper.getSharedPreferenceInt(
143+
context,
144+
AppConstants.SHARED_PREFS_SMS_SEND_DELAY_SECONDS_KEY,
145+
AppConstants.DEFAULT_SMS_SEND_DELAY_SECONDS
146+
);
147+
heartbeatInput.setSmsSendDelaySeconds(smsSendDelaySeconds);
148+
141149
// Collect SIM information
142150
SimInfoCollectionDTO simInfoCollection = new SimInfoCollectionDTO();
143151
simInfoCollection.setLastUpdated(System.currentTimeMillis());

android/app/src/main/java/com/vernu/sms/helpers/SMSHelper.java

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,14 @@
99
import android.telephony.SubscriptionManager;
1010
import android.util.Log;
1111

12-
import com.vernu.sms.ApiManager;
1312
import com.vernu.sms.AppConstants;
1413
import com.vernu.sms.TextBeeUtils;
1514
import com.vernu.sms.dtos.SMSDTO;
16-
import com.vernu.sms.dtos.SMSForwardResponseDTO;
1715
import com.vernu.sms.receivers.SMSStatusReceiver;
18-
import com.vernu.sms.services.GatewayApiService;
16+
import com.vernu.sms.workers.SMSStatusUpdateWorker;
1917

2018
import java.util.ArrayList;
2119

22-
import retrofit2.Call;
23-
import retrofit2.Callback;
24-
import retrofit2.Response;
25-
2620
public class SMSHelper {
2721
private static final String TAG = "SMSHelper";
2822

@@ -179,25 +173,8 @@ private static void updateSMSStatus(Context context, SMSDTO smsDTO) {
179173
Log.e(TAG, "Device ID or API key not found");
180174
return;
181175
}
182-
183-
GatewayApiService apiService = ApiManager.getApiService();
184-
Call<SMSForwardResponseDTO> call = apiService.updateSMSStatus(deviceId, apiKey, smsDTO);
185-
186-
call.enqueue(new Callback<SMSForwardResponseDTO>() {
187-
@Override
188-
public void onResponse(Call<SMSForwardResponseDTO> call, Response<SMSForwardResponseDTO> response) {
189-
if (response.isSuccessful()) {
190-
Log.d(TAG, "SMS status updated successfully - ID: " + smsDTO.getSmsId() + ", Status: " + smsDTO.getStatus());
191-
} else {
192-
Log.e(TAG, "Failed to update SMS status. Response code: " + response.code());
193-
}
194-
}
195-
196-
@Override
197-
public void onFailure(Call<SMSForwardResponseDTO> call, Throwable t) {
198-
Log.e(TAG, "API call failed: " + t.getMessage());
199-
}
200-
});
176+
177+
SMSStatusUpdateWorker.enqueueWork(context, deviceId, apiKey, smsDTO);
201178
}
202179

203180
private static PendingIntent createSentPendingIntent(Context context, String smsId, String smsBatchId) {

0 commit comments

Comments
 (0)