-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: run audio sources in a background service (#631)
- Loading branch information
Showing
6 changed files
with
178 additions
and
95 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
161 changes: 161 additions & 0 deletions
161
android/app/src/main/kotlin/com/rtirl/chat/AudioService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
package com.rtirl.chat | ||
|
||
import android.app.NotificationChannel | ||
import android.app.NotificationManager | ||
import android.annotation.SuppressLint | ||
import android.app.PendingIntent | ||
import android.app.Service | ||
import android.content.Context | ||
import android.content.Intent | ||
import android.graphics.PixelFormat | ||
import android.os.Build | ||
import android.os.IBinder | ||
import android.view.View | ||
import android.view.ViewGroup | ||
import android.view.WindowManager | ||
import android.webkit.* | ||
import androidx.core.app.NotificationCompat | ||
import io.flutter.Log | ||
|
||
class AudioService : Service() { | ||
companion object { | ||
private const val NOTIFICATION_ID = 68448 | ||
private const val CHANNEL_ID = "AudioSources" | ||
private const val PACKAGE_NAME = BuildConfig.APPLICATION_ID + ".AudioService" | ||
const val ACTION_START_SERVICE = "$PACKAGE_NAME.start_service" | ||
} | ||
|
||
private val views = HashMap<String, WebView>() | ||
|
||
private val notification: NotificationCompat.Builder | ||
get() { | ||
val intent = Intent(this, getMainActivityClass(this)) | ||
|
||
val builder = NotificationCompat.Builder(this, CHANNEL_ID) | ||
.setContentTitle("RealtimeChat Audio Sources") | ||
.setContentText("We're keeping your audio sources alive fam.") | ||
.setOngoing(true) | ||
.setPriority(NotificationCompat.PRIORITY_HIGH) | ||
.setColorized(true) | ||
.setColor(0xFF009FDF.toInt()) | ||
.setSmallIcon(R.drawable.notification_icon) | ||
.setWhen(System.currentTimeMillis()) | ||
.setContentIntent( | ||
PendingIntent.getActivity( | ||
this, 0, intent, | ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { | ||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE | ||
} else { | ||
PendingIntent.FLAG_UPDATE_CURRENT | ||
} | ||
) | ||
) | ||
|
||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||
builder.setChannelId(CHANNEL_ID) | ||
} | ||
|
||
return builder | ||
} | ||
|
||
private fun getMainActivityClass(context: Context): Class<*>? { | ||
val packageName = context.packageName | ||
val launchIntent = context.packageManager.getLaunchIntentForPackage(packageName) | ||
val className = launchIntent?.component?.className ?: return null | ||
|
||
return try { | ||
Class.forName(className) | ||
} catch (e: ClassNotFoundException) { | ||
e.printStackTrace() | ||
null | ||
} | ||
} | ||
|
||
override fun onBind(p0: Intent?): IBinder? { | ||
return null | ||
} | ||
|
||
@SuppressLint("SetJavaScriptEnabled") | ||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { | ||
val urls = intent?.getStringArrayListExtra("urls")?.toHashSet() | ||
|
||
if (urls != null) { | ||
val wm = getSystemService(WINDOW_SERVICE) as WindowManager | ||
|
||
val add = (urls subtract views.keys) | ||
val remove = (views.keys subtract urls) | ||
add.forEach { | ||
val view = WebView(this) | ||
view.settings.javaScriptEnabled = true | ||
view.settings.mediaPlaybackRequiresUserGesture = false | ||
view.settings.domStorageEnabled = true | ||
view.settings.databaseEnabled = true | ||
view.settings.mixedContentMode = WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE | ||
view.webChromeClient = object : WebChromeClient() { | ||
override fun onConsoleMessage(consoleMessage: ConsoleMessage): Boolean { | ||
Log.d("WebView", consoleMessage.message()) | ||
return true | ||
} | ||
} | ||
view.setLayerType(View.LAYER_TYPE_HARDWARE, null) | ||
view.loadUrl(it) | ||
view.webViewClient = object : WebViewClient() { | ||
override fun shouldOverrideUrlLoading( | ||
view: WebView?, | ||
request: WebResourceRequest? | ||
): Boolean { | ||
return false | ||
} | ||
} | ||
|
||
val params = WindowManager.LayoutParams( | ||
ViewGroup.LayoutParams.WRAP_CONTENT, | ||
ViewGroup.LayoutParams.WRAP_CONTENT, | ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY else WindowManager.LayoutParams.TYPE_PHONE, | ||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, | ||
PixelFormat.TRANSPARENT | ||
) | ||
|
||
params.x = 0 | ||
params.y = 0 | ||
params.width = 0 | ||
params.height = 0 | ||
|
||
wm.addView( | ||
view, params | ||
) | ||
views[it] = view | ||
} | ||
remove.forEach { | ||
wm.removeView(views[it]) | ||
views.remove(it)?.destroy() | ||
} | ||
} else { | ||
views.forEach { (_, view) -> view.reload() } | ||
} | ||
|
||
val nm = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager | ||
|
||
return if (views.isNotEmpty()) { | ||
// ensure the notification is shown | ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||
val mChannel = NotificationChannel( | ||
CHANNEL_ID, | ||
"Audio Sources", | ||
NotificationManager.IMPORTANCE_MIN | ||
) | ||
mChannel.setSound(null, null) | ||
nm.createNotificationChannel(mChannel) | ||
} | ||
startForeground(NOTIFICATION_ID, notification.build()) | ||
START_STICKY | ||
} else { | ||
// ensure the notification is removed | ||
stopForeground(true) | ||
nm.cancel(NOTIFICATION_ID) | ||
stopSelf() | ||
START_NOT_STICKY | ||
} | ||
} | ||
|
||
} |
80 changes: 12 additions & 68 deletions
80
android/app/src/main/kotlin/com/rtirl/chat/MainActivity.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters