Choosing Android’s System Sounds for Cordova Push Notifications, using Native Configuration Screens

One of the features most wished for in one of my Cordova-based android apps was to let the users choose another sound for push notifications, or to disable sound altogether (without having to mute the whole smart phone and still keeping vibration). The Push Plugin supports custom sounds, however all sound resources have to be embedded in the app. In my case, I wanted to let the users choose among the system sounds provided by Android itself, which apparently isn’t possible without some major changes to the plugin.

Here, I’m going to outline the changes necessary to integrate Android’s native RingtonePreference dialog into a Cordova-based app to let the users choose another notification sound. I’ve published the modified plugin that should work on android projects on Github.

Screenshot_2015-04-29-18-13-18Screenshot_2015-04-29-18-13-23

Configuration Dialog

Choosing the preferences for push notifications is done in a native android PreferenceScreen that is opened from within the app using a new javascript-method on the pushNotification-Object. The configuration screen is defined by a xml file stored at platforms/android/res/xml/pushsettings.xml:



    

        

        

        

        

    

The referenced strings are stored in platforms/android/res/values/strings-gcm.xml:



    Notifications
    Light
    Light is switched on
    Light is switched off
    Vibration
    Vibration is switched on
    Vibration is switched off
    Sound
    Sound is switched on
    Sound is switched off
    Ring tone
    Choose a ring tone

Translated versions can be stored in files like platforms/android/res/values-de/strings-gcm.xml

Opening the dialog
First of all, a new activity has to be defined in the platforms/android/AndroidManifest.xml:


The Activity itself is pretty simple, just remember to replace the „CORDOVA_PACKAGE_ID“ by the id of your app (necessary to access the R.xml.pushsettings-resource):

package com.plugin.gcm;

import CORDOVA_PACKAGE_ID.R;
import android.os.Bundle;
import android.preference.PreferenceActivity;

public class PushSettingsActivity extends PreferenceActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.pushsettings);
    }
}

This activity is called from PushPlugin.java’s execute-method, something like:

if ('showPushSettings'.equals(action)) {
    Intent i = new Intent(this.cordova.getActivity(), PushSettingsActivity.class);
    this.cordova.getActivity().startActivityForResult(i, RESULT_PUSH_SETTINGS);
    callbackContext.success();
}

The JavaScript-method to call this in PushNotification.js is:

PushNotification.prototype.showPushSettings = function (successCallback, errorCallback) {
    if (errorCallback == null) {
        errorCallback = function () {}
    }
    if (successCallback == null) {
        successCallback = function () {}
    }

    if (typeof errorCallback != "function") {
        console.log("PushNotification.showPushSettings failure: failure parameter not a function");
        return;
    }

    if (typeof successCallback != "function") {
        console.log("PushNotification.showPushSettings failure: success callback parameter must be a function");
        return;
    }

    cordova.exec(successCallback, errorCallback, "PushPlugin", "showPushSettings", []);
};

With all these changes applied, a call to plugins.pushNotification.showPushSettings() should open the configuration dialog and let the user apply her desired notification sound. The settings are stored automatically in android’s SharedPreferences. In the source code of the plugin, I also added a getPushSettings()-method that returns the settings into JavaScript, if you ever need to query these settings.

Using the settings for the notification
The actual notification sound is triggered in GCMIntentService.java’s onMessage-method. The new code reads the settings from the SharedPreferences and acts accordingly:

SharedPreferences app_preferences = PreferenceManager.getDefaultSharedPreferences(this);
boolean vibration = app_preferences.getBoolean("com.plugin.gcm.vibration", true);
boolean sound = app_preferences.getBoolean("com.plugin.gcm.sound", true);
boolean light = app_preferences.getBoolean("com.plugin.gcm.light", true);
String ringtonePath = app_preferences.getString("com.plugin.gcm.ringtone", "defValue");
int defaults = 0;
if (vibration) defaults = defaults | Notification.DEFAULT_VIBRATE;
if (light) defaults = defaults | Notification.DEFAULT_LIGHTS;
		
NotificationCompat.Builder mBuilder =
    new NotificationCompat.Builder(context)
        .setDefaults(defaults)
        .setSmallIcon(context.getApplicationInfo().icon)
        .setWhen(System.currentTimeMillis())
        .setContentTitle(extras.getString("title"))
        .setTicker(extras.getString("title"))
        .setContentIntent(contentIntent)
        .setAutoCancel(true);

if (!ringtonePath.equals("") && sound) {
    Uri uri = Uri.parse(ringtonePath);
    Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), uri);
    r.play();

    mBuilder.setSound(null);
}

I’m using the Ringtone.play() instead of the setSound()-method of NotificationCompat.Builder, as the latter one somehow didn’t actually change the sound, even with the URL of the sound provided. I haven’t yet figured out why, but there probably is a solution that’s more elegant than the one showed above.
That’s it. Have fun! 🙂

2 comments

  1. Hi ho,

    do you know if there is a way to make this work with ionic framework and the cordova plugins without native approach?

    regards,
    Chiny1

  2. Hello!

    Is there anyway I could use this on PhoneGap Build? Thanks in advance for your answer. 🙂

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert