I'm working on an Android project where I want to set a GLSL shader as the wallpaper using a WallpaperService
. The goal is to display a simple gray color shader, but I'm encountering an issue where the wallpaper is just showing a black screen.
note.
I am aware of the existence of Shader Editor but I have some new ideas that can make the process of creating and customizing shaders extremely easier.
You can view this for the most basic stripped down version highlighting the problem. Here's the setup:
GLSL code:
void main() {// basic code for testing purpose
if(gl_FragCoord.y > 250.0){
gl_FragColor = vec4(0.25);
}
else{
gl_FragColor = vec4(0.75);
}
}
WallpaperService Implementation:
import android.service.wallpaper.WallpaperService
import android.view.SurfaceHolder
import com.dhruv.hellohome.glsl.MyGLSurfaceView
class GLSLWallpaperService : WallpaperService() {
private var engine: GLWallpaperEngine? = null
fun isRunning(): Boolean {
return engine != null
}
override fun onCreateEngine(): Engine {
return GLWallpaperEngine()
}
inner class GLWallpaperEngine : Engine() {
private var glSurfaceView: MyGLSurfaceView? = null
override fun onCreate(surfaceHolder: SurfaceHolder) {
super.onCreate(surfaceHolder)
glSurfaceView = MyGLSurfaceView(this@GLSLWallpaperService)
}
override fun onSurfaceCreated(holder: SurfaceHolder) {
super.onSurfaceCreated(holder)
glSurfaceView?.surfaceCreated(holder)
}
override fun onVisibilityChanged(visible: Boolean) {
super.onVisibilityChanged(visible)
if (visible) {
glSurfaceView?.onResume()
} else {
glSurfaceView?.onPause()
}
}
override fun onDestroy() {
super.onDestroy()
glSurfaceView?.let {
it.onPause()
it.onDetachedFromWindow()
}
}
override fun onSurfaceDestroyed(holder: SurfaceHolder) {
super.onSurfaceDestroyed(holder)
glSurfaceView?.surfaceDestroyed(holder)
}
}
}
GLSurfaceView Implementation:
import android.content.Context
import android.opengl.GLSurfaceView
import android.util.Log
import android.view.SurfaceHolder
class MyGLSurfaceView(context: Context) : GLSurfaceView(context) {
private val renderer: MyGLRenderer
init {
setEGLContextClientVersion(2)
renderer = MyGLRenderer(ShaderData())
setRenderer(renderer)
renderMode = RENDERMODE_CONTINUOUSLY
}
override fun surfaceCreated(holder: SurfaceHolder) {
super.surfaceCreated(holder)
Log.d("MyGLSurfaceView", "Surface created")
}
override fun surfaceDestroyed(holder: SurfaceHolder) {
super.surfaceDestroyed(holder)
Log.d("MyGLSurfaceView", "Surface destroyed")
}
public override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
}
}
in this case the MyGLRenderer is an implementation of GLSurfaceView.Renderer that works fine. The minifest is :
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:installLocation="auto">
<supports-screens
android:largeScreens="true"
android:xlargeScreens="true"/>
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="19"/>
<!-- Required below API < 28 for exporting shaders -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28"/>
<uses-feature android:glEsVersion="0x00020000"/>
<uses-feature android:name="android.hardware.camera.any"
android:required="false"/>
<uses-feature android:name="android.hardware.camera.autofocus"
android:required="false"/>
<uses-feature android:name="android.software.live_wallpaper"/>
<queries>
<intent>
<action android:name="android.intent.action.VIEW"/>
<data android:scheme="https"/>
</intent>
</queries>
<application
tools:ignore="UnusedAttribute"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.HelloHome">
<service
android:name=".services.GLSLWallpaperService"
android:exported="true"
android:description="@string/wallpaper_description"
android:label="@string/app_name"
android:permission="android.permission.BIND_WALLPAPER">
<intent-filter>
<action android:name="android.service.wallpaper.WallpaperService" />
</intent-filter>
<meta-data
android:name="android.service.wallpaper"
android:resource="@xml/wallpaper" />
</service>
<activity android:name="com.example.openglwallpaper.SettingsActivity" />
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.HelloHome">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".PreviewActivity" />
</application>
</manifest>
and finally GLSLWallpaperService Implementation:
import android.service.wallpaper.WallpaperService
import android.view.SurfaceHolder
import com.dhruv.hellohome.glsl.MyGLSurfaceView
class GLSLWallpaperService : WallpaperService() {
private var engine: GLWallpaperEngine? = null
fun isRunning(): Boolean {
return engine != null
}
override fun onCreateEngine(): Engine {
return GLWallpaperEngine()
}
override fun onDestroy() {
super.onDestroy()
}
inner class GLWallpaperEngine : Engine() {
private var glSurfaceView: MyGLSurfaceView? = null
override fun onCreate(surfaceHolder: SurfaceHolder) {
super.onCreate(surfaceHolder)
glSurfaceView = MyGLSurfaceView(this@GLSLWallpaperService)
}
override fun onSurfaceCreated(holder: SurfaceHolder) {
super.onSurfaceCreated(holder)
glSurfaceView?.surfaceCreated(holder)
}
override fun onVisibilityChanged(visible: Boolean) {
super.onVisibilityChanged(visible)
if (visible) {
glSurfaceView?.onResume()
} else {
glSurfaceView?.onPause()
}
}
override fun onDestroy() {
super.onDestroy()
glSurfaceView?.let {
it.onPause()
it.onDetachedFromWindow()
}
}
override fun onSurfaceDestroyed(holder: SurfaceHolder) {
super.onSurfaceDestroyed(holder)
glSurfaceView?.surfaceDestroyed(holder)
}
}
}
I have been stuck for about 2 weeks and just can't seem to understand what is wrong. if any one can help me that would be a huge help.
Ok so the problem was in how GLSLWallpaperService handled engien.
this is the fixed code
class GLSLWallpaperService : WallpaperService() {
private var engine: GLWallpaperEngine? = null
fun isRunning(): Boolean {
return engine != null
}
override fun onCreateEngine(): Engine {
engine = GLWallpaperEngine()
return engine!!
}
override fun onDestroy() {
super.onDestroy()
engine = null
}
inner class GLWallpaperEngine : Engine() {
private var glSurfaceView: MyGLSurfaceView? = null
override fun onCreate(surfaceHolder: SurfaceHolder) {
super.onCreate(surfaceHolder)
glSurfaceView = GLWallpaperSurfaceView()
}
override fun onSurfaceCreated(holder: SurfaceHolder) {
super.onSurfaceCreated(holder)
glSurfaceView?.surfaceCreated(holder)
}
override fun onVisibilityChanged(visible: Boolean) {
super.onVisibilityChanged(visible)
if (visible) {
glSurfaceView?.onResume()
} else {
glSurfaceView?.onPause()
}
}
override fun onDestroy() {
super.onDestroy()
glSurfaceView?.let {
it.onPause()
it.onDetachedFromWindow()
}
}
override fun onSurfaceDestroyed(holder: SurfaceHolder) {
super.onSurfaceDestroyed(holder)
glSurfaceView?.surfaceDestroyed(holder)
}
inner class GLWallpaperSurfaceView : MyGLSurfaceView(this@GLSLWallpaperService) {
override fun getHolder(): SurfaceHolder {
return getSurfaceHolder()
}
}
}
}
it was a stupid question but I hope if anyone else is stuck on it then this might help you.