Advertisement
Guest User

Service.kt for dashbuddy

a guest
May 17th, 2025
159
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Kotlin 10.06 KB | None | 0 0
  1. package cloud.trotter.dashbuddy.bubble
  2.  
  3. import android.app.NotificationChannel
  4. import android.app.NotificationManager
  5. import android.app.PendingIntent
  6. import android.app.Service
  7. import android.content.Intent
  8. import android.content.pm.ServiceInfo
  9. import android.content.pm.ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION
  10. import android.os.Build
  11. import android.os.IBinder
  12. import android.util.Log
  13. import androidx.core.app.NotificationCompat
  14. import androidx.core.app.Person
  15. import androidx.core.content.ContextCompat
  16. import androidx.core.content.LocusIdCompat
  17. import androidx.core.content.pm.ShortcutInfoCompat
  18. import androidx.core.content.pm.ShortcutManagerCompat
  19. import androidx.core.graphics.drawable.IconCompat
  20. import cloud.trotter.dashbuddy.BubbleActivity
  21. import cloud.trotter.dashbuddy.DashBuddyApplication
  22. import cloud.trotter.dashbuddy.R
  23. import cloud.trotter.dashbuddy.bubble.Notification as BubbleNotification
  24.  
  25. class Service : Service() {
  26.     private lateinit var notificationManager: NotificationManager
  27.     private lateinit var dashBuddyPerson: Person
  28.     private lateinit var bubbleShortcut: ShortcutInfoCompat
  29.     private lateinit var dashBuddyIcon: IconCompat
  30.     private lateinit var dashBuddyLocusId: LocusIdCompat
  31.     private lateinit var messagingStyle: NotificationCompat.MessagingStyle
  32.  
  33.     // Flag to indicate if core components are initialized
  34.     private var areComponentsInitialized = false
  35.  
  36.  
  37.     companion object {
  38.         const val CHANNEL_ID = "bubble_channel"
  39.         const val NOTIFICATION_ID = 1
  40.         private const val SHORTCUT_ID = "DashBuddy_Bubble_Shortcut"
  41.         private const val TAG = "BubbleService"
  42.         const val EXTRA_MESSAGE = "extra_message_to_show" // Key for intent extra
  43.         private const val DASHBUDDY_PERSON_KEY = "dashbuddy_user_key" // Stable key for the Person
  44.  
  45.         // Publicly accessible static variable to check if the service is intended to be running.
  46.         // This is more reliable than an instance variable if the service instance gets recreated.
  47.         @Volatile
  48.         var isServiceRunningIntentional = false
  49.             private set
  50.     }
  51.  
  52.     override fun onCreate() {
  53.         super.onCreate()
  54.         Log.d(TAG, "onCreate: BubbleService creating...")
  55.         notificationManager = DashBuddyApplication.notificationManager
  56.  
  57.         // 1. Create Notification Channel (essential)
  58.         createNotificationChannel()
  59.  
  60.         // 2. Define the "Person" representing DashBuddy
  61.         dashBuddyIcon = IconCompat.createWithResource(DashBuddyApplication.context, R.drawable.bag_red_idle)
  62.         dashBuddyPerson = Person.Builder()
  63.             .setName("DashBuddy")
  64.             .setIcon(dashBuddyIcon)
  65.             .setKey(DASHBUDDY_PERSON_KEY) // Added a stable key for the Person
  66.             .setImportant(true)
  67.             .build()
  68.  
  69.         messagingStyle = NotificationCompat.MessagingStyle(dashBuddyPerson)
  70.  
  71.  
  72.         // Initialize LocusId here
  73.         dashBuddyLocusId = LocusIdCompat("${SHORTCUT_ID}_Locus")
  74.         Log.d(TAG, "Initialized LocusId: $dashBuddyLocusId")
  75.  
  76.         // 3. Create and push the dynamic shortcut for the bubble
  77.         val shortcutIntent = Intent(DashBuddyApplication.context, BubbleActivity::class.java).apply {
  78.             action = Intent.ACTION_VIEW
  79.         }
  80.         bubbleShortcut = ShortcutInfoCompat.Builder(DashBuddyApplication.context, SHORTCUT_ID)
  81.             .setLongLived(true)
  82.             .setIntent(shortcutIntent)
  83.             .setShortLabel("DashBuddy")
  84.             .setIcon(dashBuddyIcon)
  85.             .setPerson(dashBuddyPerson)
  86.             .setCategories(setOf(SHORTCUT_CATEGORY_CONVERSATION))
  87.             .setLocusId(dashBuddyLocusId) // Associate LocusId with the shortcut
  88.             .build()
  89.         ShortcutManagerCompat.pushDynamicShortcut(DashBuddyApplication.context, bubbleShortcut)
  90.  
  91.         areComponentsInitialized = true // Mark components as initialized
  92.  
  93.         // Make service instance accessible (if your design relies on this)
  94.         // Be cautious with static references to services.
  95.         DashBuddyApplication.bubbleService = this
  96.         Log.d(TAG, "onCreate: BubbleService created successfully.")
  97.     }
  98.  
  99.     override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
  100.         isServiceRunningIntentional = true // Mark that the service is now intentionally running
  101.         Log.d(TAG, "onStartCommand: BubbleService started.")
  102.  
  103.         // Use message from intent if provided (e.g., from showMessageInBubble starting the service)
  104.         // Otherwise, use a default initial message.
  105.         val messageToShow = intent?.getStringExtra(EXTRA_MESSAGE) ?: "DashBuddy is active!"
  106.  
  107.         val bubbleContentPendingIntent = PendingIntent.getActivity(
  108.             DashBuddyApplication.context,
  109.             0,
  110.             Intent(DashBuddyApplication.context, BubbleActivity::class.java),
  111.             PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
  112.         )
  113.  
  114.         val notification = BubbleNotification.create(
  115.             context = DashBuddyApplication.context,
  116.             channelId = CHANNEL_ID,
  117.             senderPerson = dashBuddyPerson,
  118.             shortcutId = bubbleShortcut.id,
  119.             bubbleIcon = dashBuddyIcon,
  120.             messagingStyle = messagingStyle,
  121.             messageText = messageToShow, // Use the determined message
  122.             contentIntent = bubbleContentPendingIntent,
  123.             locusId = dashBuddyLocusId, // Use the member variable
  124.             autoExpandBubble = true, // Good for initial launch or if explicitly requested
  125.             suppressNotification = true // Good for subsequent messages
  126.         )
  127.  
  128.         try {
  129.             val serviceType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
  130.                 ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE
  131.             } else {
  132.                 // For API < 34, no specific foregroundServiceType needed for startForeground for bubbles
  133.                 // if the manifest has foregroundServiceType="specialUse"
  134.                 0 // No specific type, but the manifest declaration counts.
  135.             }
  136.             // If serviceType is 0 for API < 34, just call startForeground(id, notification)
  137.             if (serviceType != 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
  138.                 startForeground(NOTIFICATION_ID, notification, serviceType)
  139.             } else {
  140.                 startForeground(NOTIFICATION_ID, notification)
  141.             }
  142.             Log.d(TAG, "Service started in foreground with message: '$messageToShow'")
  143.         } catch (e: Exception) {
  144.             Log.e(TAG, "Error starting foreground service", e)
  145.             isServiceRunningIntentional = false // Reset flag on error
  146.             stopSelf() // Stop the service if it can't start in foreground
  147.         }
  148.  
  149.         return START_STICKY
  150.     }
  151.  
  152.     override fun onDestroy() {
  153.         super.onDestroy()
  154.         isServiceRunningIntentional = false // Mark that the service is no longer intentionally running
  155.         areComponentsInitialized = false
  156.         if (DashBuddyApplication.bubbleService == this) {
  157.             DashBuddyApplication.bubbleService = null // Clear static reference
  158.         }
  159.         Log.d(TAG, "onDestroy: BubbleService destroyed.")
  160.     }
  161.  
  162.     override fun onBind(intent: Intent?): IBinder? {
  163.         return null
  164.     }
  165.  
  166.     private fun createNotificationChannel() {
  167.         val channel = NotificationChannel(
  168.             CHANNEL_ID,
  169.             "DashBuddy Bubbles",
  170.             NotificationManager.IMPORTANCE_HIGH
  171.         ).apply {
  172.             description = "Channel for DashBuddy bubble notifications."
  173. //            setAllowBubbles(true)
  174.             setAllowBubbles(true)
  175.             setConversationId(CHANNEL_ID, "dashbuddy")
  176.         }
  177.         notificationManager.createNotificationChannel(channel)
  178.         Log.d(TAG, "Notification channel created/updated.")
  179.     }
  180.  
  181.     /**
  182.      * Public method to show a new message in the bubble.
  183.      * If the service is not running, it will attempt to start it with this message.
  184.      */
  185.     fun showMessageInBubble(message: String) {
  186.         Log.d(TAG, "showMessageInBubble called with message: '$message'")
  187.  
  188.         if (!isServiceRunningIntentional || !areComponentsInitialized) {
  189.             Log.w(TAG, "Service not running or not initialized. Attempting to start service with this message.")
  190.             val startIntent = Intent(DashBuddyApplication.context,
  191.                 cloud.trotter.dashbuddy.bubble.Service::class.java).apply {
  192.                 putExtra(EXTRA_MESSAGE, message) // Pass the message to onStartCommand
  193.             }
  194.             ContextCompat.startForegroundService(DashBuddyApplication.context, startIntent)
  195.             // The message will be displayed when onStartCommand processes this intent.
  196.             // We return here because the current instance might not be fully ready yet.
  197.             return
  198.         }
  199.  
  200.         // If service is running and initialized, proceed to update the notification
  201.         Log.d(TAG, "Service is running. Updating bubble notification with message: '$message'")
  202.  
  203.         val bubbleContentPendingIntent = PendingIntent.getActivity(
  204.             DashBuddyApplication.context,
  205.             0,
  206.             Intent(DashBuddyApplication.context, BubbleActivity::class.java),
  207.             PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
  208.         )
  209.  
  210.         val newNotification = BubbleNotification.create(
  211.             context = DashBuddyApplication.context,
  212.             channelId = CHANNEL_ID,
  213.             senderPerson = dashBuddyPerson,
  214.             shortcutId = bubbleShortcut.id,
  215.             bubbleIcon = dashBuddyIcon,
  216.             messageText = message,
  217.             messagingStyle = messagingStyle,
  218.             contentIntent = bubbleContentPendingIntent,
  219.             locusId = dashBuddyLocusId, // Use the member variable
  220.             suppressNotification = true,
  221.             autoExpandBubble = true // Usually false for subsequent messages
  222.         )
  223.         notificationManager.notify(NOTIFICATION_ID, newNotification)
  224.  
  225.         Log.d(TAG, "Updated bubble notification posted.")
  226.     }
  227. }
  228.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement