mirror of
https://github.com/vernu/textbee.git
synced 2026-02-19 23:26:14 -05:00
feat(android): enable sticky notification service to prevent app from getting killed
This commit is contained in:
@@ -18,4 +18,5 @@ public class AppConstants {
|
||||
public static final String SHARED_PREFS_TRACK_SENT_SMS_STATUS_KEY = "TRACK_SENT_SMS_STATUS";
|
||||
public static final String SHARED_PREFS_LAST_VERSION_CODE_KEY = "LAST_VERSION_CODE";
|
||||
public static final String SHARED_PREFS_LAST_VERSION_NAME_KEY = "LAST_VERSION_NAME";
|
||||
public static final String SHARED_PREFS_STICKY_NOTIFICATION_ENABLED_KEY = "STICKY_NOTIFICATION_ENABLED";
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics;
|
||||
import com.vernu.sms.services.StickyNotificationService;
|
||||
import com.vernu.sms.helpers.SharedPreferenceHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -38,22 +39,34 @@ public class TextBeeUtils {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// Only start service if user has enabled sticky notification
|
||||
boolean stickyNotificationEnabled = SharedPreferenceHelper.getSharedPreferenceBoolean(
|
||||
context,
|
||||
AppConstants.SHARED_PREFS_STICKY_NOTIFICATION_ENABLED_KEY,
|
||||
false
|
||||
);
|
||||
|
||||
if (stickyNotificationEnabled) {
|
||||
Intent notificationIntent = new Intent(context, StickyNotificationService.class);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.startForegroundService(notificationIntent);
|
||||
} else {
|
||||
context.startService(notificationIntent);
|
||||
}
|
||||
Log.i(TAG, "Starting sticky notification service");
|
||||
} else {
|
||||
context.startService(notificationIntent);
|
||||
Log.i(TAG, "Sticky notification disabled by user, not starting service");
|
||||
}
|
||||
}
|
||||
|
||||
public static void stopStickyNotificationService(Context context) {
|
||||
Intent notificationIntent = new Intent(context, StickyNotificationService.class);
|
||||
context.stopService(notificationIntent);
|
||||
Log.i(TAG, "Stopping sticky notification service");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -42,7 +42,7 @@ import retrofit2.Response;
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
private Context mContext;
|
||||
private Switch gatewaySwitch, receiveSMSSwitch;
|
||||
private Switch gatewaySwitch, receiveSMSSwitch, stickyNotificationSwitch;
|
||||
private EditText apiKeyEditText, fcmTokenEditText, deviceIdEditText;
|
||||
private Button registerDeviceBtn, grantSMSPermissionBtn, scanQRBtn, checkUpdatesBtn;
|
||||
private ImageButton copyDeviceIdImgBtn;
|
||||
@@ -62,6 +62,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
gatewaySwitch = findViewById(R.id.gatewaySwitch);
|
||||
receiveSMSSwitch = findViewById(R.id.receiveSMSSwitch);
|
||||
stickyNotificationSwitch = findViewById(R.id.stickyNotificationSwitch);
|
||||
apiKeyEditText = findViewById(R.id.apiKeyEditText);
|
||||
fcmTokenEditText = findViewById(R.id.fcmTokenEditText);
|
||||
deviceIdEditText = findViewById(R.id.deviceIdEditText);
|
||||
@@ -98,6 +99,14 @@ public class MainActivity extends AppCompatActivity {
|
||||
crashlytics.setCustomKey("app_version", versionName);
|
||||
crashlytics.setCustomKey("app_version_code", BuildConfig.VERSION_CODE);
|
||||
|
||||
// Start sticky notification service if enabled
|
||||
boolean gatewayEnabled = SharedPreferenceHelper.getSharedPreferenceBoolean(mContext, AppConstants.SHARED_PREFS_GATEWAY_ENABLED_KEY, false);
|
||||
boolean stickyNotificationEnabled = SharedPreferenceHelper.getSharedPreferenceBoolean(mContext, AppConstants.SHARED_PREFS_STICKY_NOTIFICATION_ENABLED_KEY, false);
|
||||
if (gatewayEnabled && stickyNotificationEnabled) {
|
||||
TextBeeUtils.startStickyNotificationService(mContext);
|
||||
Log.d(TAG, "Starting sticky notification service on app start");
|
||||
}
|
||||
|
||||
if (deviceId == null || deviceId.isEmpty()) {
|
||||
registerDeviceBtn.setText("Register");
|
||||
} else {
|
||||
@@ -150,11 +159,14 @@ public class MainActivity extends AppCompatActivity {
|
||||
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);
|
||||
// }
|
||||
if (enabled) {
|
||||
// Check if sticky notification is enabled
|
||||
if (SharedPreferenceHelper.getSharedPreferenceBoolean(mContext, AppConstants.SHARED_PREFS_STICKY_NOTIFICATION_ENABLED_KEY, false)) {
|
||||
TextBeeUtils.startStickyNotificationService(mContext);
|
||||
}
|
||||
} else {
|
||||
TextBeeUtils.stopStickyNotificationService(mContext);
|
||||
}
|
||||
compoundButton.setEnabled(true);
|
||||
}
|
||||
@Override
|
||||
@@ -176,6 +188,21 @@ public class MainActivity extends AppCompatActivity {
|
||||
Snackbar.make(view, "Receive SMS " + (isCheked ? "enabled" : "disabled"), Snackbar.LENGTH_LONG).show();
|
||||
});
|
||||
|
||||
// Setup sticky notification switch
|
||||
stickyNotificationSwitch.setChecked(SharedPreferenceHelper.getSharedPreferenceBoolean(mContext, AppConstants.SHARED_PREFS_STICKY_NOTIFICATION_ENABLED_KEY, false));
|
||||
stickyNotificationSwitch.setOnCheckedChangeListener((compoundButton, isChecked) -> {
|
||||
View view = compoundButton.getRootView();
|
||||
SharedPreferenceHelper.setSharedPreferenceBoolean(mContext, AppConstants.SHARED_PREFS_STICKY_NOTIFICATION_ENABLED_KEY, isChecked);
|
||||
|
||||
if (isChecked) {
|
||||
TextBeeUtils.startStickyNotificationService(mContext);
|
||||
Snackbar.make(view, "Background service enabled - app will be more reliable", Snackbar.LENGTH_LONG).show();
|
||||
} else {
|
||||
TextBeeUtils.stopStickyNotificationService(mContext);
|
||||
Snackbar.make(view, "Background service disabled - app may be killed when in background", Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: check gateway status/api key/device validity and update UI accordingly
|
||||
registerDeviceBtn.setOnClickListener(view -> {
|
||||
String _deviceId = SharedPreferenceHelper.getSharedPreferenceString(mContext, AppConstants.SHARED_PREFS_DEVICE_ID_KEY, "");
|
||||
|
||||
@@ -15,6 +15,8 @@ import androidx.core.app.NotificationCompat;
|
||||
import com.vernu.sms.R;
|
||||
import com.vernu.sms.activities.MainActivity;
|
||||
import com.vernu.sms.receivers.SMSBroadcastReceiver;
|
||||
import com.vernu.sms.AppConstants;
|
||||
import com.vernu.sms.helpers.SharedPreferenceHelper;
|
||||
|
||||
public class StickyNotificationService extends Service {
|
||||
|
||||
@@ -32,15 +34,25 @@ public class StickyNotificationService extends Service {
|
||||
super.onCreate();
|
||||
Log.i(TAG, "Service Started");
|
||||
|
||||
// Only register receiver and show notification if enabled in preferences
|
||||
boolean stickyNotificationEnabled = SharedPreferenceHelper.getSharedPreferenceBoolean(
|
||||
getApplicationContext(),
|
||||
AppConstants.SHARED_PREFS_STICKY_NOTIFICATION_ENABLED_KEY,
|
||||
false
|
||||
);
|
||||
|
||||
// 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);
|
||||
if (stickyNotificationEnabled) {
|
||||
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);
|
||||
Log.i(TAG, "Started foreground service with sticky notification");
|
||||
} else {
|
||||
Log.i(TAG, "Sticky notification disabled by user preference");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -52,9 +64,19 @@ public class StickyNotificationService extends Service {
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
// unregisterReceiver(receiver);
|
||||
|
||||
// Only unregister if we had registered in the first place
|
||||
boolean stickyNotificationEnabled = SharedPreferenceHelper.getSharedPreferenceBoolean(
|
||||
getApplicationContext(),
|
||||
AppConstants.SHARED_PREFS_STICKY_NOTIFICATION_ENABLED_KEY,
|
||||
false
|
||||
);
|
||||
|
||||
if (stickyNotificationEnabled) {
|
||||
unregisterReceiver(receiver);
|
||||
}
|
||||
|
||||
Log.i(TAG, "StickyNotificationService destroyed");
|
||||
// Toast.makeText(this, "Service destroyed", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
private Notification createNotification() {
|
||||
@@ -72,10 +94,19 @@ public class StickyNotificationService extends Service {
|
||||
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();
|
||||
return builder.setContentTitle("TextBee Active")
|
||||
.setContentText("SMS gateway service is active")
|
||||
.setContentIntent(pendingIntent)
|
||||
.setOngoing(true)
|
||||
.setSmallIcon(R.mipmap.ic_launcher)
|
||||
.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();
|
||||
return builder.setContentTitle("TextBee Active")
|
||||
.setContentText("SMS gateway service is active")
|
||||
.setOngoing(true)
|
||||
.setSmallIcon(R.mipmap.ic_launcher)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -371,6 +371,57 @@
|
||||
android:minHeight="32dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Sticky Notification Setting -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Sticky Notification"
|
||||
android:textColor="@color/text_primary"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:src="@android:drawable/ic_dialog_info"
|
||||
android:tint="?attr/colorPrimary"
|
||||
android:layout_marginEnd="4dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Prevents app from being killed by the system (optional, but recommended for reliability)"
|
||||
android:textColor="@color/text_secondary"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<Switch
|
||||
android:id="@+id/stickyNotificationSwitch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="32dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
|
||||
Reference in New Issue
Block a user