Android (Home screen) widget - Part 3: Configurable widget

    In Part 2, I had presented one more example of widget: Broadcast Widget (which can update it's interface when clicked). Today, I would like to talk about a widget type that can be configurable at creation, this mean when you drag it to Home screen to use, a "configuration Activity" will be launched and you will perform a setting for your widget.

   In this sample project, we will allow users to choose a link and whenever it’s clicked we open this link on the browser.

Create layout for configuration Activity

    The most important work of creating this type of widget is developing the configuration Activity. Firstly, define a simple layout includes a Spinner to allow user selected one value from a set and a one value from a Button to confirm this work:
activity_config.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="@dimen/activity_horizontal_margin">

    <Spinner
        android:id="@+id/spinner"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/btn_go"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/activity_horizontal_margin"
        android:text="@string/add" />
</LinearLayout>

Activity programmatically code

    Now, looking at this source code of ConfigActivity:
ConfigActivity.java
package info.devexchanges.configurablewidget;

import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.RemoteViews;
import android.widget.Spinner;

import java.util.ArrayList;

public class ConfigActivity extends AppCompatActivity {

    private int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
    private AppWidgetManager widgetManager;
    private RemoteViews remoteViews;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setResult(RESULT_CANCELED);

        setContentView(R.layout.activity_config);

        final Spinner spinner = (Spinner)findViewById(R.id.spinner);
        View btnCreate = findViewById(R.id.btn_go);

        //create data
        ArrayList<String> spnOptions = new ArrayList<>();
        spnOptions.add("Go to my site");
        spnOptions.add("Go to Google page");

        //set adapter for the spinner
        ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, spnOptions);
        spinner.setAdapter(adapter);

        //initializing RemoteViews and AppWidgetManager
        widgetManager = AppWidgetManager.getInstance(this);
        remoteViews = new RemoteViews(this.getPackageName(), R.layout.widget_configurable);

        // Find the widget id from the intent
        Bundle extras = getIntent().getExtras();
        if (extras != null) {
            mAppWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
        }
        if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
            finish();
            return;
        }
        btnCreate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String selectedUrl;
                if (spinner.getSelectedItemPosition() == 0) {
                    // Go to my website with this selection (position = 1)
                    selectedUrl = "http://www.devexchanges.info";
                } else {
                    selectedUrl = "https://www.google.com";
                }
                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(selectedUrl));
                PendingIntent pending = PendingIntent.getActivity(ConfigActivity.this, 0, intent, 0);
                remoteViews.setOnClickPendingIntent(R.id.text_view, pending);
                if (spinner.getSelectedItemPosition() == 0) {
                    remoteViews.setTextViewText(R.id.text_view, "Click to visit my site");
                } else {
                    remoteViews.setTextViewText(R.id.text_view, "Click to visit Google");
                }
                widgetManager.updateAppWidget(mAppWidgetId, remoteViews);
                Intent resultValue = new Intent();

                // Set the results as expected from a 'configure activity'.
                resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
                setResult(RESULT_OK, resultValue);
                finish();
            }
        });
    }
}
    In onCreate() method, the first thing we do is setting setResult(RESULT_CANCELED). Why? Android triggers the configuration Activity that belongs to your widget and awaits result data. If the user did not configure as we expected, let’s say she pressed back button without entering a data, we don’t need to create a widget.
    At the Spinner, we set data to make 2 options for user to choose (go to DevExchanges home page or Google), after click the Button, we update TextView content on the widget and set the Set the RemoteViews to based on appWidgetIds.

Modifying the widget xml

    The last thing we do is modify the XML of the widget. With that modification, Android OS will know this widget has a configuration Activity. So before creating the widget, it will trigger the Activity:
res\xml\configurable_widget_info.xml
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:configure="info.devexchanges.configurablewidget.ConfigActivity"
    android:initialLayout="@layout/widget_configurable"
    android:minHeight="40dp"
    android:minWidth="40dp"
    android:previewImage="@mipmap/ic_launcher"
    android:resizeMode="horizontal|vertical"
    android:updatePeriodMillis="86400000" />
    As you notice we didn’t talk about widget class yet. We do not need to add any code for widget class because all actions done by ConfigActivity. But we have to create it anyway:
ConfigurableWidget.java
package info.devexchanges.configurablewidget;

import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;

public class ConfigurableWidget extends AppWidgetProvider {
    
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {

    }
}
    And this is the layout file for our widget:
res\layout\widget_configurable.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:padding="8dp"
        android:background="@color/colorPrimary"
        android:id="@+id/text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_gravity="center"
        android:textColor="@android:color/white"
        android:textStyle="bold" />

</RelativeLayout>

Running the application

    Before launching the app, make sure you add ConfigurableWidget as a receiver to your AndroidManifest.xml like other previous examples:
<application
        android:allowBackup="true"
        ....>
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

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

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

        <activity android:name=".ConfigActivity">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
            </intent-filter>
        </activity>
    </application>

    After app installed, move to "WIDGETS" tab, you will see it:
    And when you drag it to Home page to use, the configuration activity will be launched:
    When click on it:

     If you select other options at the configuration activity,  the widget text will be different:

Conclusions

    I've just provided one more example about Android widget, hope you can understand the way to configure widget before using it. Up to next part, I will talk about updating widget via a Service - the most popular and important feature of this topic! Coming soon!

Share


Previous post
« Prev Post
Next post
Next Post »