Skip to content

Instantly share code, notes, and snippets.

@slightfoot
Created August 22, 2014 23:21
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save slightfoot/d7fb255d6f4d3d43def8 to your computer and use it in GitHub Desktop.
Save slightfoot/d7fb255d6f4d3d43def8 to your computer and use it in GitHub Desktop.
Notification Action View
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:radius="2dp" />
<stroke android:color="#ff222222" android:width="2dp" />
<solid android:color="#ffff0000" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<com.dd.test.NotificationActionView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:clipToPadding="false"
>
<ImageView
android:id="@android:id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
tools:ignore="ContentDescription"
/>
<TextView
android:id="@android:id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignRight="@android:id/icon"
android:layout_alignTop="@android:id/icon"
android:layout_marginRight="2dp"
android:layout_marginTop="2dp"
android:background="@drawable/drawable_notification"
android:includeFontPadding="false"
android:minWidth="22dp"
android:paddingBottom="2dp"
android:paddingLeft="2dp"
android:paddingRight="4dp"
android:paddingTop="2dp"
android:textColor="#FFFFFF"
android:textSize="12dp"
android:gravity="center"
tools:ignore="SpUsage"
/>
</com.dd.test.NotificationActionView>
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/notifications"
android:title="@string/notifications"
android:icon="@drawable/ic_action_bell"
android:actionLayout="@layout/layout_notification"
android:showAsAction="always"
/>
</menu>
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v4.content.LocalBroadcastManager;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
public class NotificationActionView extends RelativeLayout
{
private static final String ACTION_SET_ABS = NotificationActionView.class.getCanonicalName() + ".ACTION_SET_ABS";
private static final String ACTION_SET_DELTA = NotificationActionView.class.getCanonicalName() + ".ACTION_SET_DELTA";
private static final String EXTRA_COUNT = "extraCount";
private ImageView mIcon;
private TextView mText;
private MenuItem mItemData;
private int mCount;
public NotificationActionView(Context context)
{
this(context, null);
}
public NotificationActionView(Context context, AttributeSet attrs)
{
this(context, attrs, android.R.attr.actionButtonStyle);
}
public NotificationActionView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
@Override
protected void onFinishInflate()
{
super.onFinishInflate();
mIcon = (ImageView)findViewById(android.R.id.icon);
mText = (TextView)findViewById(android.R.id.text1);
mText.setVisibility(View.GONE);
}
public void setItemData(MenuItem itemData)
{
mItemData = itemData;
if(mItemData != null){
setId(itemData.getItemId());
mIcon.setImageDrawable(itemData.getIcon());
setContentDescription(itemData.getTitleCondensed());
setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
setEnabled(itemData.isEnabled());
setClickable(true);
setLongClickable(true);
}
}
public MenuItem getItemData()
{
return mItemData;
}
public void setCount(int count)
{
mCount = count;
mText.setText(mCount > 99 ? "99+" : String.valueOf(mCount));
mText.setVisibility((mCount == 0) ? View.GONE : View.VISIBLE);
}
public int getCount()
{
return mCount;
}
private static Intent createNotificationIntent(Context context, String action, int count)
{
return new Intent(action)
.setPackage(context.getPackageName())
.putExtra(EXTRA_COUNT, count);
}
public static void setCountAbs(Context context, int count)
{
LocalBroadcastManager.getInstance(context)
.sendBroadcast(createNotificationIntent(context, ACTION_SET_ABS, count));
}
public static void setCountDelta(Context context, int delta)
{
LocalBroadcastManager.getInstance(context)
.sendBroadcast(createNotificationIntent(context, ACTION_SET_DELTA, delta));
}
@Override
protected void onAttachedToWindow()
{
super.onAttachedToWindow();
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_SET_ABS);
filter.addAction(ACTION_SET_DELTA);
LocalBroadcastManager.getInstance(getContext())
.registerReceiver(mUpdateReceiver, filter);
}
private BroadcastReceiver mUpdateReceiver = new BroadcastReceiver()
{
@Override
public void onReceive(Context context, Intent intent)
{
int count = intent.getIntExtra(EXTRA_COUNT, 0);
if(ACTION_SET_ABS.equals(intent.getAction())){
setCount(count);
}
else if(ACTION_SET_DELTA.equals(intent.getAction())){
setCount(getCount() + count);
}
}
};
@Override
protected void onDetachedFromWindow()
{
super.onDetachedFromWindow();
LocalBroadcastManager.getInstance(getContext())
.unregisterReceiver(mUpdateReceiver);
}
@Override
public boolean performClick()
{
boolean performed = super.performClick();
if(mItemData != null && !performed){
((Activity)getContext()).onOptionsItemSelected(mItemData);
performed = true;
}
return performed;
}
@Override
public boolean performLongClick()
{
boolean performed = super.performLongClick();
if(mItemData != null && !performed){
final int[] screenPos = new int[2];
final Rect displayFrame = new Rect();
getLocationOnScreen(screenPos);
getWindowVisibleDisplayFrame(displayFrame);
final Context context = getContext();
final int width = getWidth();
final int height = getHeight();
final int midy = screenPos[1] + height / 2;
final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
Toast cheatSheet = Toast.makeText(context, mItemData.getTitle(), Toast.LENGTH_SHORT);
if(midy < displayFrame.height()){
// Show along the top; follow action buttons
cheatSheet.setGravity(Gravity.TOP | Gravity.RIGHT, screenWidth - screenPos[0] - width / 2, height);
}
else{
// Show along the bottom center
cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height);
}
cheatSheet.show();
performed = true;
}
return performed;
}
@Override
protected Parcelable onSaveInstanceState()
{
SavedState ss = new SavedState(super.onSaveInstanceState());
ss.count = getCount();
return ss;
}
@Override
protected void onRestoreInstanceState(Parcelable state)
{
SavedState ss = (SavedState)state;
super.onRestoreInstanceState(ss.getSuperState());
setCount(ss.count);
}
static class SavedState extends BaseSavedState
{
int count;
protected SavedState(Parcelable superState)
{
super(superState);
}
protected SavedState(Parcel source)
{
super(source);
count = source.readInt();
}
@Override
public void writeToParcel(Parcel dest, int flags)
{
super.writeToParcel(dest, flags);
dest.writeInt(count);
}
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>()
{
public SavedState createFromParcel(Parcel source)
{
return new SavedState(source);
}
public SavedState[] newArray(int size)
{
return new SavedState[size];
}
};
}
}
<resources>
<string name="notifications">Show Notifications</string>
</resources>
import android.view.Menu;
import android.view.MenuItem;
import android.support.v4.app.FragmentActivity;
public class TestActivity extends FragmentActivity
{
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
getMenuInflater().inflate(R.menu.menu_notification, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu)
{
MenuItem itemData = menu.findItem(R.id.notifications);
((NotificationActionView)itemData.getActionView()).setItemData(itemData);
return super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
if(item.getItemId() == R.id.notifications){
NotificationActionView.setCountDelta(this, 3);
return true;
}
return super.onOptionsItemSelected(item);
}
}
@WillWang1989
Copy link

thx, it helps

@langaliamayank
Copy link

Sir, Im getting error please see below
your help would be appreciated

01-08 06:41:48.738: E/AndroidRuntime(1982): FATAL EXCEPTION: main
01-08 06:41:48.738: E/AndroidRuntime(1982): java.lang.ClassCastException: android.view.ContextThemeWrapper cannot be cast to android.app.Activity
01-08 06:41:48.738: E/AndroidRuntime(1982): at com.example.cartcounter.NotificationActionView.performClick(NotificationActionView.java:135)
01-08 06:41:48.738: E/AndroidRuntime(1982): at android.view.View$PerformClick.run(View.java:16966)
01-08 06:41:48.738: E/AndroidRuntime(1982): at android.os.Handler.handleCallback(Handler.java:615)
01-08 06:41:48.738: E/AndroidRuntime(1982): at android.os.Handler.dispatchMessage(Handler.java:92)
01-08 06:41:48.738: E/AndroidRuntime(1982): at android.os.Looper.loop(Looper.java:137)
01-08 06:41:48.738: E/AndroidRuntime(1982): at android.app.ActivityThread.main(ActivityThread.java:4745)
01-08 06:41:48.738: E/AndroidRuntime(1982): at java.lang.reflect.Method.invokeNative(Native Method)
01-08 06:41:48.738: E/AndroidRuntime(1982): at java.lang.reflect.Method.invoke(Method.java:511)
01-08 06:41:48.738: E/AndroidRuntime(1982): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
01-08 06:41:48.738: E/AndroidRuntime(1982): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
01-08 06:41:48.738: E/AndroidRuntime(1982): at dalvik.system.NativeStart.main(Native Method)

@mmrk09
Copy link

mmrk09 commented Jan 28, 2015

//add this to the class
   private static Activity scanForActivity(Context cont) 
{
    if (cont == null)
        return null;
    else if (cont instanceof Activity)
        return (Activity)cont;
    else if (cont instanceof ContextWrapper)
        return scanForActivity(((ContextWrapper)cont).getBaseContext());

    return null;
}

    //change performClick to this
    Activity activity = ActionViewCart.scanForActivity(getContext());
activity.onOptionsItemSelected(mItemData);
//((Activity)getContext()).onOptionsItemSelected(mItemData);

@arodrigueze0215
Copy link

How can i to show Notification view when my activity is created and not when i select item Menu?

@slightfoot
Copy link
Author

You should be able to simply replace this:

@OverRide
public boolean onPrepareOptionsMenu(Menu menu)
{
MenuItem itemData = menu.findItem(R.id.notifications);
NotificationActionView actionView = (NotificationActionView)itemData.getActionView();
actionView.setItemData(itemData);
actionView.setCount(10); // initial value
return super.onPrepareOptionsMenu(menu);
}

I've created a new repo with an updated project based on this gist. See https://github.com/slightfoot/android-notification-action-view

@arodrigueze0215
Copy link

@slightfoot Just I'm trying this code, but in my code there are something curious: when i'm running my app on Nexus 5 Android Emulator, and on huawei P6 device too, it is perfect, but i run my app in a Samsung Galaxy device it doesn't appear the "NotificationView". It is so rare... do you know what is the problem?

@hatpick
Copy link

hatpick commented Mar 13, 2015

How can we use a broadcast receiver in the manifest?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment