Code Analysis

Source Code Analysis

Here, we will take a look at how to implement providers with the sample application's source code. In addition, you can find out which style guide has been applied to the sample provider in the code.

Declaring Providers in Manifest

The sample application provides three Smart Bulletin providers. Therefore, you need to declare three providers in the manifest file. Because a provider is a BroadcastReceiver, they are declared using the <receiver> tag as shown below.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.abc.smartbulletin"
    android:versionCode="1"
    android:versionName="1.0" >
    <application
        ...        
        <receiver 
            android:name="com.abc.smartbulletin.SampleNotiProvider"
            android:label="@string/noti_provider_name">
            <intent-filter>
                <action android:name= 
                   "android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data android:name="android.appwidget.provider"
                    android:resource="@xml/noti_provider_info" />
        </receiver>
        <receiver
            android:name="com.abc.smartbulletin.SampleCardProvider"
            android:label="@string/card_provider_name">
            <intent-filter>
                <action android:name= 
                   "android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data android:name="android.appwidget.provider"
                    android:resource="@xml/card_provider_info" />
        </receiver>
        <receiver
         android:name="com.abc.smartbulletin.SampleConfigProvider"
         android:label="@string/config_provider_name">
            <intent-filter>
                <action android:name=
                   "android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data android:name="android.appwidget.provider"
                    android:resource="@xml/config_provider_info" />
        </receiver>
        ...
    </application>
</manifest>

 

As the code above shows, the configuration of manifest file is the same as that for app widgets.

Describing Metadata

As declared in the manifest file, metadata files for each app widget provider are needed.

They are the noti_provider_info.xml, card_provider_info.xml and config_provider_info.xml files. Due to the similarity of content, we will only look at the noti_provider_info.xml file.

 

<appwidget-provider 
       xmlns:android="http://schemas.android.com/apk/res/android"
    android:updatePeriodMillis="60000"
    android:initialLayout="@layout/noti_layout"
    android:resizeMode="horizontal|vertical"
    android:widgetCategory="0x100">
</appwidget-provider>

 

According to the metadata, this app widget is updated every 60 seconds and uses the noti_layout.xml file as its layout. To use this app as a Smart Bulletin provider, the widgetCategory is set as 0x100.

Setting Resources for Providers

Sample providers specify the default settings of the content that will be used when it is attached to Smart Bulletin in a resource file. The resources are defined in the res/values/smartbulletin.xml file as below.

 

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color
       name="smartbulletin_provider_bg_color">#ff0099cc</color>
    <color
       name="smartbulletin_provider_title_color">#ffffffff</color>
    <string 
        name="smartbulletin_provider_default_activity">
       com.abc.smartbulletin/com.abc.smartbulletin.SampleActivity
    </string>
</resources>

 

The colors of the background and title are specified according to the recommended colors in the style guide.

The code above shows that SampleActivity will run when the title of one of the providers is tapped. Since SampleActivity is an activity with just a simple screen, it is not covered in this document. For detailed source code, check out the SampleActivity.java file.

 

These resources are configured per application. Therefore, even if an application has more than one provider, defining resources separately for each provider is not possible.

Setting Layouts

Sample providers set their layout in onUpdate().

 

Below is the code of the sample notification provider (the SampleNotiProvider.java file). Other providers have a similar code. For more detailed version of this code, check out the SampleCardProvider.java and SampleConfigProvider.java files.

 

public class SampleNotiProvider extends AppWidgetProvider {
    ...
    @Override
    public void onUpdate(Context context,
             AppWidgetManager appWidgetManager,
             int[] appWidgetIds) {
        RemoteViews remoteViews =
                         new RemoteViews(context.getPackageName(),
                         R.layout.noti_layout);                     ①
        ComponentName provider =
                         new ComponentName(context,
                         SampleNotiProvider.class);

        remoteViews.setOnClickPendingIntent(R.id.add_once,
            getPendingSelfIntent(context, ADD_ONCE_CLICKED));  ②

        remoteViews.setOnClickPendingIntent(R.id.remove_once, ②
            getPendingSelfIntent(context, REMOVE_ONCE_CLICKED));

        remoteViews.setOnClickPendingIntent(R.id.add_ongoing, ②
            getPendingSelfIntent(context, ADD_ONGOING_CLICKED));

        appWidgetManager.updateAppWidget(provider, remoteViews); ③
    } 
    ...
}

① Sets content's screen layout.

② Sets the PendingIntent that will be transmitted when each button on the layout is tapped. Like in the case of app widgets, buttons are included in Smart Bulletin. Therefore, the provider cannot receive button events directly. If the provider sets a PendingIntent for buttons through RemoteViews, Smart Bulletin sends the intent later for the provider.

③ Updates app widgets.

 

The following is a code of the getPendingSelfIntent() which creates a PendingIntent to be transmitted when a button is tapped:

 

private PendingIntent getPendingSelfIntent(Context context, String action) {
    Intent intent = new Intent(context, getClass());            ①
    intent.setAction(action);                                        ②
    return PendingIntent.getBroadcast(context, 0, intent, 0); ③
}

① Creates an intent that will be executed when a button is tapped. Set the provider itself as the component that handles the intent.

② Specifies an action string for the intent. In the case of the Smart notification provider, the action will be something like ADD_ONCE_CLICKED.

③ Makes the created intent into the PendingIntent that will be broadcasted.

 

Handling Button Actions

The created intent is broadcasted when you tap the matching button in the content attached to Smart Bulletin. Because you set the provider as the component that handles the intent in the previous code, a code to receive and handle the intent is added to the provider's onReceive().

 

Sample Notification Provider

The sample notification provider issues or removes a notification when a button is tapped.

 

@Override
public void onReceive(Context context, Intent intent) {
    super.onReceive(context, intent);                            ①
    ComponentName provider = new ComponentName(context,
                   SampleNotiProvider.class);                      ②
    if (ADD_ONCE_CLICKED.equals(intent.getAction())) {        ③
        Notification.sendAddOnceIntent(context,                 ④
                      providerComponentName, R.drawable.noti);
    } else if (REMOVE_ONCE_CLICKED.equals(intent.getAction())) {⑤
        Notification.sendRemoveOnceIntent(context,                 ⑥
                      providerComponentName, R.drawable.noti);
    } else if (ADD_ONGOING_CLICKED.equals(intent.getAction())) {⑦
        Notification.sendAddOngoingIntent(context,                 ⑧
                      providerComponentName, 0); 
    } 
}

① Handles the basic events of app widget providers. You must invoke this method when you override onReceive().

② Retrieves the component name of a provider so as to specify the provider as a notification component.

③ In the case of a request for issuing a one-shot (once) notification,

④ Issues the one-shot (once) notification.

⑤ In the case of a request for removing a notification,

⑥ Removes the notification.

⑦ In the case of a request for issuing an ongoing notification,

⑧ Specifies a notification icon and issues the ongoing notification.

 

The methods called in ④, ⑥ and ⑧ take arguments and create an intent for the issuance or removal of a notification. This document does not describe this code in detail. For details, check out the SampleNotiProvider.java file.

 

Sample Card Provider

The sample card provider adds or removes cards. This is an example of how to expand and shrink content.

 

@Override
public void onReceive(Context context, Intent intent) {
    super.onReceive(context, intent);                     ①
    if (ADD_CARD_CLICKED.equals(intent.getAction())) { ②
        addCard(context);                                    ③
    } else if (REMOVE_ALL_CARD_CLICKED.equals(
                                           intent.getAction())) {④
        removeAllCard(context);                             ⑤
    } else if (X_CLICKED.equals(intent.getAction())) { ⑥
	removeCard(context,
                  intent.getStringExtra(EXTRA_TARGET_INDEX));⑦
    }
}

① Handles the basic events of app widget providers. You must invoke this method when you override onReceive().

② In the case of a request for adding a card,

③ Adds a card.

④ In the case of a request for removing all cards,

⑤ Removes all cards.

⑥ In case of a request to remove a specific card,

⑦ Removes the card.

 

A sample provider stores information about issued cards in a list. When a provider receives a request to add or remove a card, the provider updates its card information list and requests a screen refresh.

Card information lists are managed by the CardList object. This object stores card information by using SharedPreferences.

This document does not explain CardList's code. For more information, see source code.

 

The card addition method called in ③ is as follows:

 

private void addCard(Context context) {
    CardList list = new CardList(context);  ⑧
    int current = list.addCard("Card" + list.getCardSize()); ⑨
    RemoteViews remoteViews = 
          new RemoteViews(context.getPackageName(),
                             R.layout.card_layout); 
    init(context, remoteViews);  ⑩
    update(context, remoteViews);
}

⑧ Imports the card information list.

⑨ Adds data for a new card to the card information list.

⑩ Refreshes the screen displayed in Smart Bulletin.

 

The all cards removal method called in ⑤ is as follows:

 

private void removeAllCard(Context context) {
    CardList list = new CardList(context);
    list.clearList();  ⑪

    RemoteViews remoteViews = 
          new RemoteViews(context.getPackageName(),
                             R.layout.card_layout); 
    init(context, remoteViews);  ⑫
    update(context, remoteViews);
}

⑪ Empties the card information list.

⑫ Refreshes the screen displayed in Smart Bulletin.

 

The cards removal method called in ⑦ is as follows:

 

private void removeCard(Context context, String data) {
    CardList list = new CardList(context);
    list.removeCard(data);  ⑬

    RemoteViews remoteViews = 
          new RemoteViews(context.getPackageName(),
                             R.layout.card_layout); 
    init(context, remoteViews);  ⑭
    update(context, remoteViews);
}

⑬ Removes the specified data from the card information list.

⑭ Refreshes the screen displayed in Smart Bulletin.

 

The method called when refreshing the screen is as follows:

 

private void init(Context context, RemoteViews remoteViews) {
    ...
    remoteViews.removeAllViews(R.id.card_container); ①
    CardList list = new CardList(context);
    Set items = list.getCards();
    if (items != null) {
        list.setCardCount(items.get(items.size()-1)
                            .replace("Card", ""));

        for (String data: items) {       ②
            RemoteViews remoteCardViews = new RemoteViews( ③
                   context.getPackageName(),
                   R.layout.card);

            remoteCardViews.setOnClickPendingIntent(  ④
                   R.id.xbtn,
                   getPendingSelfIntentWithExtra(
                           context, X_CLICKED, data));

            Calendar cal = Calendar.getInstance();
            remoteCardViews.setTextViewText(        ⑤
                   R.id.card_text, data);
            remoteCardViews.setTextViewText(        ⑥
		   R.id.card_time,
		   cal.get(Calendar.HOUR) + ":" +
                   cal.get(Calendar.MINUTE) + " " +
                  (cal.get(Calendar.AM_PM) == 0? "AM" : "PM"));

            remoteViews.addView(        ⑦
                      R.id.card_container, remoteCardViews);
	}
    }
}

① Clears the card screen.

② Repeats the following tasks as many times as the number of cards stored in the card information list.

③ Imports the card layout you want to add.

④ Specifies the actions of the X button. It generates an intent when an event occurs in Smart Bulletin by using PendingIntent.

⑤ Displays information about the card in the card's main text.

⑥ Displays the current time in the card's sub-text.

 

Sample Configuration Provider

The sample configuration provider changes the screen's colors when a button is tapped.

 

@Override
public void onReceive(Context context, Intent intent) {
    super.onReceive(context, intent);                             ①

    ComponentName providerComponentName = new ComponentName(
                               context, SampleConfigProvider.class);
    if (SET_RED_COLOR.equals(intent.getAction())) {            ②
        Configuration.setColor(context, providerComponentName,
                                   Color.RED, Color.WHITE);         ③
    } else if (SET_GREEN_COLOR.equals(intent.getAction())) {  ②
        Configuration.setColor(context, providerComponentName,
                                   Color.GREEN, Color.BLACK);       ③
    } else if (SET_BLUE_COLOR.equals(intent.getAction()))    {②
        Configuration.setColor(context, providerComponentName,
                                   Color.BLUE, Color.WHITE);        ③
    }
}

① Handles the basic events of app widget providers. You must invoke this method when you override onReceive().

② In the case of a request for changing colors,

③ Changes the colors of the content according to the tapped button.

 

The color change method called in ③ is as follows:

 

public static void setColor(Context context, ComponentName componentName, int backgroundColor, int titleColor){
    Intent intent = generateIntent(context,                  ④
                      SMARTBULLETIN_CONFIGURATION_SET_COLOR_INTENT,
                      componentName, backgroundColor, titleColor);
    context.sendBroadcast(intent);                             ⑥
}

private static Intent generateIntent(Context context,
            String action, ComponentName componentName,
            int backgroundColor, int titleColor){
    Intent intent = new Intent(action);                       ⑤
    intent.putExtra(SMARTBULLETIN_COMPONENT_NAME,           ⑤
                       componentName.flattenToString());
    intent.putExtra(SMARTBULLETIN_BACKGROUND_COLOR,
                       backgroundColor);                         ⑤
    intent.putExtra(SMARTBULLETIN_TITLE_COLOR, titleColor); ⑤
    return intent;
}

④ Creates an intent for changing colors.

⑤ Specifies the chosen background and title colors and specifies the component name of the content whose colors will be changed.

⑥ Transmits the intent for changing colors.

Handling Smart Bulletin Status Intents

The sample notification provider receives the Smart Bulletin's status broadcasts and displays status messages. For this, add a BroadcastReceiver to the manifest file as shown below.

 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.abc.smartbulletin"
    android:versionCode="1"
    android:versionName="1.0" >
    ...
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/provider_name"
        android:theme="@style/AppTheme" > 
        ... 
        <receiver android:name=".SampleActionReceiver" >
            <intent-filter>
                <action android:name=
               "com.lge.launcher2.smartbulletin.action.ENABLED" />
                <action android:name= 
               "com.lge.launcher2.smartbulletin.action.DISABLED" />
                <action android:name=
               "com.lge.launcher2.smartbulletin.action.RESUMED" />
                <action android:name=
               "com.lge.launcher2.smartbulletin.action.PAUSED" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

 

As declared above, the SampleActionReceiver receives the Smart Bulletin's status broadcasts. The SmartActionReceiver simply shows the status carried in broadcasts. The code is as follows:

 

public class SampleActionReceiver extends BroadcastReceiver { ①
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(②
                           Action.SMARTBULLETIN_ACTION_ENABLED)) {②
            displayAction(context, "SmartBulletin Enabled");
        } else if (intent.getAction().equals(                     ③
                           Action.SMARTBULLETIN_ACTION_DISABLED)) {
            displayAction(context, "SmartBulletin Disabled");   ②
        } else if (intent.getAction().equals(                      ③
                           Action.SMARTBULLETIN_ACTION_RESUMED)) {
            displayAction(context, "SmartBulletin Resumed");     ③
        } else if (intent.getAction().equals(                      ②
                           Action.SMARTBULLETIN_ACTION_PAUSED)) {
            displayAction(context, "SmartBulletin Paused");      ③
        }
    }

    private void displayAction(Context context, String string) {
        Toast.makeText(context, string,
                          Toast.LENGTH_SHORT).show();
    }
}

① Declares the SampleActionReceiver class that inherited the BroadcastReceiver.

② Checks the type of the received intent.

③ Displays the proper message for the intent.

 

Navigation