In this step by step example you will learn how to create a sample widget using the AppWidgetManager.

Here is the demo image:

Step 1: Create Project

Start by creating an empty Android Studio project.

Step 2: Dependencies

Because we are using Kotlin we will add the kotlin-stdlib-jdk dependency:

    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

Then the usual AppCompat and ConstraintLayout:

    implementation "androidx.appcompat:appcompat:$buildToolsVer"
    implementation "androidx.constraintlayout:constraintlayout:$constraintLayoutVersion"

Step 3: Create Sample Widget XML

First create a folder in your res directory known as xml and add the following file:

sample_widget_info.xml

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialKeyguardLayout="@layout/sample_widget"
    android:initialLayout="@layout/sample_widget"
    android:minHeight="110dp"
    android:minWidth="110dp"
    android:previewImage="@mipmap/ic_launcher"
    android:resizeMode="horizontal|vertical"
    android:updatePeriodMillis="86400000"
    android:widgetCategory="home_screen|keyguard"></appwidget-provider>

Step 4: Design the Layouts

We will need two layouts:

(a). sample_widget.xml

Add the code containing the UI elements for your custom widget here:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="@dimen/widget_margin"
    android:orientation="vertical">

    <TextView
        android:id="@+id/appwidget_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_margin="8dp"
        android:contentDescription="@string/appwidget_text"
        android:text="This is widget example"
        android:textSize="24sp"
        android:textStyle="bold|italic" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="HIT ME"
        android:id="@+id/widget_button"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="OPEN GOOGLE PLEASE"
        android:id="@+id/google_button"/>

</LinearLayout>

(b). activity_main.xml

Then design the MainActivity layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/edit_text"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/button"
        android:text="press"/>

</LinearLayout>

Step 5: Write Widget Code

Start by creating the SampleWidget.kt file and add the following imports including the android.appwidget.AppWidgetManager and android.appwidget.AppWidgetProvider:

import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
import android.util.Log
import android.widget.RemoteViews
import android.app.PendingIntent
import android.content.Intent
import android.net.Uri
import android.content.ComponentName

Create a class that extends the AppWidgetProvider:

class SampleWidget : AppWidgetProvider() {

Define two strings:

    private val ACTION_SIMPLEAPPWIDGET = "ACTION_BROADCASTWIDGETSAMPLE"
    private val WIDGET_TAG = "SAMPLE_WIDGET_TAG"

Override the onUpdate():

    override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {

There may be multiple widgets active, so loop and update all of them:

        for (appWidgetId in appWidgetIds) {
            Log.d(WIDGET_TAG,"update")
            updateAppWidget(context, appWidgetManager, appWidgetId)
        }
    }

Override the onEnabled and onDisabled functions:

    override fun onEnabled(context: Context) {
        // Enter relevant functionality for when the first widget is created
        Log.d(WIDGET_TAG,"enabled")
    }

    override fun onDisabled(context: Context) {
        // Enter relevant functionality for when the last widget is disabled
        Log.d(WIDGET_TAG,"disabled")
    }

Now override the onReceive() callback:

    override fun onReceive(context: Context, intent: Intent) {
        super.onReceive(context, intent)

Write code that will be called when the button is clicked:

        if ((intent.action.equals(ACTION_SIMPLEAPPWIDGET))) {
            val views = RemoteViews(context.packageName, R.layout.sample_widget)
            views.setTextViewText(R.id.widget_button, "HIT ME AGAIN!")
            val appWidget = ComponentName(context, SampleWidget::class.java!!)
            val appWidgetManager = AppWidgetManager.getInstance(context)

            appWidgetManager.updateAppWidget(appWidget, views)
        }

Also write code that will be called broadcast inside the activity:

        else if ((intent.action.equals("ACTIVITY_ACTION"))) {
            val views = RemoteViews(context.packageName, R.layout.sample_widget)

            val text = intent.getStringExtra("name")

            views.setTextViewText(R.id.widget_button, text)
            val appWidget = ComponentName(context, SampleWidget::class.java!!)
            val appWidgetManager = AppWidgetManager.getInstance(context)

            appWidgetManager.updateAppWidget(appWidget, views)
        }
    }

Finally create the updateAppWidget() function:

    private fun updateAppWidget(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int) {

        // Construct the RemoteViews object
        val views = RemoteViews(context.packageName, R.layout.sample_widget)
        views.setTextViewText(R.id.appwidget_text, "WIDGET INITIALIZED")

        //Sample intent for action example
        val intent = Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com"))
        val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0)
        views.setOnClickPendingIntent(R.id.widget_button, pendingIntent)

        // Construct an Intent which is pointing this class.
        val intentTwo = Intent(context, SampleWidget::class.java)
        intentTwo.action = ACTION_SIMPLEAPPWIDGET
        val pendingIntentTwo = PendingIntent.getBroadcast(context, 0, intentTwo, PendingIntent.FLAG_UPDATE_CURRENT)

        views.setOnClickPendingIntent(R.id.google_button, pendingIntent)
        views.setOnClickPendingIntent(R.id.widget_button, pendingIntentTwo)

        // Instruct the widget manager to update the widget
        appWidgetManager.updateAppWidget(appWidgetId, views)
    }
}

Here is the full code:

SampleWidget.kt

package pramonow.com.widgetexample

import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
import android.util.Log
import android.widget.RemoteViews
import android.app.PendingIntent
import android.content.Intent
import android.net.Uri
import android.content.ComponentName

/**
 * Implementation of App Widget functionality.
 */

class SampleWidget : AppWidgetProvider() {

    private val ACTION_SIMPLEAPPWIDGET = "ACTION_BROADCASTWIDGETSAMPLE"
    private val WIDGET_TAG = "SAMPLE_WIDGET_TAG"

    override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
        // There may be multiple widgets active, so update all of them
        for (appWidgetId in appWidgetIds) {
            Log.d(WIDGET_TAG,"update")
            updateAppWidget(context, appWidgetManager, appWidgetId)
        }
    }

    override fun onEnabled(context: Context) {
        // Enter relevant functionality for when the first widget is created
        Log.d(WIDGET_TAG,"enabled")
    }

    override fun onDisabled(context: Context) {
        // Enter relevant functionality for when the last widget is disabled
        Log.d(WIDGET_TAG,"disabled")
    }

    override fun onReceive(context: Context, intent: Intent) {
        super.onReceive(context, intent)

        //This will be called when the button is clicked
        if ((intent.action.equals(ACTION_SIMPLEAPPWIDGET))) {
            val views = RemoteViews(context.packageName, R.layout.sample_widget)
            views.setTextViewText(R.id.widget_button, "HIT ME AGAIN!")
            val appWidget = ComponentName(context, SampleWidget::class.java!!)
            val appWidgetManager = AppWidgetManager.getInstance(context)

            appWidgetManager.updateAppWidget(appWidget, views)
        }
        //This will be called by broadcast inside the activity
        else if ((intent.action.equals("ACTIVITY_ACTION"))) {
            val views = RemoteViews(context.packageName, R.layout.sample_widget)

            val text = intent.getStringExtra("name")

            views.setTextViewText(R.id.widget_button, text)
            val appWidget = ComponentName(context, SampleWidget::class.java!!)
            val appWidgetManager = AppWidgetManager.getInstance(context)

            appWidgetManager.updateAppWidget(appWidget, views)
        }
    }

    private fun updateAppWidget(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int) {

        // Construct the RemoteViews object
        val views = RemoteViews(context.packageName, R.layout.sample_widget)
        views.setTextViewText(R.id.appwidget_text, "WIDGET INITIALIZED")

        //Sample intent for action example
        val intent = Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com"))
        val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0)
        views.setOnClickPendingIntent(R.id.widget_button, pendingIntent)

        // Construct an Intent which is pointing this class.
        val intentTwo = Intent(context, SampleWidget::class.java)
        intentTwo.action = ACTION_SIMPLEAPPWIDGET
        val pendingIntentTwo = PendingIntent.getBroadcast(context, 0, intentTwo, PendingIntent.FLAG_UPDATE_CURRENT)

        views.setOnClickPendingIntent(R.id.google_button, pendingIntent)
        views.setOnClickPendingIntent(R.id.widget_button, pendingIntentTwo)

        // Instruct the widget manager to update the widget
        appWidgetManager.updateAppWidget(appWidgetId, views)
    }
}

Step 6: Write MainActivity code

Go to MainActivity.kt and add imports:

import android.os.Bundle
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.snackbar.Snackbar
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import android.view.View
import android.appwidget.AppWidgetManager
import android.content.ComponentName
import androidx.core.view.accessibility.AccessibilityEventCompat.setAction
import android.content.Intent
import android.widget.Button
import android.widget.EditText

Extend the AppCompatActivity and override the onCreate():

class MainActivity : AppCompatActivity() {

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

Reference the editText and button:

        var editText = findViewById<EditText>(R.id.edit_text)
        var button = findViewById<Button>(R.id.button)

Create an intent:

        val intent = Intent(this, SampleWidget::class.java)
        intent.action = "ACTIVITY_ACTION"

Create a button that when clicked will send broadcast to update the widget:

        button.setOnClickListener { _ ->
            AppWidgetManager.getInstance(application).getAppWidgetIds(ComponentName(application,SampleWidget::class.java))
            intent.putExtra("name", editText.text.toString())
            sendBroadcast(intent)}

    }
}

Here is the full code:

MainActivity.kt

import android.os.Bundle
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.snackbar.Snackbar
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import android.view.View
import android.appwidget.AppWidgetManager
import android.content.ComponentName
import androidx.core.view.accessibility.AccessibilityEventCompat.setAction
import android.content.Intent
import android.widget.Button
import android.widget.EditText

class MainActivity : AppCompatActivity() {

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

        var editText = findViewById<EditText>(R.id.edit_text)
        var button = findViewById<Button>(R.id.button)

        val intent = Intent(this, SampleWidget::class.java)
        intent.action = "ACTIVITY_ACTION"

        //This action will send broadcast to update the widget
        button.setOnClickListener { _ ->
            AppWidgetManager.getInstance(application).getAppWidgetIds(ComponentName(application,SampleWidget::class.java))
            intent.putExtra("name", editText.text.toString())
            sendBroadcast(intent)}

    }
}

Step 7: Register Components

Go to your AndroidManifest.xml

And register your MainActivity as your launcher activity:

        <activity
            android:name=".MainActivity"
            android:label="@string/title_activity_main">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

Then register our AppWidget:

        <receiver android:name=".SampleWidget">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>

            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/sample_widget_info" />
        </receiver>

Run

Copy the code or download it in the link below, build and run.

Reference

Here are the reference links:

Number Link
1. Download Example
2. Follow code author
3. Code: Apache 2.0 License