Commit b228661c authored by Yusuke Iwaki's avatar Yusuke Iwaki Committed by GitHub

Merge pull request #124 from RocketChat/feature/gcm-push

Push notifications using the Cordova plugin code
parents 0b7927a0 37586be8
......@@ -105,6 +105,8 @@ dependencies {
compile 'com.google.firebase:firebase-core:10.0.0'
compile 'com.google.firebase:firebase-crash:10.0.0'
compile 'com.google.android.gms:play-services-gcm:10.0.0'
compile rootProject.ext.okhttp3
compile rootProject.ext.picasso
......
......@@ -4,6 +4,12 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<permission
android:name="chat.rocket.android.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="chat.rocket.android.permission.C2D_MESSAGE" />
<application
android:name=".RocketChatApplication"
......@@ -35,6 +41,34 @@
<service android:name=".service.RocketChatService" />
<service android:name=".service.notification.NotificationDismissalCallbackService" />
<receiver
android:name="com.google.android.gms.gcm.GcmReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="com.example.gcm" />
</intent-filter>
</receiver>
<service
android:name=".push.gcm.GCMIntentService"
android:exported="false">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</service>
<service
android:name=".push.gcm.GcmInstanceIDListenerService"
android:exported="false">
<intent-filter>
<action android:name="com.google.android.gms.iid.InstanceID" />
</intent-filter>
</service>
<service
android:name=".push.gcm.GcmRegistrationIntentService"
android:exported="false" />
</application>
</manifest>
......@@ -3,6 +3,8 @@ package chat.rocket.android;
import android.content.Context;
import android.content.SharedPreferences;
import java.util.UUID;
/**
* sharedpreference-based cache.
*/
......@@ -10,10 +12,45 @@ public class RocketChatCache {
public static final String KEY_SELECTED_SERVER_CONFIG_ID = "selectedServerConfigId";
public static final String KEY_SELECTED_ROOM_ID = "selectedRoomId";
private static final String PUSH_ID = "pushId";
/**
* get SharedPreference instance for RocketChat application cache.
*/
public static SharedPreferences get(Context context) {
return context.getSharedPreferences("cache", Context.MODE_PRIVATE);
}
public static String getSelectedServerConfigId(Context context) {
return get(context).getString(KEY_SELECTED_SERVER_CONFIG_ID, "");
}
public static void setSelectedServerConfigId(Context context, String serverConfigId) {
setString(get(context), KEY_SELECTED_SERVER_CONFIG_ID, serverConfigId);
}
public static String getSelectedRoomId(Context context) {
return get(context).getString(KEY_SELECTED_ROOM_ID, "");
}
public static void setSelectedRoomId(Context context, String roomId) {
setString(get(context), KEY_SELECTED_ROOM_ID, roomId);
}
public static String getPushId(Context context) {
SharedPreferences preferences = get(context);
String pushId = null;
if (!preferences.contains(PUSH_ID)) {
// generates one and save
pushId = UUID.randomUUID().toString().replace("-", "");
setString(preferences, PUSH_ID, pushId);
}
return preferences.getString(PUSH_ID, pushId);
}
private static void setString(SharedPreferences preferences, String key, String value) {
SharedPreferences.Editor editor = preferences.edit();
editor.putString(key, value);
editor.apply();
}
}
......@@ -9,6 +9,7 @@ import chat.rocket.android.LaunchUtil;
import chat.rocket.android.RocketChatCache;
import chat.rocket.android.model.ServerConfig;
import chat.rocket.android.model.ddp.RoomSubscription;
import chat.rocket.android.push.PushConstants;
import chat.rocket.android.realm_helper.RealmListObserver;
import chat.rocket.android.realm_helper.RealmStore;
import chat.rocket.android.service.RocketChatService;
......@@ -39,21 +40,33 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
Intent intent = getIntent();
if (intent != null) {
if (intent.hasExtra(ServerConfig.ID)) {
onIntent(getIntent());
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
onIntent(intent);
}
private void onIntent(Intent intent) {
if (intent == null) {
return;
}
if (intent.hasExtra(PushConstants.SERVER_CONFIG_ID)) {
SharedPreferences.Editor editor = RocketChatCache.get(this).edit();
editor.putString(RocketChatCache.KEY_SELECTED_SERVER_CONFIG_ID,
intent.getStringExtra(ServerConfig.ID));
intent.getStringExtra(PushConstants.SERVER_CONFIG_ID));
if (intent.hasExtra("roomId")) {
editor.putString(RocketChatCache.KEY_SELECTED_ROOM_ID, intent.getStringExtra("roomId"));
if (intent.hasExtra(PushConstants.ROOM_ID)) {
editor.putString(RocketChatCache.KEY_SELECTED_ROOM_ID,
intent.getStringExtra(PushConstants.ROOM_ID));
}
editor.apply();
}
}
}
}
private void updateServerConfigIdIfNeeded(SharedPreferences prefs) {
String newServerConfigId = prefs.getString(RocketChatCache.KEY_SELECTED_SERVER_CONFIG_ID, null);
......
......@@ -24,6 +24,7 @@ import hugo.weaving.DebugLog;
/**
* Utility class for creating/handling MethodCall or RPC.
*
* TODO: separate method into several manager classes (SubscriptionManager, MessageManager, ...).
*/
public class MethodCallHelper {
......@@ -37,9 +38,8 @@ public class MethodCallHelper {
protected final RealmHelper realmHelper;
protected final DDPClientWrapper ddpClient;
@Deprecated
/**
* Deprecated. use MethodCall(Context, String) instead.
* initialize with ServerConfigId.
*/
public MethodCallHelper(String serverConfigId) {
this(null, serverConfigId);
......
package chat.rocket.android.api;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import bolts.Task;
import chat.rocket.android.realm_helper.RealmHelper;
public class PushHelper extends MethodCallHelper {
public PushHelper(String serverConfigId) {
super(serverConfigId);
}
public PushHelper(Context context, String serverConfigId) {
super(context, serverConfigId);
}
public PushHelper(RealmHelper realmHelper,
DDPClientWrapper ddpClient) {
super(realmHelper, ddpClient);
}
public Task<Void> pushUpdate(@NonNull String pushId, @NonNull String token,
@Nullable String userId) {
return call("raix:push-update", TIMEOUT_MS, () -> {
JSONObject param = new PushUpdate(pushId, token, userId).toJson();
return new JSONArray().put(param);
}).onSuccessTask(task -> Task.forResult(null));
}
public Task<Void> pushSetUser(String pushId) {
return call("raix:push-setuser", TIMEOUT_MS, () -> new JSONArray().put(pushId))
.onSuccessTask(task -> Task.forResult(null));
}
private static class PushUpdate {
private String pushId;
private String gcmToken;
private String userId;
PushUpdate(@NonNull String pushId, @NonNull String gcmToken, @Nullable String userId) {
this.pushId = pushId;
this.gcmToken = gcmToken;
this.userId = userId;
}
JSONObject toJson() throws JSONException {
JSONObject param = new JSONObject();
param.put("id", pushId);
param.put("appName", "main");
param.put("userId", userId != null ? userId : JSONObject.NULL);
param.put("metadata", new JSONObject());
JSONObject tokenParam = new JSONObject();
tokenParam.put("gcm", gcmToken);
param.put("token", tokenParam);
return param;
}
}
}
......@@ -2,7 +2,6 @@ package chat.rocket.android.fragment.server_config;
import android.support.design.widget.Snackbar;
import android.widget.TextView;
import android.widget.Toast;
import org.json.JSONObject;
import chat.rocket.android.R;
......@@ -52,17 +51,11 @@ public class InputHostnameFragment extends AbstractServerConfigFragment {
@Override
public void isNotValid() {
getActivity().runOnUiThread(() ->
Toast.makeText(getActivity(), R.string.input_hostname_invalid_server_message,
Toast.LENGTH_SHORT).show());
showError(getString(R.string.input_hostname_invalid_server_message)));
}
});
}
@Override
public void onResume() {
super.onResume();
}
@Override
public void onDestroyView() {
serverConfigObserver.unsub();
......@@ -75,7 +68,7 @@ public class InputHostnameFragment extends AbstractServerConfigFragment {
return TextUtils.or(TextUtils.or(editor.getText(), editor.getHint()), "").toString();
}
private void onServerValid(String hostname) {
private void onServerValid(final String hostname) {
RocketChatCache.get(getContext()).edit()
.putString(RocketChatCache.KEY_SELECTED_SERVER_CONFIG_ID, serverConfigId)
.apply();
......
......@@ -62,11 +62,9 @@ public class LoginFragment extends AbstractServerConfigFragment {
});
final View btnUserRegistration = rootView.findViewById(R.id.btn_user_registration);
btnUserRegistration.setOnClickListener(view -> {
UserRegistrationDialogFragment.create(serverConfigId,
btnUserRegistration.setOnClickListener(view -> UserRegistrationDialogFragment.create(serverConfigId,
txtUsername.getText().toString(), txtPasswd.getText().toString())
.show(getFragmentManager(), UserRegistrationDialogFragment.class.getSimpleName());
});
.show(getFragmentManager(), UserRegistrationDialogFragment.class.getSimpleName()));
}
private void showError(String errString) {
......
......@@ -22,7 +22,7 @@ public class ServerPolicyHelper {
return "demo.rocket.chat";
}
return removeProtocol(enforceDefaultHost(hostname));
return removeTrailingSlash(removeProtocol(enforceDefaultHost(hostname)));
}
public static void isApiVersionValid(@NonNull OkHttpClient client, @NonNull String host,
......@@ -71,6 +71,15 @@ public class ServerPolicyHelper {
return hostname.replace("http://", "").replace("https://", "");
}
private static String removeTrailingSlash(String hostname) {
if (hostname.charAt(hostname.length() - 1) != '/') {
// no need for a regex - just return it
return hostname;
}
return hostname.replaceAll("/+$", "");
}
private static boolean isValid(ResponseBody body) {
if (body == null || body.contentLength() == 0) {
return false;
......
......@@ -20,6 +20,7 @@ public class ServerConfig extends RealmObject {
public static final String STATE = "state";
public static final String SESSION = "session";
public static final String ERROR = "error";
public static final String SYNC_PUSH_TOKEN = "syncPushToken";
public static final int STATE_READY = 0;
public static final int STATE_CONNECTING = 1;
......@@ -31,6 +32,7 @@ public class ServerConfig extends RealmObject {
private int state;
private String session;
private String error;
private boolean syncPushToken;
/**
* Log the server connection is lost due to some exception.
......@@ -100,4 +102,12 @@ public class ServerConfig extends RealmObject {
public void setError(String error) {
this.error = error;
}
public boolean shouldSyncPushToken() {
return syncPushToken;
}
public void setSyncPushToken(boolean syncPushToken) {
this.syncPushToken = syncPushToken;
}
}
package chat.rocket.android.push;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.support.v4.app.RemoteInput;
import chat.rocket.android.push.gcm.GCMIntentService;
public class BackgroundActionButtonHandler extends BroadcastReceiver implements PushConstants {
private static final String LOG_TAG = "BgActionButtonHandler";
@Override
public void onReceive(Context context, Intent intent) {
Bundle extras = intent.getExtras();
Log.d(LOG_TAG, "BackgroundActionButtonHandler = " + extras);
int notId = intent.getIntExtra(NOT_ID, 0);
Log.d(LOG_TAG, "not id = " + notId);
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(GCMIntentService.getAppName(context), notId);
if (extras == null) {
return;
}
Bundle originalExtras = extras.getBundle(PUSH_BUNDLE);
originalExtras.putBoolean(FOREGROUND, false);
originalExtras.putBoolean(COLDSTART, false);
originalExtras.putString(ACTION_CALLBACK, extras.getString(CALLBACK));
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
if (remoteInput != null) {
String inputString = remoteInput.getCharSequence(INLINE_REPLY).toString();
Log.d(LOG_TAG, "response: " + inputString);
originalExtras.putString(INLINE_REPLY, inputString);
}
}
}
package chat.rocket.android.push;
public interface PushConstants {
String COM_ADOBE_PHONEGAP_PUSH = "com.adobe.phonegap.push";
String REGISTRATION_ID = "registrationId";
String FOREGROUND = "foreground";
String TITLE = "title";
String NOT_ID = "notId";
String PUSH_BUNDLE = "pushBundle";
String ICON = "icon";
String ICON_COLOR = "iconColor";
String SOUND = "sound";
String SOUND_DEFAULT = "default";
String SOUND_RINGTONE = "ringtone";
String VIBRATE = "vibrate";
String ACTIONS = "actions";
String CALLBACK = "callback";
String ACTION_CALLBACK = "actionCallback";
String DRAWABLE = "drawable";
String MSGCNT = "msgcnt";
String VIBRATION_PATTERN = "vibrationPattern";
String STYLE = "style";
String SUMMARY_TEXT = "summaryText";
String PICTURE = "picture";
String GCM_N = "gcm.n.";
String GCM_NOTIFICATION = "gcm.notification";
String GCM_NOTIFICATION_BODY = "gcm.notification.body";
String UA_PREFIX = "com.urbanairship.push";
String PARSE_COM_DATA = "data";
String ALERT = "alert";
String MESSAGE = "message";
String BODY = "body";
String SOUNDNAME = "soundname";
String LED_COLOR = "ledColor";
String PRIORITY = "priority";
String IMAGE = "image";
String STYLE_INBOX = "inbox";
String STYLE_PICTURE = "picture";
String STYLE_TEXT = "text";
String BADGE = "badge";
String INITIALIZE = "init";
String SUBSCRIBE = "subscribe";
String UNSUBSCRIBE = "unsubscribe";
String UNREGISTER = "unregister";
String EXIT = "exit";
String FINISH = "finish";
String HAS_PERMISSION = "hasPermission";
String ANDROID = "android";
String SENDER_ID = "senderID";
String CLEAR_NOTIFICATIONS = "clearNotifications";
String COLDSTART = "coldstart";
String ADDITIONAL_DATA = "additionalData";
String COUNT = "count";
String FROM = "from";
String COLLAPSE_KEY = "collapse_key";
String FORCE_SHOW = "forceShow";
String GCM = "GCM";
String CONTENT_AVAILABLE = "content-available";
String TOPICS = "topics";
String SET_APPLICATION_ICON_BADGE_NUMBER = "setApplicationIconBadgeNumber";
String CLEAR_ALL_NOTIFICATIONS = "clearAllNotifications";
String VISIBILITY = "visibility";
String INLINE_REPLY = "inlineReply";
String LOC_KEY = "locKey";
String LOC_DATA = "locData";
String TWILIO_BODY = "twi_body";
String TWILIO_TITLE = "twi_title";
String TWILIO_SOUND = "twi_sound";
String START_IN_BACKGROUND = "cdvStartInBackground";
String FORCE_START = "force-start";
// RC specific constants
String SERVER_CONFIG_ID = "serverConfigId";
String ROOM_ID = "roomId";
}
\ No newline at end of file
package chat.rocket.android.push;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.RemoteInput;
import android.support.v4.util.SparseArrayCompat;
import android.text.Html;
import android.text.Spanned;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Random;
import chat.rocket.android.activity.MainActivity;
import chat.rocket.android.push.interactors.PushInteractor;
public class PushNotificationHandler implements PushConstants {
private static final String LOG_TAG = "PushNotificationHandler";
private static SparseArrayCompat<ArrayList<String>> messageMap = new SparseArrayCompat<>();
private Random random = new Random();
public void setNotification(int notId, String message) {
ArrayList<String> messageList = messageMap.get(notId);
if (messageList == null) {
messageList = new ArrayList<>();
messageMap.put(notId, messageList);
}
if (message.isEmpty()) {
messageList.clear();
} else {
messageList.add(message);
}
}
public void showNotificationIfPossible(Context context, PushInteractor pushInteractor,
Bundle extras) {
// Send a notification if there is a message or title, otherwise just send data
String message = extras.getString(MESSAGE);
String title = extras.getString(TITLE);
String contentAvailable = extras.getString(CONTENT_AVAILABLE);
String forceStart = extras.getString(FORCE_START);
Log.d(LOG_TAG, "message =[" + message + "]");
Log.d(LOG_TAG, "title =[" + title + "]");
Log.d(LOG_TAG, "contentAvailable =[" + contentAvailable + "]");
Log.d(LOG_TAG, "forceStart =[" + forceStart + "]");
if ((message != null && message.length() != 0) ||
(title != null && title.length() != 0)) {
Log.d(LOG_TAG, "create notification");
if (title == null || title.isEmpty()) {
extras.putString(TITLE, getAppName(context));
}
createNotification(context, pushInteractor, extras);
}
}
public void createNotification(Context context, PushInteractor pushInteractor, Bundle extras) {
NotificationManager mNotificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
String appName = getAppName(context);
String packageName = context.getPackageName();
Resources resources = context.getResources();
String serverUrl = getServerUrl(extras);
String roomId = getRoomId(extras);
if (serverUrl == null || roomId == null) {
return;
}
String serverConfigId = pushInteractor.getServerConfigId(serverUrl);
if (serverConfigId == null) {
return;
}
int notId = parseInt(NOT_ID, extras);
Intent notificationIntent = new Intent(context, MainActivity.class);
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
notificationIntent.putExtra(PUSH_BUNDLE, extras);
notificationIntent.putExtra(SERVER_CONFIG_ID, serverConfigId);
notificationIntent.putExtra(ROOM_ID, roomId);
notificationIntent.putExtra(NOT_ID, notId);
int requestCode = random.nextInt();
PendingIntent contentIntent = PendingIntent
.getActivity(context, requestCode, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context)
.setWhen(System.currentTimeMillis())
.setContentTitle(fromHtml(extras.getString(TITLE)))
.setTicker(fromHtml(extras.getString(TITLE)))
.setContentIntent(contentIntent)
.setAutoCancel(true);
SharedPreferences prefs = context
.getSharedPreferences(PushConstants.COM_ADOBE_PHONEGAP_PUSH, Context.MODE_PRIVATE);
String localIcon = prefs.getString(ICON, null);
String localIconColor = prefs.getString(ICON_COLOR, null);
boolean soundOption = prefs.getBoolean(SOUND, true);
boolean vibrateOption = prefs.getBoolean(VIBRATE, true);
Log.d(LOG_TAG, "stored icon=" + localIcon);
Log.d(LOG_TAG, "stored iconColor=" + localIconColor);
Log.d(LOG_TAG, "stored sound=" + soundOption);
Log.d(LOG_TAG, "stored vibrate=" + vibrateOption);
/*
* Notification Vibration
*/
setNotificationVibration(extras, vibrateOption, notificationBuilder);
/*
* Notification Icon Color
*
* Sets the small-icon background color of the notification.
* To use, add the `iconColor` key to plugin android options
*
*/
setNotificationIconColor(extras.getString("color"), notificationBuilder, localIconColor);
/*
* Notification Icon
*
* Sets the small-icon of the notification.
*
* - checks the plugin options for `icon` key
* - if none, uses the application icon
*
* The icon value must be a string that maps to a drawable resource.
* If no resource is found, falls
*
*/
setNotificationSmallIcon(context, extras, packageName, resources, notificationBuilder,
localIcon);
/*
* Notification Large-Icon
*
* Sets the large-icon of the notification
*
* - checks the gcm data for the `image` key
* - checks to see if remote image, loads it.
* - checks to see if assets image, Loads It.
* - checks to see if resource image, LOADS IT!
* - if none, we don't set the large icon
*
*/
setNotificationLargeIcon(context, extras, packageName, resources, notificationBuilder);
/*
* Notification Sound
*/
if (soundOption) {
setNotificationSound(context, extras, notificationBuilder);
}
/*
* LED Notification
*/
setNotificationLedColor(extras, notificationBuilder);
/*
* Priority Notification
*/
setNotificationPriority(extras, notificationBuilder);
/*
* Notification message
*/
setNotificationMessage(notId, extras, notificationBuilder);
/*
* Notification count
*/
setNotificationCount(context, extras, notificationBuilder);
/*
* Notification count
*/
setVisibility(context, extras, notificationBuilder);
/*
* Notification add actions
*/
createActions(context, extras, notificationBuilder, resources, packageName, notId);
mNotificationManager.notify(appName, notId, notificationBuilder.build());
}
private void createActions(Context context, Bundle extras, NotificationCompat.Builder mBuilder,
Resources resources, String packageName, int notId) {
Log.d(LOG_TAG, "create actions: with in-line");
String actions = extras.getString(ACTIONS);
if (actions == null) {
return;
}
try {
JSONArray actionsArray = new JSONArray(actions);
ArrayList<NotificationCompat.Action> wActions = new ArrayList<>();
for (int i = 0; i < actionsArray.length(); i++) {
int min = 1;
int max = 2000000000;
Random random = new Random();
int uniquePendingIntentRequestCode = random.nextInt((max - min) + 1) + min;
Log.d(LOG_TAG, "adding action");
JSONObject action = actionsArray.getJSONObject(i);
Log.d(LOG_TAG, "adding callback = " + action.getString(CALLBACK));
boolean foreground = action.optBoolean(FOREGROUND, true);
boolean inline = action.optBoolean("inline", false);
Intent intent;
PendingIntent pIntent;
if (inline) {
Log.d(LOG_TAG, "Version: " + android.os.Build.VERSION.SDK_INT + " = "
+ android.os.Build.VERSION_CODES.M);
if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.M) {
Log.d(LOG_TAG, "push activity");
intent = new Intent(context, MainActivity.class);
} else {
Log.d(LOG_TAG, "push receiver");
intent = new Intent(context, BackgroundActionButtonHandler.class);
}
updateIntent(intent, action.getString(CALLBACK), extras, foreground, notId);
if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.M) {
Log.d(LOG_TAG, "push activity for notId " + notId);
pIntent =
PendingIntent.getActivity(context, uniquePendingIntentRequestCode, intent,
PendingIntent.FLAG_ONE_SHOT);
} else {
Log.d(LOG_TAG, "push receiver for notId " + notId);
pIntent = PendingIntent
.getBroadcast(context, uniquePendingIntentRequestCode, intent,
PendingIntent.FLAG_ONE_SHOT);
}
} else if (foreground) {
intent = new Intent(context, MainActivity.class);
updateIntent(intent, action.getString(CALLBACK), extras, foreground, notId);
pIntent = PendingIntent
.getActivity(context, uniquePendingIntentRequestCode, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
} else {
intent = new Intent(context, BackgroundActionButtonHandler.class);
updateIntent(intent, action.getString(CALLBACK), extras, foreground, notId);
pIntent = PendingIntent
.getBroadcast(context, uniquePendingIntentRequestCode, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
NotificationCompat.Action.Builder actionBuilder = new NotificationCompat.Action.Builder(
resources.getIdentifier(action.optString(ICON, ""), DRAWABLE, packageName),
action.getString(TITLE), pIntent);
RemoteInput remoteInput;
if (inline) {
Log.d(LOG_TAG, "create remote input");
String replyLabel = "Enter your reply here";
remoteInput = new RemoteInput.Builder(INLINE_REPLY)
.setLabel(replyLabel)
.build();
actionBuilder.addRemoteInput(remoteInput);
}
NotificationCompat.Action wAction = actionBuilder.build();
wActions.add(actionBuilder.build());
if (inline) {
mBuilder.addAction(wAction);
} else {
mBuilder.addAction(
resources.getIdentifier(action.optString(ICON, ""), DRAWABLE, packageName),
action.getString(TITLE), pIntent);
}
wAction = null;
pIntent = null;
}
mBuilder.extend(new NotificationCompat.WearableExtender().addActions(wActions));
wActions.clear();
} catch (JSONException e) {
// nope
}
}
private void setNotificationCount(Context context, Bundle extras,
NotificationCompat.Builder mBuilder) {
int count = extractBadgeCount(extras);
if (count >= 0) {
Log.d(LOG_TAG, "count =[" + count + "]");
mBuilder.setNumber(count);
}
}
private void setVisibility(Context context, Bundle extras, NotificationCompat.Builder mBuilder) {
String visibilityStr = extras.getString(VISIBILITY);
if (visibilityStr == null) {
return;
}
try {
Integer visibility = Integer.parseInt(visibilityStr);
if (visibility >= NotificationCompat.VISIBILITY_SECRET
&& visibility <= NotificationCompat.VISIBILITY_PUBLIC) {
mBuilder.setVisibility(visibility);
} else {
Log.e(LOG_TAG, "Visibility parameter must be between -1 and 1");
}
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
private void setNotificationVibration(Bundle extras, Boolean vibrateOption,
NotificationCompat.Builder mBuilder) {
String vibrationPattern = extras.getString(VIBRATION_PATTERN);
if (vibrationPattern != null) {
String[] items = vibrationPattern.replaceAll("\\[", "").replaceAll("\\]", "").split(",");
long[] results = new long[items.length];
for (int i = 0; i < items.length; i++) {
try {
results[i] = Long.parseLong(items[i].trim());
} catch (NumberFormatException nfe) {
}
}
mBuilder.setVibrate(results);
} else {
if (vibrateOption) {
mBuilder.setDefaults(Notification.DEFAULT_VIBRATE);
}
}
}
private void setNotificationMessage(int notId, Bundle extras,
NotificationCompat.Builder mBuilder) {
String message = extras.getString(MESSAGE);
String style = extras.getString(STYLE, STYLE_TEXT);
if (STYLE_INBOX.equals(style)) {
setNotification(notId, message);
mBuilder.setContentText(fromHtml(message));
ArrayList<String> messageList = messageMap.get(notId);
Integer sizeList = messageList.size();
if (sizeList > 1) {
String sizeListMessage = sizeList.toString();
String stacking = sizeList + " more";
if (extras.getString(SUMMARY_TEXT) != null) {
stacking = extras.getString(SUMMARY_TEXT);
stacking = stacking.replace("%n%", sizeListMessage);
}
NotificationCompat.InboxStyle notificationInbox = new NotificationCompat.InboxStyle()
.setBigContentTitle(fromHtml(extras.getString(TITLE)))
.setSummaryText(fromHtml(stacking));
for (int i = messageList.size() - 1; i >= 0; i--) {
notificationInbox.addLine(fromHtml(messageList.get(i)));
}
mBuilder.setStyle(notificationInbox);
} else {
NotificationCompat.BigTextStyle bigText = new NotificationCompat.BigTextStyle();
if (message != null) {
bigText.bigText(fromHtml(message));
bigText.setBigContentTitle(fromHtml(extras.getString(TITLE)));
mBuilder.setStyle(bigText);
}
}
} else if (STYLE_PICTURE.equals(style)) {
setNotification(notId, "");
NotificationCompat.BigPictureStyle bigPicture = new NotificationCompat.BigPictureStyle();
bigPicture.bigPicture(getBitmapFromURL(extras.getString(PICTURE)));
bigPicture.setBigContentTitle(fromHtml(extras.getString(TITLE)));
bigPicture.setSummaryText(fromHtml(extras.getString(SUMMARY_TEXT)));
mBuilder.setContentTitle(fromHtml(extras.getString(TITLE)));
mBuilder.setContentText(fromHtml(message));
mBuilder.setStyle(bigPicture);
} else {
setNotification(notId, "");
NotificationCompat.BigTextStyle bigText = new NotificationCompat.BigTextStyle();
if (message != null) {
mBuilder.setContentText(fromHtml(message));
bigText.bigText(fromHtml(message));
bigText.setBigContentTitle(fromHtml(extras.getString(TITLE)));
String summaryText = extras.getString(SUMMARY_TEXT);
if (summaryText != null) {
bigText.setSummaryText(fromHtml(summaryText));
}
mBuilder.setStyle(bigText);
}
}
}
private void setNotificationSound(Context context, Bundle extras,
NotificationCompat.Builder mBuilder) {
String soundname = extras.getString(SOUNDNAME);
if (soundname == null) {
soundname = extras.getString(SOUND);
}
if (SOUND_RINGTONE.equals(soundname)) {
mBuilder.setSound(android.provider.Settings.System.DEFAULT_RINGTONE_URI);
} else if (soundname != null && !soundname.contentEquals(SOUND_DEFAULT)) {
Uri sound = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE
+ "://" + context.getPackageName() + "/raw/" + soundname);
Log.d(LOG_TAG, sound.toString());
mBuilder.setSound(sound);
} else {
mBuilder.setSound(android.provider.Settings.System.DEFAULT_NOTIFICATION_URI);
}
}
private void setNotificationLedColor(Bundle extras, NotificationCompat.Builder mBuilder) {
String ledColor = extras.getString(LED_COLOR);
if (ledColor == null) {
return;
}
// Converts parse Int Array from ledColor
String[] items = ledColor.replaceAll("\\[", "").replaceAll("\\]", "").split(",");
int[] results = new int[items.length];
for (int i = 0; i < items.length; i++) {
try {
results[i] = Integer.parseInt(items[i].trim());
} catch (NumberFormatException nfe) {
}
}
if (results.length == 4) {
mBuilder.setLights(Color.argb(results[0], results[1], results[2], results[3]), 500, 500);
} else {
Log.e(LOG_TAG, "ledColor parameter must be an array of length == 4 (ARGB)");
}
}
private void setNotificationPriority(Bundle extras, NotificationCompat.Builder mBuilder) {
String priorityStr = extras.getString(PRIORITY);
if (priorityStr == null) {
return;
}
try {
Integer priority = Integer.parseInt(priorityStr);
if (priority >= NotificationCompat.PRIORITY_MIN
&& priority <= NotificationCompat.PRIORITY_MAX) {
mBuilder.setPriority(priority);
} else {
Log.e(LOG_TAG, "Priority parameter must be between -2 and 2");
}
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
private void setNotificationLargeIcon(Context context, Bundle extras, String packageName,
Resources resources, NotificationCompat.Builder mBuilder) {
String gcmLargeIcon = extras.getString(IMAGE); // from gcm
if (gcmLargeIcon == null || "".equals(gcmLargeIcon)) {
return;
}
if (gcmLargeIcon.startsWith("http://") || gcmLargeIcon.startsWith("https://")) {
mBuilder.setLargeIcon(getBitmapFromURL(gcmLargeIcon));
Log.d(LOG_TAG, "using remote large-icon from gcm");
} else {
AssetManager assetManager = context.getAssets();
InputStream istr;
try {
istr = assetManager.open(gcmLargeIcon);
Bitmap bitmap = BitmapFactory.decodeStream(istr);
mBuilder.setLargeIcon(bitmap);
Log.d(LOG_TAG, "using assets large-icon from gcm");
} catch (IOException e) {
int largeIconId = resources.getIdentifier(gcmLargeIcon, DRAWABLE, packageName);
if (largeIconId != 0) {
Bitmap largeIconBitmap = BitmapFactory.decodeResource(resources, largeIconId);
mBuilder.setLargeIcon(largeIconBitmap);
Log.d(LOG_TAG, "using resources large-icon from gcm");
} else {
Log.d(LOG_TAG, "Not setting large icon");
}
}
}
}
private void setNotificationSmallIcon(Context context, Bundle extras, String packageName,
Resources resources, NotificationCompat.Builder mBuilder,
String localIcon) {
int iconId = 0;
String icon = extras.getString(ICON);
if (icon != null && !"".equals(icon)) {
iconId = resources.getIdentifier(icon, DRAWABLE, packageName);
Log.d(LOG_TAG, "using icon from plugin options");
} else if (localIcon != null && !"".equals(localIcon)) {
iconId = resources.getIdentifier(localIcon, DRAWABLE, packageName);
Log.d(LOG_TAG, "using icon from plugin options");
}
if (iconId == 0) {
Log.d(LOG_TAG, "no icon resource found - using default icon");
iconId = resources.getIdentifier("rocket_chat_notification", DRAWABLE, packageName);
}
mBuilder.setSmallIcon(iconId);
}
private void setNotificationIconColor(String color, NotificationCompat.Builder mBuilder,
String localIconColor) {
int iconColor = 0;
if (color != null && !"".equals(color)) {
try {
iconColor = Color.parseColor(color);
} catch (IllegalArgumentException e) {
Log.e(LOG_TAG, "couldn't parse color from android options");
}
} else if (localIconColor != null && !"".equals(localIconColor)) {
try {
iconColor = Color.parseColor(localIconColor);
} catch (IllegalArgumentException e) {
Log.e(LOG_TAG, "couldn't parse color from android options");
}
}
if (iconColor != 0) {
mBuilder.setColor(iconColor);
}
}
private void updateIntent(Intent intent, String callback, Bundle extras, boolean foreground,
int notId) {
intent.putExtra(CALLBACK, callback);
intent.putExtra(PUSH_BUNDLE, extras);
intent.putExtra(FOREGROUND, foreground);
intent.putExtra(NOT_ID, notId);
}
public Bitmap getBitmapFromURL(String strURL) {
try {
URL url = new URL(strURL);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
return BitmapFactory.decodeStream(input);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
public static String getAppName(Context context) {
CharSequence appName = context.getPackageManager()
.getApplicationLabel(context.getApplicationInfo());
return (String) appName;
}
private int parseInt(String value, Bundle extras) {
int retval = 0;
try {
retval = Integer.parseInt(extras.getString(value));
} catch (NumberFormatException e) {
Log.e(LOG_TAG, "Number format exception - Error parsing " + value + ": " + e.getMessage());
} catch (Exception e) {
Log.e(LOG_TAG, "Number format exception - Error parsing " + value + ": " + e.getMessage());
}
return retval;
}
private Spanned fromHtml(String source) {
if (source != null) {
return Html.fromHtml(source);
} else {
return null;
}
}
private int extractBadgeCount(Bundle extras) {
int count = -1;
String msgcnt = extras.getString(COUNT);
try {
if (msgcnt != null) {
count = Integer.parseInt(msgcnt);
}
} catch (NumberFormatException e) {
Log.e(LOG_TAG, e.getLocalizedMessage(), e);
}
return count;
}
private String getServerUrl(Bundle extras) {
try {
JSONObject jsonObject = new JSONObject(extras.getString("ejson", "[]"));
if (!jsonObject.has("host")) {
return null;
}
return jsonObject.getString("host");
} catch (Exception e) {
return null;
}
}
private String getRoomId(Bundle extras) {
try {
JSONObject jsonObject = new JSONObject(extras.getString("ejson", "[]"));
if (!jsonObject.has("rid")) {
return null;
}
return jsonObject.getString("rid");
} catch (Exception e) {
return null;
}
}
}
package chat.rocket.android.push.gcm;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
import android.util.Log;
import com.google.android.gms.gcm.GcmListenerService;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Iterator;
import chat.rocket.android.push.PushConstants;
import chat.rocket.android.push.PushNotificationHandler;
import chat.rocket.android.push.interactors.DefaultPushInteractor;
import chat.rocket.android.push.interactors.PushInteractor;
@SuppressLint("NewApi")
public class GCMIntentService extends GcmListenerService implements PushConstants {
private static final String LOG_TAG = "GCMIntentService";
@Override
public void onMessageReceived(String from, Bundle extras) {
Log.d(LOG_TAG, "onMessage - from: " + from);
if (extras == null) {
return;
}
Context applicationContext = getApplicationContext();
PushInteractor pushInteractor = new DefaultPushInteractor();
extras = normalizeExtras(applicationContext, extras);
PushNotificationHandler pushNotificationHandler = new PushNotificationHandler();
pushNotificationHandler.showNotificationIfPossible(applicationContext, pushInteractor, extras);
}
/*
* Change a values key in the extras bundle
*/
private void replaceKey(Context context, String oldKey, String newKey, Bundle extras,
Bundle newExtras) {
Object value = extras.get(oldKey);
if (value == null) {
return;
}
if (value instanceof String) {
value = localizeKey(context, newKey, (String) value);
newExtras.putString(newKey, (String) value);
} else if (value instanceof Boolean) {
newExtras.putBoolean(newKey, (Boolean) value);
} else if (value instanceof Number) {
newExtras.putDouble(newKey, ((Number) value).doubleValue());
} else {
newExtras.putString(newKey, String.valueOf(value));
}
}
/*
* Normalize localization for key
*/
private String localizeKey(Context context, String key, String value) {
if (key.equals(TITLE) || key.equals(MESSAGE) || key.equals(SUMMARY_TEXT)) {
try {
JSONObject localeObject = new JSONObject(value);
String localeKey = localeObject.getString(LOC_KEY);
ArrayList<String> localeFormatData = new ArrayList<>();
if (!localeObject.isNull(LOC_DATA)) {
String localeData = localeObject.getString(LOC_DATA);
JSONArray localeDataArray = new JSONArray(localeData);
for (int i = 0, size = localeDataArray.length(); i < size; i++) {
localeFormatData.add(localeDataArray.getString(i));
}
}
String packageName = context.getPackageName();
Resources resources = context.getResources();
int resourceId = resources.getIdentifier(localeKey, "string", packageName);
if (resourceId != 0) {
return resources.getString(resourceId, localeFormatData.toArray());
} else {
Log.d(LOG_TAG, "can't find resource for locale key = " + localeKey);
return value;
}
} catch (JSONException e) {
Log.d(LOG_TAG, "no locale found for key = " + key + ", error " + e.getMessage());
return value;
}
}
return value;
}
/*
* Replace alternate keys with our canonical value
*/
private String normalizeKey(String key) {
if (key.equals(BODY) || key.equals(ALERT) || key.equals(GCM_NOTIFICATION_BODY) || key
.equals(TWILIO_BODY)) {
return MESSAGE;
} else if (key.equals(TWILIO_TITLE)) {
return TITLE;
} else if (key.equals(MSGCNT) || key.equals(BADGE)) {
return COUNT;
} else if (key.equals(SOUNDNAME) || key.equals(TWILIO_SOUND)) {
return SOUND;
} else if (key.startsWith(GCM_NOTIFICATION)) {
return key.substring(GCM_NOTIFICATION.length() + 1, key.length());
} else if (key.startsWith(GCM_N)) {
return key.substring(GCM_N.length() + 1, key.length());
} else if (key.startsWith(UA_PREFIX)) {
key = key.substring(UA_PREFIX.length() + 1, key.length());
return key.toLowerCase();
} else {
return key;
}
}
/*
* Parse bundle into normalized keys.
*/
private Bundle normalizeExtras(Context context, Bundle extras) {
Log.d(LOG_TAG, "normalize extras");
Iterator<String> keyIterator = extras.keySet().iterator();
Bundle newExtras = new Bundle();
while (keyIterator.hasNext()) {
String key = keyIterator.next();
Log.d(LOG_TAG, "key = " + key);
// If normalizeKeythe key is "data" or "message" and the value is a json object extract
// This is to support parse.com and other services. Issue #147 and pull #218
if (key.equals(PARSE_COM_DATA) || key.equals(MESSAGE)) {
Object json = extras.get(key);
// Make sure data is json object stringified
if (json instanceof String && ((String) json).startsWith("{")) {
Log.d(LOG_TAG, "extracting nested message data from key = " + key);
try {
// If object contains message keys promote each value to the root of the bundle
JSONObject data = new JSONObject((String) json);
if (data.has(ALERT) || data.has(MESSAGE) || data.has(BODY) || data.has(TITLE)) {
Iterator<String> jsonIter = data.keys();
while (jsonIter.hasNext()) {
String jsonKey = jsonIter.next();
Log.d(LOG_TAG, "key = data/" + jsonKey);
String value = data.getString(jsonKey);
jsonKey = normalizeKey(jsonKey);
value = localizeKey(context, jsonKey, value);
newExtras.putString(jsonKey, value);
}
}
} catch (JSONException e) {
Log.e(LOG_TAG, "normalizeExtras: JSON exception");
}
}
} else if (key.equals(("notification"))) {
Bundle value = extras.getBundle(key);
Iterator<String> iterator = value.keySet().iterator();
while (iterator.hasNext()) {
String notifkey = iterator.next();
Log.d(LOG_TAG, "notifkey = " + notifkey);
String newKey = normalizeKey(notifkey);
Log.d(LOG_TAG, "replace key " + notifkey + " with " + newKey);
String valueData = value.getString(notifkey);
valueData = localizeKey(context, newKey, valueData);
newExtras.putString(newKey, valueData);
}
continue;
}
String newKey = normalizeKey(key);
Log.d(LOG_TAG, "replace key " + key + " with " + newKey);
replaceKey(context, key, newKey, extras, newExtras);
} // while
return newExtras;
}
public static String getAppName(Context context) {
CharSequence appName = context.getPackageManager()
.getApplicationLabel(context.getApplicationInfo());
return (String) appName;
}
}
\ No newline at end of file
package chat.rocket.android.push.gcm;
import android.content.Intent;
import com.google.android.gms.iid.InstanceIDListenerService;
import java.util.List;
import chat.rocket.android.model.ServerConfig;
import chat.rocket.android.model.ddp.PublicSetting;
import chat.rocket.android.model.ddp.PublicSettingsConstants;
import chat.rocket.android.realm_helper.RealmHelper;
import chat.rocket.android.realm_helper.RealmStore;
public class GcmInstanceIDListenerService extends InstanceIDListenerService {
@Override
public void onTokenRefresh() {
List<ServerConfig> serverConfigs = getServerConfigs();
updateSyncPushToken(serverConfigs);
if (!shouldRefreshToken(serverConfigs)) {
return;
}
Intent intent = new Intent(this, GcmRegistrationIntentService.class);
startService(intent);
}
private List<ServerConfig> getServerConfigs() {
return RealmStore.getDefault().executeTransactionForReadResults(
realm -> realm.where(ServerConfig.class).findAll());
}
private void updateSyncPushToken(List<ServerConfig> serverConfigs) {
final RealmHelper realmHelper = RealmStore.getDefault();
for (final ServerConfig serverConfig : serverConfigs) {
final RealmHelper serverRealmHelper = RealmStore
.getOrCreate(serverConfig.getServerConfigId());
boolean isPushEnable = PublicSetting
.getBoolean(serverRealmHelper, PublicSettingsConstants.Push.ENABLE, false);
String senderId = PublicSetting
.getString(serverRealmHelper, PublicSettingsConstants.Push.GCM_PROJECT_NUMBER, "").trim();
serverConfig.setSyncPushToken(isPushEnable && !"".equals(senderId));
realmHelper.executeTransaction(realm -> realm.copyToRealmOrUpdate(serverConfig));
}
}
private boolean shouldRefreshToken(List<ServerConfig> serverConfigs) {
for (ServerConfig serverConfig : serverConfigs) {
if (serverConfig.shouldSyncPushToken()) {
return true;
}
}
return false;
}
}
\ No newline at end of file
package chat.rocket.android.push.gcm;
import android.app.IntentService;
import android.content.Intent;
import com.google.android.gms.gcm.GoogleCloudMessaging;
import com.google.android.gms.iid.InstanceID;
import java.io.IOException;
import java.util.List;
import chat.rocket.android.RocketChatCache;
import chat.rocket.android.api.PushHelper;
import chat.rocket.android.model.ServerConfig;
import chat.rocket.android.model.ddp.PublicSetting;
import chat.rocket.android.model.ddp.PublicSettingsConstants;
import chat.rocket.android.model.ddp.User;
import chat.rocket.android.realm_helper.RealmHelper;
import chat.rocket.android.realm_helper.RealmStore;
public class GcmRegistrationIntentService extends IntentService {
public GcmRegistrationIntentService() {
super("GcmRegistrationIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
final List<ServerConfig> serverConfigs = getServerConfigs();
for (ServerConfig serverConfig : serverConfigs) {
sendTokenTo(serverConfig);
}
}
private List<ServerConfig> getServerConfigs() {
return RealmStore.getDefault().executeTransactionForReadResults(
realm -> realm.where(ServerConfig.class).findAll());
}
private void sendTokenTo(final ServerConfig serverConfig) {
if (!serverConfig.shouldSyncPushToken()) {
return;
}
final RealmHelper realmHelper = RealmStore.get(serverConfig.getServerConfigId());
if (realmHelper == null) {
return;
}
final String senderId = PublicSetting
.getString(realmHelper, PublicSettingsConstants.Push.GCM_PROJECT_NUMBER, "").trim();
if ("".equals(senderId)) {
markRefreshAsDone(serverConfig);
return;
}
try {
final String token = getToken(senderId);
final User currentUser = realmHelper.executeTransactionForRead(realm ->
User.queryCurrentUser(realm).findFirst());
new PushHelper(serverConfig.getServerConfigId()).pushUpdate(
RocketChatCache.getPushId(this), token, currentUser != null ? currentUser.getId() : null)
.onSuccess(task -> {
markRefreshAsDone(serverConfig);
return task;
});
} catch (Exception e) {
}
}
private String getToken(String senderId) throws IOException {
return InstanceID.getInstance(this)
.getToken(senderId, GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
}
private void markRefreshAsDone(ServerConfig serverConfig) {
serverConfig.setSyncPushToken(false);
RealmStore.getDefault().executeTransaction(realm -> realm.copyToRealm(serverConfig));
}
}
\ No newline at end of file
package chat.rocket.android.push.interactors;
import chat.rocket.android.helper.ServerPolicyHelper;
import chat.rocket.android.model.ServerConfig;
import chat.rocket.android.realm_helper.RealmStore;
public class DefaultPushInteractor implements PushInteractor {
@Override
public String getServerConfigId(String hostname) {
final ServerConfig serverConfig = RealmStore.getDefault()
.executeTransactionForRead(
realm -> realm.where(ServerConfig.class)
.equalTo(ServerConfig.HOSTNAME, ServerPolicyHelper.enforceHostname(hostname))
.findFirst());
return serverConfig != null ? serverConfig.getServerConfigId() : "";
}
}
package chat.rocket.android.push.interactors;
public interface PushInteractor {
String getServerConfigId(String hostname);
}
......@@ -12,6 +12,7 @@ import bolts.Continuation;
import bolts.Task;
import bolts.TaskCompletionSource;
import chat.rocket.android.api.DDPClientWrapper;
import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.helper.LogcatIfError;
import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.log.RCLog;
......@@ -29,7 +30,7 @@ import chat.rocket.android.service.observer.GetUsersOfRoomsProcedureObserver;
import chat.rocket.android.service.observer.LoadMessageProcedureObserver;
import chat.rocket.android.service.observer.MethodCallObserver;
import chat.rocket.android.service.observer.NewMessageObserver;
import chat.rocket.android.service.observer.NotificationItemObserver;
import chat.rocket.android.service.observer.PushSettingsObserver;
import chat.rocket.android.service.observer.ReactiveNotificationManager;
import chat.rocket.android.service.observer.SessionObserver;
import chat.rocket.android.service.observer.TokenLoginObserver;
......@@ -52,9 +53,9 @@ public class RocketChatWebSocketThread extends HandlerThread {
NewMessageObserver.class,
CurrentUserObserver.class,
ReactiveNotificationManager.class,
NotificationItemObserver.class,
FileUploadingToS3Observer.class,
FileUploadingWithUfsObserver.class
FileUploadingWithUfsObserver.class,
PushSettingsObserver.class
};
private final Context appContext;
private final String serverConfigId;
......@@ -167,7 +168,8 @@ public class RocketChatWebSocketThread extends HandlerThread {
realm.where(ServerConfig.class).equalTo(ServerConfig.ID, serverConfigId).findFirst());
prepareWebSocket(config.getHostname());
return ddpClient.connect(config.getSession()).onSuccessTask(task -> {
return ddpClient.connect(config.getSession())
.onSuccessTask(task -> {
final String session = task.getResult().session;
defaultRealm.executeTransaction(realm ->
realm.createOrUpdateObjectFromJson(ServerConfig.class, new JSONObject()
......@@ -183,11 +185,13 @@ public class RocketChatWebSocketThread extends HandlerThread {
return null;
})).continueWith(new LogcatIfError());
return task;
}).onSuccess(new Continuation<DDPClientCallback.Connect, Void>() {
})
.onSuccess(new Continuation<DDPClientCallback.Connect, Void>() {
// TODO type detection doesn't work due to retrolambda's bug...
@Override
public Void then(Task<DDPClientCallback.Connect> task)
throws Exception {
fetchPublicSettings();
registerListeners();
// handling WebSocket#onClose() callback.
......@@ -203,7 +207,8 @@ public class RocketChatWebSocketThread extends HandlerThread {
return null;
}
}).continueWithTask(task -> {
})
.continueWithTask(task -> {
if (task.isFaulted()) {
Exception error = task.getError();
if (error instanceof DDPClientCallback.Connect.Timeout) {
......@@ -216,13 +221,15 @@ public class RocketChatWebSocketThread extends HandlerThread {
});
}
private Task<Void> fetchPublicSettings() {
return new MethodCallHelper(serverConfigRealm, ddpClient).getPublicSettings();
}
//@DebugLog
private void registerListeners() {
if (!Thread.currentThread().getName().equals("RC_thread_" + serverConfigId)) {
// execute in Looper.
new Handler(getLooper()).post(() -> {
registerListeners();
});
new Handler(getLooper()).post(this::registerListeners);
return;
}
......
......@@ -6,8 +6,10 @@ import io.realm.RealmResults;
import java.util.ArrayList;
import java.util.List;
import chat.rocket.android.RocketChatCache;
import chat.rocket.android.api.DDPClientWrapper;
import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.api.PushHelper;
import chat.rocket.android.helper.LogcatIfError;
import chat.rocket.android.model.ddp.User;
import chat.rocket.android.realm_helper.RealmHelper;
......@@ -20,6 +22,7 @@ import hugo.weaving.DebugLog;
*/
public class CurrentUserObserver extends AbstractModelObserver<User> {
private final MethodCallHelper methodCall;
private final PushHelper pushHelper;
private boolean currentUserExists;
private ArrayList<Registrable> listeners;
......@@ -27,6 +30,7 @@ public class CurrentUserObserver extends AbstractModelObserver<User> {
RealmHelper realmHelper, DDPClientWrapper ddpClient) {
super(context, hostname, realmHelper, ddpClient);
methodCall = new MethodCallHelper(realmHelper, ddpClient);
pushHelper = new PushHelper(realmHelper, ddpClient);
currentUserExists = false;
}
......@@ -58,6 +62,9 @@ public class CurrentUserObserver extends AbstractModelObserver<User> {
final String userId = user.getId();
// update push info
pushHelper.pushSetUser(RocketChatCache.getPushId(context)).continueWith(new LogcatIfError());
// get and observe Room subscriptions.
methodCall.getRoomSubscriptions().onSuccess(task -> {
if (listeners != null) {
......
package chat.rocket.android.service.observer;
import android.content.Context;
import android.content.Intent;
import io.realm.Realm;
import io.realm.RealmResults;
import java.util.List;
import chat.rocket.android.api.DDPClientWrapper;
import chat.rocket.android.helper.LogcatIfError;
import chat.rocket.android.model.ServerConfig;
import chat.rocket.android.model.ddp.PublicSetting;
import chat.rocket.android.model.ddp.PublicSettingsConstants;
import chat.rocket.android.push.gcm.GcmRegistrationIntentService;
import chat.rocket.android.push.interactors.DefaultPushInteractor;
import chat.rocket.android.push.interactors.PushInteractor;
import chat.rocket.android.realm_helper.RealmHelper;
import chat.rocket.android.realm_helper.RealmStore;
public class PushSettingsObserver extends AbstractModelObserver<PublicSetting> {
public PushSettingsObserver(Context context, String hostname,
RealmHelper realmHelper, DDPClientWrapper ddpClient) {
super(context, hostname, realmHelper, ddpClient);
}
@Override
public void onUpdateResults(List<PublicSetting> results) {
RealmHelper realmHelper = RealmStore.getDefault();
PushInteractor interactor = new DefaultPushInteractor();
String serverConfigId = interactor.getServerConfigId(hostname);
final ServerConfig serverConfig = realmHelper.executeTransactionForRead(
realm -> realm.where(ServerConfig.class).equalTo(ServerConfig.ID, serverConfigId)
.findFirst());
serverConfig.setSyncPushToken(isPushEnabled(results));
realmHelper
.executeTransaction(realm -> realm.copyToRealmOrUpdate(serverConfig))
.continueWith(task -> {
if (serverConfig.shouldSyncPushToken()) {
Intent intent = new Intent(
context.getApplicationContext(), GcmRegistrationIntentService.class);
context.getApplicationContext().startService(intent);
}
return task;
})
.continueWith(new LogcatIfError());
}
@Override
public RealmResults<PublicSetting> queryItems(Realm realm) {
return realm.where(PublicSetting.class)
.equalTo(PublicSetting.ID, PublicSettingsConstants.Push.ENABLE)
.or()
.equalTo(PublicSetting.ID, PublicSettingsConstants.Push.GCM_PROJECT_NUMBER)
.findAll();
}
private boolean isPushEnabled(List<PublicSetting> results) {
return isPushEnable(results) && isGcmValid(results);
}
private boolean isPushEnable(List<PublicSetting> results) {
for (PublicSetting setting : results) {
if (PublicSettingsConstants.Push.ENABLE.equals(setting.getId())) {
return "true".equals(setting.getValue());
}
}
return false;
}
private boolean isGcmValid(List<PublicSetting> results) {
for (PublicSetting setting : results) {
if (PublicSettingsConstants.Push.GCM_PROJECT_NUMBER.equals(setting.getId())) {
return setting.getValue() != null && !"".equals(setting.getValue());
}
}
return false;
}
}
......@@ -6,9 +6,7 @@ import io.realm.RealmResults;
import java.util.List;
import chat.rocket.android.api.DDPClientWrapper;
import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.helper.LogcatIfError;
import chat.rocket.android.model.ddp.PublicSetting;
import chat.rocket.android.model.internal.GetUsersOfRoomsProcedure;
import chat.rocket.android.model.internal.LoadMessageProcedure;
import chat.rocket.android.model.internal.MethodCall;
......@@ -67,8 +65,6 @@ public class SessionObserver extends AbstractModelObserver<Session> {
@DebugLog
private void onLogin() {
streamNotifyMessage.register();
new MethodCallHelper(realmHelper, ddpClient).getPublicSettings()
.continueWith(new LogcatIfError());
}
@DebugLog
......@@ -77,7 +73,6 @@ public class SessionObserver extends AbstractModelObserver<Session> {
realmHelper.executeTransaction(realm -> {
// remove all tables. ONLY INTERNAL TABLES!.
realm.delete(PublicSetting.class);
realm.delete(MethodCall.class);
realm.delete(LoadMessageProcedure.class);
realm.delete(GetUsersOfRoomsProcedure.class);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment