diff --git a/README.md b/README.md index cee4ce3..fc8572d 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ from their application via a REST API. It utilizes android phones as SMS gateway ## Usage 1. Go to [textbee.dev](https://textbee.dev) and register or login with your account -2. Install the app on your android phone from [textbee.dev/android](https://textbee.dev/android) +2. Install the app on your android phone from [dl.textbee.dev](https://dl.textbee.dev) 3. Open the app and grant the permissions for SMS 4. Go to [textbee.dev/dashboard](https://textbee.dev/dashboard) and click register device/ generate API Key 5. Scan the QR code with the app or enter the API key manually @@ -23,10 +23,14 @@ from their application via a REST API. It utilizes android phones as SMS gateway const API_KEY = 'YOUR_API_KEY'; const DEVICE_ID = 'YOUR_DEVICE_ID'; -await axios.post(`https://api.textbee.dev/api/v1/gateway/devices/${DEVICE_ID}/sendSMS?apiKey=${API_KEY}`, { - receivers: [ '+251912345678' ], - smsBody: 'Hello World!', -}) +await axios.post(`https://api.textbee.dev/api/v1/gateway/devices/${DEVICE_ID}/sendSMS`, { + recipients: [ '+251912345678' ], + message: 'Hello World!', +}, { + headers: { + 'x-api-key': API_KEY, + }, +}); ``` diff --git a/android/app/build.gradle b/android/app/build.gradle index 9bf8284..2072a47 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -10,10 +10,16 @@ android { applicationId "com.vernu.sms" minSdk 24 targetSdk 32 - versionCode 9 - versionName "2.2.0" + versionCode 10 + versionName "2.3.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + +// javaCompileOptions { +// annotationProcessorOptions { +// arguments["room.schemaLocation"] = "$projectDir/schemas" +// } +// } } buildTypes { @@ -46,4 +52,9 @@ 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' -} \ No newline at end of file + +// def room_version = "2.4.2" +// implementation "androidx.room:room-runtime:$room_version" +// annotationProcessor "androidx.room:room-compiler:$room_version" +} + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index a84989d..a09b74d 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -3,8 +3,17 @@ xmlns:tools="http://schemas.android.com/tools" package="com.vernu.sms"> + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/java/com/vernu/sms/ApiManager.java b/android/app/src/main/java/com/vernu/sms/ApiManager.java new file mode 100644 index 0000000..e991b75 --- /dev/null +++ b/android/app/src/main/java/com/vernu/sms/ApiManager.java @@ -0,0 +1,33 @@ +package com.vernu.sms; + +import com.vernu.sms.services.GatewayApiService; + +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; + +public class ApiManager { + private static GatewayApiService apiService; + + public static GatewayApiService getApiService() { + if (apiService == null) { + apiService = createApiService(); + } + return apiService; + } + + private static GatewayApiService createApiService() { +// OkHttpClient.Builder httpClient = new OkHttpClient.Builder(); +// HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); +// loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); +// httpClient.addInterceptor(loggingInterceptor); + + Retrofit retrofit = new Retrofit.Builder() + .baseUrl(AppConstants.API_BASE_URL) +// .client(httpClient.build()) + .addConverterFactory(GsonConverterFactory.create()) + .build(); + apiService = retrofit.create(GatewayApiService.class); + + return retrofit.create(GatewayApiService.class); + } +} diff --git a/android/app/src/main/java/com/vernu/sms/AppConstants.java b/android/app/src/main/java/com/vernu/sms/AppConstants.java new file mode 100644 index 0000000..612c6a5 --- /dev/null +++ b/android/app/src/main/java/com/vernu/sms/AppConstants.java @@ -0,0 +1,19 @@ +package com.vernu.sms; + +import android.Manifest; + +public class AppConstants { + public static final String API_BASE_URL = "https://api.textbee.dev/api/v1/"; + public static final String[] requiredPermissions = new String[]{ + Manifest.permission.SEND_SMS, + Manifest.permission.READ_SMS, + Manifest.permission.RECEIVE_SMS, + Manifest.permission.READ_PHONE_STATE + }; + public static final String SHARED_PREFS_DEVICE_ID_KEY = "DEVICE_ID"; + public static final String SHARED_PREFS_API_KEY_KEY = "API_KEY"; + public static final String SHARED_PREFS_GATEWAY_ENABLED_KEY = "GATEWAY_ENABLED"; + public static final String SHARED_PREFS_PREFERRED_SIM_KEY = "PREFERRED_SIM"; + public static final String SHARED_PREFS_RECEIVE_SMS_ENABLED_KEY = "RECEIVE_SMS_ENABLED"; + public static final String SHARED_PREFS_TRACK_SENT_SMS_STATUS_KEY = "TRACK_SENT_SMS_STATUS"; +} diff --git a/android/app/src/main/java/com/vernu/sms/TextBeeUtils.java b/android/app/src/main/java/com/vernu/sms/TextBeeUtils.java new file mode 100644 index 0000000..e30c4b1 --- /dev/null +++ b/android/app/src/main/java/com/vernu/sms/TextBeeUtils.java @@ -0,0 +1,53 @@ +package com.vernu.sms; + +import android.Manifest; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Build; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; + +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; + +import com.vernu.sms.services.StickyNotificationService; + +import java.util.ArrayList; +import java.util.List; + +public class TextBeeUtils { + public static boolean isPermissionGranted(Context context, String permission) { + return ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED; + } + + public static List getAvailableSimSlots(Context context) { + + if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) { + return new ArrayList<>(); + } + + SubscriptionManager subscriptionManager = SubscriptionManager.from(context); + return subscriptionManager.getActiveSubscriptionInfoList(); + + } + + public static void startStickyNotificationService(Context context) { + + if(!isPermissionGranted(context, Manifest.permission.RECEIVE_SMS)){ + return; + } + + Intent notificationIntent = new Intent(context, StickyNotificationService.class); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + context.startForegroundService(notificationIntent); + } else { + context.startService(notificationIntent); + } + } + + public static void stopStickyNotificationService(Context context) { + Intent notificationIntent = new Intent(context, StickyNotificationService.class); + context.stopService(notificationIntent); + } +} diff --git a/android/app/src/main/java/com/vernu/sms/activities/MainActivity.java b/android/app/src/main/java/com/vernu/sms/activities/MainActivity.java index 19558d9..ff18656 100644 --- a/android/app/src/main/java/com/vernu/sms/activities/MainActivity.java +++ b/android/app/src/main/java/com/vernu/sms/activities/MainActivity.java @@ -3,18 +3,12 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; - -import android.Manifest; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; -import android.telephony.SubscriptionInfo; -import android.telephony.SubscriptionManager; import android.util.Log; import android.view.View; import android.widget.Button; @@ -25,95 +19,57 @@ import android.widget.Switch; import android.widget.TextView; import android.widget.Toast; - import com.google.android.material.snackbar.Snackbar; import com.google.firebase.messaging.FirebaseMessaging; import com.google.zxing.integration.android.IntentIntegrator; import com.google.zxing.integration.android.IntentResult; -import com.vernu.sms.services.GatewayApiService; +import com.vernu.sms.ApiManager; +import com.vernu.sms.AppConstants; +import com.vernu.sms.BuildConfig; +import com.vernu.sms.TextBeeUtils; import com.vernu.sms.R; import com.vernu.sms.dtos.RegisterDeviceInputDTO; import com.vernu.sms.dtos.RegisterDeviceResponseDTO; import com.vernu.sms.helpers.SharedPreferenceHelper; - -import java.util.ArrayList; -import java.util.List; - +import java.util.Arrays; +import java.util.Objects; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; -import retrofit2.Retrofit; -import retrofit2.converter.gson.GsonConverterFactory; public class MainActivity extends AppCompatActivity { private Context mContext; - private Retrofit retrofit; - private GatewayApiService gatewayApiService; - - private Switch gatewaySwitch; + private Switch gatewaySwitch, receiveSMSSwitch; private EditText apiKeyEditText, fcmTokenEditText; private Button registerDeviceBtn, grantSMSPermissionBtn, scanQRBtn; private ImageButton copyDeviceIdImgBtn; private TextView deviceBrandAndModelTxt, deviceIdTxt; - private RadioGroup defaultSimSlotRadioGroup; - - private static final int SEND_SMS_PERMISSION_REQUEST_CODE = 0; private static final int SCAN_QR_REQUEST_CODE = 49374; - - private static final String API_BASE_URL = "https://api.textbee.dev/api/v1/"; + private static final int PERMISSION_REQUEST_CODE = 0; private String deviceId = null; - + private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mContext = getApplicationContext(); - - retrofit = new Retrofit.Builder() - .baseUrl(API_BASE_URL) - .addConverterFactory(GsonConverterFactory.create()) - .build(); - gatewayApiService = retrofit.create(GatewayApiService.class); - - deviceId = SharedPreferenceHelper.getSharedPreferenceString(mContext, "DEVICE_ID", ""); - + deviceId = SharedPreferenceHelper.getSharedPreferenceString(mContext, AppConstants.SHARED_PREFS_DEVICE_ID_KEY, ""); setContentView(R.layout.activity_main); gatewaySwitch = findViewById(R.id.gatewaySwitch); + receiveSMSSwitch = findViewById(R.id.receiveSMSSwitch); apiKeyEditText = findViewById(R.id.apiKeyEditText); fcmTokenEditText = findViewById(R.id.fcmTokenEditText); registerDeviceBtn = findViewById(R.id.registerDeviceBtn); grantSMSPermissionBtn = findViewById(R.id.grantSMSPermissionBtn); scanQRBtn = findViewById(R.id.scanQRButton); - - deviceBrandAndModelTxt = findViewById(R.id.deviceBrandAndModelTxt); deviceIdTxt = findViewById(R.id.deviceIdTxt); - copyDeviceIdImgBtn = findViewById(R.id.copyDeviceIdImgBtn); - defaultSimSlotRadioGroup = findViewById(R.id.defaultSimSlotRadioGroup); - - try { - getAvailableSimSlots().forEach(subscriptionInfo -> { - RadioButton radioButton = new RadioButton(mContext); - radioButton.setText(subscriptionInfo.getDisplayName().toString()); - radioButton.setId(subscriptionInfo.getSubscriptionId()); - radioButton.setOnClickListener(view -> { - SharedPreferenceHelper.setSharedPreferenceInt(mContext, "PREFERED_SIM", subscriptionInfo.getSubscriptionId()); - }); - radioButton.setChecked(subscriptionInfo.getSubscriptionId() == SharedPreferenceHelper.getSharedPreferenceInt(mContext, "PREFERED_SIM", 0)); - defaultSimSlotRadioGroup.addView(radioButton); - }); - } catch (Exception e) { - Snackbar.make(defaultSimSlotRadioGroup.getRootView(), "Error: " + e.getMessage(), Snackbar.LENGTH_LONG).show(); - Log.e("SIM_SLOT_ERROR", e.getMessage()); - } - - deviceIdTxt.setText(deviceId); deviceBrandAndModelTxt.setText(Build.BRAND + " " + Build.MODEL); @@ -123,14 +79,19 @@ protected void onCreate(Bundle savedInstanceState) { registerDeviceBtn.setText("Update"); } - if (isSMSPermissionGranted(mContext) && isReadPhoneStatePermissionGranted(mContext)) { + String[] missingPermissions = Arrays.stream(AppConstants.requiredPermissions).filter(permission -> !TextBeeUtils.isPermissionGranted(mContext, permission)).toArray(String[]::new); + if (missingPermissions.length == 0) { grantSMSPermissionBtn.setEnabled(false); - grantSMSPermissionBtn.setText("SMS Permission Granted"); + grantSMSPermissionBtn.setText("Permission Granted"); + renderAvailableSimOptions(); } else { + Snackbar.make(grantSMSPermissionBtn, "Please Grant Required Permissions to continue: " + Arrays.toString(missingPermissions), Snackbar.LENGTH_SHORT).show(); grantSMSPermissionBtn.setEnabled(true); - grantSMSPermissionBtn.setOnClickListener(view -> handleSMSRequestPermission(view)); + grantSMSPermissionBtn.setOnClickListener(this::handleRequestPermissions); } +// TextBeeUtils.startStickyNotificationService(mContext); + copyDeviceIdImgBtn.setOnClickListener(view -> { ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); ClipData clip = ClipData.newPlainText("Device ID", deviceId); @@ -138,70 +99,120 @@ protected void onCreate(Bundle savedInstanceState) { Snackbar.make(view, "Copied", Snackbar.LENGTH_LONG).show(); }); - apiKeyEditText.setText(SharedPreferenceHelper.getSharedPreferenceString(mContext, "API_KEY", "")); - - gatewaySwitch.setChecked(SharedPreferenceHelper.getSharedPreferenceBoolean(mContext, "GATEWAY_ENABLED", false)); + apiKeyEditText.setText(SharedPreferenceHelper.getSharedPreferenceString(mContext, AppConstants.SHARED_PREFS_API_KEY_KEY, "")); + gatewaySwitch.setChecked(SharedPreferenceHelper.getSharedPreferenceBoolean(mContext, AppConstants.SHARED_PREFS_GATEWAY_ENABLED_KEY, false)); gatewaySwitch.setOnCheckedChangeListener((compoundButton, isCheked) -> { View view = compoundButton.getRootView(); compoundButton.setEnabled(false); String key = apiKeyEditText.getText().toString(); - RegisterDeviceInputDTO registerDeviceInput = new RegisterDeviceInputDTO(); registerDeviceInput.setEnabled(isCheked); + registerDeviceInput.setAppVersionCode(BuildConfig.VERSION_CODE); + registerDeviceInput.setAppVersionName(BuildConfig.VERSION_NAME); - Call apiCall = gatewayApiService.updateDevice(deviceId, key, registerDeviceInput); + Call apiCall = ApiManager.getApiService().updateDevice(deviceId, key, registerDeviceInput); apiCall.enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { - - if (response.isSuccessful()) { - Snackbar.make(view, "Gateway " + (isCheked ? "enabled" : "disabled"), Snackbar.LENGTH_LONG).show(); - SharedPreferenceHelper.setSharedPreferenceBoolean(mContext, "GATEWAY_ENABLED", isCheked); - compoundButton.setChecked(Boolean.TRUE.equals(response.body().data.get("enabled"))); - } else { + Log.d(TAG, response.toString()); + if (!response.isSuccessful()) { Snackbar.make(view, response.message(), Snackbar.LENGTH_LONG).show(); + compoundButton.setEnabled(true); + return; } + Snackbar.make(view, "Gateway " + (isCheked ? "enabled" : "disabled"), Snackbar.LENGTH_LONG).show(); + SharedPreferenceHelper.setSharedPreferenceBoolean(mContext, AppConstants.SHARED_PREFS_GATEWAY_ENABLED_KEY, isCheked); + boolean enabled = Boolean.TRUE.equals(Objects.requireNonNull(response.body()).data.get("enabled")); + compoundButton.setChecked(enabled); +// if (enabled) { +// TextBeeUtils.startStickyNotificationService(mContext); +// } else { +// TextBeeUtils.stopStickyNotificationService(mContext); +// } compoundButton.setEnabled(true); } - @Override public void onFailure(Call call, Throwable t) { Snackbar.make(view, "An error occured :(", Snackbar.LENGTH_LONG).show(); compoundButton.setEnabled(true); - } }); + }); - + receiveSMSSwitch.setChecked(SharedPreferenceHelper.getSharedPreferenceBoolean(mContext, AppConstants.SHARED_PREFS_RECEIVE_SMS_ENABLED_KEY, false)); + receiveSMSSwitch.setOnCheckedChangeListener((compoundButton, isCheked) -> { + View view = compoundButton.getRootView(); + SharedPreferenceHelper.setSharedPreferenceBoolean(mContext, AppConstants.SHARED_PREFS_RECEIVE_SMS_ENABLED_KEY, isCheked); + compoundButton.setChecked(isCheked); + Snackbar.make(view, "Receive SMS " + (isCheked ? "enabled" : "disabled"), Snackbar.LENGTH_LONG).show(); }); + // TODO: check gateway status/api key/device validity and update UI accordingly registerDeviceBtn.setOnClickListener(view -> handleRegisterDevice()); - scanQRBtn.setOnClickListener(view -> { IntentIntegrator intentIntegrator = new IntentIntegrator(MainActivity.this); intentIntegrator.setPrompt("Go to textbee.dev/dashboard and click Register Device to generate QR Code"); intentIntegrator.setRequestCode(SCAN_QR_REQUEST_CODE); intentIntegrator.initiateScan(); }); + } + private void renderAvailableSimOptions() { + try { + defaultSimSlotRadioGroup.removeAllViews(); + RadioButton defaultSimSlotRadioBtn = new RadioButton(mContext); + defaultSimSlotRadioBtn.setText("Device Default"); + defaultSimSlotRadioBtn.setId((int)123456); + defaultSimSlotRadioGroup.addView(defaultSimSlotRadioBtn); + TextBeeUtils.getAvailableSimSlots(mContext).forEach(subscriptionInfo -> { + String simInfo = "SIM " + (subscriptionInfo.getSimSlotIndex() + 1) + " (" + subscriptionInfo.getDisplayName() + ")"; + RadioButton radioButton = new RadioButton(mContext); + radioButton.setText(simInfo); + radioButton.setId(subscriptionInfo.getSubscriptionId()); + defaultSimSlotRadioGroup.addView(radioButton); + }); + int preferredSim = SharedPreferenceHelper.getSharedPreferenceInt(mContext, AppConstants.SHARED_PREFS_PREFERRED_SIM_KEY, -1); + if (preferredSim == -1) { + defaultSimSlotRadioGroup.check(defaultSimSlotRadioBtn.getId()); + } else { + defaultSimSlotRadioGroup.check(preferredSim); + } + defaultSimSlotRadioGroup.setOnCheckedChangeListener((radioGroup, i) -> { + RadioButton radioButton = findViewById(i); + if (radioButton == null) { + return; + } + radioButton.setChecked(true); + if("Device Default".equals(radioButton.getText().toString())) { + SharedPreferenceHelper.clearSharedPreference(mContext, AppConstants.SHARED_PREFS_PREFERRED_SIM_KEY); + } else { + SharedPreferenceHelper.setSharedPreferenceInt(mContext, AppConstants.SHARED_PREFS_PREFERRED_SIM_KEY, radioButton.getId()); + } + }); + } catch (Exception e) { + Snackbar.make(defaultSimSlotRadioGroup.getRootView(), "Error: " + e.getMessage(), Snackbar.LENGTH_LONG).show(); + Log.e(TAG, "SIM_SLOT_ERROR "+ e.getMessage()); + } } @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); - switch (requestCode) { - case SEND_SMS_PERMISSION_REQUEST_CODE: { - if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - Toast.makeText(mContext, "Yay! Permission Granted.", Toast.LENGTH_LONG).show(); - } else { - Toast.makeText(mContext, "Permission Denied :(", Toast.LENGTH_LONG).show(); - return; - } - } - } + if (requestCode != PERMISSION_REQUEST_CODE) { + return; + } + boolean allPermissionsGranted = Arrays.stream(permissions).allMatch(permission -> TextBeeUtils.isPermissionGranted(mContext, permission)); + if (allPermissionsGranted) { + Snackbar.make(findViewById(R.id.grantSMSPermissionBtn), "All Permissions Granted", Snackbar.LENGTH_SHORT).show(); + grantSMSPermissionBtn.setEnabled(false); + grantSMSPermissionBtn.setText("Permission Granted"); + renderAvailableSimOptions(); + } else { + Snackbar.make(findViewById(R.id.grantSMSPermissionBtn), "Please Grant Required Permissions to continue", Snackbar.LENGTH_SHORT).show(); + } } private void handleRegisterDevice() { @@ -230,86 +241,63 @@ private void handleRegisterDevice() { registerDeviceInput.setModel(Build.MODEL); registerDeviceInput.setBuildId(Build.ID); registerDeviceInput.setOs(Build.VERSION.BASE_OS); + registerDeviceInput.setAppVersionCode(BuildConfig.VERSION_CODE); + registerDeviceInput.setAppVersionName(BuildConfig.VERSION_NAME); - - Call apiCall = gatewayApiService.registerDevice(newKey, registerDeviceInput); + Call apiCall = ApiManager.getApiService().registerDevice(newKey, registerDeviceInput); apiCall.enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { - - if (response.isSuccessful()) { - SharedPreferenceHelper.setSharedPreferenceString(mContext, "API_KEY", newKey); - Log.e("API_RESP", response.toString()); - Snackbar.make(view, "Device Registration Successful :)", Snackbar.LENGTH_LONG).show(); - deviceId = response.body().data.get("_id").toString(); - deviceIdTxt.setText(deviceId); - SharedPreferenceHelper.setSharedPreferenceString(mContext, "DEVICE_ID", deviceId); - - } else { + Log.d(TAG, response.toString()); + if (!response.isSuccessful()) { Snackbar.make(view, response.message(), Snackbar.LENGTH_LONG).show(); + registerDeviceBtn.setEnabled(true); + registerDeviceBtn.setText("Update"); + return; } + SharedPreferenceHelper.setSharedPreferenceString(mContext, AppConstants.SHARED_PREFS_API_KEY_KEY, newKey); + Snackbar.make(view, "Device Registration Successful :)", Snackbar.LENGTH_LONG).show(); + deviceId = response.body().data.get("_id").toString(); + deviceIdTxt.setText(deviceId); + SharedPreferenceHelper.setSharedPreferenceString(mContext, AppConstants.SHARED_PREFS_DEVICE_ID_KEY, deviceId); registerDeviceBtn.setEnabled(true); registerDeviceBtn.setText("Update"); - } + } @Override public void onFailure(Call call, Throwable t) { Snackbar.make(view, "An error occured :(", Snackbar.LENGTH_LONG).show(); registerDeviceBtn.setEnabled(true); registerDeviceBtn.setText("Update"); - } }); }); } - private void handleSMSRequestPermission(View view) { - if (isSMSPermissionGranted(mContext) && isReadPhoneStatePermissionGranted(mContext)) { + private void handleRequestPermissions(View view) { + boolean allPermissionsGranted = Arrays.stream(AppConstants.requiredPermissions).allMatch(permission -> TextBeeUtils.isPermissionGranted(mContext, permission)); + if (allPermissionsGranted) { Snackbar.make(view, "Already got permissions", Snackbar.LENGTH_SHORT).show(); - } else { - Snackbar.make(view, "Grant SMS Permissions to continue", Snackbar.LENGTH_SHORT).show(); - ActivityCompat.requestPermissions(MainActivity.this, - new String[]{Manifest.permission.SEND_SMS, Manifest.permission.READ_PHONE_STATE - }, SEND_SMS_PERMISSION_REQUEST_CODE); - + return; } + String[] permissionsToRequest = Arrays.stream(AppConstants.requiredPermissions).filter(permission -> !TextBeeUtils.isPermissionGranted(mContext, permission)).toArray(String[]::new); + Snackbar.make(view, "Please Grant Required Permissions to continue", Snackbar.LENGTH_SHORT).show(); + ActivityCompat.requestPermissions(this, permissionsToRequest, PERMISSION_REQUEST_CODE); } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); - if (requestCode == SCAN_QR_REQUEST_CODE) { IntentResult intentResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data); - - if (intentResult != null) { - if (intentResult.getContents() == null) { - Toast.makeText(getBaseContext(), "Canceled", Toast.LENGTH_SHORT).show(); - } else { - String scannedQR = intentResult.getContents(); - apiKeyEditText.setText(scannedQR); - handleRegisterDevice(); - } + if (intentResult == null || intentResult.getContents() == null) { + Toast.makeText(getBaseContext(), "Canceled", Toast.LENGTH_SHORT).show(); + return; } + String scannedQR = intentResult.getContents(); + apiKeyEditText.setText(scannedQR); + handleRegisterDevice(); } } - private boolean isSMSPermissionGranted(Context context) { - return ContextCompat.checkSelfPermission(context, Manifest.permission.SEND_SMS) == PackageManager.PERMISSION_GRANTED; - } - - private boolean isReadPhoneStatePermissionGranted(Context context) { - return ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED; - } - - private List getAvailableSimSlots() { - - if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) { - return new ArrayList<>(); - } - - SubscriptionManager subscriptionManager = SubscriptionManager.from(mContext); - return subscriptionManager.getActiveSubscriptionInfoList(); - - } } \ No newline at end of file diff --git a/android/app/src/main/java/com/vernu/sms/database/local/AppDatabase.java b/android/app/src/main/java/com/vernu/sms/database/local/AppDatabase.java new file mode 100644 index 0000000..8ae2586 --- /dev/null +++ b/android/app/src/main/java/com/vernu/sms/database/local/AppDatabase.java @@ -0,0 +1,25 @@ +//package com.vernu.sms.database.local; +// +//import android.content.Context; +//import androidx.room.Database; +//import androidx.room.Room; +//import androidx.room.RoomDatabase; +// +//@Database(entities = {SMS.class}, version = 2) +//public abstract class AppDatabase extends RoomDatabase { +// private static volatile AppDatabase INSTANCE; +// +// public static AppDatabase getInstance(Context context) { +// if (INSTANCE == null) { +// synchronized (AppDatabase.class) { +// if (INSTANCE == null) { +// INSTANCE = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "db1") +// .build(); +// } +// } +// } +// return INSTANCE; +// } +// +// public abstract SMSDao localReceivedSMSDao(); +//} \ No newline at end of file diff --git a/android/app/src/main/java/com/vernu/sms/database/local/DateConverter.java b/android/app/src/main/java/com/vernu/sms/database/local/DateConverter.java new file mode 100644 index 0000000..e540f0b --- /dev/null +++ b/android/app/src/main/java/com/vernu/sms/database/local/DateConverter.java @@ -0,0 +1,17 @@ +//package com.vernu.sms.database.local; +// +//import androidx.room.TypeConverter; +// +//import java.util.Date; +// +//public class DateConverter { +// @TypeConverter +// public static Date toDate(Long dateLong) { +// return dateLong == null ? null : new Date(dateLong); +// } +// +// @TypeConverter +// public static Long fromDate(Date date) { +// return date == null ? null : date.getTime(); +// } +//} \ No newline at end of file diff --git a/android/app/src/main/java/com/vernu/sms/database/local/SMS.java b/android/app/src/main/java/com/vernu/sms/database/local/SMS.java new file mode 100644 index 0000000..297417c --- /dev/null +++ b/android/app/src/main/java/com/vernu/sms/database/local/SMS.java @@ -0,0 +1,193 @@ +//package com.vernu.sms.database.local; +// +//import androidx.annotation.NonNull; +//import androidx.room.ColumnInfo; +//import androidx.room.Entity; +//import androidx.room.PrimaryKey; +//import androidx.room.TypeConverters; +// +//import java.util.Date; +// +//@Entity(tableName = "sms") +//@TypeConverters(DateConverter.class) +//public class SMS { +// +// public SMS() { +// type = null; +// } +// +// @PrimaryKey(autoGenerate = true) +// private int id; +// +// // This is the ID of the SMS in the server +// @ColumnInfo(name = "_id") +// private String _id; +// +// @ColumnInfo(name = "message") +// private String message = ""; +// +// @ColumnInfo(name = "encrypted_message") +// private String encryptedMessage = ""; +// +// @ColumnInfo(name = "is_encrypted", defaultValue = "0") +// private boolean isEncrypted = false; +// +// @ColumnInfo(name = "sender") +// private String sender; +// +// @ColumnInfo(name = "recipient") +// private String recipient; +// +// @ColumnInfo(name = "requested_at") +// private Date requestedAt; +// +// @ColumnInfo(name = "sent_at") +// private Date sentAt; +// +// @ColumnInfo(name = "delivered_at") +// private Date deliveredAt; +// +// @ColumnInfo(name = "received_at") +// private Date receivedAt; +// +// @NonNull +// @ColumnInfo(name = "type") +// private String type; +// +// @ColumnInfo(name = "server_acknowledged_at") +// private Date serverAcknowledgedAt; +// +// public boolean hasServerAcknowledged() { +// return serverAcknowledgedAt != null; +// } +// +// @ColumnInfo(name = "last_acknowledged_request_at") +// private Date lastAcknowledgedRequestAt; +// +// @ColumnInfo(name = "retry_count", defaultValue = "0") +// private int retryCount = 0; +// +// public int getId() { +// return id; +// } +// +// public void setId(int id) { +// this.id = id; +// } +// +// public String get_id() { +// return _id; +// } +// +// public void set_id(String _id) { +// this._id = _id; +// } +// +// public String getMessage() { +// return message; +// } +// +// public void setMessage(String message) { +// this.message = message; +// } +// +// public String getEncryptedMessage() { +// return encryptedMessage; +// } +// +// public void setEncryptedMessage(String encryptedMessage) { +// this.encryptedMessage = encryptedMessage; +// } +// +// public boolean getIsEncrypted() { +// return isEncrypted; +// } +// +// public void setIsEncrypted(boolean isEncrypted) { +// this.isEncrypted = isEncrypted; +// } +// +// public String getSender() { +// return sender; +// } +// +// public void setSender(String sender) { +// this.sender = sender; +// } +// +// public String getRecipient() { +// return recipient; +// } +// +// public void setRecipient(String recipient) { +// this.recipient = recipient; +// } +// +// public Date getServerAcknowledgedAt() { +// return serverAcknowledgedAt; +// } +// +// public void setServerAcknowledgedAt(Date serverAcknowledgedAt) { +// this.serverAcknowledgedAt = serverAcknowledgedAt; +// } +// +// +// +// public Date getRequestedAt() { +// return requestedAt; +// } +// +// public void setRequestedAt(Date requestedAt) { +// this.requestedAt = requestedAt; +// } +// +// public Date getSentAt() { +// return sentAt; +// } +// +// public void setSentAt(Date sentAt) { +// this.sentAt = sentAt; +// } +// +// public Date getDeliveredAt() { +// return deliveredAt; +// } +// +// public void setDeliveredAt(Date deliveredAt) { +// this.deliveredAt = deliveredAt; +// } +// +// public Date getReceivedAt() { +// return receivedAt; +// } +// +// public void setReceivedAt(Date receivedAt) { +// this.receivedAt = receivedAt; +// } +// +// @NonNull +// public String getType() { +// return type; +// } +// +// public void setType(@NonNull String type) { +// this.type = type; +// } +// +// +// public Date getLastAcknowledgedRequestAt() { +// return lastAcknowledgedRequestAt; +// } +// +// public void setLastAcknowledgedRequestAt(Date lastAcknowledgedRequestAt) { +// this.lastAcknowledgedRequestAt = lastAcknowledgedRequestAt; +// } +// +// public int getRetryCount() { +// return retryCount; +// } +// +// public void setRetryCount(int retryCount) { +// this.retryCount = retryCount; +// } +//} \ No newline at end of file diff --git a/android/app/src/main/java/com/vernu/sms/database/local/SMSDao.java b/android/app/src/main/java/com/vernu/sms/database/local/SMSDao.java new file mode 100644 index 0000000..541ba1e --- /dev/null +++ b/android/app/src/main/java/com/vernu/sms/database/local/SMSDao.java @@ -0,0 +1,27 @@ +//package com.vernu.sms.database.local; +// +//import androidx.room.Dao; +//import androidx.room.Delete; +//import androidx.room.Insert; +//import androidx.room.OnConflictStrategy; +//import androidx.room.Query; +// +//import java.util.List; +// +//@Dao +//public interface SMSDao { +// +// @Query("SELECT * FROM sms") +// List getAll(); +// +// @Query("SELECT * FROM sms WHERE id IN (:smsIds)") +// List loadAllByIds(int[] smsIds); +// +// @Insert(onConflict = OnConflictStrategy.REPLACE) +// void insertAll(SMS... sms); +// +// +// @Delete +// void delete(SMS sms); +// +//} \ No newline at end of file diff --git a/android/app/src/main/java/com/vernu/sms/dtos/RegisterDeviceInputDTO.java b/android/app/src/main/java/com/vernu/sms/dtos/RegisterDeviceInputDTO.java index f803814..f7e47fa 100644 --- a/android/app/src/main/java/com/vernu/sms/dtos/RegisterDeviceInputDTO.java +++ b/android/app/src/main/java/com/vernu/sms/dtos/RegisterDeviceInputDTO.java @@ -11,7 +11,7 @@ public class RegisterDeviceInputDTO { private String os; private String osVersion; private String appVersionName; - private String appVersionCode; + private int appVersionCode; public RegisterDeviceInputDTO() { } @@ -100,11 +100,11 @@ public void setAppVersionName(String appVersionName) { this.appVersionName = appVersionName; } - public String getAppVersionCode() { + public int getAppVersionCode() { return appVersionCode; } - public void setAppVersionCode(String appVersionCode) { + public void setAppVersionCode(int appVersionCode) { this.appVersionCode = appVersionCode; } } diff --git a/android/app/src/main/java/com/vernu/sms/dtos/SMSDTO.java b/android/app/src/main/java/com/vernu/sms/dtos/SMSDTO.java new file mode 100644 index 0000000..c73fc18 --- /dev/null +++ b/android/app/src/main/java/com/vernu/sms/dtos/SMSDTO.java @@ -0,0 +1,42 @@ +package com.vernu.sms.dtos; + +import java.util.Date; + +public class SMSDTO { + private String sender; + private String message = ""; + private Date receivedAt; + + public SMSDTO() { + } + + public SMSDTO(String sender, String message, Date receivedAt) { + this.sender = sender; + this.message = message; + this.receivedAt = receivedAt; + } + + public String getSender() { + return sender; + } + + public void setSender(String sender) { + this.sender = sender; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public Date getReceivedAt() { + return receivedAt; + } + + public void setReceivedAt(Date receivedAt) { + this.receivedAt = receivedAt; + } +} diff --git a/android/app/src/main/java/com/vernu/sms/dtos/SMSForwardResponseDTO.java b/android/app/src/main/java/com/vernu/sms/dtos/SMSForwardResponseDTO.java new file mode 100644 index 0000000..47aebf9 --- /dev/null +++ b/android/app/src/main/java/com/vernu/sms/dtos/SMSForwardResponseDTO.java @@ -0,0 +1,9 @@ +package com.vernu.sms.dtos; + +public class SMSForwardResponseDTO { + + public SMSForwardResponseDTO() { + } + + +} diff --git a/android/app/src/main/java/com/vernu/sms/helpers/SharedPreferenceHelper.java b/android/app/src/main/java/com/vernu/sms/helpers/SharedPreferenceHelper.java index f478598..ea7386c 100644 --- a/android/app/src/main/java/com/vernu/sms/helpers/SharedPreferenceHelper.java +++ b/android/app/src/main/java/com/vernu/sms/helpers/SharedPreferenceHelper.java @@ -44,4 +44,11 @@ public static boolean getSharedPreferenceBoolean(Context context, String key, bo SharedPreferences settings = context.getSharedPreferences(PREF_FILE, 0); return settings.getBoolean(key, defValue); } + + public static void clearSharedPreference(Context context, String key) { + SharedPreferences settings = context.getSharedPreferences(PREF_FILE, 0); + SharedPreferences.Editor editor = settings.edit(); + editor.remove(key); + editor.apply(); + } } \ No newline at end of file diff --git a/android/app/src/main/java/com/vernu/sms/models/SMSPayload.java b/android/app/src/main/java/com/vernu/sms/models/SMSPayload.java index 4e5417a..db98ba1 100644 --- a/android/app/src/main/java/com/vernu/sms/models/SMSPayload.java +++ b/android/app/src/main/java/com/vernu/sms/models/SMSPayload.java @@ -1,27 +1,30 @@ package com.vernu.sms.models; public class SMSPayload { + + private String[] recipients; + private String message; + + // Legacy fields that are no longer used private String[] receivers; private String smsBody; - public SMSPayload(String[] receivers, String smsBody) { - this.receivers = receivers; - this.smsBody = smsBody; + public SMSPayload() { } - public String[] getReceivers() { - return receivers; + public String[] getRecipients() { + return recipients; } - public void setReceivers(String[] receivers) { - this.receivers = receivers; + public void setRecipients(String[] recipients) { + this.recipients = recipients; } - public String getSmsBody() { - return smsBody; + public String getMessage() { + return message; } - public void setSmsBody(String smsBody) { - this.smsBody = smsBody; + public void setMessage(String message) { + this.message = message; } } diff --git a/android/app/src/main/java/com/vernu/sms/receivers/BootCompletedReceiver.java b/android/app/src/main/java/com/vernu/sms/receivers/BootCompletedReceiver.java new file mode 100644 index 0000000..6996b7e --- /dev/null +++ b/android/app/src/main/java/com/vernu/sms/receivers/BootCompletedReceiver.java @@ -0,0 +1,21 @@ +package com.vernu.sms.receivers; + +import android.Manifest; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.Build; + +import com.vernu.sms.TextBeeUtils; +import com.vernu.sms.services.StickyNotificationService; + +public class BootCompletedReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { +// if(TextBeeUtils.isPermissionGranted(context, Manifest.permission.RECEIVE_SMS)){ +// TextBeeUtils.startStickyNotificationService(context); +// } + } + } +} diff --git a/android/app/src/main/java/com/vernu/sms/receivers/SMSBroadcastReceiver.java b/android/app/src/main/java/com/vernu/sms/receivers/SMSBroadcastReceiver.java new file mode 100644 index 0000000..2e7ab55 --- /dev/null +++ b/android/app/src/main/java/com/vernu/sms/receivers/SMSBroadcastReceiver.java @@ -0,0 +1,101 @@ +package com.vernu.sms.receivers; + +import android.content.BroadcastReceiver; +import android.content.Context; +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 java.util.Date; +import java.util.Objects; + +import retrofit2.Call; +import retrofit2.Response; + +public class SMSBroadcastReceiver extends BroadcastReceiver { + private static final String TAG = "SMSBroadcastReceiver"; + + @Override + public void onReceive(Context context, Intent intent) { + Log.d(TAG, "onReceive: " + intent.getAction()); + + if (!Objects.equals(intent.getAction(), Telephony.Sms.Intents.SMS_RECEIVED_ACTION)) { + Log.d(TAG, "Not Valid intent"); + return; + } + + SmsMessage[] messages = Telephony.Sms.Intents.getMessagesFromIntent(intent); + if (messages == null) { + Log.d(TAG, "No messages found"); + return; + } + + String deviceId = SharedPreferenceHelper.getSharedPreferenceString(context, AppConstants.SHARED_PREFS_DEVICE_ID_KEY, ""); + String apiKey = SharedPreferenceHelper.getSharedPreferenceString(context, AppConstants.SHARED_PREFS_API_KEY_KEY, ""); + boolean receiveSMSEnabled = SharedPreferenceHelper.getSharedPreferenceBoolean(context, AppConstants.SHARED_PREFS_RECEIVE_SMS_ENABLED_KEY, false); + + if (deviceId.isEmpty() || apiKey.isEmpty() || !receiveSMSEnabled) { + Log.d(TAG, "Device ID or API Key is empty or Receive SMS Feature is disabled"); + return; + } + +// SMS receivedSMS = new SMS(); +// receivedSMS.setType("RECEIVED"); +// for (SmsMessage message : messages) { +// receivedSMS.setMessage(receivedSMS.getMessage() + message.getMessageBody()); +// receivedSMS.setSender(message.getOriginatingAddress()); +// receivedSMS.setReceivedAt(new Date(message.getTimestampMillis())); +// } + + SMSDTO receivedSMSDTO = new SMSDTO(); + + for (SmsMessage message : messages) { + receivedSMSDTO.setMessage(receivedSMSDTO.getMessage() + message.getMessageBody()); + receivedSMSDTO.setSender(message.getOriginatingAddress()); + receivedSMSDTO.setReceivedAt(new Date(message.getTimestampMillis())); + } +// receivedSMSDTO.setSender(receivedSMS.getSender()); +// receivedSMSDTO.setMessage(receivedSMS.getMessage()); +// receivedSMSDTO.setReceivedAt(receivedSMS.getReceivedAt()); + + Call apiCall = ApiManager.getApiService().sendReceivedSMS(deviceId, apiKey, receivedSMSDTO); + apiCall.enqueue(new retrofit2.Callback() { + @Override + public void onResponse(Call call, Response 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 call, Throwable t) { + Log.e(TAG, "Failed to send SMS to server", t); +// receivedSMS.setServerAcknowledgedAt(null); +// receivedSMS.setLastAcknowledgedRequestAt(new Date()); +// updateLocalReceivedSMS(receivedSMS, context); + } + }); + } + +// private void updateLocalReceivedSMS(SMS localReceivedSMS, Context context) { +// Executors.newSingleThreadExecutor().execute(() -> { +// AppDatabase appDatabase = AppDatabase.getInstance(context); +// appDatabase.localReceivedSMSDao().insertAll(localReceivedSMS); +// }); +// } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/vernu/sms/services/FCMService.java b/android/app/src/main/java/com/vernu/sms/services/FCMService.java index bae9b2c..b113084 100644 --- a/android/app/src/main/java/com/vernu/sms/services/FCMService.java +++ b/android/app/src/main/java/com/vernu/sms/services/FCMService.java @@ -9,42 +9,40 @@ import android.net.Uri; import android.os.Build; import android.util.Log; - import androidx.core.app.NotificationCompat; - import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.RemoteMessage; import com.google.gson.Gson; +import com.vernu.sms.AppConstants; import com.vernu.sms.R; import com.vernu.sms.activities.MainActivity; import com.vernu.sms.helpers.SMSHelper; import com.vernu.sms.helpers.SharedPreferenceHelper; import com.vernu.sms.models.SMSPayload; - public class FCMService extends FirebaseMessagingService { - private static final String TAG = "MyFirebaseMsgService"; + private static final String TAG = "FirebaseMessagingService"; private static final String DEFAULT_NOTIFICATION_CHANNEL_ID = "N1"; @Override public void onMessageReceived(RemoteMessage remoteMessage) { - Log.d("FCM_MESSAGE", remoteMessage.getData().toString()); + Log.d(TAG, remoteMessage.getData().toString()); Gson gson = new Gson(); SMSPayload smsPayload = gson.fromJson(remoteMessage.getData().get("smsData"), SMSPayload.class); // Check if message contains a data payload. if (remoteMessage.getData().size() > 0) { - int preferedSim = SharedPreferenceHelper.getSharedPreferenceInt(this, "PREFERED_SIM", -1); - for (String receiver : smsPayload.getReceivers()) { + int preferedSim = SharedPreferenceHelper.getSharedPreferenceInt(this, AppConstants.SHARED_PREFS_PREFERRED_SIM_KEY, -1); + for (String receiver : smsPayload.getRecipients()) { if(preferedSim == -1) { - SMSHelper.sendSMS(receiver, smsPayload.getSmsBody()); + SMSHelper.sendSMS(receiver, smsPayload.getMessage()); continue; } try { - SMSHelper.sendSMSFromSpecificSim(receiver, smsPayload.getSmsBody(), preferedSim); + SMSHelper.sendSMSFromSpecificSim(receiver, smsPayload.getMessage(), preferedSim); } catch(Exception e) { Log.d("SMS_SEND_ERROR", e.getMessage()); } diff --git a/android/app/src/main/java/com/vernu/sms/services/GatewayApiService.java b/android/app/src/main/java/com/vernu/sms/services/GatewayApiService.java index 73e546e..b1fffec 100644 --- a/android/app/src/main/java/com/vernu/sms/services/GatewayApiService.java +++ b/android/app/src/main/java/com/vernu/sms/services/GatewayApiService.java @@ -1,19 +1,24 @@ package com.vernu.sms.services; +import com.vernu.sms.dtos.SMSDTO; +import com.vernu.sms.dtos.SMSForwardResponseDTO; import com.vernu.sms.dtos.RegisterDeviceInputDTO; import com.vernu.sms.dtos.RegisterDeviceResponseDTO; import retrofit2.Call; import retrofit2.http.Body; +import retrofit2.http.Header; import retrofit2.http.PATCH; import retrofit2.http.POST; import retrofit2.http.Path; -import retrofit2.http.Query; public interface GatewayApiService { @POST("gateway/devices") - Call registerDevice(@Query("apiKey") String apiKey, @Body() RegisterDeviceInputDTO body); + Call registerDevice(@Header("x-api-key") String apiKey, @Body() RegisterDeviceInputDTO body); @PATCH("gateway/devices/{deviceId}") - Call updateDevice(@Path("deviceId") String deviceId, @Query("apiKey") String apiKey, @Body() RegisterDeviceInputDTO body); + Call updateDevice(@Path("deviceId") String deviceId, @Header("x-api-key") String apiKey, @Body() RegisterDeviceInputDTO body); + + @POST("gateway/devices/{deviceId}/receiveSMS") + Call sendReceivedSMS(@Path("deviceId") String deviceId, @Header("x-api-key") String apiKey, @Body() SMSDTO body); } \ No newline at end of file diff --git a/android/app/src/main/java/com/vernu/sms/services/StickyNotificationService.java b/android/app/src/main/java/com/vernu/sms/services/StickyNotificationService.java new file mode 100644 index 0000000..b320563 --- /dev/null +++ b/android/app/src/main/java/com/vernu/sms/services/StickyNotificationService.java @@ -0,0 +1,82 @@ +package com.vernu.sms.services; + +import android.app.*; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.IBinder; +import android.provider.Telephony; +import android.util.Log; +import android.widget.Toast; + +import androidx.core.app.NotificationCompat; + +import com.vernu.sms.R; +import com.vernu.sms.activities.MainActivity; +import com.vernu.sms.receivers.SMSBroadcastReceiver; + +public class StickyNotificationService extends Service { + + private static final String TAG = "StickyNotificationService"; + private final BroadcastReceiver receiver = new SMSBroadcastReceiver(); + + @Override + public IBinder onBind(Intent intent) { + Log.i(TAG, "Service onBind " + intent.getAction()); + return null; + } + + @Override + public void onCreate() { + super.onCreate(); + Log.i(TAG, "Service Started"); + + +// IntentFilter filter = new IntentFilter(); +// filter.addAction(Telephony.Sms.Intents.SMS_RECEIVED_ACTION); +// filter.addAction(android.telephony.TelephonyManager.ACTION_PHONE_STATE_CHANGED); +// registerReceiver(receiver, filter); +// +// Notification notification = createNotification(); +// startForeground(1, notification); + + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Log.i(TAG, "Received start id " + startId + ": " + intent); + return START_STICKY; + } + + @Override + public void onDestroy() { + super.onDestroy(); +// unregisterReceiver(receiver); + Log.i(TAG, "StickyNotificationService destroyed"); +// Toast.makeText(this, "Service destroyed", Toast.LENGTH_SHORT).show(); + } + + private Notification createNotification() { + String notificationChannelId = "stickyNotificationChannel"; + + NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + NotificationChannel channel = null; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + channel = new NotificationChannel(notificationChannelId, notificationChannelId, NotificationManager.IMPORTANCE_HIGH); + channel.enableVibration(false); + channel.setShowBadge(false); + notificationManager.createNotificationChannel(channel); + + Intent notificationIntent = new Intent(this, MainActivity.class); + PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); + + Notification.Builder builder = new Notification.Builder(this, notificationChannelId); + return builder.setContentTitle("TextBee is running").setContentText("TextBee is running in the background.").setContentIntent(pendingIntent).setOngoing(true).setSmallIcon(R.drawable.ic_launcher_foreground).build(); + } else { + NotificationCompat.Builder builder = new NotificationCompat.Builder(this, notificationChannelId); + return builder.setContentTitle("TextBee is running").setContentText("TextBee is running in the background.").setOngoing(true).setSmallIcon(R.drawable.ic_launcher_foreground).build(); + } + + } +} \ No newline at end of file diff --git a/android/app/src/main/res/layout/activity_main.xml b/android/app/src/main/res/layout/activity_main.xml index eb007a3..f311a96 100644 --- a/android/app/src/main/res/layout/activity_main.xml +++ b/android/app/src/main/res/layout/activity_main.xml @@ -1,82 +1,37 @@ - - - - - - - -