11

I've been researching on how to create custom-layout notification using RemoteView.

So far, I am able to create a notification with contentView and bigContentView pointing to a RemoteView with a custom layout xml. However, what does not happen, is to have Activity (associated with custom layout) started when this RemoteView is created.

I've double checked and in my layout xml, it appears to have correct Activity class name:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="0dp"
    android:paddingLeft="0dp"
    android:paddingRight="0dp"
    android:paddingTop="0dp"
    tools:context=".LLMNotificationActivity" >

..... the rest are standard layout items: images, buttons and text

</RelativeLayout>

In manifest file, right after main application main activity, notification activity is also added:

<activity
    android:name=".LLMNotificationActivity"
    android:label="@string/app_name">
    <intent-filter>
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

I would expect when notification uses RemoteView for its content, that this RemoteView will launch activity that is attached to its layout definition. However it appears not.

Here is how I create a notification in main application Activity:

protected void startNoti() {
    if( noti!=null ) return;

    Context context = getApplicationContext();  

    RemoteViews contentView = new RemoteViews(context.getPackageName(),R.layout.activity_noti1);

    Notification.Builder notibuilder = new Notification.Builder(context);
    notibuilder.setContentTitle(" ");
    notibuilder.setContentText(" ");
    notibuilder.setSmallIcon(R.drawable.ic_launcher);
    notibuilder.setOngoing(true);

    manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    noti = notibuilder.build();

    noti.contentView = contentView;

    manager.notify(NOTIFICATION_ID, noti);  
}

LLMNotificationActivity activity class is defined as usual:

public class LLMNotificationActivity extends Activity {
    .... etc.... constructor, some button on-click handlers, nothing spectacular...
}

Can anyone point to me what I am missing or if I have misunderstood what RemoteView can do? My understanding is that RemoteView should, once created, invoke activity associated with its layout. Or - is there some API I've missed that explicitly can set intent of the RemoteView?

What I have found so far are only setting content Intent which basically just launches an Activity once user touches notification. What I'm looking for is to handle touches to some of UI elements inside custom-layout notification, not to launch an Activity regardless where the user clicks on notification surface.

For example, if I have 3 icons (i.e. ImageView) in a RemoteView which notification uses, I'd like to be able to handle touch on each one of them. I can't imagine this wouldn't be possible as if it's not, what's the point of having RemoteView in notification?

Adinia
  • 3,722
  • 5
  • 40
  • 58
Siniša
  • 2,988
  • 4
  • 24
  • 36
  • 1
    XML attributes in the `tools` Manifest only affect the tools (i.e., providing the right theme when looking at the layout preview in your IDE based on the `tools:context` that is declared). They have no effect at run time. – ianhanniballake Mar 23 '14 at 01:32
  • Thanks for pointing out. XML was auto-generated by my IDE. – Siniša Mar 25 '14 at 10:08

3 Answers3

9

You have to associate the activity thought setOnClickPendingIntent to launch the activity from remote view as below...You can set any of the layout id in the RemoteView to click.

 Intent intent = new Intent(context, YourActivity.class);
 PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent,   PendingIntent.FLAG_UPDATE_CURRENT);
 RemoteViews removeWidget = new RemoteViews(context.getPackageName(), R.layout.your_layout);
 removeWidget.setOnClickPendingIntent(R.id.layout_id, pendingIntent);

provide an +id/layout_id to the RelativeLayout your using.

If you have to launch the activity when user click on the notification, then you have to use PendingIntent as....

    NotificationCompat.Builder mBuilder =
            new NotificationCompat.Builder(this)
                    .setSmallIcon(R.drawable.ic_launcher)
                    .setContentTitle("title")
                    .setContent(mRemoteControl);
    Intent notificationIntent = new Intent(this, YourActivity.class);
    PendingIntent contentIntent = PendingIntent.getActivity(
            this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    mBuilder.setContentIntent(contentIntent);
    mNM.notify(1000,mBuilder.build());

For 3 buttons , You have to use create a custom RemoteView and use PendingIntent. some thing as below...

Here is the custom remote view i am using for one of my media player app. it has three button to handler click.

public class RemoveControlWidget extends RemoteViews
{
private final Context mContext;

public static final String ACTION_PLAY = "com.mediabook.app.ACTION_PLAY";

public static final String ACTION_PREVIOUS = "com.mediabook.app.ACTION_PREVIOUS";

public static final String ACTION_NEXT = "com.mediabook.app.ACTION_NEXT";

public RemoveControlWidget(Context context , String packageName, int layoutId)
{
    super(packageName, layoutId);
    mContext = context;
    Intent intent = new Intent(ACTION_PLAY);
    PendingIntent pendingIntent = PendingIntent.getService(mContext.getApplicationContext(),100,
            intent,PendingIntent.FLAG_UPDATE_CURRENT);
    setOnClickPendingIntent(R.id.play_control,pendingIntent);
    setOnClickPendingIntent(R.id.pause_control,pendingIntent);
    intent = new Intent(ACTION_PREVIOUS);
    pendingIntent = PendingIntent.getService(mContext.getApplicationContext(),101,
            intent,PendingIntent.FLAG_UPDATE_CURRENT);
    setOnClickPendingIntent(R.id.previous_control,pendingIntent);
    intent = new Intent(ACTION_NEXT);
    pendingIntent = PendingIntent.getService(mContext.getApplicationContext(),102,
            intent,PendingIntent.FLAG_UPDATE_CURRENT);
    setOnClickPendingIntent(R.id.next_control,pendingIntent);
}
}
Asif Patel
  • 1,744
  • 1
  • 20
  • 27
Libin
  • 16,967
  • 7
  • 61
  • 83
  • So does this mean that having activity associated with layout used for RemoteViews is not possible? – Siniša Mar 23 '14 at 01:23
  • check my updated answer. hope this is what your looking – Libin Mar 23 '14 at 01:36
  • Thanks but as I've explained I do not want to launch an activity on a notification click. I'm looking at your second part, will report back with comment asap. – Siniša Mar 23 '14 at 01:37
  • check my updated answer. use the first part. You can associate an activity with remote view. hope this is what your requirement. – Libin Mar 23 '14 at 01:57
  • I have accepter your answer, the last part to be precise - with custom RemoteViews class. I've however added a Broadcast instead of Service, and made my extended RemoteViews class to listen to specific broadcast message - so I can keep all UI-app logic within one object instance, instead of running/accessing a service. This was also because I needed to make changes to UI when user clicks on certain options. Basically my notification now runs like a small Activity :-) Thanks for your valuable input! – Siniša Mar 24 '14 at 06:24
2

All you need to do is addsetContentIntent to your Notification.Builder like this:

Intent i = new Intent(context, YourLaunchedActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, i, 0);
Notification.Builder notibuilder = new Notification.Builder(context);
notibuilder.setContentIntent(pendingIntent);

Now, when you click on your notification, YourLanchedActivity will be launched.

IgorGanapolsky
  • 26,189
  • 23
  • 116
  • 147
0

*you can implement the pending intent on click of remote view by doing the following also note that if you are trying to get a activity from the remote view then getAcitvity is used in pending intent like *

//i will create a simple method//
       //use return getActivity cause getting the bottom Activity
private static PendingIntent getActivityBottom(Context context) {
    clearNotification(context);

    Intent intentStartActivity = new Intent(context, BotomNavViewActivity.class);
    intentStartActivity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    //intentStartActivity.addFlags(/*Intent.FLAG_ACTIVITY_CLEAR_TASK |*/ Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
    return PendingIntent.getActivity(
            context,
            2,
            intentStartActivity,
            PendingIntent.FLAG_UPDATE_CURRENT);
}

public static void clearNotification(Context context) {
    NotificationManager notificationManager = (NotificationManager) context
            .getSystemService(Context.NOTIFICATION_SERVICE);
    notificationManager.cancel(NOTIFICATION_ID);
}
 

Now if you want to get your service class the use getService in pending intent

private static PendingIntent stopPlayer(Context context){
    Intent serviceIntent = new Intent(context, MusicService.class);
    
           serviceIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
           context.stopService(serviceIntent);
           mContentView.setImageViewResource(R.id.notif_play, R.drawable.ic_play);
        

    return PendingIntent.getService(
            context,
            2,
            serviceIntent,
            PendingIntent.FLAG_UPDATE_CURRENT);
}

use the above method like this

  //perfoam action on notification to go to activity
    mContentView.setOnClickPendingIntent(R.id.notif_small,getActivityBottom(context));

    //for service
    mContentView.setOnClickPendingIntent(R.id.notif_play,stopPlayer(context));
Mazhar Iqbal
  • 813
  • 7
  • 7