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:
parent
26cfd98296
commit
525839eece
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
70
app/src/main/java/emu/skyline/utils/AmbientHelper.kt
Normal file
70
app/src/main/java/emu/skyline/utils/AmbientHelper.kt
Normal 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.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
@ -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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user