@ -3,18 +3,12 @@ package com.vernu.sms.activities;
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,61 +19,48 @@ import android.widget.RadioGroup;
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.database.local.AppDatabase ;
import com.vernu.sms.database.local.SMS ;
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.Arrays ;
import java.util.List ;
import java.util.Objects ;
import java.util.concurrent.Executors ;
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 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 ) ;
apiKeyEditText = findViewById ( R . id . apiKeyEditText ) ;
@ -87,33 +68,11 @@ public class MainActivity extends AppCompatActivity {
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 +82,19 @@ public class MainActivity extends AppCompatActivity {
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" ) ;
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 +102,111 @@ public class MainActivity extends AppCompatActivity {
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 < RegisterDeviceResponseDTO > apiCall = gatewayApiService . updateDevice ( deviceId , key , registerDeviceInput ) ;
Call < RegisterDeviceResponseDTO > apiCall = ApiManager . getApiService ( ) . updateDevice ( deviceId , key , registerDeviceInput ) ;
apiCall . enqueue ( new Callback < RegisterDeviceResponseDTO > ( ) {
@Override
public void onResponse ( Call < RegisterDeviceResponseDTO > call , Response < RegisterDeviceResponseDTO > 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 < RegisterDeviceResponseDTO > call , Throwable t ) {
Snackbar . make ( view , "An error occured :(" , Snackbar . LENGTH_LONG ) . show ( ) ;
compoundButton . setEnabled ( true ) ;
}
} ) ;
} ) ;
/ / 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 +235,63 @@ public class MainActivity extends AppCompatActivity {
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 < RegisterDeviceResponseDTO > apiCall = gatewayApiService . registerDevice ( newKey , registerDeviceInput ) ;
Call < RegisterDeviceResponseDTO > apiCall = ApiManager . getApiService ( ) . registerDevice ( newKey , registerDeviceInput ) ;
apiCall . enqueue ( new Callback < RegisterDeviceResponseDTO > ( ) {
@Override
public void onResponse ( Call < RegisterDeviceResponseDTO > call , Response < RegisterDeviceResponseDTO > 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 < RegisterDeviceResponseDTO > 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 < SubscriptionInfo > getAvailableSimSlots ( ) {
if ( ActivityCompat . checkSelfPermission ( this , Manifest . permission . READ_PHONE_STATE ) ! = PackageManager . PERMISSION_GRANTED ) {
return new ArrayList < > ( ) ;
}
SubscriptionManager subscriptionManager = SubscriptionManager . from ( mContext ) ;
return subscriptionManager . getActiveSubscriptionInfoList ( ) ;
}
}