How to Make Interactive Live Wallpapers Using Kotlin for Android

Android offers a number of personalization features to help developers customize many aspects of their mobile app user experience. One of these features is live wallpaper for Android.

Live wallpapers are animated. They don’t remain as static background images and have interactive features.

A live wallpaper for Android is usually used as a background on the home screen that changes over time or animates in some way. If you own an Android device, you’ve probably seen a couple of built-in live wallpapers.

Now, as a mobile app developers company, you can also create and publish live wallpapers for Android. The process is not difficult. However, creating a live wallpaper that does not drain user’s device battery and is fascinating is something of a challenge.

But, in today’s Android application development tutorial, we’ll walk you through the process of creating a live wallpaper for Android that works well.

Let’s Get Started

Create a new project “Live Wallpaper”.

hand icon Add an Activity to mobile => Select Empty Activity.

Select-Activity

hand icon Configure the project.

Configure-project


hand icon Step 1
: Add dependencies in-app level build.gradle

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
}
Copy to Clipboard

hand icon Step 2: add below source in SetWallpaperActivity file

class SetWallpaperActivity : Activity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    fun onClick(view: View) {
        val intent = Intent(
            WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER
        )
        intent.putExtra(
            WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT,
            ComponentName(this, MyWallpaperService::class.java)
        )
        startActivity(intent)
    }
}
Copy to Clipboard

hand icon Step 3: add below source in SetWallpaperActivity file

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:id="@+id/btnWallPaper"
        android:paddingStart="@dimen/dimen_25dp"
        android:paddingEnd="@dimen/dimen_25dp"
        android:onClick="onClick"
        style="@style/button_style"
        android:text="@string/lable_click_here"
        android:layout_marginTop="@dimen/dimen_10dp"/>
</android.support.constraint.ConstraintLayout>
Copy to Clipboard

Want To Create An Android Application?

Looking to Create An Android app? Get in touch with our experienced Android app developers for a free consultation.

Cta Image

hand icon Step 4: add below source in MyWallpaperService file

class MyWallpaperService : WallpaperService() {
    override fun onCreateEngine(): WallpaperService.Engine {
        return MyWallpaperEngine()
    }

    private inner class MyWallpaperEngine : WallpaperService.Engine() {
        private val handler = Handler()
        private val drawRunner = Runnable { draw() }
        private val circles: MutableList
        private val paint = Paint()
        private var width: Int = 0
        internal var height: Int = 0
        private var visible = true
        private val maxNumber: Int
        private val touchEnabled: Boolean

        init {
            val prefs = PreferenceManager
                .getDefaultSharedPreferences(this@MyWallpaperService)
            maxNumber = Integer
                .valueOf(prefs.getString(resources.getString(R.string.lable_number_of_circles), "4")!!)
            touchEnabled = prefs.getBoolean("touch", false)
            circles = ArrayList()
            paint.isAntiAlias = true
            paint.color = Color.WHITE
            paint.style = Paint.Style.STROKE
            paint.strokeJoin = Paint.Join.ROUND
            paint.strokeWidth = 10f
            handler.post(drawRunner)
        }

        override fun onVisibilityChanged(visible: Boolean) {
            this.visible = visible
            if (visible) {
                handler.post(drawRunner)
            } else {
                handler.removeCallbacks(drawRunner)
            }
        }

        override fun onSurfaceDestroyed(holder: SurfaceHolder) {
            super.onSurfaceDestroyed(holder)
            this.visible = false
            handler.removeCallbacks(drawRunner)
        }

        override fun onSurfaceChanged(
            holder: SurfaceHolder, format: Int,
            width: Int, height: Int
        ) {
            this.width = width
            this.height = height
            super.onSurfaceChanged(holder, format, width, height)
        }

        override fun onTouchEvent(event: MotionEvent) {
            if (touchEnabled) {

                val x = event.x
                val y = event.y
                val holder = surfaceHolder
                var canvas: Canvas? = null

                canvas = holder.lockCanvas()
                if (canvas != null) {
                    canvas.drawColor(Color.BLACK)
                    circles.clear()
                    circles.add(MyPoint((circles.size + 1).toString(), x, y))
                    drawCircles(canvas, circles)

                }

                if (canvas != null)
                    holder.unlockCanvasAndPost(canvas)

                super.onTouchEvent(event)
            }
        }

        private fun draw() {
            val holder = surfaceHolder
                var canvas: Canvas? = null

            canvas = holder.lockCanvas()
            if (canvas != null) {
                if (circles.size >= maxNumber) {
                    circles.clear()
                }
                val x = (width * Math.random()).toInt()
                val y = (height * Math.random()).toInt()
                circles.add(
                    MyPoint(
                        (circles.size + 1).toString(),
                        x.toFloat(), y.toFloat()
                    )
                )
                drawCircles(canvas, circles)
            }

            if (canvas != null)
                holder.unlockCanvasAndPost(canvas)

                handler.removeCallbacks(drawRunner)
            if (visible) {
                handler.postDelayed(drawRunner, 5000)
            }
        }

        // Surface view requires that all elements are drawn completely
        private fun drawCircles(canvas: Canvas, circles: List) {
            canvas.drawColor(Color.BLACK)
            for (point in circles) {
                canvas.drawCircle(point.x, point.y, 20.0f, paint)
            }
        }
    }

}
Copy to Clipboard

hand icon Step 5: add below source in MyPoint file

class MyPoint(var text: String,var x:Float,var y:Float)
Copy to Clipboard

hand icon Step 6: add below source in MyPreferencesActivity file

class MyPreferencesActivity : PreferenceActivity() {
    /**
    * Checks that a preference is a valid numerical value
    */
    internal var numberCheckListener: Preference.OnPreferenceChangeListener =
        Preference.OnPreferenceChangeListener { preference, newValue ->
            // check that the string is an integer
            if (newValue != null && newValue.toString().length > 0
                && newValue.toString().matches("\\d*".toRegex())
            ) {
                return@OnPreferenceChangeListener true
            }
            // If now create a message to the user
            Toast.makeText(
                this@MyPreferencesActivity, resources.getString(R.string.lable_invalid_input),
                Toast.LENGTH_SHORT
            ).show()
            false
        }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        addPreferencesFromResource(R.xml.prefs)

        // add a validator to the "numberofCircles" preference so that it only
        // accepts numbers
        val circlePreference = preferenceScreen.findPreference(resources.getString(R.string.lable_number_of_circles))

        // add the validator
        circlePreference.onPreferenceChangeListener = numberCheckListener
    }
}
Copy to Clipboard

hand icon Step 7: add below source in XML.prefs file

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <CheckBoxPreference
        android:key="@string/lable_touch"
        android:title="@string/lable_enable_touch" />
    <EditTextPreference
        android:key="@string/lable_number_of_circles"
        android:title="@string/lable_number_circles" />
</PreferenceScreen>
Copy to Clipboard

hand icon Step 8: add below source in AndroidManifest.xml file

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
            package="spaceo.livewallpaperdemo">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

    <service
        android:name="spaceo.livewallpaperdemo.MyWallpaperService"
        android:enabled="true"
        android:label="Wallpaper Example"
        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/mywallpaper" />
    </service>

    <activity
        android:name="spaceo.livewallpaperdemo.MyPreferencesActivity"
        android:exported="true"
        android:label="@string/app_name" />

    <activity
        android:name="spaceo.livewallpaperdemo.SetWallpaperActivity"
        android:label="@string/app_name">
        <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    </application>

    <uses-feature
        android:name="android.software.live_wallpaper"
        android:required="true" />

</manifest>
Copy to Clipboard

And done!

Once you successfully implement this code, you can easily create live wallpaper for Android.

Remember, live wallpapers are a useful way to expand your app beyond typical boundaries. However, while making the live wallpaper, make sure to render to the screen so that the user’s experience with their device does not suffer. And if you still face difficulty or any query like how much does it cost to make live wallpaper Android /HD live wallpaper/3D moving wallpapers, or how to make a live wallpaper android or 3D animation wallpapers, text live wallpaper, then you can consult with custom Android application design and development agency to make your live wallpaper work smoothly in Android device.

Get a free copy of Android Live Wallpaper Demo from Github.

Bhaval Patel

Written by

Bhaval Patel is a Director (Operations) at Space-O Technologies. He has 20+ years of experience helping startups and enterprises with custom software solutions to drive maximum results. Under his leadership, Space-O has won the 8th GESIA annual award for being the best mobile app development company. So far, he has validated more than 300 app ideas and successfully delivered 100 custom solutions using the technologies, such as Swift, Kotlin, React Native, Flutter, PHP, RoR, IoT, AI, NFC, AR/VR, Blockchain, NFT, and more.