SDK

Customizations

In this page we'll dive deeper into several aspects of our Android library that can be customized to match your needs.

Setting keys in code

Instead of relying on the applicationKey and applicationSecret in notificareconfig.properties, it is possible to set these in code.

Notificare.shared().setApplicationKey("APP_KEY");
Notificare.shared().setApplicationSecret("APP_SECRET");
Notificare.shared().applicationKey = "APP_KEY"
Notificare.shared().applicationSecret = "APP_SECRET"

These methods should be called before calling Notificare.shared().launch() to make sure the correct keys are used. If you set keys programmatically and have a google-services.json (mandatory) you can safely delete the notificareconfig.properties.

Notification LED Settings

You can send along the preferred color and blinking time of the notification light (only works on selected device models) per notification from the Notificare REST API or the Dashboard. However, you can also set a default color for your app. If neither are set, it defaults to white colored blinking for 500 ms on and 1500 ms off.

Notificare.shared().setDefaultLightsColor("red");
Notificare.shared().setDefaultLightsOn(1000);
Notificare.shared().setDefaultLightsOff(2000);
Notificare.shared().defaultLightsColor = "red"
Notificare.shared().defaultLightsOn = 1000
Notificare.shared().defaultLightsOff = 2000

Notifications UI

Changing user interface in the Android is intrinsically decided by your app's styles. Basically Notificare will just use the styles you provide in your app, for the several elements used throughout our UI. We recommend these changes are done in your /res/values folders like shown below:

android styles

Action Labels

Labels used for Action Buttons in both the Notification Manager as well as Notification Dialogs can be localized by adding them as strings resources, where the name is equal to the label. To protect collision of resource names, they can be prefixed with a string. The prefix must then be set by calling Notificare.shared().setLocalizedActionLabelPrefix().

Handling Notification Opens

By default, the intent receiver will open a new task with a NotificationActivity on top, optionally with a parent activity, as defined in the AndroidManifest.xml. It is also possible for any of your app's activities to receive the opened notification intent instead. For that, your activity will need to add an intent filter on the re.notifica.intent.action.NotificationOpened action

<activity
    android:name="re.notifica.demo.MainActivity"
    android:configChanges="keyboardHidden|orientation"
    android:label="@string/app_name"
    android:launchMode="singleTop">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="@string/app_url_scheme" />
    </intent-filter>
    <intent-filter>
        <action android:name="re.notifica.intent.action.RemoteMessageOpened" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
    <intent-filter>
        <action android:name="re.notifica.intent.action.NotificationOpened" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

In the example above, the activity is declared to be launched singleTop, so intents will come in to your active task as a new intent and won't interfere with the current activity state. To handle the intent, use the following convenience methods

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    handleIntent(getIntent());
}

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    handleIntent(intent);
}

void handleIntent(Intent intent) {
if ()
    if (intent != null && intent.getAction() != null && intent.getAction().equals(Notificare.INTENT_ACTION_NOTIFICATION_OPENED)) {
        NotificareNotification notification = intent.getParcelableExtra(Notificare.INTENT_EXTRA_NOTIFICATION);
        if (notification != null) {
            Notificare.shared().openNotification(this, intent);
        }
    } else {
        // your normal intent handling routine
    }
}
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    handleIntent(intent)
}

override fun onNewIntent(intent: Intent?) {
    super.onNewIntent(intent)
    handleIntent(intent)
}

fun handleIntent(intent: Intent?) {
    if (intent != null && intent.action != null && intent.action == Notificare.INTENT_ACTION_NOTIFICATION_OPENED) {
        val notification: NotificareNotification =
            intent.getParcelableExtra(Notificare.INTENT_EXTRA_NOTIFICATION)
        if (notification != null) {
            Notificare.shared().openNotification(this, intent)
        }
    } else {
        // your normal intent handling routine
    }
}

This will launch the NotificationActivity on top of your current task. If you've themed the NotificationActivity to be transparent, it will make sure alerts and dialogs will be displayed transparently over the launching activity.

Customizing the Notification Fragment

If you're handling the NotificationOpened intent yourself, you can also opt to display the Notification fragment yourself instead of opening the NotificationActivity. For that, your activity needs to extend a FragmentActivity and also needs to implement the re.notifica.ui.NotificationActivityCallback interface. For example, you could implement something like this:

public class MyNotificationActivity extends AppCompatActivity implements NotificationActivityCallback {

    // ...more code
    void showNotificationFragmentFromIntent() {
        Fragment notificationFragment = NotificareFragmentFactory.createFragment(getIntent());
        if (notificationFragment != null) {
            getSupportFragmentManager()
                    .beginTransaction()
                    .add(R.id.notification_container, notificationFragment, "notification_container")
                    .commit();
        }
    }

    @Override
    public void onNotificationFragmentFinished() {
        removeNotificationFragment();
        if (getSupportActionBar() == null || !getSupportActionBar().isShowing()) {
            // prevent transition animation
            overridePendingTransition(0, 0);
        }
    }

    @Override
    public void onNotificationFragmentShouldShowActionBar(NotificareNotification notification) {
        if (getSupportActionBar() != null) {
            if (notification.getTitle() != null) {
                getSupportActionBar().setTitle(notification.getTitle());
            }
            getSupportActionBar().show();
        }
    }

    @Override
    public void onNotificationFragmentCanHideActionBar(NotificareNotification notification) {
        if (getSupportActionBar() != null) {
            getSupportActionBar().hide();
        }
    }

    @Override
    public void onNotificationFragmentStartProgress(NotificareNotification notification) {
        showProgressBar();
    }

    @Override
    public void onNotificationFragmentEndProgress(NotificareNotification notification) {
        hideProgressBar();
    }

    @Override
    public void onNotificationFragmentActionCanceled(NotificareNotification notification) {
        hideProgressBar();
        Toast.makeText(this, R.string.notificare_action_canceled, Toast.LENGTH_LONG).show();
    }

    @Override
    public void onNotificationFragmentActionFailed(NotificareNotification notification, String reason) {
        hideProgressBar();
        Toast.makeText(this, reason, Toast.LENGTH_LONG).show();
    }

    @Override
    public void onNotificationFragmentActionSucceeded(NotificareNotification notification) {
        hideProgressBar();
        Toast.makeText(this, R.string.notificare_action_success, Toast.LENGTH_SHORT).show();
    }

}
class MyNotificationActivity : AppCompatActivity(), NotificationActivityCallback {
    // ...more code
    fun showNotificationFragmentFromIntent() {
        val notificationFragment =
            NotificareFragmentFactory.createFragment(intent)
        if (notificationFragment != null) {
            supportFragmentManager
                .beginTransaction()
                .add(R.id.notification_container, notificationFragment, "notification_container")
                .commit()
        }
    }

    override fun onNotificationFragmentFinished() {
        removeNotificationFragment()
        if (supportActionBar == null || !supportActionBar!!.isShowing) {
            // prevent transition animation
            overridePendingTransition(0, 0)
        }
    }

    override fun onNotificationFragmentShouldShowActionBar(notification: NotificareNotification) {
        if (supportActionBar != null) {
            if (notification.title != null) {
                supportActionBar!!.title = notification.title
            }
            supportActionBar!!.show()
        }
    }

    override fun onNotificationFragmentCanHideActionBar(notification: NotificareNotification) {
        if (supportActionBar != null) {
            supportActionBar!!.hide()
        }
    }

    override fun onNotificationFragmentStartProgress(notification: NotificareNotification) {
        showProgressBar()
    }

    override fun onNotificationFragmentEndProgress(notification: NotificareNotification) {
        hideProgressBar()
    }

    override fun onNotificationFragmentActionCanceled(notification: NotificareNotification) {
        hideProgressBar()
        Toast.makeText(this, R.string.notificare_action_canceled, Toast.LENGTH_LONG).show()
    }

    override fun onNotificationFragmentActionFailed(
        notification: NotificareNotification,
        reason: String
    ) {
        hideProgressBar()
        Toast.makeText(this, reason, Toast.LENGTH_LONG).show()
    }

    override fun onNotificationFragmentActionSucceeded(notification: NotificareNotification) {
        hideProgressBar()
        Toast.makeText(this, R.string.notificare_action_success, Toast.LENGTH_SHORT).show()
    }
}

Customizing the IntentReceiver

If you've followed our recommendations, you've implemented your own Intent Receiver. This will offer you the possibility to customize how you handle notifications.

Handling the Extra property

There are use cases where you'll want to add extra sets of data to your notification. This is allowed via the dashboard and API and in order to handle that data you'll need to override the onNotificationReceived method:

public class MyIntentReceiver extends DefaultIntentReceiver {

    //...more code

    @Override
    public void onNotificationReceived(NotificareRemoteMessage message) {
      Log.d(TAG, " received with extra Notification" + message.getExtra().get("mykey"));
      super.onNotificationReceived(message);
    }

    //...more code
}
class MyIntentReceiver: DefaultIntentReceiver() {

    //...more code

    override fun onNotificationReceived(message: NotificareRemoteMessage?) {
        //Log.d(TAG, " received with extra Notification" + message.extra["mykey"]);
        super.onNotificationReceived(message)
    }

    //...more code

This will give you the opportunity to intercept the incoming notification and retrieve the data you've included in your notification. Please note that you should call the super.onNotificationReceived method, that will make sure that Notificare library will still handle the notification for you.

If you do not call super.onNotificationReceived, which we do not recommend, then there are a few things you might want to take in consideration.

Events Logging

In order for all metrics to be collected correctly, please make sure you add the following:

@Override
public void onNotificationOpened(@NonNull NotificareNotification notification, @Nullable String inboxItemId, @Nullable NotificareAction action, @Nullable CharSequence replyText) {

    //...more code

    Notificare.shared().getEventLogger().logOpenNotification(notification.getNotificationId());
    Notificare.shared().getEventLogger().logOpenNotificationInfluenced(notification.getNotificationId());
    if (inboxItemId != null) {
        Notificare.shared().getInboxManager().markItem(inboxItemId);
    }

  //...more code

}
    override fun onNotificationOpened(notification: NotificareNotification, inboxItemId: String?, action: NotificareAction?, replyText: CharSequence?) {

        //...more code

        Notificare.shared().eventLogger.logOpenNotification(notification.notificationId)
        Notificare.shared().eventLogger.logOpenNotificationInfluenced(notification.notificationId)
        if (inboxItemId != null) {
            Notificare.shared().inboxManager.markItem(inboxItemId)
        }

        //...more code

    }

Handling Actions

Actions from the notification drawer come in as an intent extra. You can have them handled by the Notificare library in the background.

NotificareAction action = extras.getParcelable(Notificare.INTENT_EXTRA_ACTION);
NotificareNotification notification = extras.getParcelable(Notificare.INTENT_EXTRA_NOTIFICATION);
if (action != null && notification != null) {
    try {
        Class<?> actionClass = Class.forName(action.getType());
        Constructor<?> ctor = actionClass.getConstructor(Activity.class, NotificareNotification.class, NotificareAction.class);
        NotificationAction actionHandler = (NotificationAction) ctor.newInstance(null, notification, action);
        actionHandler.handleAction(new NotificareCallback<NotificarePendingResult>() {
            @Override
                public void onSuccess(NotificarePendingResult result) {
                Log.d(TAG, "action handle successfully");
            }

            @Override
                public void onError(NotificareError error) {
                Log.e(TAG, "error handling action", error);
            }
        });
    } catch (Exception e) {
        Log.e(TAG, "error instantiating Action Handler", e);
    }
} else {

  // Display UI
}
val action: NotificareAction? = extras.getParcelable(Notificare.INTENT_EXTRA_ACTION)
val notification: NotificareNotification? = extras.getParcelable(Notificare.INTENT_EXTRA_NOTIFICATION)
if (action != null && notification != null) {
    try {
        val actionClass = Class.forName(action.type)
        val ctor = actionClass.getConstructor(Activity::class.java, NotificareNotification::class.java, NotificareAction::class.java)
        val actionHandler = ctor.newInstance(null, notification, action) as NotificationAction
        actionHandler.handleAction(object : NotificareCallback<NotificarePendingResult?> {
            override fun onSuccess(result: NotificarePendingResult?) {
                Log.d(TAG,"action handle successfully")
            }

            override fun onError(error: NotificareError) {
                Log.e(TAG, "error handling action", error)
            }
        })
    } catch (e: Exception) {
        Log.e(TAG, "error instantiating Action Handler", e)
    }
} else {

    // Display UI
}

Launching an Activity

Most likely if you are going to handle the notification yourself, you will want to launch an activity in your app. As an example, this is what the DefaultIntentReceiver does:

if (action != null && notification != null) {
// Handle actions
} else {
    // Close the notification drawer
    Notificare.shared().getApplicationContext().sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
    Intent notificationIntent = new Intent()
    .setAction(Notificare.INTENT_ACTION_NOTIFICATION_OPENED)
    .putExtras(extras)
    .putExtra(Notificare.INTENT_EXTRA_DISPLAY_MESSAGE, Notificare.shared().getDisplayMessage())
    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP)
    .setPackage(Notificare.shared().getApplicationContext().getPackageName());

    if (notificationIntent.resolveActivity(Notificare.shared().getApplicationContext().getPackageManager()) != null) {
      // Notification handled by custom activity in package
      Notificare.shared().getApplicationContext().startActivity(notificationIntent);
    } else {
      // Start a task stack with NotificationActivity on top
      notificationIntent.setClass(Notificare.shared().getApplicationContext(), Notificare.shared().getNotificationActivity());
      TaskStackBuilder stackBuilder = TaskStackBuilder.create(Notificare.shared().getApplicationContext());
      buildTaskStack(stackBuilder, notificationIntent, notification);
      stackBuilder.startActivities();
    }
}
if (action != null && notification != null) {
    // Handle actions
} else {
    // Close the notification drawer
    Notificare.shared().applicationContext.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
    val notificationIntent = Intent()
        .setAction(Notificare.INTENT_ACTION_NOTIFICATION_OPENED)
        .putExtras(extras)
        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
        .setPackage(Notificare.shared().applicationContext.packageName)
    if (notificationIntent.resolveActivity(Notificare.shared().applicationContext.packageManager) != null) {
        // Notification handled by custom activity in package
        Notificare.shared().applicationContext.startActivity(notificationIntent)
    } else {
        // Start a task stack with NotificationActivity on top
        notificationIntent.setClass(Notificare.shared().applicationContext, Notificare.shared().notificationActivity)
        val stackBuilder = TaskStackBuilder.create(Notificare.shared().applicationContext)
        buildTaskStack(stackBuilder, notificationIntent, notification)
        stackBuilder.startActivities()
    }
}

Cancel Notifications

One other thing you have to take in account when handling the notifications yourself is to make sure you clear notifications from the notification drawer whenever you read them. In your custom IntentReceiver, after presenting the notification, you can add the following:

@Override
public void onNotificationOpened(@NonNull NotificareNotification notification, @Nullable String inboxItemId, @Nullable NotificareAction action, @Nullable CharSequence replyText) {

    //...more code

    if (Notificare.shared().getAutoCancel()) {
        Notificare.shared().cancelNotification(notification.getNotificationId());
    }

    //...more code

}
override fun onNotificationOpened(notification: NotificareNotification, inboxItemId: String?, action: NotificareAction?, replyText: CharSequence?) {

    //...more code

    if (Notificare.shared().autoCancel) {
        Notificare.shared().cancelNotification(notification.notificationId)
    }

    //...more code
}

ProGuard

If you use ProGuard, rules for classes to keep are included with the AAR, so there is no need to manually add any rules for Notificare.

65k method limit exceeded

Notificare Android library is tested to work with MultiDex, both natively (Android 5 and above) as well as with the MultiDex support library.

Add the following to your app's build.gradle

defaultConfig {
    multiDexEnabled true
}

dependencies {
    compile 'androidx.multidex:multidex:2.0.1'
}

Then, override the attachBaseContext method in the Application class for your app.

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    MultiDex.install(this);
}
override fun attachBaseContext(base: Context?) {
    super.attachBaseContext(base)
    MultiDex.install(this)
}

For more information on MultiDex and possible issues and workarounds, see Android Developer Documentation