Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
package protect.card_locker.wearos

import android.Manifest
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothServerSocket
import android.bluetooth.BluetoothSocket
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.IBinder
import android.util.Log
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import org.json.JSONArray
import org.json.JSONObject
import protect.card_locker.DBHelper
Expand All @@ -33,13 +38,67 @@ class BluetoothServerService : Service() {
private const val TAG = "CatimaBtServer"
private const val NOTIFICATION_ID = NotificationInfo.WearBluetooth.NOTIFICATION_ID
private const val CHANNEL_ID = NotificationInfo.WearBluetooth.CHANNEL_ID

fun showBluetoothServerError(context: Context, error: String) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
NotificationInfo.WearBluetooth.CRITICAL_ERROR_CHANNEL_ID,
context.getString(R.string.wear_bt_critical_error_channel_name),
NotificationManager.IMPORTANCE_HIGH
).apply { setShowBadge(false) }
context.getSystemService(NotificationManager::class.java).createNotificationChannel(channel)
}
val notification = NotificationCompat.Builder(context, NotificationInfo.WearBluetooth.CRITICAL_ERROR_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification_error)
.setContentTitle(context.getString(R.string.wear_bt_failed_foreground_notification_title))
.setContentText(error)
.build()
with(NotificationManagerCompat.from(context)) {
if (ActivityCompat.checkSelfPermission(
context,
Manifest.permission.POST_NOTIFICATIONS
) != PackageManager.PERMISSION_GRANTED
) {
return@with
}
notify(
NotificationInfo.WearBluetooth.CRITICAL_ERROR_NOTIFICATION_ID,
notification
)
}
}
}

private var serverThread: AcceptThread? = null

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// First, create the foreground service with notification
// Android is very picky about requiring the foreground service to be started quickly and will crash the entire app if that doesn't happen
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
CHANNEL_ID,
getString(R.string.wear_bt_channel_name),
NotificationManager.IMPORTANCE_MIN
).apply { setShowBadge(false) }
getSystemService(NotificationManager::class.java).createNotificationChannel(channel)
}
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle(getString(R.string.wear_bt_notification_title))
.setSmallIcon(R.drawable.ic_notification_sync)
.setPriority(NotificationCompat.PRIORITY_MIN)
.setSilent(true)
.build()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
startForeground(NOTIFICATION_ID, notification,
android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE)
} else {
startForeground(NOTIFICATION_ID, notification)
}

// After we have a running notification, try error handling and stopping the service if we have issues
if (!BluetoothPermissionHelper.isBluetoothConnectGranted(this)) {
Log.w(TAG, "BLUETOOTH_CONNECT permission not granted, stopping")
showBluetoothServerError(this, getString(R.string.wear_sync_permission_required))
stopSelf()
return START_NOT_STICKY
}
Expand All @@ -56,36 +115,12 @@ class BluetoothServerService : Service() {
return START_STICKY
}

startForegroundWithNotification()
serverThread?.cancel()
serverThread = AcceptThread(adapter).also { it.start() }
Log.d(TAG, "Bluetooth server started")
return START_STICKY
}

private fun startForegroundWithNotification() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
CHANNEL_ID,
getString(R.string.wear_bt_channel_name),
NotificationManager.IMPORTANCE_MIN
).apply { setShowBadge(false) }
getSystemService(NotificationManager::class.java).createNotificationChannel(channel)
}
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle(getString(R.string.wear_bt_notification_title))
.setSmallIcon(R.drawable.ic_notification_sync)
.setPriority(NotificationCompat.PRIORITY_MIN)
.setSilent(true)
.build()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
startForeground(NOTIFICATION_ID, notification,
android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE)
} else {
startForeground(NOTIFICATION_ID, notification)
}
}

override fun onDestroy() {
serverThread?.cancel()
serverThread = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,15 @@ package protect.card_locker.wearos

import android.Manifest
import android.app.ForegroundServiceStartNotAllowedException
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import androidx.activity.result.ActivityResultCaller
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import androidx.core.content.edit
import androidx.preference.PreferenceManager
import protect.card_locker.NotificationInfo
import protect.card_locker.R
import protect.card_locker.preferences.Settings
import protect.card_locker.shared.BluetoothPermissionHelper
Expand Down Expand Up @@ -128,30 +121,7 @@ object WearSyncServiceManager {
// We naively assume we have enough permissions to show a notification as the service should never try to start
// before the user gave notification access anyway
Log.d(TAG, "Foreground service failed to launch: $e")
val channel = NotificationChannel(
NotificationInfo.WearBluetooth.CRITICAL_ERROR_CHANNEL_ID,
context.getString(R.string.wear_bt_critical_error_channel_name),
NotificationManager.IMPORTANCE_HIGH
).apply { setShowBadge(false) }
context.getSystemService(NotificationManager::class.java).createNotificationChannel(channel)
val notification = NotificationCompat.Builder(context, NotificationInfo.WearBluetooth.CRITICAL_ERROR_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification_error)
.setContentTitle(context.getString(R.string.wear_bt_failed_foreground_notification_title))
.setContentText(e.message)
.build()
with(NotificationManagerCompat.from(context)) {
if (ActivityCompat.checkSelfPermission(
context,
Manifest.permission.POST_NOTIFICATIONS
) != PackageManager.PERMISSION_GRANTED
) {
return@with
}
notify(
NotificationInfo.WearBluetooth.CRITICAL_ERROR_NOTIFICATION_ID,
notification
)
}
BluetoothServerService.showBluetoothServerError(context, context.getString(R.string.android_blocked_startup))
}
} else {
ContextCompat.startForegroundService(
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -395,4 +395,5 @@
<string name="settings_category_title_smartwatch">Smartwatch support</string>
<string name="wear_bt_failed_foreground_notification_title">Failed to start Wear OS sync service</string>
<string name="wear_bt_critical_error_channel_name">Wear OS sync critical error</string>
<string name="android_blocked_startup">Android blocked starting the Wear OS service</string>
</resources>
Loading