0
0
mirror of https://github.com/Ishan09811/pine.git synced 2025-04-24 08:55:10 +00:00

Implement Ambient mode (#44)

This commit is contained in:
Ishan09811 2024-12-31 18:24:14 +05:30 committed by GitHub
parent 26cfd98296
commit 525839eece
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 128 additions and 1 deletions

View File

@ -180,6 +180,7 @@ dependencies {
implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-compiler:$hilt_version"
implementation 'com.google.android.flexbox:flexbox:3.0.0'
implementation 'androidx.palette:palette-ktx:1.0.0'
/* Kotlin */
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"

View File

@ -31,9 +31,11 @@ import android.util.Log
import android.util.Rational
import android.util.TypedValue
import android.view.*
import android.view.ViewTreeObserver
import android.widget.Toast
import android.widget.PopupMenu
import android.widget.TextView
import android.animation.ObjectAnimator
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
@ -71,6 +73,7 @@ import emu.skyline.settings.SettingsActivity
import emu.skyline.utils.ByteBufferSerializable
import emu.skyline.utils.GpuDriverHelper
import emu.skyline.utils.serializable
import emu.skyline.utils.AmbientHelper
import emu.skyline.input.onscreen.OnScreenEditActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ -80,7 +83,7 @@ import java.nio.ByteOrder
import java.util.concurrent.FutureTask
import javax.inject.Inject
import kotlin.math.abs
import kotlinx.coroutines.*
private const val ActionPause = "${BuildConfig.APPLICATION_ID}.ACTION_EMULATOR_PAUSE"
private const val ActionMute = "${BuildConfig.APPLICATION_ID}.ACTION_EMULATOR_MUTE"
@ -207,6 +210,9 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
private external fun setAudioSink(sink: String)
private var ambientJob: Job? = null
private lateinit var ambientHelper: AmbientHelper
/**
* @see [InputHandler.initializeControllers]
*/
@ -502,6 +508,18 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
}
setInsets()
executeApplication(intent!!)
if (emulationSettings.enableAmbientMode) {
binding.gameView.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw(): Boolean {
if (binding.gameView.width > 0 && binding.gameView.height > 0) {
ambientHelper = AmbientHelper(binding.gameView)
startAmbientEffectUpdates()
binding.gameView.viewTreeObserver.removeOnPreDrawListener(this)
}
return true
}
})
}
}
private fun setInsets() {
@ -522,6 +540,36 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
}
}
private fun startAmbientEffectUpdates() {
ambientJob = CoroutineScope(Dispatchers.Main).launch {
var previousColor = Color.BLACK
while (isActive) {
ambientHelper.captureAmbientEffect(object : AmbientHelper.AmbientCallback {
override fun onColorsExtracted(vibrantColor: Int, mutedColor: Int, dominantColor: Int) {
val animator = ObjectAnimator.ofArgb(
binding.gameViewContainer,
"backgroundColor",
previousColor,
dominantColor
)
animator.duration = 300 // Smooth transition duration
animator.start()
previousColor = dominantColor
}
override fun onError(error: String) {
Log.e("AmbientHelper", error)
}
})
delay(50)
}
}
}
private fun stopAmbientEffectUpdates() {
ambientJob?.cancel()
}
@SuppressWarnings("WeakerAccess")
fun pauseEmulator() {
if (isEmulatorPaused) return

View File

@ -43,6 +43,7 @@ class EmulationSettings private constructor(context : Context, prefName : String
var respectDisplayCutout by sharedPreferences(context, false, prefName = prefName)
var enableFoldableLayout by sharedPreferences(context, false, prefName = prefName)
var showPauseButton by sharedPreferences(context, false, prefName = prefName)
var enableAmbientMode by sharedPreferences(context, false, prefName = prefName)
// CPU
var cpuBackend by sharedPreferences(context, 0, prefName = prefName)

View File

@ -0,0 +1,70 @@
package emu.skyline.utils
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.Rect
import android.os.Handler
import android.os.Looper
import android.view.SurfaceView
import android.view.PixelCopy
import androidx.palette.graphics.Palette
class AmbientHelper(private val surfaceView: SurfaceView) {
interface AmbientCallback {
fun onColorsExtracted(vibrantColor: Int, mutedColor: Int, dominantColor: Int)
fun onError(error: String)
}
/**
* Starts capturing and processing the SurfaceView for ambient effects.
*/
fun captureAmbientEffect(callback: AmbientCallback) {
val bitmap = Bitmap.createBitmap(
surfaceView.width,
surfaceView.height,
Bitmap.Config.ARGB_8888
)
val location = IntArray(2)
surfaceView.getLocationInWindow(location)
val rect = Rect(
location[0],
location[1],
location[0] + surfaceView.width,
location[1] + surfaceView.height
)
// Use PixelCopy to capture the SurfaceView contents
PixelCopy.request(
surfaceView,
bitmap,
{ result ->
if (result == PixelCopy.SUCCESS) {
extractColors(bitmap, callback)
} else {
callback.onError("Failed to capture surface content. PixelCopy result: $result")
}
},
Handler(Looper.getMainLooper())
)
}
/**
* Extracts colors from a Bitmap using the Palette library.
*/
private fun extractColors(bitmap: Bitmap, callback: AmbientCallback) {
Palette.from(bitmap).generate { palette ->
if (palette != null) {
val vibrantColor = palette.getVibrantColor(Color.BLACK)
val mutedColor = palette.getMutedColor(Color.GRAY)
val dominantColor = palette.getDominantColor(Color.DKGRAY)
callback.onColorsExtracted(vibrantColor, mutedColor, dominantColor)
} else {
callback.onError("Failed to generate palette from bitmap.")
}
}
}
}

View File

@ -127,6 +127,8 @@
<string name="show_pause_button">Show Pause Button</string>
<string name="show_pause_button_disabled">The pause button won\'t be displayed</string>
<string name="show_pause_button_enabled">The pause button will be displayed</string>
<string name="enable_ambient_mode">Enable Ambient Mode</string>
<string name="enable_ambient_mode_desc">Ambient effect will be applied</string>
<!-- Settings - Audio -->
<string name="audio">Audio</string>
<string name="audio_output_engine">Output engine</string>

View File

@ -90,6 +90,11 @@
android:summaryOn="@string/show_pause_button_enabled"
android:key="show_pause_button"
android:title="@string/show_pause_button" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:summary="@string/enable_ambient_mode_desc"
android:key="enable_ambient_mode"
android:title="@string/enable_ambient_mode" />
</PreferenceCategory>
<PreferenceCategory
android:key="category_audio"