mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
Bug 1566406 Upgrade Leanplum SDK from 3.0.2 to 4.2.7 r=petru
Differential Revision: https://phabricator.services.mozilla.com/D38675 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
5638261f4a
commit
95c2abace2
@ -1,30 +1,34 @@
|
||||
<receiver android:name="com.leanplum.LeanplumPushReceiver" android:exported="false"
|
||||
<receiver android:name="com.leanplum.LeanplumPushReceiver"
|
||||
android:exported="false"
|
||||
android:enabled="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.leanplum.LeanplumPushListenerService" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<!-- Leanplum Local Push Notification Service-->
|
||||
<service android:name="com.leanplum.LeanplumLocalPushListenerService" android:exported="false"
|
||||
android:enabled="true" />
|
||||
|
||||
<service android:name="com.leanplum.LeanplumPushListenerService" android:exported="false">
|
||||
<!-- Leanplum GCM Message Handling Service. -->
|
||||
<service
|
||||
android:name="com.leanplum.LeanplumPushListenerService"
|
||||
android:enabled="true"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
|
||||
<category android:name="@ANDROID_PACKAGE_NAME@" />
|
||||
<action android:name="com.google.android.c2dm.intent.RECEIVE"/>
|
||||
<category android:name="@ANDROID_PACKAGE_NAME@" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<!-- Leanplum GCM Registration Job Service. -->
|
||||
<service android:name="com.leanplum.LeanplumGcmRegistrationJobService"
|
||||
<!-- Leanplum GCM Registration Job Service. -->
|
||||
<service
|
||||
android:name="com.leanplum.LeanplumGcmRegistrationJobService"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"/>
|
||||
|
||||
<!-- Leanplum GCM Instance ID Service -->
|
||||
<service android:name="com.leanplum.LeanplumPushInstanceIDService" android:exported="false"
|
||||
android:enabled="true">
|
||||
<!-- Leanplum GCM Instance ID Service. -->
|
||||
<service
|
||||
android:name="com.leanplum.LeanplumPushInstanceIDService"
|
||||
android:enabled="true"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.android.gms.iid.InstanceID" />
|
||||
<action android:name="com.google.android.gms.iid.InstanceID"/>
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
|
@ -13,6 +13,7 @@ import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
|
||||
import com.leanplum.Leanplum;
|
||||
@ -80,12 +81,7 @@ public class MmaLeanplumImp implements MmaInterface {
|
||||
// SDK initialization and Activity lifecycle in the future.
|
||||
//
|
||||
// I put it under runOnUiThread because in current Leanplum's SDK design, this should be run in main thread.
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
LeanplumActivityHelper.onResume(activity);
|
||||
}
|
||||
});
|
||||
activity.runOnUiThread(() -> LeanplumActivityHelper.onResume(activity));
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@ -101,6 +97,11 @@ public class MmaLeanplumImp implements MmaInterface {
|
||||
.getNotificationChannel(NotificationHelper.Channel.DEFAULT).getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(Notification.Builder builder, Bundle notificationPayload, @Nullable Notification.Style notificationStyle) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -147,7 +148,7 @@ public class MmaLeanplumImp implements MmaInterface {
|
||||
|
||||
@Override
|
||||
public boolean handleGcmMessage(Context context, String from, Bundle bundle) {
|
||||
if (from != null && from.equals(MmaConstants.MOZ_MMA_SENDER_ID) && bundle.containsKey(Constants.Keys.PUSH_MESSAGE_TEXT)) {
|
||||
if (from != null && bundle.containsKey(Constants.Keys.PUSH_MESSAGE_TEXT)) {
|
||||
LeanplumPushService.handleNotification(context, bundle);
|
||||
return true;
|
||||
}
|
||||
|
1
mobile/android/thirdparty/build.gradle
vendored
1
mobile/android/thirdparty/build.gradle
vendored
@ -4,6 +4,7 @@ apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion project.ext.compileSdkVersion
|
||||
useLibrary 'org.apache.http.legacy'
|
||||
|
||||
defaultConfig {
|
||||
targetSdkVersion project.ext.targetSdkVersion
|
||||
|
@ -369,6 +369,7 @@ public class ActionContext extends BaseActionContext implements Comparable<Actio
|
||||
createActionContextForMessageId(messageAction.toString(), args, messageId, name, false);
|
||||
}
|
||||
}
|
||||
Leanplum.countAggregator().incrementCount("run_action_named");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -388,7 +389,7 @@ public class ActionContext extends BaseActionContext implements Comparable<Actio
|
||||
@Override
|
||||
public void variablesChanged() {
|
||||
try {
|
||||
ActionManager.getInstance().recordMessageImpression(actionContext.getMessageId());
|
||||
Leanplum.triggerMessageDisplayed(actionContext);
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
}
|
||||
@ -526,6 +527,7 @@ public class ActionContext extends BaseActionContext implements Comparable<Actio
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
}
|
||||
Leanplum.countAggregator().incrementCount("run_tracked_action_named");
|
||||
}
|
||||
|
||||
/**
|
||||
|
381
mobile/android/thirdparty/com/leanplum/Leanplum.java
vendored
381
mobile/android/thirdparty/com/leanplum/Leanplum.java
vendored
@ -22,24 +22,26 @@
|
||||
package com.leanplum;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.location.Location;
|
||||
import android.os.AsyncTask;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.os.Message;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.leanplum.ActionContext.ContextualValues;
|
||||
import com.leanplum.callbacks.ActionCallback;
|
||||
import com.leanplum.callbacks.MessageDisplayedCallback;
|
||||
import com.leanplum.callbacks.RegisterDeviceCallback;
|
||||
import com.leanplum.callbacks.RegisterDeviceFinishedCallback;
|
||||
import com.leanplum.callbacks.StartCallback;
|
||||
import com.leanplum.callbacks.VariablesChangedCallback;
|
||||
import com.leanplum.internal.ActionManager;
|
||||
import com.leanplum.internal.Constants;
|
||||
import com.leanplum.internal.FileManager;
|
||||
import com.leanplum.internal.JsonConverter;
|
||||
import com.leanplum.internal.CountAggregator;
|
||||
import com.leanplum.internal.FeatureFlagManager;
|
||||
import com.leanplum.internal.LeanplumEventDataManager;
|
||||
import com.leanplum.internal.LeanplumInternal;
|
||||
import com.leanplum.internal.LeanplumMessageMatchFilter;
|
||||
@ -47,11 +49,12 @@ import com.leanplum.internal.LeanplumUIEditorWrapper;
|
||||
import com.leanplum.internal.Log;
|
||||
import com.leanplum.internal.OsHandler;
|
||||
import com.leanplum.internal.Registration;
|
||||
import com.leanplum.internal.Request;
|
||||
import com.leanplum.internal.RequestOld;
|
||||
import com.leanplum.internal.Util;
|
||||
import com.leanplum.internal.Util.DeviceIdInfo;
|
||||
import com.leanplum.internal.VarCache;
|
||||
import com.leanplum.messagetemplates.MessageTemplates;
|
||||
import com.leanplum.models.MessageArchiveData;
|
||||
import com.leanplum.utils.BuildUtil;
|
||||
import com.leanplum.utils.SharedPreferencesUtil;
|
||||
|
||||
@ -61,8 +64,10 @@ import org.json.JSONObject;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
@ -81,6 +86,7 @@ public class Leanplum {
|
||||
* Default event name to use for Purchase events.
|
||||
*/
|
||||
public static final String PURCHASE_EVENT_NAME = "Purchase";
|
||||
private static final String LEANPLUM_PUSH_SERVICE = "com.leanplum.LeanplumPushService";
|
||||
|
||||
private static final ArrayList<StartCallback> startHandlers = new ArrayList<>();
|
||||
private static final ArrayList<VariablesChangedCallback> variablesChangedHandlers =
|
||||
@ -89,7 +95,11 @@ public class Leanplum {
|
||||
new ArrayList<>();
|
||||
private static final ArrayList<VariablesChangedCallback> onceNoDownloadsHandlers =
|
||||
new ArrayList<>();
|
||||
private static final ArrayList<MessageDisplayedCallback> messageDisplayedHandlers =
|
||||
new ArrayList<>();
|
||||
private static final Object heartbeatLock = new Object();
|
||||
private static final String LEANPLUM_NOTIFICATION_CHANNEL =
|
||||
"com.leanplum.LeanplumNotificationChannel";
|
||||
private static RegisterDeviceCallback registerDeviceHandler;
|
||||
private static RegisterDeviceFinishedCallback registerDeviceFinishedHandler;
|
||||
private static LeanplumDeviceIdMode deviceIdMode = LeanplumDeviceIdMode.MD5_MAC_ADDRESS;
|
||||
@ -98,11 +108,14 @@ public class Leanplum {
|
||||
private static boolean userSpecifiedDeviceId;
|
||||
private static boolean initializedMessageTemplates = false;
|
||||
private static boolean locationCollectionEnabled = true;
|
||||
private static ScheduledExecutorService heartbeatExecutor;
|
||||
private static ScheduledExecutorService heartbeatExecutor = null;
|
||||
private static Context context;
|
||||
|
||||
private static Runnable pushStartCallback;
|
||||
|
||||
private static CountAggregator countAggregator = new CountAggregator();
|
||||
private static FeatureFlagManager featureFlagManager = FeatureFlagManager.INSTANCE;
|
||||
|
||||
private Leanplum() {
|
||||
}
|
||||
|
||||
@ -221,7 +234,7 @@ public class Leanplum {
|
||||
}
|
||||
|
||||
Constants.isDevelopmentModeEnabled = true;
|
||||
Request.setAppId(appId, accessKey);
|
||||
RequestOld.setAppId(appId, accessKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -242,7 +255,7 @@ public class Leanplum {
|
||||
}
|
||||
|
||||
Constants.isDevelopmentModeEnabled = false;
|
||||
Request.setAppId(appId, accessKey);
|
||||
RequestOld.setAppId(appId, accessKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -252,6 +265,16 @@ public class Leanplum {
|
||||
LeanplumInternal.enableAutomaticScreenTracking();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this to true if you want details about the variable assignments
|
||||
* on the server.
|
||||
* Default is NO.
|
||||
*/
|
||||
public static void setVariantDebugInfoEnabled(boolean variantDebugInfoEnabled) {
|
||||
LeanplumInternal.setIsVariantDebugInfoEnabled(variantDebugInfoEnabled);
|
||||
countAggregator.incrementCount("set_variant_debug_info_enabled");
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether screen tracking is enabled or not.
|
||||
*
|
||||
@ -307,7 +330,7 @@ public class Leanplum {
|
||||
Log.e("Leanplum.start() must be called before calling getDeviceId.");
|
||||
return null;
|
||||
}
|
||||
return Request.deviceId();
|
||||
return RequestOld.deviceId();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -368,6 +391,7 @@ public class Leanplum {
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
}
|
||||
countAggregator.incrementCount("sync_resources");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -383,6 +407,7 @@ public class Leanplum {
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
}
|
||||
countAggregator.incrementCount("sync_resources");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -404,6 +429,7 @@ public class Leanplum {
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
}
|
||||
countAggregator.incrementCount("sync_resource_paths");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -425,6 +451,7 @@ public class Leanplum {
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
}
|
||||
countAggregator.incrementCount("sync_resource_paths");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -521,7 +548,8 @@ public class Leanplum {
|
||||
VarCache.getUpdateRuleDiffs(),
|
||||
VarCache.getEventRuleDiffs(),
|
||||
new HashMap<String, Object>(),
|
||||
new ArrayList<Map<String, Object>>());
|
||||
new ArrayList<Map<String, Object>>(),
|
||||
new HashMap<String, Object>());
|
||||
LeanplumInbox.getInstance().update(new HashMap<String, LeanplumInboxMessage>(), 0, false);
|
||||
return;
|
||||
}
|
||||
@ -558,7 +586,7 @@ public class Leanplum {
|
||||
LeanplumInternal.getUserAttributeChanges().add(validAttributes);
|
||||
}
|
||||
|
||||
Request.loadToken();
|
||||
RequestOld.loadToken();
|
||||
VarCache.setSilent(true);
|
||||
VarCache.loadDiffs();
|
||||
VarCache.setSilent(false);
|
||||
@ -569,12 +597,12 @@ public class Leanplum {
|
||||
@Override
|
||||
public void updateCache() {
|
||||
triggerVariablesChanged();
|
||||
if (Request.numPendingDownloads() == 0) {
|
||||
if (RequestOld.numPendingDownloads() == 0) {
|
||||
triggerVariablesChangedAndNoDownloadsPending();
|
||||
}
|
||||
}
|
||||
});
|
||||
Request.onNoPendingDownloads(new Request.NoPendingDownloadsCallback() {
|
||||
RequestOld.onNoPendingDownloads(new RequestOld.NoPendingDownloadsCallback() {
|
||||
@Override
|
||||
public void noPendingDownloads() {
|
||||
triggerVariablesChangedAndNoDownloadsPending();
|
||||
@ -593,18 +621,35 @@ public class Leanplum {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
Util.initExceptionHandling(context);
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
}
|
||||
countAggregator.incrementCount("start_with_user_id");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for leanplum notifications modules and if someone present - invoke onStart method.
|
||||
*/
|
||||
private static void checkAndStartNotificationsModules() {
|
||||
if (Util.hasPlayServices()) {
|
||||
try {
|
||||
Class.forName(LEANPLUM_PUSH_SERVICE).getDeclaredMethod("onStart")
|
||||
.invoke(null);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
} else {
|
||||
Log.i("No valid Google Play Services APK found.");
|
||||
}
|
||||
}
|
||||
|
||||
private static void startHelper(
|
||||
String userId, final Map<String, ?> attributes, final boolean isBackground) {
|
||||
LeanplumEventDataManager.init(context);
|
||||
LeanplumPushService.onStart();
|
||||
|
||||
checkAndStartNotificationsModules();
|
||||
Boolean limitAdTracking = null;
|
||||
String deviceId = Request.deviceId();
|
||||
String deviceId = RequestOld.deviceId();
|
||||
if (deviceId == null) {
|
||||
if (!userSpecifiedDeviceId && Constants.defaultDeviceId != null) {
|
||||
deviceId = Constants.defaultDeviceId;
|
||||
@ -615,16 +660,16 @@ public class Leanplum {
|
||||
deviceId = deviceIdInfo.id;
|
||||
limitAdTracking = deviceIdInfo.limitAdTracking;
|
||||
}
|
||||
Request.setDeviceId(deviceId);
|
||||
RequestOld.setDeviceId(deviceId);
|
||||
}
|
||||
|
||||
if (userId == null) {
|
||||
userId = Request.userId();
|
||||
userId = RequestOld.userId();
|
||||
if (userId == null) {
|
||||
userId = Request.deviceId();
|
||||
userId = RequestOld.deviceId();
|
||||
}
|
||||
}
|
||||
Request.setUserId(userId);
|
||||
RequestOld.setUserId(userId);
|
||||
|
||||
// Setup parameters.
|
||||
String versionName = Util.getVersionName();
|
||||
@ -649,7 +694,9 @@ public class Leanplum {
|
||||
params.put(Constants.Params.DEVICE_MODEL, Util.getDeviceModel());
|
||||
params.put(Constants.Params.DEVICE_SYSTEM_NAME, Util.getSystemName());
|
||||
params.put(Constants.Params.DEVICE_SYSTEM_VERSION, Util.getSystemVersion());
|
||||
params.put(Constants.Params.DEVICE_PUSH_TOKEN, registrationId);
|
||||
if (!TextUtils.isEmpty(registrationId)) {
|
||||
params.put(Constants.Params.DEVICE_PUSH_TOKEN, registrationId);
|
||||
}
|
||||
params.put(Constants.Keys.TIMEZONE, localTimeZone.getID());
|
||||
params.put(Constants.Keys.TIMEZONE_OFFSET_SECONDS, Integer.toString(timezoneOffsetSeconds));
|
||||
params.put(Constants.Keys.LOCALE, Util.getLocale());
|
||||
@ -670,11 +717,13 @@ public class Leanplum {
|
||||
// Get the current inbox messages on the device.
|
||||
params.put(Constants.Params.INBOX_MESSAGES, LeanplumInbox.getInstance().messagesIds());
|
||||
|
||||
params.put(Constants.Params.INCLUDE_VARIANT_DEBUG_INFO, LeanplumInternal.getIsVariantDebugInfoEnabled());
|
||||
|
||||
Util.initializePreLeanplumInstall(params);
|
||||
|
||||
// Issue start API call.
|
||||
final Request request = Request.post(Constants.Methods.START, params);
|
||||
request.onApiResponse(new Request.ApiResponseCallback() {
|
||||
final RequestOld request = RequestOld.post(Constants.Methods.START, params);
|
||||
request.onApiResponse(new RequestOld.ApiResponseCallback() {
|
||||
@Override
|
||||
public void response(List<Map<String, Object>> requests, JSONObject response, int countOfEvents) {
|
||||
Leanplum.handleApiResponse(response, requests, request, countOfEvents);
|
||||
@ -691,8 +740,7 @@ public class Leanplum {
|
||||
}
|
||||
|
||||
private static void handleApiResponse(JSONObject response, List<Map<String, Object>> requests,
|
||||
final Request request, int countOfUnsentRequests) {
|
||||
boolean hasStartResponse = false;
|
||||
final RequestOld request, int countOfUnsentRequests) {
|
||||
JSONObject lastStartResponse = null;
|
||||
|
||||
// Find and handle the last start response.
|
||||
@ -704,23 +752,12 @@ public class Leanplum {
|
||||
request.setDataBaseIndex(request.getDataBaseIndex() - countOfUnsentRequests);
|
||||
return;
|
||||
}
|
||||
|
||||
final int responseCount = Request.numResponses(response);
|
||||
for (int i = requests.size() - 1; i >= 0; i--) {
|
||||
Map<String, Object> currentRequest = requests.get(i);
|
||||
if (Constants.Methods.START.equals(currentRequest.get(Constants.Params.ACTION))) {
|
||||
if (i < responseCount) {
|
||||
lastStartResponse = Request.getResponseAt(response, i);
|
||||
}
|
||||
hasStartResponse = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
lastStartResponse = parseLastStartResponse(response, requests);
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
}
|
||||
|
||||
if (hasStartResponse) {
|
||||
if (lastStartResponse != null) {
|
||||
if (!LeanplumInternal.hasStarted()) {
|
||||
// Set start response to null.
|
||||
request.onApiResponse(null);
|
||||
@ -729,11 +766,35 @@ public class Leanplum {
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static JSONObject parseLastStartResponse(JSONObject response, List<Map<String, Object>> requests) {
|
||||
final int responseCount = RequestOld.numResponses(response);
|
||||
for (int i = requests.size() - 1; i >= 0; i--) {
|
||||
Map<String, Object> currentRequest = requests.get(i);
|
||||
if (Constants.Methods.START.equals(currentRequest.get(Constants.Params.ACTION))) {
|
||||
if (currentRequest.containsKey(RequestOld.REQUEST_ID_KEY)) {
|
||||
for (int j = RequestOld.numResponses(response) - 1; j >= 0; j--) {
|
||||
JSONObject currentResponse = RequestOld.getResponseAt(response, j);
|
||||
if (currentRequest.get(RequestOld.REQUEST_ID_KEY)
|
||||
.equals(currentResponse.optString(RequestOld.REQUEST_ID_KEY))) {
|
||||
return currentResponse;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i < responseCount) {
|
||||
return RequestOld.getResponseAt(response, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void handleStartResponse(final JSONObject response) {
|
||||
Util.executeAsyncTask(false, new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
boolean success = Request.isResponseSuccess(response);
|
||||
boolean success = RequestOld.isResponseSuccess(response);
|
||||
Leanplum.countAggregator().incrementCount("on_start_response");
|
||||
if (!success) {
|
||||
try {
|
||||
LeanplumInternal.setHasStarted(true);
|
||||
@ -781,24 +842,23 @@ public class Leanplum {
|
||||
String defaultNotificationChannel = response.optString(
|
||||
Constants.Keys.DEFAULT_NOTIFICATION_CHANNEL);
|
||||
|
||||
// Configure notification channels and groups
|
||||
LeanplumNotificationChannel.configureNotificationGroups(
|
||||
context, notificationGroups);
|
||||
LeanplumNotificationChannel.configureNotificationChannels(
|
||||
context, notificationChannels);
|
||||
|
||||
if (notificationChannels == null && (defaultNotificationChannel == null
|
||||
|| defaultNotificationChannel.isEmpty())) {
|
||||
defaultNotificationChannel = getDefaultChannelId();
|
||||
}
|
||||
|
||||
LeanplumNotificationChannel.configureDefaultNotificationChannel(
|
||||
context, defaultNotificationChannel);
|
||||
try {
|
||||
Class.forName(LEANPLUM_NOTIFICATION_CHANNEL)
|
||||
.getDeclaredMethod("configureChannels", Context.class, JSONArray.class,
|
||||
JSONArray.class, String.class).invoke(new Object(), context,
|
||||
notificationGroups, notificationChannels, defaultNotificationChannel);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
String token = response.optString(Constants.Keys.TOKEN, null);
|
||||
Request.setToken(token);
|
||||
Request.saveToken();
|
||||
RequestOld.setToken(token);
|
||||
RequestOld.saveToken();
|
||||
|
||||
applyContentInResponse(response, true);
|
||||
|
||||
@ -813,6 +873,12 @@ public class Leanplum {
|
||||
Constants.loggingEnabled = true;
|
||||
}
|
||||
|
||||
Set<String> enabledCounters = parseSdkCounters(response);
|
||||
countAggregator.setEnabledCounters(enabledCounters);
|
||||
Set<String> enabledFeatureFlags = parseFeatureFlags(response);
|
||||
FeatureFlagManager.INSTANCE.setEnabledFeatureFlags((enabledFeatureFlags));
|
||||
parseVariantDebugInfo(response);
|
||||
|
||||
// Allow bidirectional realtime variable updates.
|
||||
if (Constants.isDevelopmentModeEnabled) {
|
||||
|
||||
@ -861,24 +927,10 @@ public class Leanplum {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
NotificationCompat.Builder builder =
|
||||
LeanplumNotificationHelper.getDefaultCompatNotificationBuilder(context,
|
||||
BuildUtil.isNotificationChannelSupported(context));
|
||||
if (builder == null) {
|
||||
return;
|
||||
}
|
||||
builder.setSmallIcon(android.R.drawable.star_on)
|
||||
.setContentTitle("Leanplum")
|
||||
.setContentText("Your device is registered.");
|
||||
builder.setContentIntent(PendingIntent.getActivity(
|
||||
currentContext.getApplicationContext(), 0, new Intent(), 0));
|
||||
NotificationManager mNotificationManager =
|
||||
(NotificationManager) currentContext.getSystemService(
|
||||
Context.NOTIFICATION_SERVICE);
|
||||
// mId allows you to update the notification later on.
|
||||
mNotificationManager.notify(0, builder.build());
|
||||
} catch (Throwable t) {
|
||||
Log.i("Device is registered.");
|
||||
Class.forName(Leanplum.LEANPLUM_PUSH_SERVICE)
|
||||
.getDeclaredMethod("showDeviceRegistedPush", Context.class,
|
||||
Context.class).invoke(new Object(), context, currentContext);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -948,6 +1000,8 @@ public class Leanplum {
|
||||
response.optJSONObject(Constants.Keys.REGIONS));
|
||||
List<Map<String, Object>> variants = JsonConverter.listFromJsonOrDefault(
|
||||
response.optJSONArray(Constants.Keys.VARIANTS));
|
||||
Map<String, Object> variantDebugInfo = JsonConverter.mapFromJsonOrDefault(
|
||||
response.optJSONObject(Constants.Keys.VARIANT_DEBUG_INFO));
|
||||
|
||||
if (alwaysApply
|
||||
|| !values.equals(VarCache.getDiffs())
|
||||
@ -956,7 +1010,7 @@ public class Leanplum {
|
||||
|| !eventRules.equals(VarCache.getEventRuleDiffs())
|
||||
|| !regions.equals(VarCache.regions())) {
|
||||
VarCache.applyVariableDiffs(values, messages, updateRules,
|
||||
eventRules, regions, variants);
|
||||
eventRules, regions, variants, variantDebugInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@ -998,7 +1052,7 @@ public class Leanplum {
|
||||
}
|
||||
|
||||
private static void pauseInternal() {
|
||||
Request.post(Constants.Methods.PAUSE_SESSION, null).sendIfConnected();
|
||||
RequestOld.post(Constants.Methods.PAUSE_SESSION, null).sendIfConnected();
|
||||
pauseHeartbeat();
|
||||
LeanplumInternal.setIsPaused(true);
|
||||
}
|
||||
@ -1032,7 +1086,7 @@ public class Leanplum {
|
||||
}
|
||||
|
||||
private static void resumeInternal() {
|
||||
Request request = Request.post(Constants.Methods.RESUME_SESSION, null);
|
||||
RequestOld request = RequestOld.post(Constants.Methods.RESUME_SESSION, null);
|
||||
if (LeanplumInternal.hasStartedInBackground()) {
|
||||
LeanplumInternal.setStartedInBackground(false);
|
||||
request.sendIfConnected();
|
||||
@ -1050,16 +1104,9 @@ public class Leanplum {
|
||||
*/
|
||||
private static void startHeartbeat() {
|
||||
synchronized (heartbeatLock) {
|
||||
heartbeatExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||
heartbeatExecutor.scheduleAtFixedRate(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
Request.post(Constants.Methods.HEARTBEAT, null).sendIfDelayed();
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
}
|
||||
}
|
||||
}, 15, 15, TimeUnit.MINUTES);
|
||||
if (heartbeatExecutor == null) {
|
||||
createHeartbeatExecutor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1067,6 +1114,7 @@ public class Leanplum {
|
||||
synchronized (heartbeatLock) {
|
||||
if (heartbeatExecutor != null) {
|
||||
heartbeatExecutor.shutdown();
|
||||
heartbeatExecutor = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1075,6 +1123,19 @@ public class Leanplum {
|
||||
startHeartbeat();
|
||||
}
|
||||
|
||||
private static void createHeartbeatExecutor() {
|
||||
heartbeatExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||
heartbeatExecutor.scheduleAtFixedRate(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
RequestOld.post(Constants.Methods.HEARTBEAT, null).sendIfDelayed();
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
}
|
||||
}
|
||||
}, 15, 15, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this to explicitly end the session. This should not be used in most cases, so we won't
|
||||
* make it public for now.
|
||||
@ -1105,7 +1166,7 @@ public class Leanplum {
|
||||
}
|
||||
|
||||
private static void stopInternal() {
|
||||
Request.post(Constants.Methods.STOP, null).sendIfConnected();
|
||||
RequestOld.post(Constants.Methods.STOP, null).sendIfConnected();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1115,6 +1176,19 @@ public class Leanplum {
|
||||
return LeanplumInternal.hasStarted();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the userId in the current Leanplum session. This should only be called after
|
||||
* Leanplum.start().
|
||||
*/
|
||||
public static String getUserId() {
|
||||
if (hasStarted()) {
|
||||
return RequestOld.userId();
|
||||
} else {
|
||||
Log.e("Leanplum.start() must be called before calling getUserId()");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance to the singleton LeanplumInbox object.
|
||||
*/
|
||||
@ -1233,7 +1307,7 @@ public class Leanplum {
|
||||
noDownloadsHandlers.add(handler);
|
||||
}
|
||||
if (VarCache.hasReceivedDiffs()
|
||||
&& Request.numPendingDownloads() == 0) {
|
||||
&& RequestOld.numPendingDownloads() == 0) {
|
||||
handler.variablesChanged();
|
||||
}
|
||||
}
|
||||
@ -1254,6 +1328,60 @@ public class Leanplum {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a callback for when a message is displayed.
|
||||
*/
|
||||
public static void addMessageDisplayedHandler(
|
||||
MessageDisplayedCallback handler) {
|
||||
if (handler == null) {
|
||||
Log.e("addMessageDisplayedHandler - Invalid handler parameter " +
|
||||
"provided.");
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (messageDisplayedHandlers) {
|
||||
messageDisplayedHandlers.add(handler);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a variables changed and no downloads pending callback.
|
||||
*/
|
||||
public static void removeMessageDisplayedHandler(
|
||||
MessageDisplayedCallback handler) {
|
||||
if (handler == null) {
|
||||
Log.e("removeMessageDisplayedHandler - Invalid handler parameter " +
|
||||
"provided.");
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (messageDisplayedHandlers) {
|
||||
messageDisplayedHandlers.remove(handler);
|
||||
}
|
||||
}
|
||||
|
||||
public static void triggerMessageDisplayed(ActionContext actionContext) {
|
||||
ActionManager.getInstance().recordMessageImpression(actionContext.getMessageId());
|
||||
synchronized (messageDisplayedHandlers) {
|
||||
for (MessageDisplayedCallback callback : messageDisplayedHandlers) {
|
||||
String messageID = actionContext.getMessageId();
|
||||
String messageBody = "";
|
||||
try {
|
||||
messageBody = (String) actionContext.getArgs().get("Message");
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
}
|
||||
String recipientUserID = Leanplum.getUserId();
|
||||
Date deliveryDateTime = new Date();
|
||||
|
||||
MessageArchiveData messageArchiveData = new MessageArchiveData(messageID,
|
||||
messageBody, recipientUserID, deliveryDateTime);
|
||||
callback.setMessageArchiveData(messageArchiveData);
|
||||
OsHandler.getInstance().post(callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a callback to call ONCE when no more file downloads are pending (either when no files
|
||||
* needed to be downloaded or all downloads have been completed).
|
||||
@ -1267,7 +1395,7 @@ public class Leanplum {
|
||||
}
|
||||
|
||||
if (VarCache.hasReceivedDiffs()
|
||||
&& Request.numPendingDownloads() == 0) {
|
||||
&& RequestOld.numPendingDownloads() == 0) {
|
||||
handler.variablesChanged();
|
||||
} else {
|
||||
synchronized (onceNoDownloadsHandlers) {
|
||||
@ -1363,6 +1491,7 @@ public class Leanplum {
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
}
|
||||
Leanplum.countAggregator().incrementCount("define_action");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1433,9 +1562,9 @@ public class Leanplum {
|
||||
|
||||
private static void setUserAttributesInternal(String userId,
|
||||
HashMap<String, Object> requestArgs) {
|
||||
Request.post(Constants.Methods.SET_USER_ATTRIBUTES, requestArgs).send();
|
||||
RequestOld.post(Constants.Methods.SET_USER_ATTRIBUTES, requestArgs).send();
|
||||
if (userId != null && userId.length() > 0) {
|
||||
Request.setUserId(userId);
|
||||
RequestOld.setUserId(userId);
|
||||
if (LeanplumInternal.hasStarted()) {
|
||||
VarCache.saveDiffs();
|
||||
}
|
||||
@ -1483,7 +1612,7 @@ public class Leanplum {
|
||||
try {
|
||||
HashMap<String, Object> params = new HashMap<>();
|
||||
params.put(Constants.Params.DEVICE_PUSH_TOKEN, registrationId);
|
||||
Request.post(Constants.Methods.SET_DEVICE_ATTRIBUTES, params).send();
|
||||
RequestOld.post(Constants.Methods.SET_DEVICE_ATTRIBUTES, params).send();
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
}
|
||||
@ -1534,7 +1663,7 @@ public class Leanplum {
|
||||
}
|
||||
|
||||
private static void setTrafficSourceInfoInternal(HashMap<String, Object> params) {
|
||||
Request.post(Constants.Methods.SET_TRAFFIC_SOURCE_INFO, params).send();
|
||||
RequestOld.post(Constants.Methods.SET_TRAFFIC_SOURCE_INFO, params).send();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1557,6 +1686,7 @@ public class Leanplum {
|
||||
public static void track(final String event, double value, String info,
|
||||
Map<String, ?> params) {
|
||||
LeanplumInternal.track(event, value, info, params, null);
|
||||
countAggregator.incrementCount("track");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1792,6 +1922,7 @@ public class Leanplum {
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
}
|
||||
countAggregator.incrementCount("advance_to");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1803,7 +1934,7 @@ public class Leanplum {
|
||||
*/
|
||||
private static void advanceToInternal(String state, Map<String, ?> params,
|
||||
Map<String, Object> requestParams) {
|
||||
Request.post(Constants.Methods.ADVANCE, requestParams).send();
|
||||
RequestOld.post(Constants.Methods.ADVANCE, requestParams).send();
|
||||
|
||||
ContextualValues contextualValues = new ContextualValues();
|
||||
contextualValues.parameters = params;
|
||||
@ -1883,7 +2014,7 @@ public class Leanplum {
|
||||
}
|
||||
|
||||
private static void pauseStateInternal() {
|
||||
Request.post(Constants.Methods.PAUSE_STATE, new HashMap<String, Object>()).send();
|
||||
RequestOld.post(Constants.Methods.PAUSE_STATE, new HashMap<String, Object>()).send();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1919,7 +2050,7 @@ public class Leanplum {
|
||||
}
|
||||
|
||||
private static void resumeStateInternal() {
|
||||
Request.post(Constants.Methods.RESUME_STATE, new HashMap<String, Object>()).send();
|
||||
RequestOld.post(Constants.Methods.RESUME_STATE, new HashMap<String, Object>()).send();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1951,8 +2082,10 @@ public class Leanplum {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put(Constants.Params.INCLUDE_DEFAULTS, Boolean.toString(false));
|
||||
params.put(Constants.Params.INBOX_MESSAGES, LeanplumInbox.getInstance().messagesIds());
|
||||
Request req = Request.post(Constants.Methods.GET_VARS, params);
|
||||
req.onResponse(new Request.ResponseCallback() {
|
||||
params.put(Constants.Params.INCLUDE_VARIANT_DEBUG_INFO, LeanplumInternal.getIsVariantDebugInfoEnabled());
|
||||
|
||||
RequestOld req = RequestOld.post(Constants.Methods.GET_VARS, params);
|
||||
req.onResponse(new RequestOld.ResponseCallback() {
|
||||
@Override
|
||||
public void response(JSONObject response) {
|
||||
try {
|
||||
@ -1968,6 +2101,8 @@ public class Leanplum {
|
||||
if (response.optBoolean(Constants.Keys.LOGGING_ENABLED, false)) {
|
||||
Constants.loggingEnabled = true;
|
||||
}
|
||||
|
||||
parseVariantDebugInfo(response);
|
||||
}
|
||||
if (callback != null) {
|
||||
OsHandler.getInstance().post(callback);
|
||||
@ -1977,7 +2112,7 @@ public class Leanplum {
|
||||
}
|
||||
}
|
||||
});
|
||||
req.onError(new Request.ErrorCallback() {
|
||||
req.onError(new RequestOld.ErrorCallback() {
|
||||
@Override
|
||||
public void error(Exception e) {
|
||||
if (callback != null) {
|
||||
@ -1990,6 +2125,7 @@ public class Leanplum {
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
}
|
||||
countAggregator.incrementCount("force_content_update");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2070,6 +2206,13 @@ public class Leanplum {
|
||||
return messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Details about the variable assignments on the server.
|
||||
*/
|
||||
public static Map<String, Object> getVariantDebugInfo() {
|
||||
return VarCache.getVariantDebugInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set location manually. Calls setDeviceLocation with cell type. Best if used in after calling
|
||||
* disableLocationCollection.
|
||||
@ -2119,4 +2262,56 @@ public class Leanplum {
|
||||
public static boolean isLocationCollectionEnabled() {
|
||||
return locationCollectionEnabled;
|
||||
}
|
||||
|
||||
private static void parseVariantDebugInfo(JSONObject response) {
|
||||
Map<String, Object> variantDebugInfo = JsonConverter.mapFromJsonOrDefault(
|
||||
response.optJSONObject(Constants.Keys.VARIANT_DEBUG_INFO));
|
||||
if (variantDebugInfo.size() > 0) {
|
||||
VarCache.setVariantDebugInfo(variantDebugInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears cached values for messages, variables and test assignments.
|
||||
* Use sparingly as if the app is updated, you'll have to deal with potentially
|
||||
* inconsistent state or user experience.
|
||||
*/
|
||||
public static void clearUserContent() {
|
||||
VarCache.clearUserContent();
|
||||
countAggregator.incrementCount("clear_user_content");
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static Set<String> parseSdkCounters(JSONObject response) {
|
||||
JSONArray enabledCounters = response.optJSONArray(
|
||||
Constants.Keys.ENABLED_COUNTERS);
|
||||
Set<String> counterSet = toSet(enabledCounters);
|
||||
return counterSet;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static Set<String> parseFeatureFlags(JSONObject response) {
|
||||
JSONArray enabledFeatureFlags = response.optJSONArray(
|
||||
Constants.Keys.ENABLED_FEATURE_FLAGS);
|
||||
Set<String> featureFlagSet = toSet(enabledFeatureFlags);
|
||||
return featureFlagSet;
|
||||
}
|
||||
|
||||
private static Set<String> toSet(JSONArray array) {
|
||||
Set<String> set = new HashSet<>();
|
||||
if (array != null) {
|
||||
for (int i = 0; i < array.length(); i++) {
|
||||
set.add(array.optString(i));
|
||||
}
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
public static CountAggregator countAggregator() {
|
||||
return countAggregator;
|
||||
}
|
||||
|
||||
public static FeatureFlagManager featureFlagManager() {
|
||||
return featureFlagManager;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,26 @@
|
||||
/*
|
||||
* Copyright 2018, Leanplum, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package com.leanplum;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.job.JobParameters;
|
||||
import android.app.job.JobService;
|
||||
@ -10,16 +32,16 @@ import android.app.job.JobService;
|
||||
*/
|
||||
@TargetApi(21)
|
||||
public class LeanplumGcmRegistrationJobService extends JobService {
|
||||
public static final int JOB_ID = -63722755;
|
||||
public static final int JOB_ID = -63722755;
|
||||
|
||||
@Override
|
||||
public boolean onStartJob(JobParameters jobParameters) {
|
||||
LeanplumNotificationHelper.startPushRegistrationService(this, "GCM");
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean onStartJob(JobParameters jobParameters) {
|
||||
LeanplumNotificationHelper.startPushRegistrationService(this, "GCM");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onStopJob(JobParameters jobParameters) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public boolean onStopJob(JobParameters jobParameters) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ import com.leanplum.internal.Constants;
|
||||
import com.leanplum.internal.JsonConverter;
|
||||
import com.leanplum.internal.Log;
|
||||
import com.leanplum.internal.OsHandler;
|
||||
import com.leanplum.internal.Request;
|
||||
import com.leanplum.internal.RequestOld;
|
||||
import com.leanplum.internal.Util;
|
||||
import com.leanplum.utils.SharedPreferencesUtil;
|
||||
|
||||
@ -61,15 +61,15 @@ public class LeanplumInbox {
|
||||
static Set<String> downloadedImageUrls;
|
||||
static boolean isInboxImagePrefetchingEnabled = true;
|
||||
|
||||
private int unreadCount;
|
||||
private Map<String, LeanplumInboxMessage> messages;
|
||||
private boolean didLoad = false;
|
||||
private volatile int unreadCount;
|
||||
private volatile Map<String, LeanplumInboxMessage> messages;
|
||||
private volatile boolean didLoad = false;
|
||||
|
||||
private final List<InboxChangedCallback> changedCallbacks;
|
||||
private final List<InboxSyncedCallback> syncedCallbacks;
|
||||
private final Object updatingLock = new Object();
|
||||
|
||||
protected LeanplumInbox() {
|
||||
private LeanplumInbox() {
|
||||
this.unreadCount = 0;
|
||||
this.messages = new HashMap<>();
|
||||
this.didLoad = false;
|
||||
@ -255,7 +255,7 @@ public class LeanplumInbox {
|
||||
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put(Constants.Params.INBOX_MESSAGE_ID, messageId);
|
||||
Request req = Request.post(Constants.Methods.DELETE_INBOX_MESSAGE, params);
|
||||
RequestOld req = RequestOld.post(Constants.Methods.DELETE_INBOX_MESSAGE, params);
|
||||
req.send();
|
||||
}
|
||||
|
||||
@ -287,12 +287,12 @@ public class LeanplumInbox {
|
||||
Context context = Leanplum.getContext();
|
||||
SharedPreferences defaults = context.getSharedPreferences(
|
||||
"__leanplum__", Context.MODE_PRIVATE);
|
||||
if (Request.token() == null) {
|
||||
if (RequestOld.token() == null) {
|
||||
update(new HashMap<String, LeanplumInboxMessage>(), 0, false);
|
||||
return;
|
||||
}
|
||||
int unreadCount = 0;
|
||||
AESCrypt aesContext = new AESCrypt(Request.appId(), Request.token());
|
||||
AESCrypt aesContext = new AESCrypt(RequestOld.appId(), RequestOld.token());
|
||||
String newsfeedString = aesContext.decodePreference(
|
||||
defaults, Constants.Defaults.INBOX_KEY, "{}");
|
||||
Map<String, Object> newsfeed = JsonConverter.fromJson(newsfeedString);
|
||||
@ -322,7 +322,7 @@ public class LeanplumInbox {
|
||||
if (Constants.isNoop()) {
|
||||
return;
|
||||
}
|
||||
if (Request.token() == null) {
|
||||
if (RequestOld.token() == null) {
|
||||
return;
|
||||
}
|
||||
Context context = Leanplum.getContext();
|
||||
@ -337,7 +337,7 @@ public class LeanplumInbox {
|
||||
messages.put(messageId, data);
|
||||
}
|
||||
String messagesJson = JsonConverter.toJson(messages);
|
||||
AESCrypt aesContext = new AESCrypt(Request.appId(), Request.token());
|
||||
AESCrypt aesContext = new AESCrypt(RequestOld.appId(), RequestOld.token());
|
||||
editor.putString(Constants.Defaults.INBOX_KEY, aesContext.encrypt(messagesJson));
|
||||
SharedPreferencesUtil.commitChanges(editor);
|
||||
}
|
||||
@ -347,8 +347,8 @@ public class LeanplumInbox {
|
||||
return;
|
||||
}
|
||||
|
||||
final Request req = Request.post(Constants.Methods.GET_INBOX_MESSAGES, null);
|
||||
req.onResponse(new Request.ResponseCallback() {
|
||||
final RequestOld req = RequestOld.post(Constants.Methods.GET_INBOX_MESSAGES, null);
|
||||
req.onResponse(new RequestOld.ResponseCallback() {
|
||||
@Override
|
||||
public void response(JSONObject response) {
|
||||
try {
|
||||
@ -411,7 +411,7 @@ public class LeanplumInbox {
|
||||
}
|
||||
}
|
||||
});
|
||||
req.onError(new Request.ErrorCallback() {
|
||||
req.onError(new RequestOld.ErrorCallback() {
|
||||
@Override
|
||||
public void error(Exception e) {
|
||||
triggerInboxSyncedWithStatus(false);
|
||||
@ -430,11 +430,15 @@ public class LeanplumInbox {
|
||||
}
|
||||
try {
|
||||
for (String messageId : messagesIds()) {
|
||||
messages.add(messageForId(messageId));
|
||||
LeanplumInboxMessage message = messageForId(messageId);
|
||||
if (message != null) {
|
||||
messages.add(message);
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
}
|
||||
Leanplum.countAggregator().incrementCount("all_messages_inbox");
|
||||
return messages;
|
||||
}
|
||||
|
||||
|
@ -26,9 +26,10 @@ import android.text.TextUtils;
|
||||
|
||||
import com.leanplum.internal.CollectionUtil;
|
||||
import com.leanplum.internal.Constants;
|
||||
import com.leanplum.internal.FileManager;
|
||||
import com.leanplum.internal.JsonConverter;
|
||||
import com.leanplum.internal.Log;
|
||||
import com.leanplum.internal.Request;
|
||||
import com.leanplum.internal.RequestOld;
|
||||
import com.leanplum.internal.Util;
|
||||
|
||||
import org.json.JSONObject;
|
||||
@ -39,9 +40,6 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.leanplum.internal.FileManager.DownloadFileResult;
|
||||
import static com.leanplum.internal.FileManager.fileExistsAtPath;
|
||||
import static com.leanplum.internal.FileManager.fileValue;
|
||||
import static com.leanplum.internal.FileManager.maybeDownloadFile;
|
||||
|
||||
/**
|
||||
* LeanplumInboxMessage class.
|
||||
@ -77,8 +75,8 @@ public class LeanplumInboxMessage {
|
||||
* Returns the image file path of the inbox message. Can be null.
|
||||
*/
|
||||
public String getImageFilePath() {
|
||||
String path = fileValue(imageFileName);
|
||||
if (fileExistsAtPath(path)) {
|
||||
String path = FileManager.fileValue(imageFileName);
|
||||
if (FileManager.fileExistsAtPath(path)) {
|
||||
return new File(path).getAbsolutePath();
|
||||
}
|
||||
if (!LeanplumInbox.getInstance().isInboxImagePrefetchingEnabled()) {
|
||||
@ -94,8 +92,8 @@ public class LeanplumInboxMessage {
|
||||
* It will return the file Uri path instead if the image is in cache.
|
||||
*/
|
||||
public Uri getImageUrl() {
|
||||
String path = fileValue(imageFileName);
|
||||
if (fileExistsAtPath(path)) {
|
||||
String path = FileManager.fileValue(imageFileName);
|
||||
if (FileManager.fileExistsAtPath(path)) {
|
||||
return Uri.fromFile(new File(path));
|
||||
}
|
||||
if (TextUtils.isEmpty(imageUrl)) {
|
||||
@ -187,7 +185,7 @@ public class LeanplumInboxMessage {
|
||||
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put(Constants.Params.INBOX_MESSAGE_ID, messageId);
|
||||
Request req = Request.post(Constants.Methods.MARK_INBOX_MESSAGE_AS_READ,
|
||||
RequestOld req = RequestOld.post(Constants.Methods.MARK_INBOX_MESSAGE_AS_READ,
|
||||
params);
|
||||
req.send();
|
||||
}
|
||||
@ -253,7 +251,7 @@ public class LeanplumInboxMessage {
|
||||
return false;
|
||||
}
|
||||
|
||||
DownloadFileResult result = maybeDownloadFile(true, imageFileName,
|
||||
DownloadFileResult result = FileManager.maybeDownloadFile(true, imageFileName,
|
||||
imageUrl, imageUrl, null);
|
||||
LeanplumInbox.downloadedImageUrls.add(imageUrl);
|
||||
return DownloadFileResult.DOWNLOADING == result;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,3 @@
|
||||
package com.leanplum;
|
||||
|
||||
/*
|
||||
* Copyright 2017, Leanplum, Inc. All rights reserved.
|
||||
*
|
||||
@ -20,6 +18,7 @@ package com.leanplum;
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.leanplum;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Notification;
|
||||
@ -35,6 +34,7 @@ import android.graphics.drawable.AdaptiveIconDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.text.TextUtils;
|
||||
import android.util.TypedValue;
|
||||
@ -43,8 +43,11 @@ import android.widget.RemoteViews;
|
||||
import com.leanplum.internal.Constants;
|
||||
import com.leanplum.internal.JsonConverter;
|
||||
import com.leanplum.internal.Log;
|
||||
import com.leanplum.internal.Util;
|
||||
import com.leanplum.utils.BitmapUtil;
|
||||
import com.leanplum.utils.BuildUtil;
|
||||
|
||||
import java.util.IllegalFormatCodePointException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
@ -57,345 +60,470 @@ import java.util.TreeSet;
|
||||
*/
|
||||
class LeanplumNotificationHelper {
|
||||
|
||||
private static final int BIGPICTURE_TEXT_TOP_PADDING = -14;
|
||||
private static final int BIGPICTURE_TEXT_SIZE = 14;
|
||||
private static final String LEANPLUM_DEFAULT_PUSH_ICON = "leanplum_default_push_icon";
|
||||
private static final int BIGPICTURE_TEXT_TOP_PADDING = -14;
|
||||
private static final int BIGPICTURE_TEXT_SIZE = 14;
|
||||
private static final String LEANPLUM_DEFAULT_PUSH_ICON = "leanplum_default_push_icon";
|
||||
private static final int MAX_ONE_LINE_TEXT_LENGTH = 37;
|
||||
|
||||
/**
|
||||
* If notification channels are supported this method will try to create
|
||||
* NotificationCompat.Builder with default notification channel if default channel id is provided.
|
||||
* If notification channels not supported this method will return NotificationCompat.Builder for
|
||||
* context.
|
||||
*
|
||||
* @param context The application context.
|
||||
* @param isNotificationChannelSupported True if notification channels are supported.
|
||||
* @return NotificationCompat.Builder for provided context or null.
|
||||
*/
|
||||
// NotificationCompat.Builder(Context context) constructor was deprecated in API level 26.
|
||||
@SuppressWarnings("deprecation")
|
||||
static NotificationCompat.Builder getDefaultCompatNotificationBuilder(Context context,
|
||||
boolean isNotificationChannelSupported) {
|
||||
if (!isNotificationChannelSupported) {
|
||||
return new NotificationCompat.Builder(context);
|
||||
}
|
||||
String channelId = LeanplumNotificationChannel.getDefaultNotificationChannelId(context);
|
||||
if (!TextUtils.isEmpty(channelId)) {
|
||||
return new NotificationCompat.Builder(context, channelId);
|
||||
/**
|
||||
* If notification channels are supported this method will try to create
|
||||
* NotificationCompat.Builder with default notification channel if default channel id is provided.
|
||||
* If notification channels not supported this method will return NotificationCompat.Builder for
|
||||
* context.
|
||||
*
|
||||
* @param context The application context.
|
||||
* @param isNotificationChannelSupported True if notification channels are supported.
|
||||
* @return NotificationCompat.Builder for provided context or null.
|
||||
*/
|
||||
// NotificationCompat.Builder(Context context) constructor was deprecated in API level 26.
|
||||
@SuppressWarnings("deprecation")
|
||||
static NotificationCompat.Builder getDefaultCompatNotificationBuilder(Context context,
|
||||
boolean isNotificationChannelSupported) {
|
||||
if (!isNotificationChannelSupported) {
|
||||
return new NotificationCompat.Builder(context);
|
||||
}
|
||||
String channelId = LeanplumNotificationChannel.getDefaultNotificationChannelId(context);
|
||||
if (!TextUtils.isEmpty(channelId)) {
|
||||
return new NotificationCompat.Builder(context, channelId);
|
||||
} else {
|
||||
Log.w("Failed to post notification, there are no notification channels configured.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If notification channels are supported this method will try to create
|
||||
* Notification.Builder with default notification channel if default channel id is provided.
|
||||
* If notification channels not supported this method will return Notification.Builder for
|
||||
* context.
|
||||
*
|
||||
* @param context The application context.
|
||||
* @param isNotificationChannelSupported True if notification channels are supported.
|
||||
* @return Notification.Builder for provided context or null.
|
||||
*/
|
||||
// Notification.Builder(Context context) constructor was deprecated in API level 26.
|
||||
@SuppressWarnings("deprecation")
|
||||
private static Notification.Builder getDefaultNotificationBuilder(Context context,
|
||||
boolean isNotificationChannelSupported) {
|
||||
if (!isNotificationChannelSupported) {
|
||||
return new Notification.Builder(context);
|
||||
}
|
||||
String channelId = LeanplumNotificationChannel.getDefaultNotificationChannelId(context);
|
||||
if (!TextUtils.isEmpty(channelId)) {
|
||||
return new Notification.Builder(context, channelId);
|
||||
} else {
|
||||
Log.w("Failed to post notification, there are no notification channels configured.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If notification channels are supported this method will try to create a channel with
|
||||
* information from the message if it doesn't exist and return NotificationCompat.Builder for this
|
||||
* channel. In the case where no channel information inside the message, we will try to get a
|
||||
* channel with default channel id. If notification channels not supported this method will return
|
||||
* NotificationCompat.Builder for context.
|
||||
*
|
||||
* @param context The application context.
|
||||
* @param message Push notification Bundle.
|
||||
* @return NotificationCompat.Builder or null.
|
||||
*/
|
||||
// NotificationCompat.Builder(Context context) constructor was deprecated in API level 26.
|
||||
@SuppressWarnings("deprecation")
|
||||
static NotificationCompat.Builder getNotificationCompatBuilder(Context context, Bundle message) {
|
||||
NotificationCompat.Builder builder = null;
|
||||
// If we are targeting API 26, try to find supplied channel to post notification.
|
||||
if (BuildUtil.isNotificationChannelSupported(context)) {
|
||||
try {
|
||||
String channel = message.getString("lp_channel");
|
||||
if (!TextUtils.isEmpty(channel)) {
|
||||
// Create channel if it doesn't exist and post notification to that channel.
|
||||
Map<String, Object> channelDetails = JsonConverter.fromJson(channel);
|
||||
String channelId = LeanplumNotificationChannel.createNotificationChannel(context,
|
||||
channelDetails);
|
||||
if (!TextUtils.isEmpty(channelId)) {
|
||||
builder = new NotificationCompat.Builder(context, channelId);
|
||||
} else {
|
||||
Log.w("Failed to post notification to specified channel.");
|
||||
}
|
||||
} else {
|
||||
Log.w("Failed to post notification, there are no notification channels configured.");
|
||||
return null;
|
||||
// If channel isn't supplied, try to look up for default channel.
|
||||
builder = LeanplumNotificationHelper.getDefaultCompatNotificationBuilder(context, true);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("Failed to post notification to specified channel.");
|
||||
}
|
||||
} else {
|
||||
builder = new NotificationCompat.Builder(context);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* If notification channels are supported this method will try to create a channel with
|
||||
* information from the message if it doesn't exist and return Notification.Builder for this
|
||||
* channel. In the case where no channel information inside the message, we will try to get a
|
||||
* channel with default channel id. If notification channels not supported this method will return
|
||||
* Notification.Builder for context.
|
||||
*
|
||||
* @param context The application context.
|
||||
* @param message Push notification Bundle.
|
||||
* @return Notification.Builder or null.
|
||||
*/
|
||||
private static Notification.Builder getNotificationBuilder(Context context, Bundle message) {
|
||||
Notification.Builder builder = null;
|
||||
// If we are targeting API 26, try to find supplied channel to post notification.
|
||||
if (BuildUtil.isNotificationChannelSupported(context)) {
|
||||
try {
|
||||
String channel = message.getString("lp_channel");
|
||||
if (!TextUtils.isEmpty(channel)) {
|
||||
// Create channel if it doesn't exist and post notification to that channel.
|
||||
Map<String, Object> channelDetails = JsonConverter.fromJson(channel);
|
||||
String channelId = LeanplumNotificationChannel.createNotificationChannel(context,
|
||||
channelDetails);
|
||||
if (!TextUtils.isEmpty(channelId)) {
|
||||
builder = new Notification.Builder(context, channelId);
|
||||
} else {
|
||||
Log.w("Failed to post notification to specified channel.");
|
||||
}
|
||||
} else {
|
||||
// If channel isn't supplied, try to look up for default channel.
|
||||
builder = LeanplumNotificationHelper.getDefaultNotificationBuilder(context, true);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("Failed to post notification to specified channel.");
|
||||
}
|
||||
} else {
|
||||
builder = new Notification.Builder(context);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets NotificationCompat.Builder for provided parameters.
|
||||
*
|
||||
* @param context The application context.
|
||||
* @param message Push notification Bundle.
|
||||
* @param contentIntent PendingIntent.
|
||||
* @param title String with title for push notification.
|
||||
* @param messageText String with text for push notification.
|
||||
* @param bigPicture Bitmap for BigPictureStyle notification.
|
||||
* @param defaultNotificationIconResourceId int Resource id for default push notification icon.
|
||||
* @return NotificationCompat.Builder or null.
|
||||
*/
|
||||
static NotificationCompat.Builder getNotificationCompatBuilder(Context context, Bundle message,
|
||||
PendingIntent contentIntent, String title, final String messageText, Bitmap bigPicture,
|
||||
int defaultNotificationIconResourceId) {
|
||||
if (message == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts push registration service to update GCM/FCM InstanceId token.
|
||||
*
|
||||
* @param context Current application context.
|
||||
* @param providerName Name of push notification provider.
|
||||
*/
|
||||
static void startPushRegistrationService(Context context, String providerName) {
|
||||
NotificationCompat.Builder notificationCompatBuilder =
|
||||
getNotificationCompatBuilder(context, message);
|
||||
|
||||
if (notificationCompatBuilder == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (defaultNotificationIconResourceId == 0) {
|
||||
notificationCompatBuilder.setSmallIcon(context.getApplicationInfo().icon);
|
||||
} else {
|
||||
notificationCompatBuilder.setSmallIcon(defaultNotificationIconResourceId);
|
||||
}
|
||||
|
||||
notificationCompatBuilder.setContentTitle(title)
|
||||
.setStyle(new NotificationCompat.BigTextStyle()
|
||||
.bigText(messageText))
|
||||
.setContentText(messageText);
|
||||
|
||||
if (bigPicture != null) {
|
||||
notificationCompatBuilder.setStyle(new NotificationCompat.BigPictureStyle()
|
||||
.bigPicture(bigPicture)
|
||||
.setBigContentTitle(title)
|
||||
.setSummaryText(messageText));
|
||||
}
|
||||
|
||||
// Try to put a notification on top of the notification area. This method was deprecated in API
|
||||
// level 26. For API level 26 and above we must use setImportance(int) for each notification
|
||||
// channel, not for each notification message.
|
||||
if (Build.VERSION.SDK_INT >= 16 && !BuildUtil.isNotificationChannelSupported(context)) {
|
||||
//noinspection deprecation
|
||||
notificationCompatBuilder.setPriority(Notification.PRIORITY_MAX);
|
||||
}
|
||||
notificationCompatBuilder.setAutoCancel(true);
|
||||
notificationCompatBuilder.setContentIntent(contentIntent);
|
||||
|
||||
return notificationCompatBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls setStyle for notificationBuilder and it tries modify notification layout to support two
|
||||
* lines.
|
||||
*
|
||||
* @param notificationBuilder current Notification.Builder.
|
||||
* @param bigPictureStyle current Notification.BigPictureStyle.
|
||||
*/
|
||||
static void setModifiedBigPictureStyle(Notification.Builder notificationBuilder,
|
||||
Notification.Style bigPictureStyle) {
|
||||
if (Build.VERSION.SDK_INT < 16 || notificationBuilder == null || bigPictureStyle == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
notificationBuilder.setStyle(bigPictureStyle);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
// By default we cannot reach getStandardView method on API>=24. If we call
|
||||
// createBigContentView, Android will call getStandardView method and we can get
|
||||
// modified RemoteView.
|
||||
try {
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
Log.i("Updating " + providerName + " InstanceId token.");
|
||||
// Fetch updated Instance ID token and notify our app's server of any changes (if applicable).
|
||||
Intent intent = new Intent(context, LeanplumPushRegistrationService.class);
|
||||
context.startService(intent);
|
||||
RemoteViews remoteView = notificationBuilder.createBigContentView();
|
||||
if (remoteView != null) {
|
||||
// We need to set received RemoteView as a custom big content view.
|
||||
notificationBuilder.setCustomBigContentView(remoteView);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Log.e("Couldn't update " + providerName + " InstanceId token.", t);
|
||||
Log.e("Cannot modify push notification layout.", t);
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Log.e("Cannot set BigPicture style for push notification.", t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets Notification.BigPictureStyle with 2 lines text.
|
||||
*
|
||||
* @param message Push notification Bundle.
|
||||
* @param bigPicture Bitmap for BigPictureStyle notification.
|
||||
* @param title String with title for push notification.
|
||||
* @param messageText String with text for push notification.
|
||||
* @return Notification.BigPictureStyle or null.
|
||||
*/
|
||||
static Notification.BigPictureStyle getBigPictureStyle(Bundle message, Bitmap bigPicture,
|
||||
String title, final String messageText) {
|
||||
if (Build.VERSION.SDK_INT < 16 || message == null || bigPicture == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule JobService to JobScheduler.
|
||||
*
|
||||
* @param context Current application context.
|
||||
* @param clazz JobService class.
|
||||
* @param jobId JobService id.
|
||||
*/
|
||||
@TargetApi(21)
|
||||
static void scheduleJobService(Context context, Class clazz, int jobId) {
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
ComponentName serviceName = new ComponentName(context, clazz);
|
||||
JobScheduler jobScheduler =
|
||||
(JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
|
||||
if (jobScheduler != null) {
|
||||
jobId = verifyJobId(jobScheduler.getAllPendingJobs(), jobId);
|
||||
JobInfo startMyServiceJobInfo = new JobInfo.Builder(jobId, serviceName)
|
||||
.setMinimumLatency(10).build();
|
||||
jobScheduler.schedule(startMyServiceJobInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that jobId don't present on JobScheduler pending jobs. If jobId present on
|
||||
* JobScheduler pending jobs generates a new one.
|
||||
*
|
||||
* @param allPendingJobs List of current pending jobs.
|
||||
* @param jobId JobService id.
|
||||
* @return jobId if jobId don't present on JobScheduler pending jobs
|
||||
*/
|
||||
@TargetApi(21)
|
||||
private static int verifyJobId(List<JobInfo> allPendingJobs, int jobId) {
|
||||
if (allPendingJobs != null && !allPendingJobs.isEmpty()) {
|
||||
TreeSet<Integer> idsSet = new TreeSet<>();
|
||||
for (JobInfo jobInfo : allPendingJobs) {
|
||||
idsSet.add(jobInfo.getId());
|
||||
}
|
||||
if (idsSet.contains(jobId)) {
|
||||
if (idsSet.first() > Integer.MIN_VALUE) {
|
||||
jobId = idsSet.first() - 1;
|
||||
} else if (idsSet.last() < Integer.MIN_VALUE) {
|
||||
jobId = idsSet.last() + 1;
|
||||
} else {
|
||||
while (idsSet.contains(jobId)) {
|
||||
jobId = new Random().nextInt();
|
||||
}
|
||||
}
|
||||
Notification.BigPictureStyle bigPictureStyle = new Notification.BigPictureStyle() {
|
||||
@Override
|
||||
protected RemoteViews getStandardView(int layoutId) {
|
||||
RemoteViews remoteViews = super.getStandardView(layoutId);
|
||||
if (messageText != null && messageText.length() >= MAX_ONE_LINE_TEXT_LENGTH) {
|
||||
// Modifications of standard push RemoteView.
|
||||
try {
|
||||
int id = Resources.getSystem().getIdentifier("text", "id", "android");
|
||||
remoteViews.setBoolean(id, "setSingleLine", false);
|
||||
remoteViews.setInt(id, "setLines", 2);
|
||||
if (Build.VERSION.SDK_INT < 23) {
|
||||
// Make text smaller.
|
||||
remoteViews.setViewPadding(id, 0, BIGPICTURE_TEXT_TOP_PADDING, 0, 0);
|
||||
remoteViews.setTextViewTextSize(id, TypedValue.COMPLEX_UNIT_SP, BIGPICTURE_TEXT_SIZE);
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
Log.e("Cannot modify push notification layout.");
|
||||
}
|
||||
}
|
||||
return jobId;
|
||||
return remoteViews;
|
||||
}
|
||||
};
|
||||
|
||||
bigPictureStyle.bigPicture(bigPicture)
|
||||
.setBigContentTitle(title)
|
||||
.setSummaryText(message.getString(Constants.Keys.PUSH_MESSAGE_TEXT));
|
||||
|
||||
return bigPictureStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets Notification.Builder for provided parameters.
|
||||
*
|
||||
* @param context The application context.
|
||||
* @param message Push notification Bundle.
|
||||
* @param contentIntent PendingIntent.
|
||||
* @param title String with title for push notification.
|
||||
* @param messageText String with text for push notification.
|
||||
* @param defaultNotificationIconResourceId int Resource id for default push notification icon.
|
||||
* @return Notification.Builder or null.
|
||||
*/
|
||||
static Notification.Builder getNotificationBuilder(Context context, Bundle message,
|
||||
PendingIntent contentIntent, String title, final String messageText,
|
||||
int defaultNotificationIconResourceId) {
|
||||
Notification.Builder notificationBuilder = getNotificationBuilder(context, message);
|
||||
if (notificationBuilder == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* If notification channels are supported this method will try to create
|
||||
* Notification.Builder with default notification channel if default channel id is provided.
|
||||
* If notification channels not supported this method will return Notification.Builder for
|
||||
* context.
|
||||
*
|
||||
* @param context The application context.
|
||||
* @param isNotificationChannelSupported True if notification channels are supported.
|
||||
* @return Notification.Builder for provided context or null.
|
||||
*/
|
||||
// Notification.Builder(Context context) constructor was deprecated in API level 26.
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
@SuppressWarnings("deprecation")
|
||||
private static Notification.Builder getDefaultNotificationBuilder(Context context,
|
||||
boolean isNotificationChannelSupported) {
|
||||
if (!isNotificationChannelSupported) {
|
||||
return new Notification.Builder(context);
|
||||
}
|
||||
String channelId = LeanplumNotificationChannel.getDefaultNotificationChannelId(context);
|
||||
if (!TextUtils.isEmpty(channelId)) {
|
||||
return new Notification.Builder(context, channelId);
|
||||
if (defaultNotificationIconResourceId == 0) {
|
||||
notificationBuilder.setSmallIcon(context.getApplicationInfo().icon);
|
||||
} else {
|
||||
notificationBuilder.setSmallIcon(defaultNotificationIconResourceId);
|
||||
}
|
||||
|
||||
notificationBuilder.setContentTitle(title)
|
||||
.setContentText(messageText);
|
||||
|
||||
if (Build.VERSION.SDK_INT > 16) {
|
||||
notificationBuilder.setStyle(new Notification.BigTextStyle()
|
||||
.bigText(messageText));
|
||||
if (!BuildUtil.isNotificationChannelSupported(context)) {
|
||||
//noinspection deprecation
|
||||
notificationBuilder.setPriority(Notification.PRIORITY_MAX);
|
||||
}
|
||||
}
|
||||
notificationBuilder.setAutoCancel(true);
|
||||
notificationBuilder.setContentIntent(contentIntent);
|
||||
return notificationBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a possibility to create icon drawable from current app icon.
|
||||
*
|
||||
* @param context Current application context.
|
||||
* @return boolean True if it is possible to create a drawable from current app icon.
|
||||
*/
|
||||
private static boolean canCreateIconDrawable(Context context) {
|
||||
try {
|
||||
// Try to create icon drawable.
|
||||
Drawable drawable = AdaptiveIconDrawable.createFromStream(
|
||||
context.getResources().openRawResource(context.getApplicationInfo().icon),
|
||||
"applicationInfo.icon");
|
||||
// If there was no crash, we still need to check for null.
|
||||
if (drawable != null) {
|
||||
return true;
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation of Application icon for small icon on push notification.
|
||||
*
|
||||
* @param context Current application context.
|
||||
* @return boolean True if application icon can be used for small icon on push notification.
|
||||
*/
|
||||
static boolean isApplicationIconValid(Context context) {
|
||||
if (context == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Potentially there should be checked for Build.VERSION.SDK_INT != 26, but we need to
|
||||
// TODO: confirm that adaptive icon works well on 27, before to change it.
|
||||
if (Build.VERSION.SDK_INT < 26) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return canCreateIconDrawable(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets default push notification resource id for LEANPLUM_DEFAULT_PUSH_ICON in drawable.
|
||||
*
|
||||
* @param context Current application context.
|
||||
* @return int Resource id.
|
||||
*/
|
||||
static int getDefaultPushNotificationIconResourceId(Context context) {
|
||||
try {
|
||||
Resources resources = context.getResources();
|
||||
return resources.getIdentifier(LEANPLUM_DEFAULT_PUSH_ICON, "drawable",
|
||||
context.getPackageName());
|
||||
} catch (Throwable ignored) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule JobService to JobScheduler.
|
||||
*
|
||||
* @param context Current application context.
|
||||
* @param clazz JobService class.
|
||||
* @param jobId JobService id.
|
||||
*/
|
||||
@TargetApi(21)
|
||||
static void scheduleJobService(Context context, Class clazz, int jobId) {
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
ComponentName serviceName = new ComponentName(context, clazz);
|
||||
JobScheduler jobScheduler =
|
||||
(JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
|
||||
if (jobScheduler != null) {
|
||||
jobId = verifyJobId(jobScheduler.getAllPendingJobs(), jobId);
|
||||
JobInfo startMyServiceJobInfo = new JobInfo.Builder(jobId, serviceName)
|
||||
.setMinimumLatency(10).build();
|
||||
jobScheduler.schedule(startMyServiceJobInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that jobId don't present on JobScheduler pending jobs. If jobId present on
|
||||
* JobScheduler pending jobs generates a new one.
|
||||
*
|
||||
* @param allPendingJobs List of current pending jobs.
|
||||
* @param jobId JobService id.
|
||||
* @return jobId if jobId don't present on JobScheduler pending jobs
|
||||
*/
|
||||
@TargetApi(21)
|
||||
private static int verifyJobId(List<JobInfo> allPendingJobs, int jobId) {
|
||||
if (allPendingJobs != null && !allPendingJobs.isEmpty()) {
|
||||
TreeSet<Integer> idsSet = new TreeSet<>();
|
||||
for (JobInfo jobInfo : allPendingJobs) {
|
||||
idsSet.add(jobInfo.getId());
|
||||
}
|
||||
if (idsSet.contains(jobId)) {
|
||||
if (idsSet.first() > Integer.MIN_VALUE) {
|
||||
jobId = idsSet.first() - 1;
|
||||
} else if (idsSet.last() < Integer.MIN_VALUE) {
|
||||
jobId = idsSet.last() + 1;
|
||||
} else {
|
||||
Log.w("Failed to post notification, there are no notification channels configured.");
|
||||
return null;
|
||||
while (idsSet.contains(jobId)) {
|
||||
jobId = new Random().nextInt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return jobId;
|
||||
}
|
||||
|
||||
/**
|
||||
* If notification channels are supported this method will try to create a channel with
|
||||
* information from the message if it doesn't exist and return NotificationCompat.Builder for this
|
||||
* channel. In the case where no channel information inside the message, we will try to get a
|
||||
* channel with default channel id. If notification channels not supported this method will return
|
||||
* NotificationCompat.Builder for context.
|
||||
*
|
||||
* @param context The application context.
|
||||
* @param message Push notification Bundle.
|
||||
* @return NotificationCompat.Builder or null.
|
||||
*/
|
||||
// NotificationCompat.Builder(Context context) constructor was deprecated in API level 26.
|
||||
@SuppressWarnings("deprecation")
|
||||
static NotificationCompat.Builder getNotificationCompatBuilder(Context context, Bundle message) {
|
||||
NotificationCompat.Builder builder = null;
|
||||
// If we are targeting API 26, try to find supplied channel to post notification.
|
||||
if (BuildUtil.isNotificationChannelSupported(context)) {
|
||||
try {
|
||||
String channel = message.getString("lp_channel");
|
||||
if (!TextUtils.isEmpty(channel)) {
|
||||
// Create channel if it doesn't exist and post notification to that channel.
|
||||
Map<String, Object> channelDetails = JsonConverter.fromJson(channel);
|
||||
String channelId = LeanplumNotificationChannel.createNotificationChannel(context,
|
||||
channelDetails);
|
||||
if (!TextUtils.isEmpty(channelId)) {
|
||||
builder = new NotificationCompat.Builder(context, channelId);
|
||||
} else {
|
||||
Log.w("Failed to post notification to specified channel.");
|
||||
}
|
||||
} else {
|
||||
// If channel isn't supplied, try to look up for default channel.
|
||||
builder = LeanplumNotificationHelper.getDefaultCompatNotificationBuilder(context, true);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("Failed to post notification to specified channel.");
|
||||
}
|
||||
} else {
|
||||
builder = new NotificationCompat.Builder(context);
|
||||
}
|
||||
return builder;
|
||||
/**
|
||||
* Starts push registration service to update GCM/FCM InstanceId token.
|
||||
*
|
||||
* @param context Current application context.
|
||||
* @param providerName Name of push notification provider.
|
||||
*/
|
||||
static void startPushRegistrationService(Context context, String providerName) {
|
||||
try {
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
Log.i("Updating " + providerName + " InstanceId token.");
|
||||
// Fetch updated Instance ID token and notify our app's server of any changes (if applicable).
|
||||
Intent intent = new Intent(context, LeanplumPushRegistrationService.class);
|
||||
context.startService(intent);
|
||||
} catch (Throwable t) {
|
||||
Log.e("Couldn't update " + providerName + " InstanceId token.", t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If notification channels are supported this method will try to create a channel with
|
||||
* information from the message if it doesn't exist and return Notification.Builder for this
|
||||
* channel. In the case where no channel information inside the message, we will try to get a
|
||||
* channel with default channel id. If notification channels not supported this method will return
|
||||
* Notification.Builder for context.
|
||||
*
|
||||
* @param context The application context.
|
||||
* @param message Push notification Bundle.
|
||||
* @return Notification.Builder or null.
|
||||
*/
|
||||
static Notification.Builder getNotificationBuilder(Context context, Bundle message) {
|
||||
Notification.Builder builder = null;
|
||||
// If we are targeting API 26, try to find supplied channel to post notification.
|
||||
if (BuildUtil.isNotificationChannelSupported(context)) {
|
||||
try {
|
||||
String channel = message.getString("lp_channel");
|
||||
if (!TextUtils.isEmpty(channel)) {
|
||||
// Create channel if it doesn't exist and post notification to that channel.
|
||||
Map<String, Object> channelDetails = JsonConverter.fromJson(channel);
|
||||
String channelId = LeanplumNotificationChannel.createNotificationChannel(context,
|
||||
channelDetails);
|
||||
if (!TextUtils.isEmpty(channelId)) {
|
||||
builder = new Notification.Builder(context, channelId);
|
||||
} else {
|
||||
Log.w("Failed to post notification to specified channel.");
|
||||
}
|
||||
} else {
|
||||
// If channel isn't supplied, try to look up for default channel.
|
||||
builder = LeanplumNotificationHelper.getDefaultNotificationBuilder(context, true);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("Failed to post notification to specified channel.");
|
||||
}
|
||||
} else {
|
||||
builder = new Notification.Builder(context);
|
||||
}
|
||||
return builder;
|
||||
/**
|
||||
* Gets bitmap for BigPicture style push notification.
|
||||
*
|
||||
* @param context Current application context.
|
||||
* @param imageUrl String with url to image.
|
||||
* @return Scaled bitmap for push notification with big image or null.
|
||||
*/
|
||||
@Nullable
|
||||
static Bitmap getBigPictureBitmap(Context context, String imageUrl) {
|
||||
Bitmap bigPicture = null;
|
||||
// BigPictureStyle support requires API 16 and higher.
|
||||
if (!TextUtils.isEmpty(imageUrl) && Build.VERSION.SDK_INT >= 16) {
|
||||
bigPicture = BitmapUtil.getScaledBitmap(context, imageUrl);
|
||||
if (bigPicture == null) {
|
||||
Log.w(String.format("Image download failed for push notification with big picture. " +
|
||||
"No image will be included with the push notification. Image URL: %s.", imageUrl));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets Notification.Builder with 2 lines at BigPictureStyle notification text.
|
||||
*
|
||||
* @param context The application context.
|
||||
* @param message Push notification Bundle.
|
||||
* @param contentIntent PendingIntent.
|
||||
* @param title String with title for push notification.
|
||||
* @param messageText String with text for push notification.
|
||||
* @param bigPicture Bitmap for BigPictureStyle notification.
|
||||
* @param defaultNotificationIconResourceId int Resource id for default push notification icon.
|
||||
* @return Notification.Builder or null.
|
||||
*/
|
||||
static Notification.Builder getNotificationBuilder(Context context, Bundle message,
|
||||
PendingIntent contentIntent, String title, final String messageText, Bitmap bigPicture,
|
||||
int defaultNotificationIconResourceId) {
|
||||
if (Build.VERSION.SDK_INT < 16) {
|
||||
return null;
|
||||
}
|
||||
Notification.Builder notificationBuilder =
|
||||
getNotificationBuilder(context, message);
|
||||
if (defaultNotificationIconResourceId == 0) {
|
||||
notificationBuilder.setSmallIcon(context.getApplicationInfo().icon);
|
||||
} else {
|
||||
notificationBuilder.setSmallIcon(defaultNotificationIconResourceId);
|
||||
}
|
||||
notificationBuilder.setContentTitle(title)
|
||||
.setContentText(messageText);
|
||||
Notification.BigPictureStyle bigPictureStyle = new Notification.BigPictureStyle() {
|
||||
@Override
|
||||
protected RemoteViews getStandardView(int layoutId) {
|
||||
RemoteViews remoteViews = super.getStandardView(layoutId);
|
||||
// Modifications of stanxdard push RemoteView.
|
||||
try {
|
||||
int id = Resources.getSystem().getIdentifier("text", "id", "android");
|
||||
remoteViews.setBoolean(id, "setSingleLine", false);
|
||||
remoteViews.setInt(id, "setLines", 2);
|
||||
if (Build.VERSION.SDK_INT < 23) {
|
||||
// Make text smaller.
|
||||
remoteViews.setViewPadding(id, 0, BIGPICTURE_TEXT_TOP_PADDING, 0, 0);
|
||||
remoteViews.setTextViewTextSize(id, TypedValue.COMPLEX_UNIT_SP, BIGPICTURE_TEXT_SIZE);
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
Log.e("Cannot modify push notification layout.");
|
||||
}
|
||||
return remoteViews;
|
||||
}
|
||||
};
|
||||
|
||||
bigPictureStyle.bigPicture(bigPicture)
|
||||
.setBigContentTitle(title)
|
||||
.setSummaryText(message.getString(Constants.Keys.PUSH_MESSAGE_TEXT));
|
||||
notificationBuilder.setStyle(bigPictureStyle);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
// By default we cannot reach getStandardView method on API>=24. I we call
|
||||
// createBigContentView, Android will call getStandardView method and we can get
|
||||
// modified RemoteView.
|
||||
try {
|
||||
RemoteViews remoteView = notificationBuilder.createBigContentView();
|
||||
if (remoteView != null) {
|
||||
// We need to set received RemoteView as a custom big content view.
|
||||
notificationBuilder.setCustomBigContentView(remoteView);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Log.e("Cannot modify push notification layout.", t);
|
||||
}
|
||||
}
|
||||
|
||||
notificationBuilder.setAutoCancel(true);
|
||||
notificationBuilder.setContentIntent(contentIntent);
|
||||
return notificationBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a possibility to create icon drawable from current app icon.
|
||||
*
|
||||
* @param context Current application context.
|
||||
* @return boolean True if it is possible to create a drawable from current app icon.
|
||||
*/
|
||||
private static boolean canCreateIconDrawable(Context context) {
|
||||
try {
|
||||
// Try to create icon drawable.
|
||||
Drawable drawable = AdaptiveIconDrawable.createFromStream(
|
||||
context.getResources().openRawResource(context.getApplicationInfo().icon),
|
||||
"applicationInfo.icon");
|
||||
// If there was no crash, we still need to check for null.
|
||||
if (drawable != null) {
|
||||
return true;
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation of Application icon for small icon on push notification.
|
||||
*
|
||||
* @param context Current application context.
|
||||
* @return boolean True if application icon can be used for small icon on push notification.
|
||||
*/
|
||||
static boolean isApplicationIconValid(Context context) {
|
||||
if (context == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Potentially there should be checked for Build.VERSION.SDK_INT != 26, but we need to
|
||||
// TODO: confirm that adaptive icon works well on 27, before to change it.
|
||||
if (Build.VERSION.SDK_INT < 26) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return canCreateIconDrawable(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets default push notification resource id for LEANPLUM_DEFAULT_PUSH_ICON in drawable.
|
||||
*
|
||||
* @param context Current application context.
|
||||
* @return int Resource id.
|
||||
*/
|
||||
static int getDefaultPushNotificationIconResourceId(Context context) {
|
||||
try {
|
||||
Resources resources = context.getResources();
|
||||
return resources.getIdentifier(LEANPLUM_DEFAULT_PUSH_ICON, "drawable",
|
||||
context.getPackageName());
|
||||
} catch (Throwable ignored) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return bigPicture;
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ public class LeanplumPushInstanceIDService extends InstanceIDListenerService {
|
||||
LeanplumNotificationHelper.startPushRegistrationService(this, "GCM");
|
||||
} else {
|
||||
LeanplumNotificationHelper.scheduleJobService(this,
|
||||
LeanplumGcmRegistrationJobService.class, LeanplumGcmRegistrationJobService.JOB_ID);
|
||||
LeanplumGcmRegistrationJobService.class, LeanplumGcmRegistrationJobService.JOB_ID);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Log.e("Failed to update GCM token.", t);
|
||||
|
@ -44,6 +44,9 @@ public class LeanplumPushListenerService extends GcmListenerService {
|
||||
public void onMessageReceived(String senderId, Bundle data) {
|
||||
try {
|
||||
if (data.containsKey(Keys.PUSH_MESSAGE_TEXT)) {
|
||||
if (Leanplum.getContext() == null) {
|
||||
Leanplum.setApplicationContext(this);
|
||||
}
|
||||
LeanplumPushService.handleNotification(this, data);
|
||||
}
|
||||
Log.i("Received: " + data.toString());
|
||||
|
@ -21,12 +21,45 @@
|
||||
|
||||
package com.leanplum;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
|
||||
/**
|
||||
* Implement LeanplumPushNotificationCustomizer to customize the appearance of notifications.
|
||||
*/
|
||||
public interface LeanplumPushNotificationCustomizer {
|
||||
/**
|
||||
* Implement this method to customize push notification. Please call {@link
|
||||
* LeanplumPushService#setCustomizer(LeanplumPushNotificationCustomizer)} to activate this method.
|
||||
* Leave this method empty if you want to support 2 lines of text
|
||||
* in BigPicture style push notification and implement {@link
|
||||
* LeanplumPushNotificationCustomizer#customize(Notification.Builder, Bundle, Notification.Style)}
|
||||
*
|
||||
* @param builder NotificationCompat.Builder for push notification.
|
||||
* @param notificationPayload Bundle notification payload.
|
||||
*/
|
||||
void customize(NotificationCompat.Builder builder, Bundle notificationPayload);
|
||||
|
||||
/**
|
||||
* Implement this method to support 2 lines of text in BigPicture style push notification,
|
||||
* otherwise implement {@link
|
||||
* LeanplumPushNotificationCustomizer#customize(NotificationCompat.Builder, Bundle)} and leave
|
||||
* this method empty. Please call {@link
|
||||
* LeanplumPushService#setCustomizer(LeanplumPushNotificationCustomizer, boolean)} with true
|
||||
* value to activate this method.
|
||||
*
|
||||
* @param builder Notification.Builder for push notification.
|
||||
* @param notificationPayload Bundle notification payload.
|
||||
* @param notificationStyle - Notification.BigPictureStyle or null - BigPicture style for current
|
||||
* push notification. Call ((Notification.BigPictureStyle) notificationStyle).bigLargeIcon(largeIcon)
|
||||
* if you want to set large icon on expanded push notification. If notificationStyle wasn't null
|
||||
* it will be set to push notification. Note: If you call notificationStyle = new
|
||||
* Notification.BigPictureStyle() or other Notification.Style - there will be no support 2 lines
|
||||
* of text on BigPicture push and you need to call builder.setStyle(notificationStyle) to set
|
||||
* yours expanded layout for push notification.
|
||||
*/
|
||||
void customize(Notification.Builder builder, Bundle notificationPayload,
|
||||
@Nullable Notification.Style notificationStyle);
|
||||
}
|
||||
|
@ -64,5 +64,6 @@ public class LeanplumPushReceiver extends BroadcastReceiver {
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
}
|
||||
Leanplum.countAggregator().incrementCount("did_receive_remote_notification");
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ package com.leanplum;
|
||||
|
||||
import android.app.IntentService;
|
||||
import android.content.Intent;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.leanplum.internal.Log;
|
||||
|
||||
@ -41,22 +42,26 @@ public class LeanplumPushRegistrationService extends IntentService {
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
LeanplumCloudMessagingProvider provider = LeanplumPushService.getCloudMessagingProvider();
|
||||
if (provider == null) {
|
||||
Log.e("Failed to complete registration token refresh.");
|
||||
return;
|
||||
}
|
||||
String registrationId = provider.getRegistrationId();
|
||||
if (registrationId != null) {
|
||||
if (existingRegistrationId != null && !registrationId.equals(existingRegistrationId)) {
|
||||
Log.e("WARNING: It appears your app is registering " +
|
||||
"with GCM/FCM using multiple GCM/FCM sender ids. Please be sure to call " +
|
||||
"LeanplumPushService.setGcmSenderIds() with " +
|
||||
"all of the GCM sender ids that you use, not just the one that you use with " +
|
||||
"Leanplum. Otherwise, GCM/FCM push notifications may not work consistently.");
|
||||
try {
|
||||
LeanplumCloudMessagingProvider provider = LeanplumPushService.getCloudMessagingProvider();
|
||||
if (provider == null) {
|
||||
Log.e("Failed to complete registration token refresh.");
|
||||
return;
|
||||
}
|
||||
existingRegistrationId = registrationId;
|
||||
provider.onRegistrationIdReceived(getApplicationContext(), registrationId);
|
||||
String registrationId = provider.getRegistrationId();
|
||||
if (!TextUtils.isEmpty(registrationId)) {
|
||||
if (existingRegistrationId != null && !registrationId.equals(existingRegistrationId)) {
|
||||
Log.e("WARNING: It appears your app is registering " +
|
||||
"with GCM/FCM using multiple GCM/FCM sender ids. Please be sure to call " +
|
||||
"LeanplumPushService.setGcmSenderIds() with " +
|
||||
"all of the GCM sender ids that you use, not just the one that you use with " +
|
||||
"Leanplum. Otherwise, GCM/FCM push notifications may not work consistently.");
|
||||
}
|
||||
existingRegistrationId = registrationId;
|
||||
provider.onRegistrationIdReceived(getApplicationContext(), registrationId);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Log.e("Failed to complete registration token refresh.", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,10 +31,8 @@ import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.leanplum.callbacks.VariablesChangedCallback;
|
||||
import com.leanplum.internal.ActionManager;
|
||||
@ -44,12 +42,10 @@ import com.leanplum.internal.Constants.Methods;
|
||||
import com.leanplum.internal.Constants.Params;
|
||||
import com.leanplum.internal.JsonConverter;
|
||||
import com.leanplum.internal.LeanplumInternal;
|
||||
import com.leanplum.internal.LeanplumManifestHelper;
|
||||
import com.leanplum.internal.Log;
|
||||
import com.leanplum.internal.Request;
|
||||
import com.leanplum.internal.RequestOld;
|
||||
import com.leanplum.internal.Util;
|
||||
import com.leanplum.internal.VarCache;
|
||||
import com.leanplum.utils.BitmapUtil;
|
||||
import com.leanplum.utils.BuildUtil;
|
||||
import com.leanplum.utils.SharedPreferencesUtil;
|
||||
|
||||
@ -91,24 +87,88 @@ public class LeanplumPushService {
|
||||
* LeanplumPushService#parseNotificationBundle(Bundle)}.
|
||||
*/
|
||||
public static final String LEANPLUM_MESSAGE_ID = "lp_message_id";
|
||||
private static final String LEANPLUM_PUSH_SERVICE_GCM = "com.leanplum.LeanplumPushServiceGcm";
|
||||
private static final String LEANPLUM_PUSH_SERVICE_FCM = "com.leanplum.LeanplumPushServiceFcm";
|
||||
|
||||
private static final String LEANPLUM_PUSH_FCM_LISTENER_SERVICE_CLASS =
|
||||
"com.leanplum.LeanplumPushFcmListenerService";
|
||||
private static final String PUSH_FIREBASE_MESSAGING_SERVICE_CLASS =
|
||||
"com.leanplum.LeanplumPushFirebaseMessagingService";
|
||||
private static final String LEANPLUM_PUSH_INSTANCE_ID_SERVICE_CLASS =
|
||||
"com.leanplum.LeanplumPushInstanceIDService";
|
||||
private static final String LEANPLUM_PUSH_LISTENER_SERVICE_CLASS =
|
||||
"com.leanplum.LeanplumPushListenerService";
|
||||
private static final String GCM_RECEIVER_CLASS = "com.google.android.gms.gcm.GcmReceiver";
|
||||
private static final int NOTIFICATION_ID = 1;
|
||||
private static final String OPEN_URL = "Open URL";
|
||||
private static final String URL = "URL";
|
||||
private static final String OPEN_ACTION = "Open";
|
||||
private static final int MAX_ONE_LINE_TEXT_LENGTH = 37;
|
||||
private static final String COM_LEANPLUM_GCM_PROVIDER = "com.leanplum.LeanplumGcmProvider";
|
||||
private static Class<? extends Activity> callbackClass;
|
||||
private static LeanplumCloudMessagingProvider provider;
|
||||
private static boolean isFirebaseEnabled = false;
|
||||
private static LeanplumPushNotificationCustomizer customizer;
|
||||
private static boolean useNotificationBuilderCustomizer = false;
|
||||
|
||||
/**
|
||||
* Sets the Google Cloud Messaging sender ID. Required for push GCM notifications to work.
|
||||
*
|
||||
* @param senderId The GCM sender ID to permit notifications from. Use {@link
|
||||
* LeanplumPushService#LEANPLUM_SENDER_ID} to use the built-in sender ID for GCM. If you have
|
||||
* multiple sender IDs, use {@link LeanplumPushService#setGcmSenderIds}.
|
||||
*/
|
||||
public static void setGcmSenderId(String senderId) {
|
||||
try {
|
||||
Class.forName(COM_LEANPLUM_GCM_PROVIDER).getDeclaredMethod("setSenderId",
|
||||
String.class).invoke(new Object(), senderId);
|
||||
} catch (Throwable throwable) {
|
||||
Log.e("Couldn't invoke a LeanplumGcmProvider.setGcmSenderId method, please be " +
|
||||
"sure you include LeanplumGCM module.", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Google Cloud Messaging sender ID. Required for push GCM notifications to work.
|
||||
*
|
||||
* @param senderIds The GCM sender IDs to permit notifications from. Use {@link
|
||||
* LeanplumPushService#LEANPLUM_SENDER_ID} to use the built-in sender ID.
|
||||
*/
|
||||
public static void setGcmSenderIds(String... senderIds) {
|
||||
StringBuilder joinedSenderIds = new StringBuilder();
|
||||
for (String senderId : senderIds) {
|
||||
if (joinedSenderIds.length() > 0) {
|
||||
joinedSenderIds.append(',');
|
||||
}
|
||||
joinedSenderIds.append(senderId);
|
||||
}
|
||||
|
||||
try {
|
||||
Class.forName(COM_LEANPLUM_GCM_PROVIDER).getDeclaredMethod("setSenderId",
|
||||
String.class).invoke(new Object(), joinedSenderIds.toString());
|
||||
} catch (Throwable throwable) {
|
||||
Log.e("Couldn't invoke a LeanplumGcmProvider.setGcmSenderId method, please be " +
|
||||
"sure you include LeanplumGCM module.", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use Firebase Cloud Messaging, instead of the default Google Cloud Messaging.
|
||||
*
|
||||
* @deprecated FCM is no longer packaged in the SDK. Instead it is split up into modules.
|
||||
* Modify your build.gradle by replacing implementation 'com.leanplum:Leanplum:+'
|
||||
* with each module separately.
|
||||
*
|
||||
* For example:
|
||||
* implementation 'com.leanplum:leanplum-fcm:+'
|
||||
* implementation 'com.leanplum:leanplum-location:+'
|
||||
*/
|
||||
@Deprecated
|
||||
public static void enableFirebase() {
|
||||
LeanplumPushService.isFirebaseEnabled = true;
|
||||
Log.e("enableFirebase() is deprecated and FCM is not enabled! " +
|
||||
"SDK has been split up into modules and you need to modify your build.gradle. " +
|
||||
"See the doc for more info.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether Firebase Cloud Messaging is enabled or not.
|
||||
*
|
||||
* @return Boolean - true if enabled
|
||||
*/
|
||||
static boolean isFirebaseEnabled() {
|
||||
return isFirebaseEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Cloud Messaging provider. By default - GCM.
|
||||
@ -119,6 +179,10 @@ public class LeanplumPushService {
|
||||
return provider;
|
||||
}
|
||||
|
||||
static void setCloudMessagingProvider(LeanplumCloudMessagingProvider cloudMessagingProvider) {
|
||||
provider = cloudMessagingProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the default activity to launch if the user opens a push notification.
|
||||
*
|
||||
@ -129,43 +193,31 @@ public class LeanplumPushService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an object used to customize the appearance of notifications. <p>Call this from your
|
||||
* Sets an object used to customize the appearance of notifications. Call this from your
|
||||
* Application class's onCreate method so that the customizer is set when your application starts
|
||||
* in the background.
|
||||
*
|
||||
* @param customizer LeanplumPushNotificationCustomizer push notification customizer.
|
||||
*/
|
||||
public static void setCustomizer(LeanplumPushNotificationCustomizer customizer) {
|
||||
setCustomizer(customizer, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an object used to customize the appearance of notifications. Call this from your
|
||||
* Application class's onCreate method so that the customizer is set when your application starts
|
||||
* in the background.
|
||||
*
|
||||
* @param customizer LeanplumPushNotificationCustomizer push notification customizer.
|
||||
* @param useNotificationBuilderCustomizer True if if you want to support 2 lines of text on
|
||||
* BigPicture style push notification.
|
||||
*/
|
||||
public static void setCustomizer(LeanplumPushNotificationCustomizer customizer,
|
||||
boolean useNotificationBuilderCustomizer) {
|
||||
LeanplumPushService.customizer = customizer;
|
||||
LeanplumPushService.useNotificationBuilderCustomizer = useNotificationBuilderCustomizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Google Cloud Messaging/Firebase Cloud Messaging sender ID. Required for push
|
||||
* notifications to work.
|
||||
*
|
||||
* @param senderId The GCM/FCM sender ID to permit notifications from. Use {@link
|
||||
* LeanplumPushService#LEANPLUM_SENDER_ID} to use the built-in sender ID for GCM. If you have
|
||||
* multiple sender IDs, use {@link LeanplumPushService#setGcmSenderIds}.
|
||||
*/
|
||||
public static void setGcmSenderId(String senderId) {
|
||||
LeanplumGcmProvider.setSenderId(senderId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Google Cloud Messaging/Firebase Cloud Messaging sender ID. Required for push
|
||||
* notifications to work.
|
||||
*
|
||||
* @param senderIds The GCM/FCM sender IDs to permit notifications from. Use {@link
|
||||
* LeanplumPushService#LEANPLUM_SENDER_ID} to use the built-in sender ID.
|
||||
*/
|
||||
public static void setGcmSenderIds(String... senderIds) {
|
||||
StringBuilder joinedSenderIds = new StringBuilder();
|
||||
for (String senderId : senderIds) {
|
||||
if (joinedSenderIds.length() > 0) {
|
||||
joinedSenderIds.append(',');
|
||||
}
|
||||
joinedSenderIds.append(senderId);
|
||||
}
|
||||
LeanplumGcmProvider.setSenderId(joinedSenderIds.toString());
|
||||
}
|
||||
|
||||
private static Class<? extends Activity> getCallbackClass() {
|
||||
return callbackClass;
|
||||
@ -190,8 +242,8 @@ public class LeanplumPushService {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put(Params.INCLUDE_DEFAULTS, Boolean.toString(false));
|
||||
params.put(Params.INCLUDE_MESSAGE_ID, messageId);
|
||||
Request req = Request.post(Methods.GET_VARS, params);
|
||||
req.onResponse(new Request.ResponseCallback() {
|
||||
RequestOld req = RequestOld.post(Methods.GET_VARS, params);
|
||||
req.onResponse(new RequestOld.ResponseCallback() {
|
||||
@Override
|
||||
public void response(JSONObject response) {
|
||||
try {
|
||||
@ -215,7 +267,7 @@ public class LeanplumPushService {
|
||||
messages = null;
|
||||
}
|
||||
if (values != null || messages != null) {
|
||||
VarCache.applyVariableDiffs(values, messages, null, null, regions, variants);
|
||||
VarCache.applyVariableDiffs(values, messages, null, null, regions, variants, null);
|
||||
}
|
||||
}
|
||||
onComplete.variablesChanged();
|
||||
@ -224,7 +276,7 @@ public class LeanplumPushService {
|
||||
}
|
||||
}
|
||||
});
|
||||
req.onError(new Request.ErrorCallback() {
|
||||
req.onError(new RequestOld.ErrorCallback() {
|
||||
@Override
|
||||
public void error(Exception e) {
|
||||
onComplete.variablesChanged();
|
||||
@ -279,6 +331,8 @@ public class LeanplumPushService {
|
||||
// Leanplum.track("Displayed", 0.0, null, null, requestArgs);
|
||||
|
||||
showNotification(context, message);
|
||||
|
||||
Leanplum.countAggregator().incrementCount("handle_notification");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -309,75 +363,69 @@ public class LeanplumPushService {
|
||||
final NotificationManager notificationManager = (NotificationManager)
|
||||
context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
if (notificationManager == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(context, LeanplumPushReceiver.class);
|
||||
intent.addCategory("lpAction");
|
||||
intent.putExtras(message);
|
||||
PendingIntent contentIntent = PendingIntent.getBroadcast(
|
||||
context.getApplicationContext(), new Random().nextInt(),
|
||||
intent, 0);
|
||||
PendingIntent contentIntent = PendingIntent.getBroadcast(context.getApplicationContext(),
|
||||
new Random().nextInt(), intent, 0);
|
||||
|
||||
String title = Util.getApplicationName(context.getApplicationContext());
|
||||
if (message.getString("title") != null) {
|
||||
title = message.getString("title");
|
||||
}
|
||||
final NotificationCompat.Builder notificationCompatBuilder =
|
||||
LeanplumNotificationHelper.getNotificationCompatBuilder(context, message);
|
||||
NotificationCompat.Builder notificationCompatBuilder = null;
|
||||
Notification.Builder notificationBuilder = null;
|
||||
|
||||
if (notificationCompatBuilder == null) {
|
||||
final String messageText = message.getString(Keys.PUSH_MESSAGE_TEXT);
|
||||
Bitmap bigPicture = LeanplumNotificationHelper.getBigPictureBitmap(context,
|
||||
message.getString(Keys.PUSH_MESSAGE_IMAGE_URL));
|
||||
|
||||
if (customizer != null && !useNotificationBuilderCustomizer) {
|
||||
notificationCompatBuilder = LeanplumNotificationHelper.getNotificationCompatBuilder(context,
|
||||
message, contentIntent, title, messageText, bigPicture, defaultIconId);
|
||||
} else {
|
||||
notificationBuilder = LeanplumNotificationHelper.getNotificationBuilder(context, message,
|
||||
contentIntent, title, messageText, defaultIconId);
|
||||
}
|
||||
|
||||
if ((notificationCompatBuilder == null && notificationBuilder == null) ||
|
||||
(customizer != null & !useNotificationBuilderCustomizer &&
|
||||
notificationCompatBuilder == null) ||
|
||||
(customizer != null && useNotificationBuilderCustomizer && notificationBuilder == null) ||
|
||||
(customizer == null && notificationBuilder == null)) {
|
||||
return;
|
||||
}
|
||||
final String messageText = message.getString(Keys.PUSH_MESSAGE_TEXT);
|
||||
|
||||
if (defaultIconId == 0) {
|
||||
notificationCompatBuilder.setSmallIcon(context.getApplicationInfo().icon);
|
||||
} else {
|
||||
notificationCompatBuilder.setSmallIcon(defaultIconId);
|
||||
}
|
||||
|
||||
notificationCompatBuilder.setContentTitle(title)
|
||||
.setStyle(new NotificationCompat.BigTextStyle()
|
||||
.bigText(messageText))
|
||||
.setContentText(messageText);
|
||||
|
||||
String imageUrl = message.getString(Keys.PUSH_MESSAGE_IMAGE_URL);
|
||||
Notification.Builder notificationBuilder = null;
|
||||
// BigPictureStyle support requires API 16 and higher.
|
||||
if (!TextUtils.isEmpty(imageUrl) && Build.VERSION.SDK_INT >= 16) {
|
||||
Bitmap bigPicture = BitmapUtil.getScaledBitmap(context, imageUrl);
|
||||
if (bigPicture != null) {
|
||||
if ((messageText != null && messageText.length() < MAX_ONE_LINE_TEXT_LENGTH) ||
|
||||
customizer != null) {
|
||||
notificationCompatBuilder.setStyle(new NotificationCompat.BigPictureStyle()
|
||||
.bigPicture(bigPicture)
|
||||
.setBigContentTitle(title)
|
||||
.setSummaryText(messageText));
|
||||
} else {
|
||||
notificationBuilder = LeanplumNotificationHelper.getNotificationBuilder(context, message,
|
||||
contentIntent, title, messageText, bigPicture, defaultIconId);
|
||||
}
|
||||
} else {
|
||||
Log.w(String.format("Image download failed for push notification with big picture. " +
|
||||
"No image will be included with the push notification. Image URL: %s.", imageUrl));
|
||||
}
|
||||
}
|
||||
|
||||
// Try to put a notification on top of the notification area. This method was deprecated in API
|
||||
// level 26. For API level 26 and above we must use setImportance(int) for each notification
|
||||
// channel, not for each notification message.
|
||||
if (Build.VERSION.SDK_INT >= 16 && !BuildUtil.isNotificationChannelSupported(context)) {
|
||||
//noinspection deprecation
|
||||
notificationCompatBuilder.setPriority(Notification.PRIORITY_MAX);
|
||||
}
|
||||
notificationCompatBuilder.setAutoCancel(true);
|
||||
notificationCompatBuilder.setContentIntent(contentIntent);
|
||||
|
||||
if (LeanplumPushService.customizer != null) {
|
||||
if (customizer != null) {
|
||||
try {
|
||||
LeanplumPushService.customizer.customize(notificationCompatBuilder, message);
|
||||
if (useNotificationBuilderCustomizer) {
|
||||
if (bigPicture != null) {
|
||||
Notification.BigPictureStyle bigPictureStyle =
|
||||
LeanplumNotificationHelper.getBigPictureStyle(message, bigPicture, title,
|
||||
messageText);
|
||||
customizer.customize(notificationBuilder, message, bigPictureStyle);
|
||||
LeanplumNotificationHelper.setModifiedBigPictureStyle(notificationBuilder,
|
||||
bigPictureStyle);
|
||||
} else {
|
||||
customizer.customize(notificationBuilder, message, null);
|
||||
}
|
||||
} else {
|
||||
customizer.customize(notificationCompatBuilder, message);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Log.e("Unable to customize push notification: ", Log.getStackTraceString(t));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (bigPicture != null) {
|
||||
Notification.BigPictureStyle bigPictureStyle =
|
||||
LeanplumNotificationHelper.getBigPictureStyle(message, bigPicture, title, messageText);
|
||||
LeanplumNotificationHelper.setModifiedBigPictureStyle(notificationBuilder, bigPictureStyle);
|
||||
}
|
||||
}
|
||||
|
||||
int notificationId = LeanplumPushService.NOTIFICATION_ID;
|
||||
@ -403,13 +451,14 @@ public class LeanplumPushService {
|
||||
JsonConverter.fromJson(message.getString(Keys.PUSH_MESSAGE_ACTION)))) {
|
||||
final int currentNotificationId = notificationId;
|
||||
final Notification.Builder currentNotificationBuilder = notificationBuilder;
|
||||
final NotificationCompat.Builder currentNotificationCompatBuilder = notificationCompatBuilder;
|
||||
Leanplum.forceContentUpdate(new VariablesChangedCallback() {
|
||||
@Override
|
||||
public void variablesChanged() {
|
||||
if (currentNotificationBuilder != null) {
|
||||
notificationManager.notify(currentNotificationId, currentNotificationBuilder.build());
|
||||
} else {
|
||||
notificationManager.notify(currentNotificationId, notificationCompatBuilder.build());
|
||||
notificationManager.notify(currentNotificationId, currentNotificationCompatBuilder.build());
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -426,6 +475,7 @@ public class LeanplumPushService {
|
||||
Log.e("Unable to show push notification.", t);
|
||||
Util.handleException(t);
|
||||
}
|
||||
Leanplum.countAggregator().incrementCount("show_with_title");
|
||||
}
|
||||
|
||||
static void openNotification(Context context, Intent intent) {
|
||||
@ -669,13 +719,16 @@ public class LeanplumPushService {
|
||||
* Stores the registration ID and app versionCode in the application's shared preferences.
|
||||
*/
|
||||
private static void registerInBackground() {
|
||||
Context context = Leanplum.getContext();
|
||||
if (context == null) {
|
||||
Log.e("Failed to register application with GCM/FCM. Your application context is not set.");
|
||||
return;
|
||||
try {
|
||||
Context context = Leanplum.getContext();
|
||||
if (context == null) {
|
||||
Log.e("Failed to register application with GCM/FCM. Your application context is not set.");
|
||||
return;
|
||||
}
|
||||
Intent registerIntent = new Intent(context, LeanplumPushRegistrationService.class);
|
||||
context.startService(registerIntent);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
Intent registerIntent = new Intent(context, LeanplumPushRegistrationService.class);
|
||||
context.startService(registerIntent);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -688,126 +741,57 @@ public class LeanplumPushService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this when Leanplum starts.
|
||||
* Call this when Leanplum starts. This method will call by reflection from AndroidSDKCore.
|
||||
*/
|
||||
static void onStart() {
|
||||
Class leanplumGcmPushServiceClass = null;
|
||||
Class leanplumFcmPushServiceClass = null;
|
||||
|
||||
try {
|
||||
if (Util.hasPlayServices()) {
|
||||
initPushService();
|
||||
} else {
|
||||
Log.i("No valid Google Play Services APK found.");
|
||||
}
|
||||
} catch (LeanplumException e) {
|
||||
Log.e("There was an error registering for push notifications.\n" +
|
||||
Log.getStackTraceString(e));
|
||||
leanplumGcmPushServiceClass = Class.forName(LEANPLUM_PUSH_SERVICE_GCM);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
try {
|
||||
leanplumFcmPushServiceClass = Class.forName(LEANPLUM_PUSH_SERVICE_FCM);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
if (leanplumGcmPushServiceClass != null && leanplumFcmPushServiceClass != null) {
|
||||
Log.e("Leanplum does not support leanplum-gcm and leanplum-fcm library at the " +
|
||||
"same time. To support Leanplum GCM and Location services modify your build.gradle by " +
|
||||
"including only implementation 'com.leanplum:leanplum:+' " +
|
||||
"To support only GCM services, use implementation 'com.leanplum:leanplum-gcm:+' " +
|
||||
"For FCM services include implementation 'com.leanplum:leanplum-fcm:+'" +
|
||||
" If you wish to use Leanplum FCM and Location services you also need to include " +
|
||||
"implementation 'com.leanplum:leanplum-location:+'.");
|
||||
|
||||
} else if (leanplumGcmPushServiceClass != null) {
|
||||
try {
|
||||
leanplumGcmPushServiceClass.getDeclaredMethod("onStart").invoke(null);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
} else if (leanplumFcmPushServiceClass != null) {
|
||||
try {
|
||||
leanplumFcmPushServiceClass.getDeclaredMethod("onStart").invoke(null);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize push service.
|
||||
*/
|
||||
private static void initPushService() {
|
||||
if (!enableGcmServices()) {
|
||||
Log.w("Failed to initialize GCM services.");
|
||||
return;
|
||||
}
|
||||
provider = new LeanplumGcmProvider();
|
||||
|
||||
static void initPushService() {
|
||||
if (!provider.isInitialized() || !provider.isManifestSetup()) {
|
||||
return;
|
||||
}
|
||||
if (hasAppIDChanged(Request.appId())) {
|
||||
if (hasAppIDChanged(RequestOld.appId())) {
|
||||
provider.unregister();
|
||||
}
|
||||
registerInBackground();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables GCM services. By default, all GCM services are disabled.
|
||||
*
|
||||
* @return true if services are successfully enabled, false otherwise
|
||||
*/
|
||||
private static boolean enableGcmServices() {
|
||||
Context context = Leanplum.getContext();
|
||||
if (context == null) {
|
||||
Log.i("Failed to enable FCM services, context is null.");
|
||||
return false;
|
||||
}
|
||||
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
if (packageManager == null) {
|
||||
Log.i("Failed to enable FCM services, PackageManager is null.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Class gcm = LeanplumManifestHelper.getClassForName(LEANPLUM_PUSH_INSTANCE_ID_SERVICE_CLASS);
|
||||
if (gcm == null) {
|
||||
Log.e("Failed to setup GCM, please compile GCM library.");
|
||||
return false;
|
||||
}
|
||||
// We will only enable component once, if we are switching from FCM to GCM, we have to disable
|
||||
// FCM services first.
|
||||
if (!LeanplumManifestHelper.wasComponentEnabled(context, packageManager, gcm)) {
|
||||
// Try to disable FCM first.
|
||||
disableFcmServices();
|
||||
LeanplumManifestHelper.enableComponent(context, packageManager, gcm);
|
||||
|
||||
// Make sure we can find the class before enabling it.
|
||||
Class gcmReceiver = LeanplumManifestHelper.getClassForName(GCM_RECEIVER_CLASS);
|
||||
if (gcmReceiver != null) {
|
||||
LeanplumManifestHelper.enableComponent(context, packageManager, gcmReceiver);
|
||||
}
|
||||
Class pushListener = LeanplumManifestHelper.getClassForName(LEANPLUM_PUSH_LISTENER_SERVICE_CLASS);
|
||||
if (pushListener != null) {
|
||||
LeanplumManifestHelper.enableComponent(context, packageManager, pushListener);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables FCM services.
|
||||
*/
|
||||
private static void disableFcmServices() {
|
||||
Context context = Leanplum.getContext();
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
if (packageManager == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
LeanplumManifestHelper.disableComponent(context, packageManager,
|
||||
LEANPLUM_PUSH_FCM_LISTENER_SERVICE_CLASS);
|
||||
LeanplumManifestHelper.disableComponent(context, packageManager,
|
||||
PUSH_FIREBASE_MESSAGING_SERVICE_CLASS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables GCM services
|
||||
*/
|
||||
private static void disableGcmServices() {
|
||||
Context context = Leanplum.getContext();
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
if (packageManager == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
LeanplumManifestHelper.disableComponent(context, packageManager,
|
||||
LEANPLUM_PUSH_INSTANCE_ID_SERVICE_CLASS);
|
||||
LeanplumManifestHelper.disableComponent(context, packageManager,
|
||||
GCM_RECEIVER_CLASS);
|
||||
LeanplumManifestHelper.disableComponent(context, packageManager,
|
||||
LEANPLUM_PUSH_LISTENER_SERVICE_CLASS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current application id is different from stored one.
|
||||
*
|
||||
@ -837,4 +821,34 @@ public class LeanplumPushService {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show notification that device was registered for push notifications. This method will call by
|
||||
* reflection from AndroidSDKCore.
|
||||
*
|
||||
* @param context The application context.
|
||||
* @param currentContext Current application context.
|
||||
*/
|
||||
static void showDeviceRegistedPush(Context context, Context currentContext) {
|
||||
try {
|
||||
NotificationCompat.Builder builder =
|
||||
LeanplumNotificationHelper.getDefaultCompatNotificationBuilder(context,
|
||||
BuildUtil.isNotificationChannelSupported(context));
|
||||
if (builder == null) {
|
||||
return;
|
||||
}
|
||||
builder.setSmallIcon(android.R.drawable.star_on)
|
||||
.setContentTitle("Leanplum")
|
||||
.setContentText("Your device is registered.");
|
||||
builder.setContentIntent(PendingIntent.getActivity(
|
||||
currentContext.getApplicationContext(), 0, new Intent(), 0));
|
||||
NotificationManager mNotificationManager =
|
||||
(NotificationManager) currentContext.getSystemService(
|
||||
Context.NOTIFICATION_SERVICE);
|
||||
// mId allows you to update the notification later on.
|
||||
mNotificationManager.notify(0, builder.build());
|
||||
} catch (Throwable t) {
|
||||
Log.i("Device is registered.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
24
mobile/android/thirdparty/com/leanplum/LeanplumPushServiceGcm.java
vendored
Normal file
24
mobile/android/thirdparty/com/leanplum/LeanplumPushServiceGcm.java
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
package com.leanplum;
|
||||
|
||||
import com.leanplum.internal.Log;
|
||||
|
||||
/**
|
||||
* Created by anna on 12/15/17.
|
||||
*/
|
||||
|
||||
public class LeanplumPushServiceGcm {
|
||||
/**
|
||||
* Call this when from {@link LeanplumPushService#onStart()} when Leanplum start for initialize
|
||||
* push provider and init push service. This method will call by reflection from AndroidSDKPush.
|
||||
*/
|
||||
static void onStart() {
|
||||
try {
|
||||
LeanplumPushService.setCloudMessagingProvider(new LeanplumGcmProvider());
|
||||
LeanplumPushService.initPushService();
|
||||
} catch (LeanplumException e) {
|
||||
Log.e("There was an error registering for push notifications.\n" +
|
||||
Log.getStackTraceString(e));
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
}
|
@ -26,7 +26,7 @@ import java.util.Set;
|
||||
|
||||
/**
|
||||
* Public interface to LocationManager. This is abstracted away so that the Google Play Services
|
||||
* dependencies are constrained to {@link LocationManagerImplementation}.
|
||||
* dependencies are constrained to LocationManagerImplementation in AndroidSDKLocation.
|
||||
*
|
||||
* @author Andrew First
|
||||
*/
|
||||
|
@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015, Leanplum, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package com.leanplum;
|
||||
|
||||
import com.leanplum.callbacks.InboxChangedCallback;
|
||||
import com.leanplum.callbacks.NewsfeedChangedCallback;
|
||||
|
||||
/**
|
||||
* Newsfeed class.
|
||||
*
|
||||
* @author Aleksandar Gyorev
|
||||
*/
|
||||
public class Newsfeed extends LeanplumInbox {
|
||||
private static Newsfeed instance = new Newsfeed();
|
||||
/**
|
||||
* A private constructor, which prevents any other class from instantiating.
|
||||
*/
|
||||
private Newsfeed() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Static 'getInstance' method.
|
||||
*/
|
||||
static Newsfeed getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a callback for when the newsfeed receives new values from the server.
|
||||
*
|
||||
* @deprecated use {@link #addChangedHandler(InboxChangedCallback)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public void addNewsfeedChangedHandler(NewsfeedChangedCallback handler) {
|
||||
super.addChangedHandler(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a newsfeed changed callback.
|
||||
*
|
||||
* @deprecated use {@link #removeChangedHandler(InboxChangedCallback)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public void removeNewsfeedChangedHandler(NewsfeedChangedCallback handler) {
|
||||
super.removeChangedHandler(handler);
|
||||
}
|
||||
}
|
@ -1,219 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015, Leanplum, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package com.leanplum;
|
||||
|
||||
import com.leanplum.internal.Constants;
|
||||
import com.leanplum.internal.Request;
|
||||
import com.leanplum.internal.Util;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* NewsfeedMessage class.
|
||||
*
|
||||
* @author Aleksandar Gyorev
|
||||
*/
|
||||
public abstract class NewsfeedMessage {
|
||||
private String messageId;
|
||||
private Long deliveryTimestamp;
|
||||
private Long expirationTimestamp;
|
||||
private boolean isRead;
|
||||
private ActionContext context;
|
||||
|
||||
NewsfeedMessage(String messageId, Long deliveryTimestamp, Long expirationTimestamp,
|
||||
boolean isRead, ActionContext context) {
|
||||
this.messageId = messageId;
|
||||
this.deliveryTimestamp = deliveryTimestamp;
|
||||
this.expirationTimestamp = expirationTimestamp;
|
||||
this.isRead = isRead;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
Map<String, Object> toJsonMap() {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put(Constants.Keys.DELIVERY_TIMESTAMP, this.deliveryTimestamp);
|
||||
map.put(Constants.Keys.EXPIRATION_TIMESTAMP, this.expirationTimestamp);
|
||||
map.put(Constants.Keys.MESSAGE_DATA, this.actionArgs());
|
||||
map.put(Constants.Keys.IS_READ, this.isRead());
|
||||
return map;
|
||||
}
|
||||
|
||||
Map<String, Object> actionArgs() {
|
||||
return context.getArgs();
|
||||
}
|
||||
|
||||
void setIsRead(boolean isRead) {
|
||||
this.isRead = isRead;
|
||||
}
|
||||
|
||||
boolean isActive() {
|
||||
if (expirationTimestamp == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Date now = new Date();
|
||||
return now.before(new Date(expirationTimestamp));
|
||||
}
|
||||
|
||||
static boolean isValidMessageId(String messageId) {
|
||||
return messageId.split("##").length == 2;
|
||||
}
|
||||
|
||||
ActionContext getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message identifier of the newsfeed message.
|
||||
*
|
||||
* @deprecated As of release 1.3.0, replaced by {@link #getMessageId()}
|
||||
*/
|
||||
@Deprecated
|
||||
public String messageId() {
|
||||
return getMessageId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message identifier of the newsfeed message.
|
||||
*/
|
||||
public String getMessageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title of the newsfeed message.
|
||||
*
|
||||
* @deprecated As of release 1.3.0, replaced by {@link #getTitle()}
|
||||
*/
|
||||
@Deprecated
|
||||
public String title() {
|
||||
return getTitle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title of the newsfeed message.
|
||||
*/
|
||||
public String getTitle() {
|
||||
return context.stringNamed(Constants.Keys.TITLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subtitle of the newsfeed message.
|
||||
*
|
||||
* @deprecated As of release 1.3.0, replaced by {@link #getSubtitle()}
|
||||
*/
|
||||
@Deprecated
|
||||
public String subtitle() {
|
||||
return getSubtitle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subtitle of the newsfeed message.
|
||||
*/
|
||||
public String getSubtitle() {
|
||||
return context.stringNamed(Constants.Keys.SUBTITLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the delivery timestamp of the newsfeed message.
|
||||
*
|
||||
* @deprecated As of release 1.3.0, replaced by {@link #getDeliveryTimestamp()}
|
||||
*/
|
||||
@Deprecated
|
||||
public Date deliveryTimestamp() {
|
||||
return getDeliveryTimestamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the delivery timestamp of the newsfeed message.
|
||||
*/
|
||||
public Date getDeliveryTimestamp() {
|
||||
return new Date(deliveryTimestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the expiration timestamp of the newsfeed message.
|
||||
*
|
||||
* @deprecated As of release 1.3.0, replaced by {@link #getExpirationTimestamp()}
|
||||
*/
|
||||
@Deprecated
|
||||
public Date expirationTimestamp() {
|
||||
return getExpirationTimestamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the expiration timestamp of the newsfeed message.
|
||||
*/
|
||||
public Date getExpirationTimestamp() {
|
||||
if (expirationTimestamp == null) {
|
||||
return null;
|
||||
}
|
||||
return new Date(expirationTimestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if the newsfeed message is read.
|
||||
*/
|
||||
public boolean isRead() {
|
||||
return isRead;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the newsfeed message, marking it as read and invoking its open action.
|
||||
*/
|
||||
public void read() {
|
||||
try {
|
||||
if (Constants.isNoop()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isRead) {
|
||||
setIsRead(true);
|
||||
|
||||
int unreadCount = Newsfeed.getInstance().unreadCount() - 1;
|
||||
Newsfeed.getInstance().updateUnreadCount(unreadCount);
|
||||
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put(Constants.Params.INBOX_MESSAGE_ID, messageId);
|
||||
Request req = Request.post(Constants.Methods.MARK_INBOX_MESSAGE_AS_READ,
|
||||
params);
|
||||
req.send();
|
||||
}
|
||||
this.context.runTrackedActionNamed(Constants.Values.DEFAULT_PUSH_ACTION);
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the newsfeed message from the newsfeed.
|
||||
*/
|
||||
public void remove() {
|
||||
try {
|
||||
Newsfeed.getInstance().removeMessage(messageId);
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013, Leanplum, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package com.leanplum.activities;
|
||||
|
||||
import android.accounts.AccountAuthenticatorActivity;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import com.leanplum.Leanplum;
|
||||
import com.leanplum.LeanplumActivityHelper;
|
||||
|
||||
/**
|
||||
* @deprecated due to rising minimal API to 14. This class will be removed in a
|
||||
* future major release. Please use {@link LeanplumActivityHelper} to track your activities
|
||||
* automatically.
|
||||
*/
|
||||
@Deprecated
|
||||
public class LeanplumAccountAuthenticatorActivity extends AccountAuthenticatorActivity {
|
||||
private LeanplumActivityHelper helper;
|
||||
|
||||
private LeanplumActivityHelper getHelper() {
|
||||
if (helper == null) {
|
||||
helper = new LeanplumActivityHelper(this);
|
||||
}
|
||||
return helper;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
getHelper().onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
getHelper().onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
getHelper().onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources getResources() {
|
||||
if (Leanplum.isTestModeEnabled() || !Leanplum.isResourceSyncingEnabled()) {
|
||||
return super.getResources();
|
||||
}
|
||||
return getHelper().getLeanplumResources(super.getResources());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(final int layoutResID) {
|
||||
if (Leanplum.isTestModeEnabled() || !Leanplum.isResourceSyncingEnabled()) {
|
||||
super.setContentView(layoutResID);
|
||||
return;
|
||||
}
|
||||
getHelper().setContentView(layoutResID);
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013, Leanplum, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package com.leanplum.activities;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import com.leanplum.Leanplum;
|
||||
import com.leanplum.LeanplumActivityHelper;
|
||||
|
||||
/**
|
||||
* @deprecated due to rising minimal API to 14. This class will be removed in a
|
||||
* future major release. Please use {@link LeanplumActivityHelper} to track your activities
|
||||
* automatically.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class LeanplumActivity extends Activity {
|
||||
private LeanplumActivityHelper helper;
|
||||
|
||||
private LeanplumActivityHelper getHelper() {
|
||||
if (helper == null) {
|
||||
helper = new LeanplumActivityHelper(this);
|
||||
}
|
||||
return helper;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
getHelper().onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
getHelper().onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
getHelper().onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources getResources() {
|
||||
if (Leanplum.isTestModeEnabled() || !Leanplum.isResourceSyncingEnabled()) {
|
||||
return super.getResources();
|
||||
}
|
||||
return getHelper().getLeanplumResources(super.getResources());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(final int layoutResID) {
|
||||
if (Leanplum.isTestModeEnabled() || !Leanplum.isResourceSyncingEnabled()) {
|
||||
super.setContentView(layoutResID);
|
||||
return;
|
||||
}
|
||||
getHelper().setContentView(layoutResID);
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013, Leanplum, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package com.leanplum.activities;
|
||||
|
||||
import android.app.ActivityGroup;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import com.leanplum.Leanplum;
|
||||
import com.leanplum.LeanplumActivityHelper;
|
||||
|
||||
/**
|
||||
* @deprecated due to rising minimal API to 14. This class will be removed in a
|
||||
* future major release. Please use {@link LeanplumActivityHelper} to track your activities
|
||||
* automatically.
|
||||
*/
|
||||
@Deprecated
|
||||
public class LeanplumActivityGroup extends ActivityGroup {
|
||||
private LeanplumActivityHelper helper;
|
||||
|
||||
private LeanplumActivityHelper getHelper() {
|
||||
if (helper == null) {
|
||||
helper = new LeanplumActivityHelper(this);
|
||||
}
|
||||
return helper;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
getHelper().onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
getHelper().onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
getHelper().onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources getResources() {
|
||||
if (Leanplum.isTestModeEnabled() || !Leanplum.isResourceSyncingEnabled()) {
|
||||
return super.getResources();
|
||||
}
|
||||
return getHelper().getLeanplumResources(super.getResources());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(final int layoutResID) {
|
||||
if (Leanplum.isTestModeEnabled() || !Leanplum.isResourceSyncingEnabled()) {
|
||||
super.setContentView(layoutResID);
|
||||
return;
|
||||
}
|
||||
getHelper().setContentView(layoutResID);
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013, Leanplum, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package com.leanplum.activities;
|
||||
|
||||
import android.app.AliasActivity;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import com.leanplum.Leanplum;
|
||||
import com.leanplum.LeanplumActivityHelper;
|
||||
|
||||
/**
|
||||
* @deprecated due to rising minimal API to 14. This class will be removed in a
|
||||
* future major release. Please use {@link LeanplumActivityHelper} to track your activities
|
||||
* automatically.
|
||||
*/
|
||||
@Deprecated
|
||||
public class LeanplumAliasActivity extends AliasActivity {
|
||||
private LeanplumActivityHelper helper;
|
||||
|
||||
private LeanplumActivityHelper getHelper() {
|
||||
if (helper == null) {
|
||||
helper = new LeanplumActivityHelper(this);
|
||||
}
|
||||
return helper;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
getHelper().onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
getHelper().onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
getHelper().onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources getResources() {
|
||||
if (Leanplum.isTestModeEnabled() || !Leanplum.isResourceSyncingEnabled()) {
|
||||
return super.getResources();
|
||||
}
|
||||
return getHelper().getLeanplumResources(super.getResources());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(final int layoutResID) {
|
||||
if (Leanplum.isTestModeEnabled() || !Leanplum.isResourceSyncingEnabled()) {
|
||||
super.setContentView(layoutResID);
|
||||
return;
|
||||
}
|
||||
getHelper().setContentView(layoutResID);
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015, Leanplum, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package com.leanplum.activities;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
|
||||
import com.leanplum.Leanplum;
|
||||
import com.leanplum.LeanplumActivityHelper;
|
||||
|
||||
/**
|
||||
* @deprecated due to rising minimal API to 14. This class will be removed in a
|
||||
* future major release. Please use {@link LeanplumActivityHelper} to track your activities
|
||||
* automatically.
|
||||
*/
|
||||
@Deprecated
|
||||
public class LeanplumAppCompatActivity extends AppCompatActivity {
|
||||
private LeanplumActivityHelper helper;
|
||||
|
||||
private LeanplumActivityHelper getHelper() {
|
||||
if (helper == null) {
|
||||
helper = new LeanplumActivityHelper(this);
|
||||
}
|
||||
return helper;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
getHelper().onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
getHelper().onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
getHelper().onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources getResources() {
|
||||
if (Leanplum.isTestModeEnabled() || !Leanplum.isResourceSyncingEnabled()) {
|
||||
return super.getResources();
|
||||
}
|
||||
return getHelper().getLeanplumResources(super.getResources());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(final int layoutResID) {
|
||||
if (Leanplum.isTestModeEnabled() || !Leanplum.isResourceSyncingEnabled()) {
|
||||
super.setContentView(layoutResID);
|
||||
return;
|
||||
}
|
||||
getHelper().setContentView(layoutResID);
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013, Leanplum, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package com.leanplum.activities;
|
||||
|
||||
import android.app.ExpandableListActivity;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import com.leanplum.Leanplum;
|
||||
import com.leanplum.LeanplumActivityHelper;
|
||||
|
||||
/**
|
||||
* @deprecated due to rising minimal API to 14. This class will be removed in a
|
||||
* future major release. Please use {@link LeanplumActivityHelper} to track your activities
|
||||
* automatically.
|
||||
*/
|
||||
@Deprecated
|
||||
public class LeanplumExpandableListActivity extends ExpandableListActivity {
|
||||
private LeanplumActivityHelper helper;
|
||||
|
||||
private LeanplumActivityHelper getHelper() {
|
||||
if (helper == null) {
|
||||
helper = new LeanplumActivityHelper(this);
|
||||
}
|
||||
return helper;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
getHelper().onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
getHelper().onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
getHelper().onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources getResources() {
|
||||
if (Leanplum.isTestModeEnabled() || !Leanplum.isResourceSyncingEnabled()) {
|
||||
return super.getResources();
|
||||
}
|
||||
return getHelper().getLeanplumResources(super.getResources());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(final int layoutResID) {
|
||||
if (Leanplum.isTestModeEnabled() || !Leanplum.isResourceSyncingEnabled()) {
|
||||
super.setContentView(layoutResID);
|
||||
return;
|
||||
}
|
||||
getHelper().setContentView(layoutResID);
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013, Leanplum, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package com.leanplum.activities;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
|
||||
import com.leanplum.Leanplum;
|
||||
import com.leanplum.LeanplumActivityHelper;
|
||||
|
||||
/**
|
||||
* @deprecated due to rising minimal API to 14. This class will be removed in a
|
||||
* future major release. Please use {@link LeanplumActivityHelper} to track your activities
|
||||
* automatically.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class LeanplumFragmentActivity extends FragmentActivity {
|
||||
private LeanplumActivityHelper helper;
|
||||
|
||||
private LeanplumActivityHelper getHelper() {
|
||||
if (helper == null) {
|
||||
helper = new LeanplumActivityHelper(this);
|
||||
}
|
||||
return helper;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
getHelper().onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
getHelper().onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
getHelper().onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources getResources() {
|
||||
if (Leanplum.isTestModeEnabled() || !Leanplum.isResourceSyncingEnabled()) {
|
||||
return super.getResources();
|
||||
}
|
||||
return getHelper().getLeanplumResources(super.getResources());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(final int layoutResID) {
|
||||
if (Leanplum.isTestModeEnabled() || !Leanplum.isResourceSyncingEnabled()) {
|
||||
super.setContentView(layoutResID);
|
||||
return;
|
||||
}
|
||||
getHelper().setContentView(layoutResID);
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013, Leanplum, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package com.leanplum.activities;
|
||||
|
||||
import android.app.LauncherActivity;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import com.leanplum.Leanplum;
|
||||
import com.leanplum.LeanplumActivityHelper;
|
||||
|
||||
/**
|
||||
* @deprecated due to rising minimal API to 14. This class will be removed in a
|
||||
* future major release. Please use {@link LeanplumActivityHelper} to track your activities
|
||||
* automatically.
|
||||
*/
|
||||
@Deprecated
|
||||
public class LeanplumLauncherActivity extends LauncherActivity {
|
||||
private LeanplumActivityHelper helper;
|
||||
|
||||
private LeanplumActivityHelper getHelper() {
|
||||
if (helper == null) {
|
||||
helper = new LeanplumActivityHelper(this);
|
||||
}
|
||||
return helper;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
getHelper().onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
getHelper().onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
getHelper().onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources getResources() {
|
||||
if (Leanplum.isTestModeEnabled() || !Leanplum.isResourceSyncingEnabled()) {
|
||||
return super.getResources();
|
||||
}
|
||||
return getHelper().getLeanplumResources(super.getResources());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(final int layoutResID) {
|
||||
if (Leanplum.isTestModeEnabled() || !Leanplum.isResourceSyncingEnabled()) {
|
||||
super.setContentView(layoutResID);
|
||||
return;
|
||||
}
|
||||
getHelper().setContentView(layoutResID);
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013, Leanplum, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package com.leanplum.activities;
|
||||
|
||||
import android.app.ListActivity;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import com.leanplum.Leanplum;
|
||||
import com.leanplum.LeanplumActivityHelper;
|
||||
|
||||
/**
|
||||
* @deprecated due to rising minimal API to 14. This class will be removed in a
|
||||
* future major release. Please use {@link LeanplumActivityHelper} to track your activities
|
||||
* automatically.
|
||||
*/
|
||||
@Deprecated
|
||||
public class LeanplumListActivity extends ListActivity {
|
||||
private LeanplumActivityHelper helper;
|
||||
|
||||
private LeanplumActivityHelper getHelper() {
|
||||
if (helper == null) {
|
||||
helper = new LeanplumActivityHelper(this);
|
||||
}
|
||||
return helper;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
getHelper().onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
getHelper().onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
getHelper().onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources getResources() {
|
||||
if (Leanplum.isTestModeEnabled() || !Leanplum.isResourceSyncingEnabled()) {
|
||||
return super.getResources();
|
||||
}
|
||||
return getHelper().getLeanplumResources(super.getResources());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(final int layoutResID) {
|
||||
if (Leanplum.isTestModeEnabled() || !Leanplum.isResourceSyncingEnabled()) {
|
||||
super.setContentView(layoutResID);
|
||||
return;
|
||||
}
|
||||
getHelper().setContentView(layoutResID);
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013, Leanplum, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package com.leanplum.activities;
|
||||
|
||||
import android.app.NativeActivity;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import com.leanplum.Leanplum;
|
||||
import com.leanplum.LeanplumActivityHelper;
|
||||
|
||||
/**
|
||||
* @deprecated due to rising minimal API to 14. This class will be removed in a
|
||||
* future major release. Please use {@link LeanplumActivityHelper} to track your activities
|
||||
* automatically.
|
||||
*/
|
||||
@Deprecated
|
||||
public class LeanplumNativeActivity extends NativeActivity {
|
||||
private LeanplumActivityHelper helper;
|
||||
|
||||
private LeanplumActivityHelper getHelper() {
|
||||
if (helper == null) {
|
||||
helper = new LeanplumActivityHelper(this);
|
||||
}
|
||||
return helper;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
getHelper().onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
getHelper().onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
getHelper().onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources getResources() {
|
||||
if (Leanplum.isTestModeEnabled() || !Leanplum.isResourceSyncingEnabled()) {
|
||||
return super.getResources();
|
||||
}
|
||||
return getHelper().getLeanplumResources(super.getResources());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(final int layoutResID) {
|
||||
if (Leanplum.isTestModeEnabled() || !Leanplum.isResourceSyncingEnabled()) {
|
||||
super.setContentView(layoutResID);
|
||||
return;
|
||||
}
|
||||
getHelper().setContentView(layoutResID);
|
||||
}
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013, Leanplum, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.leanplum.activities;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.preference.PreferenceActivity;
|
||||
|
||||
import com.leanplum.Leanplum;
|
||||
import com.leanplum.LeanplumActivityHelper;
|
||||
|
||||
/**
|
||||
* @deprecated due to rising minimal API to 14. This class will be removed in a
|
||||
* future major release. Please use {@link LeanplumActivityHelper} to track your activities
|
||||
* automatically.
|
||||
*/
|
||||
@Deprecated
|
||||
public class LeanplumPreferenceActivity extends PreferenceActivity {
|
||||
private LeanplumActivityHelper helper;
|
||||
|
||||
private LeanplumActivityHelper getHelper() {
|
||||
if (helper == null) {
|
||||
helper = new LeanplumActivityHelper(this);
|
||||
}
|
||||
return helper;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
getHelper().onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
getHelper().onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
getHelper().onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources getResources() {
|
||||
if (Leanplum.isTestModeEnabled() || !Leanplum.isResourceSyncingEnabled()) {
|
||||
return super.getResources();
|
||||
}
|
||||
return getHelper().getLeanplumResources(super.getResources());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(final int layoutResID) {
|
||||
if (Leanplum.isTestModeEnabled() || !Leanplum.isResourceSyncingEnabled()) {
|
||||
super.setContentView(layoutResID);
|
||||
return;
|
||||
}
|
||||
getHelper().setContentView(layoutResID);
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013, Leanplum, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package com.leanplum.activities;
|
||||
|
||||
import android.app.TabActivity;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import com.leanplum.Leanplum;
|
||||
import com.leanplum.LeanplumActivityHelper;
|
||||
|
||||
/**
|
||||
* @deprecated due to rising minimal API to 14. This class will be removed in a
|
||||
* future major release. Please use {@link LeanplumActivityHelper} to track your activities
|
||||
* automatically.
|
||||
*/
|
||||
@Deprecated
|
||||
public class LeanplumTabActivity extends TabActivity {
|
||||
private LeanplumActivityHelper helper;
|
||||
|
||||
private LeanplumActivityHelper getHelper() {
|
||||
if (helper == null) {
|
||||
helper = new LeanplumActivityHelper(this);
|
||||
}
|
||||
return helper;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
getHelper().onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
getHelper().onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
getHelper().onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources getResources() {
|
||||
if (Leanplum.isTestModeEnabled() || !Leanplum.isResourceSyncingEnabled()) {
|
||||
return super.getResources();
|
||||
}
|
||||
return getHelper().getLeanplumResources(super.getResources());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(final int layoutResID) {
|
||||
if (Leanplum.isTestModeEnabled() || !Leanplum.isResourceSyncingEnabled()) {
|
||||
super.setContentView(layoutResID);
|
||||
return;
|
||||
}
|
||||
getHelper().setContentView(layoutResID);
|
||||
}
|
||||
}
|
@ -1,3 +1,24 @@
|
||||
/*
|
||||
* Copyright 2017, Leanplum, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package com.leanplum.callbacks;
|
||||
|
||||
/**
|
||||
@ -6,20 +27,19 @@ package com.leanplum.callbacks;
|
||||
* @author Anna Orlova
|
||||
*/
|
||||
public abstract class InboxSyncedCallback implements Runnable {
|
||||
private boolean success;
|
||||
private boolean success;
|
||||
|
||||
public void setSuccess(boolean success) {
|
||||
this.success = success;
|
||||
}
|
||||
public void setSuccess(boolean success) {
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
this.onForceContentUpdate(success);
|
||||
}
|
||||
public void run() {
|
||||
this.onForceContentUpdate(success);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call when forceContentUpdate was called.
|
||||
*
|
||||
* @param success True if syncing was successful.
|
||||
*/
|
||||
public abstract void onForceContentUpdate(boolean success);
|
||||
}
|
||||
/**
|
||||
* Call when forceContentUpdate was called.
|
||||
* @param success True if syncing was successful.
|
||||
*/
|
||||
public abstract void onForceContentUpdate(boolean success);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015, Leanplum, Inc. All rights reserved.
|
||||
* Copyright 2013, Leanplum, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
@ -21,16 +21,24 @@
|
||||
|
||||
package com.leanplum.callbacks;
|
||||
|
||||
import com.leanplum.models.MessageArchiveData;
|
||||
|
||||
/**
|
||||
* Newsfeed changed callback.
|
||||
* Message displayed callback.
|
||||
*
|
||||
* @author Aleksandar Gyorev
|
||||
* @author Mayank Sanganeria
|
||||
*/
|
||||
public abstract class NewsfeedChangedCallback extends InboxChangedCallback {
|
||||
@Override
|
||||
public void inboxChanged() {
|
||||
newsfeedChanged();
|
||||
public abstract class MessageDisplayedCallback implements Runnable {
|
||||
|
||||
private MessageArchiveData messageArchiveData;
|
||||
|
||||
public void setMessageArchiveData(MessageArchiveData messageArchiveData) {
|
||||
this.messageArchiveData = messageArchiveData;
|
||||
}
|
||||
|
||||
public abstract void newsfeedChanged();
|
||||
public void run() {
|
||||
this.messageDisplayed(messageArchiveData);
|
||||
}
|
||||
|
||||
public abstract void messageDisplayed(MessageArchiveData messageArchiveData);
|
||||
}
|
@ -21,6 +21,8 @@
|
||||
|
||||
package com.leanplum.callbacks;
|
||||
|
||||
import com.leanplum.Leanplum;
|
||||
|
||||
/**
|
||||
* Callback that gets run when the device needs to be registered.
|
||||
*
|
||||
@ -45,10 +47,10 @@ public abstract class RegisterDeviceCallback implements Runnable {
|
||||
|
||||
public void setResponseHandler(EmailCallback callback) {
|
||||
this.callback = callback;
|
||||
Leanplum.countAggregator().incrementCount("init_with_callback");
|
||||
}
|
||||
|
||||
public void run() {
|
||||
this.onResponse(callback);
|
||||
public void run() { this.onResponse(callback);
|
||||
}
|
||||
|
||||
public abstract void onResponse(EmailCallback callback);
|
||||
|
47
mobile/android/thirdparty/com/leanplum/internal/APIConfig.java
vendored
Normal file
47
mobile/android/thirdparty/com/leanplum/internal/APIConfig.java
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2018, Leanplum, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package com.leanplum.internal;
|
||||
|
||||
public class APIConfig {
|
||||
private final FeatureFlagManager featureFlagManager;
|
||||
private final CountAggregator countAggregator;
|
||||
private String appId;
|
||||
private String accessKey;
|
||||
private String token;
|
||||
|
||||
public APIConfig(FeatureFlagManager featureFlagManager, CountAggregator countAggregator) {
|
||||
this.featureFlagManager = featureFlagManager;
|
||||
this.countAggregator = countAggregator;
|
||||
}
|
||||
|
||||
public void setAppId(String appId, String accessKey) {
|
||||
this.appId = appId;
|
||||
this.accessKey = accessKey;
|
||||
|
||||
this.countAggregator.incrementCount("set_app_id");
|
||||
}
|
||||
|
||||
public void loadToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ package com.leanplum.internal;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.io.InputStream;
|
||||
import com.leanplum.Leanplum;
|
||||
|
||||
/**
|
||||
* Represents an argument for a message or action.
|
||||
@ -46,6 +47,7 @@ public class ActionArg<T> {
|
||||
arg.name = name;
|
||||
arg.kind = kind;
|
||||
arg.defaultValue = defaultValue;
|
||||
Leanplum.countAggregator().incrementCount("arg_named");
|
||||
return arg;
|
||||
}
|
||||
|
||||
|
@ -21,21 +21,17 @@
|
||||
|
||||
package com.leanplum.internal;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import com.leanplum.ActionContext;
|
||||
import com.leanplum.ActionContext.ContextualValues;
|
||||
import com.leanplum.Leanplum;
|
||||
import com.leanplum.LeanplumLocalPushListenerService;
|
||||
import com.leanplum.LocationManager;
|
||||
import com.leanplum.callbacks.ActionCallback;
|
||||
import com.leanplum.utils.SharedPreferencesUtil;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -55,13 +51,17 @@ public class ActionManager {
|
||||
|
||||
public static final String PUSH_NOTIFICATION_ACTION_NAME = "__Push Notification";
|
||||
public static final String HELD_BACK_ACTION_NAME = "__held_back";
|
||||
|
||||
private static final String LEANPLUM_LOCAL_PUSH_HELPER =
|
||||
"com.leanplum.internal.LeanplumLocalPushHelper";
|
||||
private static final String PREFERENCES_NAME = "__leanplum_messaging__";
|
||||
private static LocationManager locationManager;
|
||||
private static boolean loggedLocationManagerFailure = false;
|
||||
|
||||
public static class MessageMatchResult {
|
||||
public boolean matchedTrigger;
|
||||
public boolean matchedUnlessTrigger;
|
||||
public boolean matchedLimit;
|
||||
public boolean matchedActivePeriod;
|
||||
}
|
||||
|
||||
public static synchronized ActionManager getInstance() {
|
||||
@ -71,11 +71,29 @@ public class ActionManager {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private static boolean loggedLocationManagerFailure = false;
|
||||
public static synchronized LocationManager getLocationManager() {
|
||||
if (locationManager != null) {
|
||||
return locationManager;
|
||||
}
|
||||
|
||||
public static LocationManager getLocationManager() {
|
||||
if (Util.hasPlayServices()) {
|
||||
loggedLocationManagerFailure = true;
|
||||
try {
|
||||
// Reflection here prevents linker errors
|
||||
// if Google Play Services is not used in the client app.
|
||||
locationManager = (LocationManager) Class
|
||||
.forName("com.leanplum.LocationManagerImplementation")
|
||||
.getMethod("instance").invoke(null);
|
||||
return locationManager;
|
||||
} catch (Throwable t) {
|
||||
if (!loggedLocationManagerFailure) {
|
||||
Log.w("Geofencing support requires leanplum-location module and Google Play " +
|
||||
"Services v8.1 and higher.\n" +
|
||||
"Add this to your build.gradle file:\n" +
|
||||
"implementation 'com.google.android.gms:play-services-location:8.3.0+'\n" +
|
||||
"implementation 'com.leanplum:leanplum-location:+'");
|
||||
loggedLocationManagerFailure = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -112,91 +130,20 @@ public class ActionManager {
|
||||
return false;
|
||||
}
|
||||
long eta = System.currentTimeMillis() + ((Number) countdownObj).longValue() * 1000L;
|
||||
|
||||
Context context = Leanplum.getContext();
|
||||
Intent intentAlarm = new Intent(context, LeanplumLocalPushListenerService.class);
|
||||
AlarmManager alarmManager = (AlarmManager) context.getSystemService(
|
||||
Context.ALARM_SERVICE);
|
||||
|
||||
// If there's already one scheduled before the eta, discard this.
|
||||
// Otherwise, discard the scheduled one.
|
||||
SharedPreferences preferences = context.getSharedPreferences(
|
||||
PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
long existingEta = preferences.getLong(String.format(
|
||||
Constants.Defaults.LOCAL_NOTIFICATION_KEY, messageId), 0L);
|
||||
if (existingEta > 0L && existingEta > System.currentTimeMillis()) {
|
||||
if (existingEta < eta) {
|
||||
return false;
|
||||
} else if (existingEta >= eta) {
|
||||
PendingIntent existingIntent = PendingIntent.getService(
|
||||
context, messageId.hashCode(), intentAlarm,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
alarmManager.cancel(existingIntent);
|
||||
}
|
||||
}
|
||||
|
||||
// Specify custom data for the notification
|
||||
Map<String, Serializable> data = actionContext.objectNamed("Advanced options.Data");
|
||||
if (data != null) {
|
||||
for (String key : data.keySet()) {
|
||||
intentAlarm.putExtra(key, data.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
// Specify open action
|
||||
String openAction = actionContext.stringNamed(Constants.Values.DEFAULT_PUSH_ACTION);
|
||||
boolean muteInsideApp = Boolean.TRUE.equals(actionContext.objectNamed(
|
||||
"Advanced options.Mute inside app"));
|
||||
if (openAction != null) {
|
||||
if (muteInsideApp) {
|
||||
intentAlarm.putExtra(Constants.Keys.PUSH_MESSAGE_ID_MUTE_WITH_ACTION, messageId);
|
||||
} else {
|
||||
intentAlarm.putExtra(Constants.Keys.PUSH_MESSAGE_ID_NO_MUTE_WITH_ACTION, messageId);
|
||||
}
|
||||
} else {
|
||||
if (muteInsideApp) {
|
||||
intentAlarm.putExtra(Constants.Keys.PUSH_MESSAGE_ID_MUTE, messageId);
|
||||
} else {
|
||||
intentAlarm.putExtra(Constants.Keys.PUSH_MESSAGE_ID_NO_MUTE, messageId);
|
||||
}
|
||||
}
|
||||
|
||||
// Message.
|
||||
String message = actionContext.stringNamed("Message");
|
||||
intentAlarm.putExtra(Constants.Keys.PUSH_MESSAGE_TEXT,
|
||||
message != null ? message : Constants.Values.DEFAULT_PUSH_MESSAGE);
|
||||
|
||||
// Collapse key.
|
||||
String collapseKey = actionContext.stringNamed("Android options.Collapse key");
|
||||
if (collapseKey != null) {
|
||||
intentAlarm.putExtra("collapseKey", collapseKey);
|
||||
}
|
||||
|
||||
// Delay while idle.
|
||||
boolean delayWhileIdle = Boolean.TRUE.equals(actionContext.objectNamed(
|
||||
"Android options.Delay while idle"));
|
||||
if (delayWhileIdle) {
|
||||
intentAlarm.putExtra("delayWhileIdle", true);
|
||||
}
|
||||
|
||||
// Schedule notification.
|
||||
PendingIntent operation = PendingIntent.getService(
|
||||
context, messageId.hashCode(), intentAlarm,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
alarmManager.set(AlarmManager.RTC_WAKEUP, eta, operation);
|
||||
|
||||
// Save notification so we can cancel it later.
|
||||
SharedPreferences.Editor editor = preferences.edit();
|
||||
editor.putLong(String.format(Constants.Defaults.LOCAL_NOTIFICATION_KEY, messageId), eta);
|
||||
SharedPreferencesUtil.commitChanges(editor);
|
||||
|
||||
Log.i("Scheduled notification");
|
||||
return true;
|
||||
try {
|
||||
return (boolean) Class.forName(LEANPLUM_LOCAL_PUSH_HELPER)
|
||||
.getDeclaredMethod("scheduleLocalPush", ActionContext.class, String.class,
|
||||
long.class).invoke(new Object(), actionContext, messageId, eta);
|
||||
} catch (Throwable throwable) {
|
||||
return false;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Leanplum.onAction("__Cancel" + PUSH_NOTIFICATION_ACTION_NAME, new ActionCallback() {
|
||||
@ -209,24 +156,26 @@ public class ActionManager {
|
||||
Context context = Leanplum.getContext();
|
||||
SharedPreferences preferences = context.getSharedPreferences(
|
||||
PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
String preferencesKey = String.format(Constants.Defaults.LOCAL_NOTIFICATION_KEY, messageId);
|
||||
String preferencesKey = String.format(Constants.Defaults.LOCAL_NOTIFICATION_KEY,
|
||||
messageId);
|
||||
long existingEta = preferences.getLong(preferencesKey, 0L);
|
||||
SharedPreferences.Editor editor = preferences.edit();
|
||||
editor.remove(preferencesKey);
|
||||
SharedPreferencesUtil.commitChanges(editor);
|
||||
|
||||
// Cancel notification.
|
||||
Intent intentAlarm = new Intent(context, LeanplumLocalPushListenerService.class);
|
||||
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
||||
PendingIntent existingIntent = PendingIntent.getService(
|
||||
context, messageId.hashCode(), intentAlarm, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
alarmManager.cancel(existingIntent);
|
||||
|
||||
boolean didCancel = existingEta > System.currentTimeMillis();
|
||||
if (didCancel) {
|
||||
Log.i("Cancelled notification");
|
||||
try {
|
||||
Class.forName(LEANPLUM_LOCAL_PUSH_HELPER)
|
||||
.getDeclaredMethod("cancelLocalPush", Context.class, String.class)
|
||||
.invoke(new Object(), context, messageId);
|
||||
boolean didCancel = existingEta > System.currentTimeMillis();
|
||||
if (didCancel) {
|
||||
Log.i("Cancelled notification");
|
||||
}
|
||||
return didCancel;
|
||||
} catch (Throwable throwable) {
|
||||
return false;
|
||||
}
|
||||
return didCancel;
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
return false;
|
||||
@ -260,7 +209,7 @@ public class ActionManager {
|
||||
String.format(Constants.Defaults.MESSAGE_IMPRESSION_OCCURRENCES_KEY, messageId),
|
||||
JsonConverter.toJson(occurrences));
|
||||
messageImpressionOccurrences.put(messageId, occurrences);
|
||||
SharedPreferencesUtil.commitChanges(editor);
|
||||
SharedPreferencesUtil.commitChanges(editor);
|
||||
}
|
||||
|
||||
public int getMessageTriggerOccurrences(String messageId) {
|
||||
@ -304,8 +253,8 @@ public class ActionManager {
|
||||
// 2. Must match at least one trigger.
|
||||
result.matchedTrigger = matchedTriggers(messageConfig.get("whenTriggers"), when, eventName,
|
||||
contextualValues);
|
||||
result.matchedUnlessTrigger = matchedTriggers(messageConfig.get("unlessTriggers"), when, eventName,
|
||||
contextualValues);
|
||||
result.matchedUnlessTrigger =
|
||||
matchedTriggers(messageConfig.get("unlessTriggers"), when, eventName, contextualValues);
|
||||
if (!result.matchedTrigger && !result.matchedUnlessTrigger) {
|
||||
return result;
|
||||
}
|
||||
@ -317,6 +266,18 @@ public class ActionManager {
|
||||
limitConfig = CollectionUtil.uncheckedCast(limitConfigObj);
|
||||
}
|
||||
result.matchedLimit = matchesLimits(messageId, limitConfig);
|
||||
|
||||
// 4. Must be within active period.
|
||||
Object messageStartTime = messageConfig.get("startTime");
|
||||
Object messageEndTime = messageConfig.get("endTime");
|
||||
if (messageStartTime == null || messageEndTime == null) {
|
||||
result.matchedActivePeriod = true;
|
||||
} else {
|
||||
long currentTime = new Date().getTime();
|
||||
result.matchedActivePeriod = currentTime >= (long) messageStartTime &&
|
||||
currentTime <= (long) messageEndTime;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -489,6 +450,7 @@ public class ActionManager {
|
||||
int occurrences = getMessageTriggerOccurrences(messageId);
|
||||
occurrences++;
|
||||
saveMessageTriggerOccurrences(occurrences, messageId);
|
||||
Leanplum.countAggregator().incrementCount("record_message_trigger");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -22,7 +22,6 @@
|
||||
package com.leanplum.internal;
|
||||
|
||||
|
||||
import org.mozilla.gecko.thirdparty_unused.BuildConfig;
|
||||
|
||||
/**
|
||||
* Leanplum constants.
|
||||
@ -39,7 +38,7 @@ public class Constants {
|
||||
public static int NETWORK_TIMEOUT_SECONDS_FOR_DOWNLOADS = 10;
|
||||
static final String LEANPLUM_PACKAGE_IDENTIFIER = "s"; //TODO investigate what this should be
|
||||
|
||||
public static String LEANPLUM_VERSION = "3.0.2";
|
||||
public static String LEANPLUM_VERSION = "4.2.7";
|
||||
public static String CLIENT = "android";
|
||||
|
||||
static final String INVALID_MAC_ADDRESS = "02:00:00:00:00:00";
|
||||
@ -146,12 +145,15 @@ public class Constants {
|
||||
public static final String IAP_CURRENCY_CODE = "currencyCode";
|
||||
public static final String IAP_ITEM = "item";
|
||||
public static final String INCLUDE_DEFAULTS = "includeDefaults";
|
||||
public static final String INCLUDE_VARIANT_DEBUG_INFO = "includeVariantDebugInfo";
|
||||
public static final String INCLUDE_MESSAGE_ID = "includeMessageId";
|
||||
public static final String INFO = "info";
|
||||
public static final String INSTALL_DATE = "installDate";
|
||||
public static final String KINDS = "kinds";
|
||||
public static final String LIMIT_TRACKING = "limitTracking";
|
||||
public static final String MESSAGE = "message";
|
||||
public static final String NAME = "name";
|
||||
public static final String COUNT = "count";
|
||||
public static final String MESSAGE_ID = "messageId";
|
||||
public static final String NEW_USER_ID = "newUserId";
|
||||
public static final String INBOX_MESSAGE_ID = "newsfeedMessageId";
|
||||
@ -202,10 +204,13 @@ public class Constants {
|
||||
public static final String REGION = "region";
|
||||
public static final String REGION_STATE = "regionState";
|
||||
public static final String REGIONS = "regions";
|
||||
public static final String VARIANT_DEBUG_INFO = "variantDebugInfo";
|
||||
public static final String SIZE = "size";
|
||||
public static final String SUBTITLE = "Subtitle";
|
||||
public static final String SYNC_INBOX = "syncNewsfeed";
|
||||
public static final String LOGGING_ENABLED = "loggingEnabled";
|
||||
public static final String ENABLED_COUNTERS = "enabledSdkCounters";
|
||||
public static final String ENABLED_FEATURE_FLAGS = "enabledFeatureFlags";
|
||||
public static final String TIMEZONE = "timezone";
|
||||
public static final String TIMEZONE_OFFSET_SECONDS = "timezoneOffsetSeconds";
|
||||
public static final String TITLE = "Title";
|
||||
@ -250,6 +255,7 @@ public class Constants {
|
||||
public static final String DEFAULT_PUSH_MESSAGE = "Push message goes here.";
|
||||
public static final String SDK_LOG = "sdkLog";
|
||||
public static final String SDK_ERROR = "sdkError";
|
||||
public static final String SDK_COUNT = "sdkCount";
|
||||
public static final String FILE_PREFIX = "__file__";
|
||||
}
|
||||
|
||||
|
71
mobile/android/thirdparty/com/leanplum/internal/CountAggregator.java
vendored
Normal file
71
mobile/android/thirdparty/com/leanplum/internal/CountAggregator.java
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
package com.leanplum.internal;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class CountAggregator {
|
||||
private Set<String> enabledCounters = new HashSet<>();
|
||||
private final Map<String, Integer> counts = new HashMap<>();
|
||||
|
||||
public void setEnabledCounters(Set<String> enabledCounters) {
|
||||
this.enabledCounters = enabledCounters;
|
||||
}
|
||||
|
||||
public void incrementCount(@NonNull String name) {
|
||||
incrementCount(name, 1);
|
||||
}
|
||||
|
||||
public void incrementCount(@NonNull String name, int incrementCount) {
|
||||
if (enabledCounters.contains(name)) {
|
||||
Integer count = 0;
|
||||
if (counts.containsKey(name)) {
|
||||
count = counts.get(name);
|
||||
}
|
||||
count = count + incrementCount;
|
||||
counts.put(name, count);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public Map<String, Integer> getAndClearCounts() {
|
||||
Map<String, Integer> previousCounts = new HashMap<>();
|
||||
previousCounts.putAll(counts);
|
||||
counts.clear();
|
||||
return previousCounts;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public Map<String, Object> makeParams(@NonNull String name, int count) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
|
||||
params.put(Constants.Params.TYPE, Constants.Values.SDK_COUNT);
|
||||
params.put(Constants.Params.NAME, name);
|
||||
params.put(Constants.Params.COUNT, count);
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
public void sendAllCounts() {
|
||||
Map<String, Integer> counts = getAndClearCounts();
|
||||
|
||||
for(Map.Entry<String, Integer> entry : counts.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
Integer count = entry.getValue();
|
||||
Map<String, Object> params = makeParams(name, count);
|
||||
try {
|
||||
RequestOld.post(Constants.Methods.LOG, params).sendEventually();
|
||||
} catch (Throwable t) {
|
||||
android.util.Log.e("Leanplum", "Unable to send count.", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Integer> getCounts() {
|
||||
return counts;
|
||||
}
|
||||
}
|
30
mobile/android/thirdparty/com/leanplum/internal/FeatureFlagManager.java
vendored
Normal file
30
mobile/android/thirdparty/com/leanplum/internal/FeatureFlagManager.java
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
package com.leanplum.internal;
|
||||
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
|
||||
import com.leanplum.Leanplum;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class FeatureFlagManager {
|
||||
public static final FeatureFlagManager INSTANCE = new FeatureFlagManager();
|
||||
|
||||
public static final String FEATURE_FLAG_REQUEST_REFACTOR = "request_refactor";
|
||||
|
||||
private Set<String> enabledFeatureFlags = new HashSet<>();
|
||||
|
||||
@VisibleForTesting
|
||||
FeatureFlagManager() {
|
||||
super();
|
||||
}
|
||||
|
||||
public void setEnabledFeatureFlags(Set<String> enabledFeatureFlags) {
|
||||
this.enabledFeatureFlags = enabledFeatureFlags;
|
||||
}
|
||||
|
||||
public Boolean isFeatureFlagEnabled(String featureFlagName) {
|
||||
Leanplum.countAggregator().incrementCount("is_feature_flag_enabled");
|
||||
return this.enabledFeatureFlags.contains(featureFlagName);
|
||||
}
|
||||
}
|
@ -108,8 +108,8 @@ public class FileManager {
|
||||
if (!FileManager.fileExistsAtPath(realPath)) {
|
||||
realPath = FileManager.fileRelativeToDocuments(stringValue);
|
||||
if (!FileManager.fileExistsAtPath(realPath)) {
|
||||
Request downloadRequest = Request.get(Constants.Methods.DOWNLOAD_FILE, null);
|
||||
downloadRequest.onResponse(new Request.ResponseCallback() {
|
||||
RequestOld downloadRequest = RequestOld.get(Constants.Methods.DOWNLOAD_FILE, null);
|
||||
downloadRequest.onResponse(new RequestOld.ResponseCallback() {
|
||||
@Override
|
||||
public void response(JSONObject response) {
|
||||
if (onComplete != null) {
|
||||
@ -117,7 +117,7 @@ public class FileManager {
|
||||
}
|
||||
}
|
||||
});
|
||||
downloadRequest.onError(new Request.ErrorCallback() {
|
||||
downloadRequest.onError(new RequestOld.ErrorCallback() {
|
||||
@Override
|
||||
public void error(Exception e) {
|
||||
if (onComplete != null) {
|
||||
@ -130,6 +130,7 @@ public class FileManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
Leanplum.countAggregator().incrementCount("maybe_download_file");
|
||||
return DownloadFileResult.NONE;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
package com.leanplum.internal;
|
||||
|
||||
/*
|
||||
* Copyright 2017, Leanplum, Inc. All rights reserved.
|
||||
*
|
||||
@ -20,10 +18,13 @@ package com.leanplum.internal;
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.leanplum.internal;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.leanplum.Leanplum;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.HashMap;
|
||||
@ -36,117 +37,120 @@ import java.util.Map;
|
||||
* @author Anna Orlova
|
||||
*/
|
||||
class LeanplumEventCallbackManager {
|
||||
// Event callbacks map.
|
||||
private final Map<Request, LeanplumEventCallbacks> eventCallbacks = new HashMap<>();
|
||||
// Event callbacks map.
|
||||
private final Map<RequestOld, LeanplumEventCallbacks> eventCallbacks = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Add callbacks to the event callbacks Map.
|
||||
*
|
||||
* @param request Event.
|
||||
* @param responseCallback Response callback.
|
||||
* @param errorCallback Error callback.
|
||||
*/
|
||||
void addCallbacks(Request request, Request.ResponseCallback responseCallback,
|
||||
Request.ErrorCallback errorCallback) {
|
||||
if (request == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (responseCallback == null && errorCallback == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
eventCallbacks.put(request, new LeanplumEventCallbacks(responseCallback, errorCallback));
|
||||
/**
|
||||
* Add callbacks to the event callbacks Map.
|
||||
*
|
||||
* @param request Event.
|
||||
* @param responseCallback Response callback.
|
||||
* @param errorCallback Error callback.
|
||||
*/
|
||||
void addCallbacks(RequestOld request, RequestOld.ResponseCallback responseCallback,
|
||||
RequestOld.ErrorCallback errorCallback) {
|
||||
if (request == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke potential error callbacks for all events with database index less than a count of events
|
||||
* that we got from database.
|
||||
*
|
||||
* @param error Exception.
|
||||
* @param countOfEvents Count of events that we got from database.
|
||||
*/
|
||||
void invokeAllCallbacksWithError(@NonNull final Exception error, int countOfEvents) {
|
||||
if (eventCallbacks.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Iterator<Map.Entry<Request, LeanplumEventCallbacks>> iterator =
|
||||
eventCallbacks.entrySet().iterator();
|
||||
// Loop over all callbacks.
|
||||
for (; iterator.hasNext(); ) {
|
||||
final Map.Entry<Request, LeanplumEventCallbacks> entry = iterator.next();
|
||||
if (entry.getKey() == null) {
|
||||
continue;
|
||||
}
|
||||
if (entry.getKey().getDataBaseIndex() >= countOfEvents) {
|
||||
entry.getKey().setDataBaseIndex(entry.getKey().getDataBaseIndex() - countOfEvents);
|
||||
} else {
|
||||
if (entry.getValue() != null && entry.getValue().errorCallback != null) {
|
||||
// Start callback asynchronously, to avoid creation of new Request object from the same
|
||||
// thread.
|
||||
Util.executeAsyncTask(false, new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
entry.getValue().errorCallback.error(error);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
if (responseCallback == null && errorCallback == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke potential response callbacks for all events with database index less than a count of
|
||||
* events that we got from database.
|
||||
*
|
||||
* @param responseBody JSONObject withs server response.
|
||||
* @param countOfEvents Count of events that we got from database.
|
||||
*/
|
||||
void invokeAllCallbacksForResponse(@NonNull final JSONObject responseBody, int countOfEvents) {
|
||||
if (eventCallbacks.size() == 0) {
|
||||
return;
|
||||
}
|
||||
eventCallbacks.put(request, new LeanplumEventCallbacks(responseCallback, errorCallback));
|
||||
Leanplum.countAggregator().incrementCount("add_event_callback_at");
|
||||
}
|
||||
|
||||
Iterator<Map.Entry<Request, LeanplumEventCallbacks>> iterator =
|
||||
eventCallbacks.entrySet().iterator();
|
||||
// Loop over all callbacks.
|
||||
for (; iterator.hasNext(); ) {
|
||||
final Map.Entry<Request, LeanplumEventCallbacks> entry = iterator.next();
|
||||
if (entry.getKey() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry.getKey().getDataBaseIndex() >= countOfEvents) {
|
||||
entry.getKey().setDataBaseIndex(entry.getKey().getDataBaseIndex() - countOfEvents);
|
||||
} else {
|
||||
if (entry.getValue() != null && entry.getValue().responseCallback != null) {
|
||||
// Start callback asynchronously, to avoid creation of new Request object from the same
|
||||
// thread.
|
||||
Util.executeAsyncTask(false, new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
entry.getValue().responseCallback.response(Request.getResponseAt(responseBody,
|
||||
(int) entry.getKey().getDataBaseIndex()));
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Invoke potential error callbacks for all events with database index less than a count of events
|
||||
* that we got from database.
|
||||
*
|
||||
* @param error Exception.
|
||||
* @param countOfEvents Count of events that we got from database.
|
||||
*/
|
||||
void invokeAllCallbacksWithError(@NonNull final Exception error, int countOfEvents) {
|
||||
if (eventCallbacks.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
private static class LeanplumEventCallbacks {
|
||||
private Request.ResponseCallback responseCallback;
|
||||
private Request.ErrorCallback errorCallback;
|
||||
|
||||
LeanplumEventCallbacks(Request.ResponseCallback responseCallback, Request.ErrorCallback
|
||||
errorCallback) {
|
||||
this.responseCallback = responseCallback;
|
||||
this.errorCallback = errorCallback;
|
||||
Iterator<Map.Entry<RequestOld, LeanplumEventCallbacks>> iterator =
|
||||
eventCallbacks.entrySet().iterator();
|
||||
// Loop over all callbacks.
|
||||
for (; iterator.hasNext(); ) {
|
||||
final Map.Entry<RequestOld, LeanplumEventCallbacks> entry = iterator.next();
|
||||
if (entry.getKey() == null) {
|
||||
continue;
|
||||
}
|
||||
if (entry.getKey().getDataBaseIndex() >= countOfEvents) {
|
||||
entry.getKey().setDataBaseIndex(entry.getKey().getDataBaseIndex() - countOfEvents);
|
||||
} else {
|
||||
if (entry.getValue() != null && entry.getValue().errorCallback != null) {
|
||||
// Start callback asynchronously, to avoid creation of new RequestOld object from the same
|
||||
// thread.
|
||||
Util.executeAsyncTask(false, new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
entry.getValue().errorCallback.error(error);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
Leanplum.countAggregator().incrementCount("invoke_error_callbacks_on_responses");
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke potential response callbacks for all events with database index less than a count of
|
||||
* events that we got from database.
|
||||
*
|
||||
* @param responseBody JSONObject withs server response.
|
||||
* @param countOfEvents Count of events that we got from database.
|
||||
*/
|
||||
void invokeAllCallbacksForResponse(@NonNull final JSONObject responseBody, int countOfEvents) {
|
||||
if (eventCallbacks.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Iterator<Map.Entry<RequestOld, LeanplumEventCallbacks>> iterator =
|
||||
eventCallbacks.entrySet().iterator();
|
||||
// Loop over all callbacks.
|
||||
for (; iterator.hasNext(); ) {
|
||||
final Map.Entry<RequestOld, LeanplumEventCallbacks> entry = iterator.next();
|
||||
if (entry.getKey() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry.getKey().getDataBaseIndex() >= countOfEvents) {
|
||||
entry.getKey().setDataBaseIndex(entry.getKey().getDataBaseIndex() - countOfEvents);
|
||||
} else {
|
||||
if (entry.getValue() != null && entry.getValue().responseCallback != null) {
|
||||
// Start callback asynchronously, to avoid creation of new RequestOld object from the same
|
||||
// thread.
|
||||
Util.executeAsyncTask(false, new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
entry.getValue().responseCallback.response(RequestOld.getResponseAt(responseBody,
|
||||
(int) entry.getKey().getDataBaseIndex()));
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
Leanplum.countAggregator().incrementCount("invoke_success_callbacks_on_responses");
|
||||
}
|
||||
|
||||
private static class LeanplumEventCallbacks {
|
||||
private RequestOld.ResponseCallback responseCallback;
|
||||
private RequestOld.ErrorCallback errorCallback;
|
||||
|
||||
LeanplumEventCallbacks(RequestOld.ResponseCallback responseCallback, RequestOld.ErrorCallback
|
||||
errorCallback) {
|
||||
this.responseCallback = responseCallback;
|
||||
this.errorCallback = errorCallback;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
package com.leanplum.internal;
|
||||
|
||||
/*
|
||||
* Copyright 2017, Leanplum, Inc. All rights reserved.
|
||||
*
|
||||
@ -21,6 +19,8 @@ package com.leanplum.internal;
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package com.leanplum.internal;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
@ -48,212 +48,215 @@ import java.util.UUID;
|
||||
* @author Anna Orlova
|
||||
*/
|
||||
public class LeanplumEventDataManager {
|
||||
private static final String DATABASE_NAME = "__leanplum.db";
|
||||
private static final int DATABASE_VERSION = 1;
|
||||
private static final String EVENT_TABLE_NAME = "event";
|
||||
private static final String COLUMN_DATA = "data";
|
||||
private static final String KEY_ROWID = "rowid";
|
||||
private static final String DATABASE_NAME = "__leanplum.db";
|
||||
private static final int DATABASE_VERSION = 1;
|
||||
private static final String EVENT_TABLE_NAME = "event";
|
||||
private static final String COLUMN_DATA = "data";
|
||||
private static final String KEY_ROWID = "rowid";
|
||||
|
||||
private static SQLiteDatabase database;
|
||||
private static LeanplumDataBaseManager databaseManager;
|
||||
private static ContentValues contentValues = new ContentValues();
|
||||
private static SQLiteDatabase database;
|
||||
private static LeanplumDataBaseManager databaseManager;
|
||||
private static ContentValues contentValues = new ContentValues();
|
||||
|
||||
static boolean willSendErrorLog = false;
|
||||
static boolean willSendErrorLog = false;
|
||||
|
||||
/**
|
||||
* Creates connection to database, if database is not present, it will automatically create it.
|
||||
*
|
||||
* @param context Current context.
|
||||
*/
|
||||
public static void init(Context context) {
|
||||
if (database != null) {
|
||||
Log.e("Database is already initialized.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create database if needed.
|
||||
try {
|
||||
if (databaseManager == null) {
|
||||
databaseManager = new LeanplumDataBaseManager(context);
|
||||
}
|
||||
database = databaseManager.getWritableDatabase();
|
||||
} catch (Throwable t) {
|
||||
handleSQLiteError("Cannot create database.", t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts event to event table.
|
||||
*
|
||||
* @param event String with json of event.
|
||||
*/
|
||||
static void insertEvent(String event) {
|
||||
if (database == null) {
|
||||
return;
|
||||
}
|
||||
contentValues.put(COLUMN_DATA, event);
|
||||
try {
|
||||
database.insert(EVENT_TABLE_NAME, null, contentValues);
|
||||
willSendErrorLog = false;
|
||||
} catch (Throwable t) {
|
||||
handleSQLiteError("Unable to insert event to database.", t);
|
||||
}
|
||||
contentValues.clear();
|
||||
Leanplum.countAggregator().incrementCount("add_event");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets first count events from event table.
|
||||
*
|
||||
* @param count Number of events.
|
||||
* @return List of events.
|
||||
*/
|
||||
static List<Map<String, Object>> getEvents(int count) {
|
||||
List<Map<String, Object>> events = new ArrayList<>();
|
||||
if (database == null) {
|
||||
return events;
|
||||
}
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = database.query(EVENT_TABLE_NAME, new String[] {COLUMN_DATA}, null, null, null,
|
||||
null, KEY_ROWID + " ASC", "" + count);
|
||||
willSendErrorLog = false;
|
||||
while (cursor.moveToNext()) {
|
||||
Map<String, Object> requestArgs = JsonConverter.mapFromJson(new JSONObject(
|
||||
cursor.getString(cursor.getColumnIndex(COLUMN_DATA))));
|
||||
events.add(requestArgs);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
handleSQLiteError("Unable to get events from the table.", t);
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
Leanplum.countAggregator().incrementCount("events_with_limit");
|
||||
return events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes first count elements from event table.
|
||||
*
|
||||
* @param count Number of event that need to be deleted.
|
||||
*/
|
||||
static void deleteEvents(int count) {
|
||||
if (database == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
database.delete(EVENT_TABLE_NAME, KEY_ROWID + " in (select " + KEY_ROWID + " from " +
|
||||
EVENT_TABLE_NAME + " ORDER BY " + KEY_ROWID + " ASC LIMIT " + count + ")", null);
|
||||
willSendErrorLog = false;
|
||||
} catch (Throwable t) {
|
||||
handleSQLiteError("Unable to delete events from the table.", t);
|
||||
}
|
||||
Leanplum.countAggregator().incrementCount("delete_events_with_limit");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets number of rows in the event table.
|
||||
*
|
||||
* @return Number of rows in the event table.
|
||||
*/
|
||||
static long getEventsCount() {
|
||||
long count = 0;
|
||||
if (database == null) {
|
||||
return count;
|
||||
}
|
||||
try {
|
||||
count = DatabaseUtils.queryNumEntries(database, EVENT_TABLE_NAME);
|
||||
willSendErrorLog = false;
|
||||
} catch (Throwable t) {
|
||||
handleSQLiteError("Unable to get a number of rows in the table.", t);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that logs and sends errors to the server.
|
||||
*/
|
||||
private static void handleSQLiteError(String log, Throwable t) {
|
||||
Log.e(log, t);
|
||||
// Send error log. Using willSendErrorLog to prevent infinte loop.
|
||||
if (!willSendErrorLog) {
|
||||
willSendErrorLog = true;
|
||||
Util.handleException(t);
|
||||
}
|
||||
}
|
||||
|
||||
private static class LeanplumDataBaseManager extends SQLiteOpenHelper {
|
||||
LeanplumDataBaseManager(Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
// Create event table.
|
||||
db.execSQL("CREATE TABLE IF NOT EXISTS " + EVENT_TABLE_NAME + "(" + COLUMN_DATA +
|
||||
" TEXT)");
|
||||
|
||||
// Migrate old data from shared preferences.
|
||||
try {
|
||||
migrateFromSharedPreferences(db);
|
||||
} catch (Throwable t) {
|
||||
Log.e("Cannot move old data from shared preferences to SQLite table.", t);
|
||||
Util.handleException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
// No used for now.
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates connection to database, if database is not present, it will automatically create it.
|
||||
*
|
||||
* @param context Current context.
|
||||
* Migrate data from shared preferences to SQLite.
|
||||
*/
|
||||
public static void init(Context context) {
|
||||
if (database != null) {
|
||||
Log.e("Database is already initialized.");
|
||||
return;
|
||||
private static void migrateFromSharedPreferences(SQLiteDatabase db) {
|
||||
synchronized (RequestOld.class) {
|
||||
Context context = Leanplum.getContext();
|
||||
SharedPreferences preferences = context.getSharedPreferences(
|
||||
RequestOld.LEANPLUM, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = preferences.edit();
|
||||
int count = preferences.getInt(Constants.Defaults.COUNT_KEY, 0);
|
||||
if (count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create database if needed.
|
||||
List<Map<String, Object>> requestData = new ArrayList<>();
|
||||
for (int i = 0; i < count; i++) {
|
||||
String itemKey = String.format(Locale.US, Constants.Defaults.ITEM_KEY, i);
|
||||
Map<String, Object> requestArgs;
|
||||
try {
|
||||
requestArgs = JsonConverter.mapFromJson(new JSONObject(
|
||||
preferences.getString(itemKey, "{}")));
|
||||
requestData.add(requestArgs);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
editor.remove(itemKey);
|
||||
}
|
||||
|
||||
editor.remove(Constants.Defaults.COUNT_KEY);
|
||||
|
||||
try {
|
||||
if (databaseManager == null) {
|
||||
databaseManager = new LeanplumDataBaseManager(context);
|
||||
}
|
||||
database = databaseManager.getWritableDatabase();
|
||||
String uuid = preferences.getString(Constants.Defaults.UUID_KEY, null);
|
||||
if (uuid == null || count % RequestOld.MAX_EVENTS_PER_API_CALL == 0) {
|
||||
uuid = UUID.randomUUID().toString();
|
||||
editor.putString(Constants.Defaults.UUID_KEY, uuid);
|
||||
}
|
||||
for (Map<String, Object> event : requestData) {
|
||||
event.put(RequestOld.UUID_KEY, uuid);
|
||||
contentValues.put(COLUMN_DATA, JsonConverter.toJson(event));
|
||||
db.insert(EVENT_TABLE_NAME, null, contentValues);
|
||||
contentValues.clear();
|
||||
}
|
||||
SharedPreferencesUtil.commitChanges(editor);
|
||||
} catch (Throwable t) {
|
||||
handleSQLiteError("Cannot create database.", t);
|
||||
Log.e("Failed on migration data from shared preferences.", t);
|
||||
Util.handleException(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts event to event table.
|
||||
*
|
||||
* @param event String with json of event.
|
||||
*/
|
||||
static void insertEvent(String event) {
|
||||
if (database == null) {
|
||||
return;
|
||||
}
|
||||
contentValues.put(COLUMN_DATA, event);
|
||||
try {
|
||||
database.insert(EVENT_TABLE_NAME, null, contentValues);
|
||||
willSendErrorLog = false;
|
||||
} catch (Throwable t) {
|
||||
handleSQLiteError("Unable to insert event to database.", t);
|
||||
}
|
||||
contentValues.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets first count events from event table.
|
||||
*
|
||||
* @param count Number of events.
|
||||
* @return List of events.
|
||||
*/
|
||||
static List<Map<String, Object>> getEvents(int count) {
|
||||
List<Map<String, Object>> events = new ArrayList<>();
|
||||
if (database == null) {
|
||||
return events;
|
||||
}
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = database.query(EVENT_TABLE_NAME, new String[]{COLUMN_DATA}, null, null, null,
|
||||
null, KEY_ROWID + " ASC", "" + count);
|
||||
willSendErrorLog = false;
|
||||
while (cursor.moveToNext()) {
|
||||
Map<String, Object> requestArgs = JsonConverter.mapFromJson(new JSONObject(
|
||||
cursor.getString(cursor.getColumnIndex(COLUMN_DATA))));
|
||||
events.add(requestArgs);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
handleSQLiteError("Unable to get events from the table.", t);
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes first count elements from event table.
|
||||
*
|
||||
* @param count Number of event that need to be deleted.
|
||||
*/
|
||||
static void deleteEvents(int count) {
|
||||
if (database == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
database.delete(EVENT_TABLE_NAME, KEY_ROWID + " in (select " + KEY_ROWID + " from " +
|
||||
EVENT_TABLE_NAME + " ORDER BY " + KEY_ROWID + " ASC LIMIT " + count + ")", null);
|
||||
willSendErrorLog = false;
|
||||
} catch (Throwable t) {
|
||||
handleSQLiteError("Unable to delete events from the table.", t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets number of rows in the event table.
|
||||
*
|
||||
* @return Number of rows in the event table.
|
||||
*/
|
||||
static long getEventsCount() {
|
||||
long count = 0;
|
||||
if (database == null) {
|
||||
return count;
|
||||
}
|
||||
try {
|
||||
count = DatabaseUtils.queryNumEntries(database, EVENT_TABLE_NAME);
|
||||
willSendErrorLog = false;
|
||||
} catch (Throwable t) {
|
||||
handleSQLiteError("Unable to get a number of rows in the table.", t);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that logs and sends errors to the server.
|
||||
*/
|
||||
private static void handleSQLiteError(String log, Throwable t) {
|
||||
Log.e(log, t);
|
||||
// Send error log. Using willSendErrorLog to prevent infinte loop.
|
||||
if (!willSendErrorLog) {
|
||||
willSendErrorLog = true;
|
||||
Util.handleException(t);
|
||||
}
|
||||
}
|
||||
|
||||
private static class LeanplumDataBaseManager extends SQLiteOpenHelper {
|
||||
LeanplumDataBaseManager(Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
// Create event table.
|
||||
db.execSQL("CREATE TABLE IF NOT EXISTS " + EVENT_TABLE_NAME + "(" + COLUMN_DATA +
|
||||
" TEXT)");
|
||||
|
||||
// Migrate old data from shared preferences.
|
||||
try {
|
||||
migrateFromSharedPreferences(db);
|
||||
} catch (Throwable t) {
|
||||
Log.e("Cannot move old data from shared preferences to SQLite table.", t);
|
||||
Util.handleException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
// No used for now.
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate data from shared preferences to SQLite.
|
||||
*/
|
||||
private static void migrateFromSharedPreferences(SQLiteDatabase db) {
|
||||
synchronized (Request.class) {
|
||||
Context context = Leanplum.getContext();
|
||||
SharedPreferences preferences = context.getSharedPreferences(
|
||||
Request.LEANPLUM, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = preferences.edit();
|
||||
int count = preferences.getInt(Constants.Defaults.COUNT_KEY, 0);
|
||||
if (count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<Map<String, Object>> requestData = new ArrayList<>();
|
||||
for (int i = 0; i < count; i++) {
|
||||
String itemKey = String.format(Locale.US, Constants.Defaults.ITEM_KEY, i);
|
||||
Map<String, Object> requestArgs;
|
||||
try {
|
||||
requestArgs = JsonConverter.mapFromJson(new JSONObject(
|
||||
preferences.getString(itemKey, "{}")));
|
||||
requestData.add(requestArgs);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
editor.remove(itemKey);
|
||||
}
|
||||
|
||||
editor.remove(Constants.Defaults.COUNT_KEY);
|
||||
|
||||
try {
|
||||
String uuid = preferences.getString(Constants.Defaults.UUID_KEY, null);
|
||||
if (uuid == null || count % Request.MAX_EVENTS_PER_API_CALL == 0) {
|
||||
uuid = UUID.randomUUID().toString();
|
||||
editor.putString(Constants.Defaults.UUID_KEY, uuid);
|
||||
}
|
||||
for (Map<String, Object> event : requestData) {
|
||||
event.put(Request.UUID_KEY, uuid);
|
||||
contentValues.put(COLUMN_DATA, JsonConverter.toJson(event));
|
||||
db.insert(EVENT_TABLE_NAME, null, contentValues);
|
||||
contentValues.clear();
|
||||
}
|
||||
SharedPreferencesUtil.commitChanges(editor);
|
||||
} catch (Throwable t) {
|
||||
Log.e("Failed on migration data from shared preferences.", t);
|
||||
Util.handleException(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -73,6 +73,7 @@ public class LeanplumInternal {
|
||||
private static final Queue<Map<String, ?>> userAttributeChanges = new ConcurrentLinkedQueue<>();
|
||||
private static final ArrayList<Runnable> startIssuedHandlers = new ArrayList<>();
|
||||
private static boolean isScreenTrackingEnabled = false;
|
||||
private static boolean isVariantDebugInfoEnabled = false;
|
||||
|
||||
private static void onHasStartedAndRegisteredAsDeveloperAndFinishedSyncing() {
|
||||
if (!hasStartedAndRegisteredAsDeveloper) {
|
||||
@ -183,6 +184,7 @@ public class LeanplumInternal {
|
||||
result.matchedTrigger |= conditionResult.matchedTrigger;
|
||||
result.matchedUnlessTrigger |= conditionResult.matchedUnlessTrigger;
|
||||
result.matchedLimit |= conditionResult.matchedLimit;
|
||||
result.matchedActivePeriod |= conditionResult.matchedActivePeriod;
|
||||
}
|
||||
|
||||
// Make sure we cancel before matching in case the criteria overlap.
|
||||
@ -205,6 +207,11 @@ public class LeanplumInternal {
|
||||
});
|
||||
}
|
||||
|
||||
// Make sure message is within the active period.
|
||||
if(!result.matchedActivePeriod){
|
||||
continue;
|
||||
}
|
||||
|
||||
if (result.matchedTrigger) {
|
||||
ActionManager.getInstance().recordMessageTrigger(internalMessageId);
|
||||
|
||||
@ -252,7 +259,7 @@ public class LeanplumInternal {
|
||||
@Override
|
||||
public void variablesChanged() {
|
||||
try {
|
||||
ActionManager.getInstance().recordMessageImpression(actionContext.getMessageId());
|
||||
Leanplum.triggerMessageDisplayed(actionContext);
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
}
|
||||
@ -328,7 +335,7 @@ public class LeanplumInternal {
|
||||
*/
|
||||
private static void trackInternal(String event, Map<String, ?> params,
|
||||
Map<String, Object> requestArgs) {
|
||||
Request.post(Constants.Methods.TRACK, requestArgs).send();
|
||||
RequestOld.post(Constants.Methods.TRACK, requestArgs).send();
|
||||
|
||||
String eventTriggerName = event;
|
||||
String messageId = null;
|
||||
@ -426,14 +433,14 @@ public class LeanplumInternal {
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
Request req = Request.post(Constants.Methods.SET_USER_ATTRIBUTES, params);
|
||||
req.onResponse(new Request.ResponseCallback() {
|
||||
RequestOld req = RequestOld.post(Constants.Methods.SET_USER_ATTRIBUTES, params);
|
||||
req.onResponse(new RequestOld.ResponseCallback() {
|
||||
@Override
|
||||
public void response(JSONObject response) {
|
||||
callback.response(true);
|
||||
}
|
||||
});
|
||||
req.onError(new Request.ErrorCallback() {
|
||||
req.onError(new RequestOld.ErrorCallback() {
|
||||
@Override
|
||||
public void error(Exception e) {
|
||||
callback.response(false);
|
||||
@ -667,6 +674,14 @@ public class LeanplumInternal {
|
||||
return isScreenTrackingEnabled;
|
||||
}
|
||||
|
||||
public static boolean getIsVariantDebugInfoEnabled() {
|
||||
return isVariantDebugInfoEnabled;
|
||||
}
|
||||
|
||||
public static void setIsVariantDebugInfoEnabled(boolean isVariantDebugInfoEnabled) {
|
||||
LeanplumInternal.isVariantDebugInfoEnabled = isVariantDebugInfoEnabled;
|
||||
}
|
||||
|
||||
public static void enableAutomaticScreenTracking() {
|
||||
isScreenTrackingEnabled = true;
|
||||
}
|
||||
|
159
mobile/android/thirdparty/com/leanplum/internal/LeanplumLocalPushHelper.java
vendored
Normal file
159
mobile/android/thirdparty/com/leanplum/internal/LeanplumLocalPushHelper.java
vendored
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2018, Leanplum, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package com.leanplum.internal;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import com.leanplum.ActionContext;
|
||||
import com.leanplum.Leanplum;
|
||||
import com.leanplum.LeanplumLocalPushListenerService;
|
||||
import com.leanplum.utils.SharedPreferencesUtil;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Leanplum local push notification helper class.
|
||||
*
|
||||
* @author Anna Orlova
|
||||
*/
|
||||
class LeanplumLocalPushHelper {
|
||||
private static final String PREFERENCES_NAME = "__leanplum_messaging__";
|
||||
|
||||
/**
|
||||
* Schedule local push notification. This method will call by reflection from AndroidSDKCore.
|
||||
*
|
||||
* @param actionContext Action Context.
|
||||
* @param messageId String message id for local push notification.
|
||||
* @param eta Eta for local push notification.
|
||||
* @return True if notification was scheduled.
|
||||
*/
|
||||
static boolean scheduleLocalPush(ActionContext actionContext, String messageId, long eta) {
|
||||
try {
|
||||
Context context = Leanplum.getContext();
|
||||
Intent intentAlarm = new Intent(context, LeanplumLocalPushListenerService.class);
|
||||
AlarmManager alarmManager = (AlarmManager) context.getSystemService(
|
||||
Context.ALARM_SERVICE);
|
||||
|
||||
// If there's already one scheduled before the eta, discard this.
|
||||
// Otherwise, discard the scheduled one.
|
||||
SharedPreferences preferences = context.getSharedPreferences(
|
||||
PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
long existingEta = preferences.getLong(String.format(
|
||||
Constants.Defaults.LOCAL_NOTIFICATION_KEY, messageId), 0L);
|
||||
if (existingEta > 0L && existingEta > System.currentTimeMillis()) {
|
||||
if (existingEta < eta) {
|
||||
return false;
|
||||
} else if (existingEta >= eta) {
|
||||
PendingIntent existingIntent = PendingIntent.getService(
|
||||
context, messageId.hashCode(), intentAlarm,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
alarmManager.cancel(existingIntent);
|
||||
}
|
||||
}
|
||||
|
||||
// Specify custom data for the notification
|
||||
Map<String, Serializable> data = actionContext.objectNamed("Advanced options.Data");
|
||||
if (data != null) {
|
||||
for (String key : data.keySet()) {
|
||||
intentAlarm.putExtra(key, data.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
// Specify open action
|
||||
String openAction = actionContext.stringNamed(Constants.Values.DEFAULT_PUSH_ACTION);
|
||||
boolean muteInsideApp = Boolean.TRUE.equals(actionContext.objectNamed(
|
||||
"Advanced options.Mute inside app"));
|
||||
if (openAction != null) {
|
||||
if (muteInsideApp) {
|
||||
intentAlarm.putExtra(Constants.Keys.PUSH_MESSAGE_ID_MUTE_WITH_ACTION, messageId);
|
||||
} else {
|
||||
intentAlarm.putExtra(Constants.Keys.PUSH_MESSAGE_ID_NO_MUTE_WITH_ACTION, messageId);
|
||||
}
|
||||
} else {
|
||||
if (muteInsideApp) {
|
||||
intentAlarm.putExtra(Constants.Keys.PUSH_MESSAGE_ID_MUTE, messageId);
|
||||
} else {
|
||||
intentAlarm.putExtra(Constants.Keys.PUSH_MESSAGE_ID_NO_MUTE, messageId);
|
||||
}
|
||||
}
|
||||
|
||||
// Message.
|
||||
String message = actionContext.stringNamed("Message");
|
||||
intentAlarm.putExtra(Constants.Keys.PUSH_MESSAGE_TEXT,
|
||||
message != null ? message : Constants.Values.DEFAULT_PUSH_MESSAGE);
|
||||
|
||||
// Collapse key.
|
||||
String collapseKey = actionContext.stringNamed("Android options.Collapse key");
|
||||
if (collapseKey != null) {
|
||||
intentAlarm.putExtra("collapseKey", collapseKey);
|
||||
}
|
||||
|
||||
// Delay while idle.
|
||||
boolean delayWhileIdle = Boolean.TRUE.equals(actionContext.objectNamed(
|
||||
"Android options.Delay while idle"));
|
||||
if (delayWhileIdle) {
|
||||
intentAlarm.putExtra("delayWhileIdle", true);
|
||||
}
|
||||
|
||||
// Schedule notification.
|
||||
PendingIntent operation = PendingIntent.getService(
|
||||
context, messageId.hashCode(), intentAlarm,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
alarmManager.set(AlarmManager.RTC_WAKEUP, eta, operation);
|
||||
|
||||
// Save notification so we can cancel it later.
|
||||
SharedPreferences.Editor editor = preferences.edit();
|
||||
editor.putLong(String.format(Constants.Defaults.LOCAL_NOTIFICATION_KEY, messageId), eta);
|
||||
SharedPreferencesUtil.commitChanges(editor);
|
||||
|
||||
Log.i("Scheduled notification.");
|
||||
return true;
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel local push notification. This method will call by reflection from AndroidSDKCore.
|
||||
*
|
||||
* @param context The application context.
|
||||
* @param messageId Message id of notification that should be canceled.
|
||||
*/
|
||||
static void cancelLocalPush(Context context, String messageId) {
|
||||
try {
|
||||
Intent intentAlarm = new Intent(context, LeanplumLocalPushListenerService.class);
|
||||
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
||||
PendingIntent existingIntent = PendingIntent.getService(
|
||||
context, messageId.hashCode(), intentAlarm, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
if (alarmManager != null && existingIntent != null) {
|
||||
alarmManager.cancel(existingIntent);
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
}
|
@ -1,160 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017, Leanplum, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package com.leanplum.internal;
|
||||
|
||||
/**
|
||||
* LeanplumManifestParser class for get AndroidManifest.xml. http://stackoverflow.com/questions/2097813/how-to-parse-the-androidmanifest-xml-file-inside-an-apk-package
|
||||
*
|
||||
* @author Anna Orlova
|
||||
*/
|
||||
class LeanplumManifestParser {
|
||||
// XML tags and attributes:
|
||||
// Every XML start and end tag consists of 6 32 bit words:
|
||||
// 0th word: 02011000 for START_TAG and 03011000 for END_TAG
|
||||
// 1st word: a flag?, like 38000000
|
||||
// 2nd word: Line of where this tag appeared in the original source file
|
||||
// 3rd word: FFFFFFFF ??
|
||||
// 4th word: StringIndex of NameSpace name, or FFFFFFFF for default NS
|
||||
// 5th word: StringIndex of Element Name
|
||||
// (Note: 01011000 in 0th word means end of XML document, END_DOC_TAG).
|
||||
|
||||
// Start tags (not end tags) contain 3 more words:
|
||||
// 6th word: 14001400 meaning??
|
||||
// 7th word: Number of Attributes that follow this tag(follow word 8th)
|
||||
// 8th word: 00000000 meaning??
|
||||
|
||||
// Attributes consist of 5 words:
|
||||
// 0th word: StringIndex of Attribute Name's Namespace, or FFFFFFFF
|
||||
// 1st word: StringIndex of Attribute Name
|
||||
// 2nd word: StringIndex of Attribute Value, or FFFFFFF if ResourceId used
|
||||
// 3rd word: Flags?
|
||||
// 4th word: str ind of attr value again, or ResourceId of value.
|
||||
// END_DOC_TAG = 0x00100101;
|
||||
private static final int START_TAG = 0x00100102;
|
||||
private static final int END_TAG = 0x00100103;
|
||||
private static final String SPACES = " ";
|
||||
|
||||
/**
|
||||
* Parse the 'compressed' binary form of Android XML docs such as for AndroidManifest.xml in .apk
|
||||
* files.
|
||||
*
|
||||
* @param xml byte array of AndroidManifest.xml.
|
||||
* @return String with data of AndroidManifest.xml.
|
||||
*/
|
||||
static String decompressXml(byte[] xml) {
|
||||
String out = "";
|
||||
// Compressed XML file/bytes starts with 24x bytes of data,
|
||||
// 9 32 bit words in little endian order (LSB first):
|
||||
// 0th word is 03 00 08 00
|
||||
// 3rd word SEEMS TO BE: Offset at then of StringTable
|
||||
// 4th word is: Number of strings in string table
|
||||
// WARNING: Sometime I indiscriminately display or refer to word in
|
||||
// little endian storage format, or in integer format (ie MSB first).
|
||||
int numbStrings = littleEndianValue(xml, 4 * 4);
|
||||
// StringIndexTable starts at offset 24x, an array of 32 bit LE offsets
|
||||
// of the length/string data in the StringTable.
|
||||
int sitOff = 0x24; // Offset of start of StringIndexTable.
|
||||
// StringTable, each string is represented with a 16 bit little endian
|
||||
// character count, followed by that number of 16 bit (LE) (Unicode) chars.
|
||||
int stOff = sitOff + numbStrings * 4; // StringTable follows StrIndexTable.
|
||||
// Step through the XML tree element tags and attributes.
|
||||
int off = scanForFirstStartTag(xml);
|
||||
int indent = 0;
|
||||
|
||||
while (off < xml.length) {
|
||||
int tag0 = littleEndianValue(xml, off);
|
||||
int nameSi = littleEndianValue(xml, off + 5 * 4);
|
||||
if (tag0 == START_TAG) {
|
||||
int numbAttrs = littleEndianValue(xml, off + 7 * 4); // Number of Attributes to follow.
|
||||
off += 9 * 4; // Skip over 6+3 words of START_TAG data
|
||||
String name = compXmlString(xml, sitOff, stOff, nameSi);
|
||||
// Look for the Attributes
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int ii = 0; ii < numbAttrs; ii++) {
|
||||
int attrNameSi = littleEndianValue(xml, off + 4); // AttrName String Index.
|
||||
int attrValueSi = littleEndianValue(xml, off + 2 * 4); // AttrValue Str Ind, or FFFFFFFF.
|
||||
int attrResId = littleEndianValue(xml, off + 4 * 4); // AttrValue ResourceId or dup.
|
||||
// AttrValue StrInd.
|
||||
off += 5 * 4; // Skip over the 5 words of an attribute.
|
||||
String attrName = compXmlString(xml, sitOff, stOff, attrNameSi);
|
||||
String attrValue = attrValueSi != -1
|
||||
? compXmlString(xml, sitOff, stOff, attrValueSi)
|
||||
: "resourceID 0x" + Integer.toHexString(attrResId);
|
||||
sb.append(" ").append(attrName).append("=\"").append(attrValue).append("\"");
|
||||
}
|
||||
out += SPACES.substring(0, Math.min(indent * 2, SPACES.length())) + "<" + name + sb + ">";
|
||||
indent++;
|
||||
} else if (tag0 == END_TAG) {
|
||||
indent--;
|
||||
off += 6 * 4; // Skip over 6 words of END_TAG data
|
||||
String name = compXmlString(xml, sitOff, stOff, nameSi);
|
||||
out += SPACES.substring(0, Math.min(indent * 2, SPACES.length())) + "</" + name + ">";
|
||||
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private static String compXmlString(byte[] xml, int sitOff, int stOff, int strInd) {
|
||||
if (strInd < 0) return null;
|
||||
int strOff = stOff + littleEndianValue(xml, sitOff + strInd * 4);
|
||||
return compXmlStringAt(xml, strOff);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Return the string stored in StringTable format at offset strOff. This offset points to
|
||||
* the 16 bit string length, which is followed by that number of 16 bit (Unicode) chars.
|
||||
*/
|
||||
private static String compXmlStringAt(byte[] arr, int strOff) {
|
||||
int strLen = arr[strOff + 1] << 8 & 0xff00 | arr[strOff] & 0xff;
|
||||
byte[] chars = new byte[strLen];
|
||||
for (int ii = 0; ii < strLen; ii++) {
|
||||
chars[ii] = arr[strOff + 2 + ii * 2];
|
||||
}
|
||||
return new String(chars); // Hack, just use 8 byte chars.
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Return value of a Little Endian 32 bit word from the byte array at offset off.
|
||||
*/
|
||||
private static int littleEndianValue(byte[] arr, int off) {
|
||||
return arr[off + 3] << 24 & 0xff000000 | arr[off + 2] << 16 & 0xff0000
|
||||
| arr[off + 1] << 8 & 0xff00 | arr[off] & 0xFF;
|
||||
}
|
||||
|
||||
private static int scanForFirstStartTag(byte[] xml) {
|
||||
// XMLTags, The XML tag tree starts after some unknown content after the
|
||||
// StringTable. There is some unknown data after the StringTable, scan forward
|
||||
// from this point to the flag for the start of an XML start tag.
|
||||
int xmlTagOff = littleEndianValue(xml, 3 * 4); // Start from the offset in the 3rd word.
|
||||
// Scan forward until we find the bytes: 0x02011000(x00100102 in normal int).
|
||||
for (int ii = xmlTagOff; ii < xml.length - 4; ii += 4) {
|
||||
if (littleEndianValue(xml, ii) == START_TAG) {
|
||||
xmlTagOff = ii;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return xmlTagOff;
|
||||
}
|
||||
}
|
@ -23,6 +23,7 @@ package com.leanplum.internal;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import com.leanplum.Leanplum;
|
||||
import com.leanplum.LeanplumEditorMode;
|
||||
import com.leanplum.LeanplumUIEditor;
|
||||
|
||||
@ -34,6 +35,8 @@ import static com.leanplum.internal.Constants.ClassUtil.UI_INTERFACE_EDITOR;
|
||||
/**
|
||||
* Wrapper class for the UI Editor. Method calls will be forwarded to UI Editor package if its
|
||||
* available.
|
||||
*
|
||||
* @author Ben Marten
|
||||
*/
|
||||
public class LeanplumUIEditorWrapper implements LeanplumUIEditor {
|
||||
private static LeanplumUIEditor interfaceEditorSingleton;
|
||||
@ -44,17 +47,17 @@ public class LeanplumUIEditorWrapper implements LeanplumUIEditor {
|
||||
}
|
||||
|
||||
static {
|
||||
Class<?> clazz = null;
|
||||
Class clazz = null;
|
||||
try {
|
||||
clazz = Class.forName(UI_INTERFACE_EDITOR);
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
if (clazz != null) {
|
||||
Method method = null;
|
||||
try {
|
||||
method = clazz.getMethod("getInstance");
|
||||
} catch (NoSuchMethodException e) {
|
||||
Util.handleException(e);
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
}
|
||||
if (method != null) {
|
||||
try {
|
||||
@ -66,6 +69,8 @@ public class LeanplumUIEditorWrapper implements LeanplumUIEditor {
|
||||
Util.handleException(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
Util.handleException(e);
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -104,6 +109,7 @@ public class LeanplumUIEditorWrapper implements LeanplumUIEditor {
|
||||
if (interfaceEditorSingleton != null) {
|
||||
interfaceEditorSingleton.startUpdating();
|
||||
}
|
||||
Leanplum.countAggregator().incrementCount("start_updating_ui");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -121,6 +127,7 @@ public class LeanplumUIEditorWrapper implements LeanplumUIEditor {
|
||||
if (interfaceEditorSingleton != null) {
|
||||
interfaceEditorSingleton.sendUpdate();
|
||||
}
|
||||
Leanplum.countAggregator().incrementCount("send_update_ui");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -22,6 +22,8 @@
|
||||
package com.leanplum.internal;
|
||||
|
||||
|
||||
import org.mozilla.gecko.thirdparty_unused.BuildConfig;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
@ -132,7 +134,10 @@ public class Log {
|
||||
case PRIVATE:
|
||||
maybeSendLog(tag + prefix + message);
|
||||
return;
|
||||
default:
|
||||
default: // DEBUG
|
||||
if (BuildConfig.DEBUG) {
|
||||
android.util.Log.d(tag, prefix + message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,7 +188,7 @@ public class Log {
|
||||
HashMap<String, Object> params = new HashMap<>();
|
||||
params.put(Constants.Params.TYPE, Constants.Values.SDK_LOG);
|
||||
params.put(Constants.Params.MESSAGE, message);
|
||||
Request.post(Constants.Methods.LOG, params).sendEventually();
|
||||
RequestOld.post(Constants.Methods.LOG, params).sendEventually();
|
||||
} catch (Throwable t) {
|
||||
android.util.Log.e("Leanplum", "Unable to send log.", t);
|
||||
} finally {
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
package com.leanplum.internal;
|
||||
|
||||
import com.leanplum.Leanplum;
|
||||
import com.leanplum.callbacks.StartCallback;
|
||||
|
||||
import org.json.JSONObject;
|
||||
@ -32,21 +33,21 @@ public class Registration {
|
||||
public static void registerDevice(String email, final StartCallback callback) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put(Constants.Params.EMAIL, email);
|
||||
Request request = Request.post(Constants.Methods.REGISTER_FOR_DEVELOPMENT, params);
|
||||
request.onResponse(new Request.ResponseCallback() {
|
||||
RequestOld request = RequestOld.post(Constants.Methods.REGISTER_FOR_DEVELOPMENT, params);
|
||||
request.onResponse(new RequestOld.ResponseCallback() {
|
||||
@Override
|
||||
public void response(final JSONObject response) {
|
||||
OsHandler.getInstance().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
boolean isSuccess = Request.isResponseSuccess(response);
|
||||
boolean isSuccess = RequestOld.isResponseSuccess(response);
|
||||
if (isSuccess) {
|
||||
if (callback != null) {
|
||||
callback.onResponse(true);
|
||||
}
|
||||
} else {
|
||||
Log.e(Request.getResponseError(response));
|
||||
Log.e(RequestOld.getResponseError(response));
|
||||
if (callback != null) {
|
||||
callback.onResponse(false);
|
||||
}
|
||||
@ -58,7 +59,7 @@ public class Registration {
|
||||
});
|
||||
}
|
||||
});
|
||||
request.onError(new Request.ErrorCallback() {
|
||||
request.onError(new RequestOld.ErrorCallback() {
|
||||
@Override
|
||||
public void error(final Exception e) {
|
||||
OsHandler.getInstance().post(new Runnable() {
|
||||
@ -72,5 +73,6 @@ public class Registration {
|
||||
}
|
||||
});
|
||||
request.sendIfConnected();
|
||||
Leanplum.countAggregator().incrementCount("register_device");
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013, Leanplum, Inc. All rights reserved.
|
||||
* Copyright 2018, Leanplum, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
@ -21,935 +21,53 @@
|
||||
|
||||
package com.leanplum.internal;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.leanplum.Leanplum;
|
||||
import com.leanplum.utils.SharedPreferencesUtil;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Leanplum request class.
|
||||
*
|
||||
* @author Andrew First
|
||||
*/
|
||||
public class Request {
|
||||
private static final long DEVELOPMENT_MIN_DELAY_MS = 100;
|
||||
private static final long DEVELOPMENT_MAX_DELAY_MS = 5000;
|
||||
private static final long PRODUCTION_DELAY = 60000;
|
||||
static final int MAX_EVENTS_PER_API_CALL;
|
||||
static final String LEANPLUM = "__leanplum__";
|
||||
static final String UUID_KEY = "uuid";
|
||||
|
||||
private static String appId;
|
||||
private static String accessKey;
|
||||
private static String deviceId;
|
||||
private static String userId;
|
||||
|
||||
private static final LeanplumEventCallbackManager eventCallbackManager =
|
||||
new LeanplumEventCallbackManager();
|
||||
private static final Map<String, Boolean> fileTransferStatus = new HashMap<>();
|
||||
private static int pendingDownloads;
|
||||
private static NoPendingDownloadsCallback noPendingDownloadsBlock;
|
||||
|
||||
// The token is saved primarily for legacy SharedPreferences decryption. This could
|
||||
// likely be removed in the future.
|
||||
private static String token = null;
|
||||
private static final Map<File, Long> fileUploadSize = new HashMap<>();
|
||||
private static final Map<File, Double> fileUploadProgress = new HashMap<>();
|
||||
private static String fileUploadProgressString = "";
|
||||
private static long lastSendTimeMs;
|
||||
private static final Object uploadFileLock = new Object();
|
||||
public class Request implements Requesting {
|
||||
|
||||
private final String httpMethod;
|
||||
private final String apiMethod;
|
||||
private final Map<String, Object> params;
|
||||
private ResponseCallback response;
|
||||
private ErrorCallback error;
|
||||
private boolean sent;
|
||||
private long dataBaseIndex;
|
||||
|
||||
private static ApiResponseCallback apiResponse;
|
||||
|
||||
private static List<Map<String, Object>> localErrors = new ArrayList<>();
|
||||
|
||||
static {
|
||||
if (Build.VERSION.SDK_INT <= 17) {
|
||||
MAX_EVENTS_PER_API_CALL = 5000;
|
||||
} else {
|
||||
MAX_EVENTS_PER_API_CALL = 10000;
|
||||
}
|
||||
}
|
||||
|
||||
public static void setAppId(String appId, String accessKey) {
|
||||
if (!TextUtils.isEmpty(appId)) {
|
||||
Request.appId = appId.trim();
|
||||
}
|
||||
if (!TextUtils.isEmpty(accessKey)) {
|
||||
Request.accessKey = accessKey.trim();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setDeviceId(String deviceId) {
|
||||
Request.deviceId = deviceId;
|
||||
}
|
||||
|
||||
public static void setUserId(String userId) {
|
||||
Request.userId = userId;
|
||||
}
|
||||
|
||||
public static void setToken(String token) {
|
||||
Request.token = token;
|
||||
}
|
||||
|
||||
public static String token() {
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Since requests are batched there can be a case where other Request can take future Request
|
||||
* events. We need to have for each Request database index for handle response, error or start
|
||||
* callbacks.
|
||||
*
|
||||
* @return Index of event at database.
|
||||
*/
|
||||
public long getDataBaseIndex() {
|
||||
return dataBaseIndex;
|
||||
}
|
||||
|
||||
// Update index of event at database.
|
||||
public void setDataBaseIndex(long dataBaseIndex) {
|
||||
this.dataBaseIndex = dataBaseIndex;
|
||||
}
|
||||
|
||||
public static void loadToken() {
|
||||
Context context = Leanplum.getContext();
|
||||
SharedPreferences defaults = context.getSharedPreferences(
|
||||
LEANPLUM, Context.MODE_PRIVATE);
|
||||
String token = defaults.getString(Constants.Defaults.TOKEN_KEY, null);
|
||||
if (token == null) {
|
||||
return;
|
||||
}
|
||||
setToken(token);
|
||||
}
|
||||
|
||||
public static void saveToken() {
|
||||
Context context = Leanplum.getContext();
|
||||
SharedPreferences defaults = context.getSharedPreferences(
|
||||
LEANPLUM, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = defaults.edit();
|
||||
editor.putString(Constants.Defaults.TOKEN_KEY, Request.token());
|
||||
SharedPreferencesUtil.commitChanges(editor);
|
||||
}
|
||||
|
||||
public static String appId() {
|
||||
return appId;
|
||||
}
|
||||
|
||||
public static String deviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public static String userId() {
|
||||
return Request.userId;
|
||||
}
|
||||
private ResponseCallback response = null;
|
||||
private ErrorCallback error = null;
|
||||
private final CountAggregator countAggregator;
|
||||
|
||||
public Request(String httpMethod, String apiMethod, Map<String, Object> params) {
|
||||
this.httpMethod = httpMethod;
|
||||
this.apiMethod = apiMethod;
|
||||
this.params = params != null ? params : new HashMap<String, Object>();
|
||||
// Check if it is error and here was SQLite exception.
|
||||
if (Constants.Methods.LOG.equals(apiMethod) && LeanplumEventDataManager.willSendErrorLog) {
|
||||
localErrors.add(createArgsDictionary());
|
||||
}
|
||||
// Make sure the Handler is initialized on the main thread.
|
||||
OsHandler.getInstance();
|
||||
dataBaseIndex = -1;
|
||||
this.params = params;
|
||||
this.countAggregator = Leanplum.countAggregator();
|
||||
}
|
||||
|
||||
public static Request get(String apiMethod, Map<String, Object> params) {
|
||||
Log.LeanplumLogType level = Constants.Methods.LOG.equals(apiMethod) ?
|
||||
Log.LeanplumLogType.DEBUG : Log.LeanplumLogType.VERBOSE;
|
||||
Log.log(level, "Will call API method " + apiMethod + " with arguments " + params);
|
||||
return RequestFactory.getInstance().createRequest("GET", apiMethod, params);
|
||||
|
||||
Leanplum.countAggregator().incrementCount("get_lprequest");
|
||||
|
||||
return new Request("GET", apiMethod, params);
|
||||
}
|
||||
|
||||
public static Request post(String apiMethod, Map<String, Object> params) {
|
||||
Log.LeanplumLogType level = Constants.Methods.LOG.equals(apiMethod) ?
|
||||
Log.LeanplumLogType.DEBUG : Log.LeanplumLogType.VERBOSE;
|
||||
Log.log(level, "Will call API method " + apiMethod + " with arguments " + params);
|
||||
return RequestFactory.getInstance().createRequest("POST", apiMethod, params);
|
||||
|
||||
Leanplum.countAggregator().incrementCount("post_lprequest");
|
||||
|
||||
return new Request("POST", apiMethod, params);
|
||||
}
|
||||
|
||||
public void onResponse(ResponseCallback response) {
|
||||
this.response = response;
|
||||
this.countAggregator.incrementCount("on_response_lprequest");
|
||||
}
|
||||
|
||||
public void onError(ErrorCallback error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public void onApiResponse(ApiResponseCallback apiResponse) {
|
||||
Request.apiResponse = apiResponse;
|
||||
}
|
||||
|
||||
private Map<String, Object> createArgsDictionary() {
|
||||
Map<String, Object> args = new HashMap<>();
|
||||
args.put(Constants.Params.DEVICE_ID, deviceId);
|
||||
args.put(Constants.Params.USER_ID, userId);
|
||||
args.put(Constants.Params.ACTION, apiMethod);
|
||||
args.put(Constants.Params.SDK_VERSION, Constants.LEANPLUM_VERSION);
|
||||
args.put(Constants.Params.DEV_MODE, Boolean.toString(Constants.isDevelopmentModeEnabled));
|
||||
args.put(Constants.Params.TIME, Double.toString(new Date().getTime() / 1000.0));
|
||||
if (token != null) {
|
||||
args.put(Constants.Params.TOKEN, token);
|
||||
}
|
||||
args.putAll(params);
|
||||
return args;
|
||||
}
|
||||
|
||||
private void saveRequestForLater(Map<String, Object> args) {
|
||||
synchronized (Request.class) {
|
||||
Context context = Leanplum.getContext();
|
||||
SharedPreferences preferences = context.getSharedPreferences(
|
||||
LEANPLUM, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = preferences.edit();
|
||||
long count = LeanplumEventDataManager.getEventsCount();
|
||||
String uuid = preferences.getString(Constants.Defaults.UUID_KEY, null);
|
||||
if (uuid == null || count % MAX_EVENTS_PER_API_CALL == 0) {
|
||||
uuid = UUID.randomUUID().toString();
|
||||
editor.putString(Constants.Defaults.UUID_KEY, uuid);
|
||||
SharedPreferencesUtil.commitChanges(editor);
|
||||
}
|
||||
args.put(UUID_KEY, uuid);
|
||||
LeanplumEventDataManager.insertEvent(JsonConverter.toJson(args));
|
||||
|
||||
dataBaseIndex = count;
|
||||
// Checks if here response and/or error callback for this request. We need to add callbacks to
|
||||
// eventCallbackManager only if here was internet connection, otherwise triggerErrorCallback
|
||||
// will handle error callback for this event.
|
||||
if (response != null || error != null && !Util.isConnected()) {
|
||||
eventCallbackManager.addCallbacks(this, response, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void send() {
|
||||
this.sendEventually();
|
||||
if (Constants.isDevelopmentModeEnabled) {
|
||||
long currentTimeMs = System.currentTimeMillis();
|
||||
long delayMs;
|
||||
if (lastSendTimeMs == 0 || currentTimeMs - lastSendTimeMs > DEVELOPMENT_MAX_DELAY_MS) {
|
||||
delayMs = DEVELOPMENT_MIN_DELAY_MS;
|
||||
} else {
|
||||
delayMs = (lastSendTimeMs + DEVELOPMENT_MAX_DELAY_MS) - currentTimeMs;
|
||||
}
|
||||
OsHandler.getInstance().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
sendIfConnected();
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
}
|
||||
}
|
||||
}, delayMs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait 1 second for potential other API calls, and then sends the call synchronously if no other
|
||||
* call has been sent within 1 minute.
|
||||
*/
|
||||
public void sendIfDelayed() {
|
||||
sendEventually();
|
||||
OsHandler.getInstance().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
sendIfDelayedHelper();
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
}
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the call synchronously if no other call has been sent within 1 minute.
|
||||
*/
|
||||
private void sendIfDelayedHelper() {
|
||||
if (Constants.isDevelopmentModeEnabled) {
|
||||
send();
|
||||
} else {
|
||||
long currentTimeMs = System.currentTimeMillis();
|
||||
if (lastSendTimeMs == 0 || currentTimeMs - lastSendTimeMs > PRODUCTION_DELAY) {
|
||||
sendIfConnected();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void sendIfConnected() {
|
||||
if (Util.isConnected()) {
|
||||
this.sendNow();
|
||||
} else {
|
||||
this.sendEventually();
|
||||
Log.i("Device is offline, will send later");
|
||||
triggerErrorCallback(new Exception("Not connected to the Internet"));
|
||||
}
|
||||
}
|
||||
|
||||
private void triggerErrorCallback(Exception e) {
|
||||
if (error != null) {
|
||||
error.error(e);
|
||||
}
|
||||
if (apiResponse != null) {
|
||||
List<Map<String, Object>> requests = getUnsentRequests();
|
||||
List<Map<String, Object>> requestsToSend = removeIrrelevantBackgroundStartRequests(requests);
|
||||
apiResponse.response(requestsToSend, null, requests.size());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
private static boolean attachApiKeys(Map<String, Object> dict) {
|
||||
if (appId == null || accessKey == null) {
|
||||
Log.e("API keys are not set. Please use Leanplum.setAppIdForDevelopmentMode or "
|
||||
+ "Leanplum.setAppIdForProductionMode.");
|
||||
return false;
|
||||
}
|
||||
dict.put(Constants.Params.APP_ID, appId);
|
||||
dict.put(Constants.Params.CLIENT_KEY, accessKey);
|
||||
dict.put(Constants.Params.CLIENT, Constants.CLIENT);
|
||||
return true;
|
||||
}
|
||||
|
||||
public interface ResponseCallback {
|
||||
void response(JSONObject response);
|
||||
}
|
||||
|
||||
public interface ApiResponseCallback {
|
||||
void response(List<Map<String, Object>> requests, JSONObject response, int countOfEvents);
|
||||
}
|
||||
|
||||
public interface ErrorCallback {
|
||||
void error(Exception e);
|
||||
}
|
||||
|
||||
public interface NoPendingDownloadsCallback {
|
||||
void noPendingDownloads();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse response body from server. Invoke potential error or response callbacks for all events
|
||||
* of this request.
|
||||
*
|
||||
* @param responseBody JSONObject with response body from server.
|
||||
* @param requestsToSend List of requests that were sent to the server/
|
||||
* @param error Exception.
|
||||
* @param unsentRequestsSize Size of unsent request, that we will delete.
|
||||
*/
|
||||
private void parseResponseBody(JSONObject responseBody, List<Map<String, Object>>
|
||||
requestsToSend, Exception error, int unsentRequestsSize) {
|
||||
synchronized (Request.class) {
|
||||
if (responseBody == null && error != null) {
|
||||
// Invoke potential error callbacks for all events of this request.
|
||||
eventCallbackManager.invokeAllCallbacksWithError(error, unsentRequestsSize);
|
||||
return;
|
||||
} else if (responseBody == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Response for last start call.
|
||||
if (apiResponse != null) {
|
||||
apiResponse.response(requestsToSend, responseBody, unsentRequestsSize);
|
||||
}
|
||||
|
||||
// We will replace it with error from response body, if we found it.
|
||||
Exception lastResponseError = error;
|
||||
// Valid response, parse and handle response body.
|
||||
int numResponses = Request.numResponses(responseBody);
|
||||
for (int i = 0; i < numResponses; i++) {
|
||||
JSONObject response = Request.getResponseAt(responseBody, i);
|
||||
if (Request.isResponseSuccess(response)) {
|
||||
continue; // If event response is successful, proceed with next one.
|
||||
}
|
||||
|
||||
// If event response was not successful, handle error.
|
||||
String errorMessage = getReadableErrorMessage(Request.getResponseError(response));
|
||||
Log.e(errorMessage);
|
||||
// Throw an exception if last event response is negative.
|
||||
if (i == numResponses - 1) {
|
||||
lastResponseError = new Exception(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
if (lastResponseError != null) {
|
||||
// Invoke potential error callbacks for all events of this request.
|
||||
eventCallbackManager.invokeAllCallbacksWithError(lastResponseError, unsentRequestsSize);
|
||||
} else {
|
||||
// Invoke potential response callbacks for all events of this request.
|
||||
eventCallbackManager.invokeAllCallbacksForResponse(responseBody, unsentRequestsSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse error message from server response and return readable error message.
|
||||
*
|
||||
* @param errorMessage String of error from server response.
|
||||
* @return String of readable error message.
|
||||
*/
|
||||
@NonNull
|
||||
private String getReadableErrorMessage(String errorMessage) {
|
||||
if (errorMessage == null || errorMessage.length() == 0) {
|
||||
errorMessage = "API error";
|
||||
} else if (errorMessage.startsWith("App not found")) {
|
||||
errorMessage = "No app matching the provided app ID was found.";
|
||||
Constants.isInPermanentFailureState = true;
|
||||
} else if (errorMessage.startsWith("Invalid access key")) {
|
||||
errorMessage = "The access key you provided is not valid for this app.";
|
||||
Constants.isInPermanentFailureState = true;
|
||||
} else if (errorMessage.startsWith("Development mode requested but not permitted")) {
|
||||
errorMessage = "A call to Leanplum.setAppIdForDevelopmentMode "
|
||||
+ "with your production key was made, which is not permitted.";
|
||||
Constants.isInPermanentFailureState = true;
|
||||
} else {
|
||||
errorMessage = "API error: " + errorMessage;
|
||||
}
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
private void sendNow() {
|
||||
if (Constants.isTestMode) {
|
||||
return;
|
||||
}
|
||||
if (appId == null) {
|
||||
Log.e("Cannot send request. appId is not set.");
|
||||
return;
|
||||
}
|
||||
if (accessKey == null) {
|
||||
Log.e("Cannot send request. accessKey is not set.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.sendEventually();
|
||||
|
||||
Util.executeAsyncTask(true, new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
sendRequests();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void sendRequests() {
|
||||
List<Map<String, Object>> unsentRequests = new ArrayList<>();
|
||||
List<Map<String, Object>> requestsToSend;
|
||||
// Check if we have localErrors, if yes then we will send only errors to the server.
|
||||
if (localErrors.size() != 0) {
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
for (Map<String, Object> error : localErrors) {
|
||||
error.put(UUID_KEY, uuid);
|
||||
unsentRequests.add(error);
|
||||
}
|
||||
requestsToSend = unsentRequests;
|
||||
} else {
|
||||
unsentRequests = getUnsentRequests();
|
||||
requestsToSend = removeIrrelevantBackgroundStartRequests(unsentRequests);
|
||||
}
|
||||
|
||||
if (requestsToSend.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Map<String, Object> multiRequestArgs = new HashMap<>();
|
||||
if (!Request.attachApiKeys(multiRequestArgs)) {
|
||||
return;
|
||||
}
|
||||
multiRequestArgs.put(Constants.Params.DATA, jsonEncodeUnsentRequests(requestsToSend));
|
||||
multiRequestArgs.put(Constants.Params.SDK_VERSION, Constants.LEANPLUM_VERSION);
|
||||
multiRequestArgs.put(Constants.Params.ACTION, Constants.Methods.MULTI);
|
||||
multiRequestArgs.put(Constants.Params.TIME, Double.toString(new Date().getTime() / 1000.0));
|
||||
|
||||
JSONObject responseBody;
|
||||
HttpURLConnection op = null;
|
||||
try {
|
||||
try {
|
||||
op = Util.operation(
|
||||
Constants.API_HOST_NAME,
|
||||
Constants.API_SERVLET,
|
||||
multiRequestArgs,
|
||||
httpMethod,
|
||||
Constants.API_SSL,
|
||||
Constants.NETWORK_TIMEOUT_SECONDS);
|
||||
|
||||
responseBody = Util.getJsonResponse(op);
|
||||
int statusCode = op.getResponseCode();
|
||||
|
||||
Exception errorException;
|
||||
if (statusCode >= 200 && statusCode <= 299) {
|
||||
if (responseBody == null) {
|
||||
errorException = new Exception("Response JSON is null.");
|
||||
deleteSentRequests(unsentRequests.size());
|
||||
parseResponseBody(null, requestsToSend, errorException, unsentRequests.size());
|
||||
return;
|
||||
}
|
||||
|
||||
Exception exception = null;
|
||||
// Checks if we received the same number of responses as a number of sent request.
|
||||
int numResponses = Request.numResponses(responseBody);
|
||||
if (numResponses != requestsToSend.size()) {
|
||||
Log.w("Sent " + requestsToSend.size() + " requests but only" +
|
||||
" received " + numResponses);
|
||||
}
|
||||
parseResponseBody(responseBody, requestsToSend, null, unsentRequests.size());
|
||||
// Clear localErrors list.
|
||||
localErrors.clear();
|
||||
deleteSentRequests(unsentRequests.size());
|
||||
|
||||
// Send another request if the last request had maximum events per api call.
|
||||
if (unsentRequests.size() == MAX_EVENTS_PER_API_CALL) {
|
||||
sendRequests();
|
||||
}
|
||||
} else {
|
||||
errorException = new Exception("HTTP error " + statusCode);
|
||||
if (statusCode != -1 && statusCode != 408 && !(statusCode >= 500 && statusCode <= 599)) {
|
||||
deleteSentRequests(unsentRequests.size());
|
||||
parseResponseBody(responseBody, requestsToSend, errorException, unsentRequests.size());
|
||||
}
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e("Error parsing JSON response: " + e.toString() + "\n" + Log.getStackTraceString(e));
|
||||
deleteSentRequests(unsentRequests.size());
|
||||
parseResponseBody(null, requestsToSend, e, unsentRequests.size());
|
||||
} catch (Exception e) {
|
||||
Log.e("Unable to send request: " + e.toString() + "\n" + Log.getStackTraceString(e));
|
||||
} finally {
|
||||
if (op != null) {
|
||||
op.disconnect();
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendEventually() {
|
||||
if (Constants.isTestMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (LeanplumEventDataManager.willSendErrorLog) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sent) {
|
||||
sent = true;
|
||||
Map<String, Object> args = createArgsDictionary();
|
||||
saveRequestForLater(args);
|
||||
}
|
||||
}
|
||||
|
||||
static void deleteSentRequests(int requestsCount) {
|
||||
if (requestsCount == 0) {
|
||||
return;
|
||||
}
|
||||
synchronized (Request.class) {
|
||||
LeanplumEventDataManager.deleteEvents(requestsCount);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Map<String, Object>> getUnsentRequests() {
|
||||
List<Map<String, Object>> requestData;
|
||||
|
||||
synchronized (Request.class) {
|
||||
lastSendTimeMs = System.currentTimeMillis();
|
||||
Context context = Leanplum.getContext();
|
||||
SharedPreferences preferences = context.getSharedPreferences(
|
||||
LEANPLUM, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = preferences.edit();
|
||||
|
||||
requestData = LeanplumEventDataManager.getEvents(MAX_EVENTS_PER_API_CALL);
|
||||
editor.remove(Constants.Defaults.UUID_KEY);
|
||||
SharedPreferencesUtil.commitChanges(editor);
|
||||
}
|
||||
|
||||
return requestData;
|
||||
}
|
||||
|
||||
/**
|
||||
* In various scenarios we can end up batching a big number of requests (e.g. device is offline,
|
||||
* background sessions), which could make the stored API calls batch look something like:
|
||||
* <p>
|
||||
* <code>start(B), start(B), start(F), track, start(B), track, start(F), resumeSession</code>
|
||||
* <p>
|
||||
* where <code>start(B)</code> indicates a start in the background, and <code>start(F)</code>
|
||||
* one in the foreground.
|
||||
* <p>
|
||||
* In this case the first two <code>start(B)</code> can be dropped because they don't contribute
|
||||
* any relevant information for the batch call.
|
||||
* <p>
|
||||
* Essentially we drop every <code>start(B)</code> call, that is directly followed by any kind of
|
||||
* a <code>start</code> call.
|
||||
*
|
||||
* @param requestData A list of the requests, stored on the device.
|
||||
* @return A list of only these requests, which contain relevant information for the API call.
|
||||
*/
|
||||
private static List<Map<String, Object>> removeIrrelevantBackgroundStartRequests(
|
||||
List<Map<String, Object>> requestData) {
|
||||
List<Map<String, Object>> relevantRequests = new ArrayList<>();
|
||||
|
||||
int requestCount = requestData.size();
|
||||
if (requestCount > 0) {
|
||||
for (int i = 0; i < requestCount; i++) {
|
||||
Map<String, Object> currentRequest = requestData.get(i);
|
||||
if (i < requestCount - 1
|
||||
&& Constants.Methods.START.equals(requestData.get(i + 1).get(Constants.Params.ACTION))
|
||||
&& Constants.Methods.START.equals(currentRequest.get(Constants.Params.ACTION))
|
||||
&& Boolean.TRUE.toString().equals(currentRequest.get(Constants.Params.BACKGROUND))) {
|
||||
continue;
|
||||
}
|
||||
relevantRequests.add(currentRequest);
|
||||
}
|
||||
}
|
||||
|
||||
return relevantRequests;
|
||||
}
|
||||
|
||||
private static String jsonEncodeUnsentRequests(List<Map<String, Object>> requestData) {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put(Constants.Params.DATA, requestData);
|
||||
return JsonConverter.toJson(data);
|
||||
}
|
||||
|
||||
|
||||
private static String getSizeAsString(int bytes) {
|
||||
if (bytes < (1 << 10)) {
|
||||
return bytes + " B";
|
||||
} else if (bytes < (1 << 20)) {
|
||||
return (bytes >> 10) + " KB";
|
||||
} else {
|
||||
return (bytes >> 20) + " MB";
|
||||
}
|
||||
}
|
||||
|
||||
private static void printUploadProgress() {
|
||||
int totalFiles = fileUploadSize.size();
|
||||
int sentFiles = 0;
|
||||
int totalBytes = 0;
|
||||
int sentBytes = 0;
|
||||
for (Map.Entry<File, Long> entry : fileUploadSize.entrySet()) {
|
||||
File file = entry.getKey();
|
||||
long fileSize = entry.getValue();
|
||||
double fileProgress = fileUploadProgress.get(file);
|
||||
if (fileProgress == 1) {
|
||||
sentFiles++;
|
||||
}
|
||||
sentBytes += (int) (fileSize * fileProgress);
|
||||
totalBytes += fileSize;
|
||||
}
|
||||
String progressString = "Uploading resources. " +
|
||||
sentFiles + '/' + totalFiles + " files completed; " +
|
||||
getSizeAsString(sentBytes) + '/' + getSizeAsString(totalBytes) + " transferred.";
|
||||
if (!fileUploadProgressString.equals(progressString)) {
|
||||
fileUploadProgressString = progressString;
|
||||
Log.i(progressString);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendFilesNow(final List<String> filenames, final List<InputStream> streams) {
|
||||
if (Constants.isTestMode) {
|
||||
return;
|
||||
}
|
||||
final Map<String, Object> dict = createArgsDictionary();
|
||||
if (!attachApiKeys(dict)) {
|
||||
return;
|
||||
}
|
||||
final List<File> filesToUpload = new ArrayList<>();
|
||||
|
||||
// First set up the files for upload
|
||||
for (int i = 0; i < filenames.size(); i++) {
|
||||
String filename = filenames.get(i);
|
||||
if (filename == null || Boolean.TRUE.equals(fileTransferStatus.get(filename))) {
|
||||
continue;
|
||||
}
|
||||
File file = new File(filename);
|
||||
long size;
|
||||
try {
|
||||
size = streams.get(i).available();
|
||||
} catch (IOException e) {
|
||||
size = file.length();
|
||||
} catch (NullPointerException e) {
|
||||
// Not good. Can't read asset.
|
||||
Log.e("Unable to read file " + filename);
|
||||
continue;
|
||||
}
|
||||
fileTransferStatus.put(filename, true);
|
||||
filesToUpload.add(file);
|
||||
fileUploadSize.put(file, size);
|
||||
fileUploadProgress.put(file, 0.0);
|
||||
}
|
||||
if (filesToUpload.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
printUploadProgress();
|
||||
|
||||
// Now upload the files
|
||||
Util.executeAsyncTask(false, new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
synchronized (uploadFileLock) { // Don't overload app and server with many upload tasks
|
||||
JSONObject result;
|
||||
HttpURLConnection op = null;
|
||||
|
||||
try {
|
||||
op = Util.uploadFilesOperation(
|
||||
Constants.Params.FILE,
|
||||
filesToUpload,
|
||||
streams,
|
||||
Constants.API_HOST_NAME,
|
||||
Constants.API_SERVLET,
|
||||
dict,
|
||||
httpMethod,
|
||||
Constants.API_SSL,
|
||||
60);
|
||||
|
||||
if (op != null) {
|
||||
result = Util.getJsonResponse(op);
|
||||
int statusCode = op.getResponseCode();
|
||||
if (statusCode != 200) {
|
||||
throw new Exception("Leanplum: Error sending request: " + statusCode);
|
||||
}
|
||||
if (Request.this.response != null) {
|
||||
Request.this.response.response(result);
|
||||
}
|
||||
} else {
|
||||
if (error != null) {
|
||||
error.error(new Exception("Leanplum: Unable to read file."));
|
||||
}
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e("Unable to convert to JSON.", e);
|
||||
if (error != null) {
|
||||
error.error(e);
|
||||
}
|
||||
} catch (SocketTimeoutException e) {
|
||||
Log.e("Timeout uploading files. Try again or limit the number of files " +
|
||||
"to upload with parameters to syncResourcesAsync.");
|
||||
if (error != null) {
|
||||
error.error(e);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("Unable to send file.", e);
|
||||
if (error != null) {
|
||||
error.error(e);
|
||||
}
|
||||
} finally {
|
||||
if (op != null) {
|
||||
op.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
for (File file : filesToUpload) {
|
||||
fileUploadProgress.put(file, 1.0);
|
||||
}
|
||||
printUploadProgress();
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Upload progress
|
||||
}
|
||||
|
||||
void downloadFile(final String path, final String url) {
|
||||
if (Constants.isTestMode) {
|
||||
return;
|
||||
}
|
||||
if (Boolean.TRUE.equals(fileTransferStatus.get(path))) {
|
||||
return;
|
||||
}
|
||||
pendingDownloads++;
|
||||
Log.i("Downloading resource " + path);
|
||||
fileTransferStatus.put(path, true);
|
||||
final Map<String, Object> dict = createArgsDictionary();
|
||||
dict.put(Constants.Keys.FILENAME, path);
|
||||
if (!attachApiKeys(dict)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Util.executeAsyncTask(false, new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
try {
|
||||
downloadHelper(Constants.API_HOST_NAME, Constants.API_SERVLET, path, url, dict);
|
||||
} catch (Throwable t) {
|
||||
Util.handleException(t);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
// TODO: Download progress
|
||||
}
|
||||
|
||||
private void downloadHelper(String hostName, String servlet, final String path, final String url,
|
||||
final Map<String, Object> dict) {
|
||||
HttpURLConnection op = null;
|
||||
URL originalURL = null;
|
||||
try {
|
||||
if (url == null) {
|
||||
op = Util.operation(
|
||||
hostName,
|
||||
servlet,
|
||||
dict,
|
||||
httpMethod,
|
||||
Constants.API_SSL,
|
||||
Constants.NETWORK_TIMEOUT_SECONDS_FOR_DOWNLOADS);
|
||||
} else {
|
||||
op = Util.createHttpUrlConnection(url, httpMethod, url.startsWith("https://"),
|
||||
Constants.NETWORK_TIMEOUT_SECONDS_FOR_DOWNLOADS);
|
||||
}
|
||||
originalURL = op.getURL();
|
||||
op.connect();
|
||||
int statusCode = op.getResponseCode();
|
||||
if (statusCode != 200) {
|
||||
throw new Exception("Leanplum: Error sending request to: " + hostName +
|
||||
", HTTP status code: " + statusCode);
|
||||
}
|
||||
Stack<String> dirs = new Stack<>();
|
||||
String currentDir = path;
|
||||
while ((currentDir = new File(currentDir).getParent()) != null) {
|
||||
dirs.push(currentDir);
|
||||
}
|
||||
while (!dirs.isEmpty()) {
|
||||
String directory = FileManager.fileRelativeToDocuments(dirs.pop());
|
||||
boolean isCreated = new File(directory).mkdir();
|
||||
if (!isCreated) {
|
||||
Log.w("Failed to create directory: ", directory);
|
||||
}
|
||||
}
|
||||
|
||||
FileOutputStream out = new FileOutputStream(
|
||||
new File(FileManager.fileRelativeToDocuments(path)));
|
||||
Util.saveResponse(op, out);
|
||||
pendingDownloads--;
|
||||
if (Request.this.response != null) {
|
||||
Request.this.response.response(null);
|
||||
}
|
||||
if (pendingDownloads == 0 && noPendingDownloadsBlock != null) {
|
||||
noPendingDownloadsBlock.noPendingDownloads();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (e instanceof EOFException) {
|
||||
if (op != null && !op.getURL().equals(originalURL)) {
|
||||
downloadHelper(null, op.getURL().toString(), path, url, new HashMap<String, Object>());
|
||||
return;
|
||||
}
|
||||
}
|
||||
Log.e("Error downloading resource:" + path, e);
|
||||
pendingDownloads--;
|
||||
if (error != null) {
|
||||
error.error(e);
|
||||
}
|
||||
if (pendingDownloads == 0 && noPendingDownloadsBlock != null) {
|
||||
noPendingDownloadsBlock.noPendingDownloads();
|
||||
}
|
||||
} finally {
|
||||
if (op != null) {
|
||||
op.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int numPendingDownloads() {
|
||||
return pendingDownloads;
|
||||
}
|
||||
|
||||
public static void onNoPendingDownloads(NoPendingDownloadsCallback block) {
|
||||
noPendingDownloadsBlock = block;
|
||||
}
|
||||
|
||||
|
||||
public static int numResponses(JSONObject response) {
|
||||
if (response == null) {
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
return response.getJSONArray("response").length();
|
||||
} catch (JSONException e) {
|
||||
Log.e("Could not parse JSON response.", e);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static JSONObject getResponseAt(JSONObject response, int index) {
|
||||
try {
|
||||
return response.getJSONArray("response").getJSONObject(index);
|
||||
} catch (JSONException e) {
|
||||
Log.e("Could not parse JSON response.", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static JSONObject getLastResponse(JSONObject response) {
|
||||
int numResponses = numResponses(response);
|
||||
if (numResponses > 0) {
|
||||
return getResponseAt(response, numResponses - 1);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isResponseSuccess(JSONObject response) {
|
||||
if (response == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return response.getBoolean("success");
|
||||
} catch (JSONException e) {
|
||||
Log.e("Could not parse JSON response.", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getResponseError(JSONObject response) {
|
||||
if (response == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
JSONObject error = response.optJSONObject("error");
|
||||
if (error == null) {
|
||||
return null;
|
||||
}
|
||||
return error.getString("message");
|
||||
} catch (JSONException e) {
|
||||
Log.e("Could not parse JSON response.", e);
|
||||
return null;
|
||||
}
|
||||
this.countAggregator.incrementCount("on_error_lprequest");
|
||||
}
|
||||
}
|
||||
|
@ -21,20 +21,179 @@
|
||||
|
||||
package com.leanplum.internal;
|
||||
|
||||
import com.leanplum.Leanplum;
|
||||
import java.util.Map;
|
||||
|
||||
public class RequestFactory {
|
||||
|
||||
private static final String API_METHOD_START = "start";
|
||||
private static final String API_METHOD_GET_VARS = "getVars";
|
||||
private static final String API_METHOD_SET_VARS = "setVars";
|
||||
private static final String API_METHOD_STOP = "stop";
|
||||
private static final String API_METHOD_RESTART = "restart";
|
||||
private static final String API_METHOD_TRACK = "track";
|
||||
private static final String API_METHOD_ADVANCE = "advance";
|
||||
private static final String API_METHOD_PAUSE_SESSION = "pauseSession";
|
||||
private static final String API_METHOD_PAUSE_STATE = "pauseState";
|
||||
private static final String API_METHOD_RESUME_SESSION = "resumeSession";
|
||||
private static final String API_METHOD_RESUME_STATE = "resumeState";
|
||||
private static final String API_METHOD_MULTI = "multi";
|
||||
private static final String API_METHOD_REGISTER_FOR_DEVELOPMENT = "registerDevice";
|
||||
private static final String API_METHOD_SET_USER_ATTRIBUTES = "setUserAttributes";
|
||||
private static final String API_METHOD_SET_DEVICE_ATTRIBUTES = "setDeviceAttributes";
|
||||
private static final String API_METHOD_SET_TRAFFIC_SOURCE_INFO = "setTrafficSourceInfo";
|
||||
private static final String API_METHOD_UPLOAD_FILE = "uploadFile";
|
||||
private static final String API_METHOD_DOWNLOAD_FILE = "downloadFile";
|
||||
private static final String API_METHOD_HEARTBEAT = "heartbeat";
|
||||
private static final String API_METHOD_SAVE_VIEW_CONTROLLER_VERSION = "saveInterface";
|
||||
private static final String API_METHOD_SAVE_VIEW_CONTROLLER_IMAGE = "saveInterfaceImage";
|
||||
private static final String API_METHOD_GET_VIEW_CONTROLLER_VERSIONS_LIST = "getViewControllerVersionsList";
|
||||
private static final String API_METHOD_LOG = "log";
|
||||
private static final String API_METHOD_GET_INBOX_MESSAGES = "getNewsfeedMessages";
|
||||
private static final String API_METHOD_MARK_INBOX_MESSAGE_AS_READ = "markNewsfeedMessageAsRead";
|
||||
private static final String API_METHOD_DELETE_INBOX_MESSAGE = "deleteNewsfeedMessage";
|
||||
|
||||
public static RequestFactory defaultFactory;
|
||||
|
||||
private CountAggregator countAggregator;
|
||||
private FeatureFlagManager featureFlagManager;
|
||||
|
||||
public synchronized static RequestFactory getInstance() {
|
||||
if (defaultFactory == null) {
|
||||
defaultFactory = new RequestFactory();
|
||||
defaultFactory.countAggregator = Leanplum.countAggregator();
|
||||
defaultFactory.featureFlagManager = Leanplum.featureFlagManager();
|
||||
}
|
||||
return defaultFactory;
|
||||
}
|
||||
|
||||
public Request createRequest(
|
||||
public RequestOld createRequest(
|
||||
String httpMethod, String apiMethod, Map<String, Object> params) {
|
||||
return new Request(httpMethod, apiMethod, params);
|
||||
Leanplum.countAggregator().incrementCount("createRequest");
|
||||
return new RequestOld(httpMethod, apiMethod, params);
|
||||
}
|
||||
|
||||
public Requesting startWithParams(Map<String, Object> params) {
|
||||
return createPostForApiMethod(API_METHOD_START, params);
|
||||
}
|
||||
|
||||
public Requesting getVarsWithParams(Map<String, Object> params) {
|
||||
return createPostForApiMethod(API_METHOD_GET_VARS, params);
|
||||
}
|
||||
|
||||
public Requesting setVarsWithParams(Map<String, Object> params) {
|
||||
return createPostForApiMethod(API_METHOD_SET_VARS, params);
|
||||
}
|
||||
|
||||
public Requesting stopWithParams(Map<String, Object> params) {
|
||||
return createPostForApiMethod(API_METHOD_STOP, params);
|
||||
}
|
||||
|
||||
public Requesting restartWithParams(Map<String, Object> params) {
|
||||
return createPostForApiMethod(API_METHOD_RESTART, params);
|
||||
}
|
||||
|
||||
public Requesting trackWithParams(Map<String, Object> params) {
|
||||
return createPostForApiMethod(API_METHOD_TRACK, params);
|
||||
}
|
||||
|
||||
public Requesting advanceWithParams(Map<String, Object> params) {
|
||||
return createPostForApiMethod(API_METHOD_ADVANCE, params);
|
||||
}
|
||||
|
||||
public Requesting pauseSessionWithParams(Map<String, Object> params) {
|
||||
return createPostForApiMethod(API_METHOD_PAUSE_SESSION, params);
|
||||
}
|
||||
|
||||
public Requesting pauseStateWithParams(Map<String, Object> params) {
|
||||
return createPostForApiMethod(API_METHOD_PAUSE_STATE, params);
|
||||
}
|
||||
|
||||
public Requesting resumeSessionWithParams(Map<String, Object> params) {
|
||||
return createPostForApiMethod(API_METHOD_RESUME_SESSION, params);
|
||||
}
|
||||
|
||||
public Requesting resumeStateWithParams(Map<String, Object> params) {
|
||||
return createPostForApiMethod(API_METHOD_RESUME_STATE, params);
|
||||
}
|
||||
|
||||
public Requesting multiWithParams(Map<String, Object> params) {
|
||||
return createPostForApiMethod(API_METHOD_MULTI, params);
|
||||
}
|
||||
|
||||
public Requesting registerDeviceWithParams(Map<String, Object> params) {
|
||||
return createPostForApiMethod(API_METHOD_REGISTER_FOR_DEVELOPMENT, params);
|
||||
}
|
||||
|
||||
public Requesting setUserAttributesWithParams(Map<String, Object> params) {
|
||||
return createPostForApiMethod(API_METHOD_SET_USER_ATTRIBUTES, params);
|
||||
}
|
||||
|
||||
public Requesting setDeviceAttributesWithParams(Map<String, Object> params) {
|
||||
return createPostForApiMethod(API_METHOD_SET_DEVICE_ATTRIBUTES, params);
|
||||
}
|
||||
|
||||
public Requesting setTrafficSourceInfoWithParams(Map<String, Object> params) {
|
||||
return createPostForApiMethod(API_METHOD_SET_TRAFFIC_SOURCE_INFO, params);
|
||||
}
|
||||
|
||||
public Requesting uploadFileWithParams(Map<String, Object> params) {
|
||||
return createPostForApiMethod(API_METHOD_UPLOAD_FILE, params);
|
||||
}
|
||||
|
||||
public Requesting downloadFileWithParams(Map<String, Object> params) {
|
||||
return createPostForApiMethod(API_METHOD_DOWNLOAD_FILE, params);
|
||||
}
|
||||
|
||||
public Requesting heartbeatWithParams(Map<String, Object> params) {
|
||||
return createPostForApiMethod(API_METHOD_HEARTBEAT, params);
|
||||
}
|
||||
|
||||
public Requesting saveInterfaceWithParams(Map<String, Object> params) {
|
||||
return createPostForApiMethod(API_METHOD_SAVE_VIEW_CONTROLLER_VERSION, params);
|
||||
}
|
||||
|
||||
public Requesting saveInterfaceImageWithParams(Map<String, Object> params) {
|
||||
return createPostForApiMethod(API_METHOD_SAVE_VIEW_CONTROLLER_IMAGE, params);
|
||||
}
|
||||
|
||||
public Requesting getViewControllerVersionsListWithParams(Map<String, Object> params) {
|
||||
return createPostForApiMethod(API_METHOD_GET_VIEW_CONTROLLER_VERSIONS_LIST, params);
|
||||
}
|
||||
|
||||
public Requesting logWithParams(Map<String, Object> params) {
|
||||
return createPostForApiMethod(API_METHOD_LOG, params);
|
||||
}
|
||||
|
||||
public Requesting getNewsfeedMessagesWithParams(Map<String, Object> params) {
|
||||
return createPostForApiMethod(API_METHOD_GET_INBOX_MESSAGES, params);
|
||||
}
|
||||
|
||||
public Requesting markNewsfeedMessageAsReadWithParams(Map<String, Object> params) {
|
||||
return createPostForApiMethod(API_METHOD_MARK_INBOX_MESSAGE_AS_READ, params);
|
||||
}
|
||||
|
||||
public Requesting deleteNewsfeedMessagesWithParams(Map<String, Object> params) {
|
||||
return createPostForApiMethod(API_METHOD_DELETE_INBOX_MESSAGE, params);
|
||||
}
|
||||
|
||||
|
||||
private Requesting createGetForApiMethod(String apiMethod, Map<String, Object> params) {
|
||||
if (shouldReturnLPRequestClass()) {
|
||||
return Request.get(apiMethod, params);
|
||||
}
|
||||
return RequestOld.get(apiMethod, params);
|
||||
}
|
||||
|
||||
private Requesting createPostForApiMethod(String apiMethod, Map<String, Object> params) {
|
||||
if (shouldReturnLPRequestClass()) {
|
||||
return Request.post(apiMethod, params);
|
||||
}
|
||||
return RequestOld.post(apiMethod, params);
|
||||
}
|
||||
|
||||
private Boolean shouldReturnLPRequestClass() {
|
||||
return Leanplum.featureFlagManager().isFeatureFlagEnabled(Leanplum.featureFlagManager().FEATURE_FLAG_REQUEST_REFACTOR);
|
||||
}
|
||||
|
||||
}
|
||||
|
1104
mobile/android/thirdparty/com/leanplum/internal/RequestOld.java
vendored
Normal file
1104
mobile/android/thirdparty/com/leanplum/internal/RequestOld.java
vendored
Normal file
File diff suppressed because it is too large
Load Diff
16
mobile/android/thirdparty/com/leanplum/internal/RequestSequenceRecorder.java
vendored
Normal file
16
mobile/android/thirdparty/com/leanplum/internal/RequestSequenceRecorder.java
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
package com.leanplum.internal;
|
||||
|
||||
/** Records request call sequence of read/write operations to database. */
|
||||
public interface RequestSequenceRecorder {
|
||||
/** Executes before database read in RequestOld. */
|
||||
void beforeRead();
|
||||
|
||||
/** Executes after database read in RequestOld. */
|
||||
void afterRead();
|
||||
|
||||
/** Executes before database write in RequestOld. */
|
||||
void beforeWrite();
|
||||
|
||||
/** Executes after database write in RequestOld. */
|
||||
void afterWrite();
|
||||
}
|
14
mobile/android/thirdparty/com/leanplum/internal/Requesting.java
vendored
Normal file
14
mobile/android/thirdparty/com/leanplum/internal/Requesting.java
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
package com.leanplum.internal;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
public interface Requesting {
|
||||
|
||||
interface ResponseCallback {
|
||||
void response(JSONObject response);
|
||||
}
|
||||
|
||||
interface ErrorCallback {
|
||||
void error(Exception e);
|
||||
}
|
||||
}
|
@ -97,8 +97,8 @@ public class Socket {
|
||||
Log.i("Connected to development server");
|
||||
try {
|
||||
Map<String, String> args = Util.newMap(
|
||||
Constants.Params.APP_ID, Request.appId(),
|
||||
Constants.Params.DEVICE_ID, Request.deviceId());
|
||||
Constants.Params.APP_ID, RequestOld.appId(),
|
||||
Constants.Params.DEVICE_ID, RequestOld.deviceId());
|
||||
try {
|
||||
sio.emit("auth", new JSONArray(Collections.singletonList(new JSONObject(args))));
|
||||
} catch (JSONException e) {
|
||||
@ -111,6 +111,7 @@ public class Socket {
|
||||
connected = true;
|
||||
connecting = false;
|
||||
}
|
||||
Leanplum.countAggregator().incrementCount("connect_to_app_id");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -202,6 +203,7 @@ public class Socket {
|
||||
} catch (JSONException e) {
|
||||
Log.e("Failed to create JSON data object: " + e.getMessage());
|
||||
}
|
||||
Leanplum.countAggregator().incrementCount("send_event_socket");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -232,7 +234,7 @@ public class Socket {
|
||||
((BaseActionContext) context).setIsPreview(true);
|
||||
context.update();
|
||||
LeanplumInternal.triggerAction(context);
|
||||
ActionManager.getInstance().recordMessageImpression(messageId);
|
||||
Leanplum.triggerMessageDisplayed(context);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e("Error getting action info", e);
|
||||
@ -307,7 +309,7 @@ public class Socket {
|
||||
return;
|
||||
}
|
||||
VarCache.applyVariableDiffs(
|
||||
JsonConverter.mapFromJson(object), null, null, null, null, null);
|
||||
JsonConverter.mapFromJson(object), null, null, null, null, null, null);
|
||||
} catch (JSONException e) {
|
||||
Log.e("Couldn't applyVars for preview.", e);
|
||||
} catch (Throwable e) {
|
||||
|
@ -30,21 +30,24 @@ import com.leanplum.Leanplum;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.mozilla.gecko.thirdparty_unused.BuildConfig;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
|
||||
import ch.boye.httpclientandroidlib.HttpResponse;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpPost;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest;
|
||||
import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient;
|
||||
import ch.boye.httpclientandroidlib.impl.client.HttpClients;
|
||||
|
||||
// Suppressing deprecated apache dependency.
|
||||
@SuppressWarnings("deprecation")
|
||||
class SocketIOClient {
|
||||
interface Handler {
|
||||
void onConnect();
|
||||
@ -70,30 +73,22 @@ class SocketIOClient {
|
||||
mHandler = handler;
|
||||
}
|
||||
|
||||
private String downloadUriAsString()
|
||||
throws IOException {
|
||||
URL url = new URL(this.mURL);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
private static String userAgentString() {
|
||||
String appName = (Leanplum.getContext() != null) ?
|
||||
Util.getApplicationName(Leanplum.getContext()) + "/" + Util.getVersionName() : "websocket";
|
||||
return appName + "(" + RequestOld.appId() + "; " + Constants.CLIENT + "; "
|
||||
+ Constants.LEANPLUM_VERSION + "/" + Constants.LEANPLUM_PACKAGE_IDENTIFIER + ")";
|
||||
}
|
||||
|
||||
private static String downloadUriAsString(final HttpUriRequest req)
|
||||
throws IOException {
|
||||
CloseableHttpClient client = HttpClients.createMinimal();
|
||||
req.setHeader("User-Agent", userAgentString());
|
||||
try {
|
||||
InputStream inputStream = connection.getInputStream();
|
||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
|
||||
String tempStr;
|
||||
StringBuffer stringBuffer = new StringBuffer();
|
||||
|
||||
while ((tempStr = bufferedReader.readLine()) != null) {
|
||||
stringBuffer.append(tempStr);
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
inputStream.close();
|
||||
return stringBuffer.toString();
|
||||
|
||||
HttpResponse res = client.execute(req);
|
||||
return readToEnd(res.getEntity().getContent());
|
||||
} finally {
|
||||
if (connection != null) {
|
||||
connection.disconnect();
|
||||
}
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -244,9 +239,9 @@ class SocketIOClient {
|
||||
return;
|
||||
new Thread() {
|
||||
public void run() {
|
||||
|
||||
HttpPost post = new HttpPost(mURL);
|
||||
try {
|
||||
String line = downloadUriAsString();
|
||||
String line = downloadUriAsString(post);
|
||||
String[] parts = line.split(":");
|
||||
mSession = parts[0];
|
||||
String heartbeat = parts[1];
|
||||
|
@ -41,13 +41,13 @@ import android.support.annotation.RequiresPermission;
|
||||
import android.text.TextUtils;
|
||||
import android.util.TypedValue;
|
||||
|
||||
import com.google.android.gms.ads.identifier.AdvertisingIdClient;
|
||||
import com.leanplum.Leanplum;
|
||||
import com.leanplum.LeanplumActivityHelper;
|
||||
import com.leanplum.LeanplumDeviceIdMode;
|
||||
import com.leanplum.LeanplumException;
|
||||
import com.leanplum.internal.Constants.Methods;
|
||||
import com.leanplum.internal.Constants.Params;
|
||||
import com.leanplum.monitoring.ExceptionHandler;
|
||||
import com.leanplum.utils.SharedPreferencesUtil;
|
||||
|
||||
import org.json.JSONException;
|
||||
@ -203,14 +203,18 @@ public class Util {
|
||||
*/
|
||||
private static DeviceIdInfo getAdvertisingId(Context caller) throws Exception {
|
||||
try {
|
||||
AdvertisingIdClient.Info info = AdvertisingIdClient.getAdvertisingIdInfo(caller);
|
||||
if (info != null) {
|
||||
String advertisingId = info.getId();
|
||||
String deviceId = checkDeviceId("advertising id", advertisingId);
|
||||
if (deviceId != null) {
|
||||
boolean limitedTracking = info.isLimitAdTrackingEnabled();
|
||||
return new DeviceIdInfo(deviceId, limitedTracking);
|
||||
}
|
||||
// Using reflection because the app will either crash or print warnings
|
||||
// if the app doesn't link to Google Play Services, even if this method is not called.
|
||||
Object adInfo = Class.forName("com.google.android.gms.ads.identifier.AdvertisingIdClient")
|
||||
.getMethod("getAdvertisingIdInfo", Context.class).invoke(null, caller);
|
||||
String id = checkDeviceId(
|
||||
"advertising id", (String) adInfo.getClass().getMethod("getId")
|
||||
.invoke(adInfo));
|
||||
if (id != null) {
|
||||
boolean limitTracking = (Boolean) adInfo.getClass()
|
||||
.getMethod("isLimitAdTrackingEnabled").invoke(adInfo);
|
||||
Log.v("Using advertising device id: " + id);
|
||||
return new DeviceIdInfo(id, limitTracking);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Log.e("Error getting advertising ID. Google Play Services are not available: ", t);
|
||||
@ -450,7 +454,7 @@ public class Util {
|
||||
Uri.Builder builder = new Uri.Builder();
|
||||
for (Map.Entry<String, Object> pair : params.entrySet()) {
|
||||
if (pair.getValue() == null) {
|
||||
Log.w("Request parameter for key: " + pair.getKey() + " is null.");
|
||||
Log.w("RequestOld parameter for key: " + pair.getKey() + " is null.");
|
||||
continue;
|
||||
}
|
||||
builder.appendQueryParameter(pair.getKey(), pair.getValue().toString());
|
||||
@ -550,7 +554,7 @@ public class Util {
|
||||
urlConnection.setInstanceFollowRedirects(true);
|
||||
Context context = Leanplum.getContext();
|
||||
urlConnection.setRequestProperty("User-Agent",
|
||||
getApplicationName(context) + "/" + getVersionName() + "/" + Request.appId() + "/" +
|
||||
getApplicationName(context) + "/" + getVersionName() + "/" + RequestOld.appId() + "/" +
|
||||
Constants.CLIENT + "/" + Constants.LEANPLUM_VERSION + "/" + getSystemName() + "/" +
|
||||
getSystemVersion() + "/" + Constants.LEANPLUM_PACKAGE_IDENTIFIER);
|
||||
return urlConnection;
|
||||
@ -834,10 +838,19 @@ public class Util {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize exception handling in the SDK.
|
||||
*/
|
||||
public static void initExceptionHandling(Context context) {
|
||||
ExceptionHandler.getInstance().setContext(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles uncaught exceptions in the SDK.
|
||||
*/
|
||||
public static void handleException(Throwable t) {
|
||||
ExceptionHandler.getInstance().reportException(t);
|
||||
|
||||
if (t instanceof OutOfMemoryError) {
|
||||
if (Constants.isDevelopmentModeEnabled) {
|
||||
throw (OutOfMemoryError) t;
|
||||
@ -880,7 +893,7 @@ public class Util {
|
||||
params.put("stackTrace", stringWriter.toString());
|
||||
|
||||
params.put(Params.VERSION_NAME, versionName);
|
||||
Request.post(Methods.LOG, params).send();
|
||||
RequestOld.post(Methods.LOG, params).send();
|
||||
} catch (Throwable t2) {
|
||||
Log.e("Unable to send error report.", t2);
|
||||
}
|
||||
|
@ -87,6 +87,7 @@ public class VarCache {
|
||||
private static boolean silent;
|
||||
private static int contentVersion;
|
||||
private static Map<String, Object> userAttributes;
|
||||
private static Map<String, Object> variantDebugInfo = new HashMap<>();
|
||||
|
||||
private static final String NAME_COMPONENT_REGEX = "(?:[^\\.\\[.(\\\\]+|\\\\.)+";
|
||||
private static final Pattern NAME_COMPONENT_PATTERN = Pattern.compile(NAME_COMPONENT_REGEX);
|
||||
@ -322,25 +323,38 @@ public class VarCache {
|
||||
return hasReceivedDiffs;
|
||||
}
|
||||
|
||||
public static Map<String, Object> getVariantDebugInfo() {
|
||||
return variantDebugInfo;
|
||||
}
|
||||
|
||||
public static void setVariantDebugInfo(Map<String, Object> variantDebugInfo) {
|
||||
if (variantDebugInfo != null) {
|
||||
VarCache.variantDebugInfo = variantDebugInfo;
|
||||
} else {
|
||||
VarCache.variantDebugInfo = new HashMap<>();
|
||||
}
|
||||
}
|
||||
|
||||
public static void loadDiffs() {
|
||||
if (Constants.isNoop()) {
|
||||
return;
|
||||
}
|
||||
Context context = Leanplum.getContext();
|
||||
SharedPreferences defaults = context.getSharedPreferences(LEANPLUM, Context.MODE_PRIVATE);
|
||||
if (Request.token() == null) {
|
||||
if (RequestOld.token() == null) {
|
||||
applyVariableDiffs(
|
||||
new HashMap<String, Object>(),
|
||||
new HashMap<String, Object>(),
|
||||
new ArrayList<Map<String, Object>>(),
|
||||
new ArrayList<Map<String, Object>>(),
|
||||
new HashMap<String, Object>(),
|
||||
new ArrayList<Map<String, Object>>());
|
||||
new ArrayList<Map<String, Object>>(),
|
||||
new HashMap<String, Object>());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// Crypt functions return input text if there was a problem.
|
||||
AESCrypt aesContext = new AESCrypt(Request.appId(), Request.token());
|
||||
AESCrypt aesContext = new AESCrypt(RequestOld.appId(), RequestOld.token());
|
||||
String variables = aesContext.decodePreference(
|
||||
defaults, Constants.Defaults.VARIABLES_KEY, "{}");
|
||||
String messages = aesContext.decodePreference(
|
||||
@ -351,17 +365,19 @@ public class VarCache {
|
||||
defaults, Constants.Defaults.EVENT_RULES_KEY, "[]");
|
||||
String regions = aesContext.decodePreference(defaults, Constants.Defaults.REGIONS_KEY, "{}");
|
||||
String variants = aesContext.decodePreference(defaults, Constants.Keys.VARIANTS, "[]");
|
||||
String variantDebugInfo = aesContext.decodePreference(defaults, Constants.Keys.VARIANT_DEBUG_INFO, "{}");
|
||||
applyVariableDiffs(
|
||||
JsonConverter.fromJson(variables),
|
||||
JsonConverter.fromJson(messages),
|
||||
JsonConverter.<Map<String, Object>>listFromJson(new JSONArray(updateRules)),
|
||||
JsonConverter.<Map<String, Object>>listFromJson(new JSONArray(eventRules)),
|
||||
JsonConverter.fromJson(regions),
|
||||
JsonConverter.<Map<String, Object>>listFromJson(new JSONArray(variants)));
|
||||
JsonConverter.<Map<String, Object>>listFromJson(new JSONArray(variants)),
|
||||
JsonConverter.fromJson(variantDebugInfo));
|
||||
String deviceId = aesContext.decodePreference(defaults, Constants.Params.DEVICE_ID, null);
|
||||
if (deviceId != null) {
|
||||
if (Util.isValidDeviceId(deviceId)) {
|
||||
Request.setDeviceId(deviceId);
|
||||
RequestOld.setDeviceId(deviceId);
|
||||
} else {
|
||||
Log.w("Invalid stored device id found: \"" + deviceId + "\"; discarding.");
|
||||
}
|
||||
@ -369,7 +385,7 @@ public class VarCache {
|
||||
String userId = aesContext.decodePreference(defaults, Constants.Params.USER_ID, null);
|
||||
if (userId != null) {
|
||||
if (Util.isValidUserId(userId)) {
|
||||
Request.setUserId(userId);
|
||||
RequestOld.setUserId(userId);
|
||||
} else {
|
||||
Log.w("Invalid stored user id found: \"" + userId + "\"; discarding.");
|
||||
}
|
||||
@ -383,13 +399,14 @@ public class VarCache {
|
||||
Log.e("Could not load variable diffs.\n" + Log.getStackTraceString(e));
|
||||
}
|
||||
userAttributes();
|
||||
Leanplum.countAggregator().incrementCount("load_diffs");
|
||||
}
|
||||
|
||||
public static void saveDiffs() {
|
||||
if (Constants.isNoop()) {
|
||||
return;
|
||||
}
|
||||
if (Request.token() == null) {
|
||||
if (RequestOld.token() == null) {
|
||||
return;
|
||||
}
|
||||
Context context = Leanplum.getContext();
|
||||
@ -397,7 +414,7 @@ public class VarCache {
|
||||
SharedPreferences.Editor editor = defaults.edit();
|
||||
|
||||
// Crypt functions return input text if there was a problem.
|
||||
AESCrypt aesContext = new AESCrypt(Request.appId(), Request.token());
|
||||
AESCrypt aesContext = new AESCrypt(RequestOld.appId(), RequestOld.token());
|
||||
String variablesCipher = aesContext.encrypt(JsonConverter.toJson(diffs));
|
||||
editor.putString(Constants.Defaults.VARIABLES_KEY, variablesCipher);
|
||||
|
||||
@ -405,17 +422,21 @@ public class VarCache {
|
||||
editor.putString(Constants.Defaults.MESSAGES_KEY, messagesCipher);
|
||||
|
||||
try {
|
||||
String updateRulesCipher = aesContext.encrypt(
|
||||
JsonConverter.listToJsonArray(updateRuleDiffs).toString());
|
||||
editor.putString(Constants.Defaults.UPDATE_RULES_KEY, updateRulesCipher);
|
||||
if (updateRuleDiffs != null && !updateRuleDiffs.isEmpty()) {
|
||||
String updateRulesCipher = aesContext.encrypt(
|
||||
JsonConverter.listToJsonArray(updateRuleDiffs).toString());
|
||||
editor.putString(Constants.Defaults.UPDATE_RULES_KEY, updateRulesCipher);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e("Error converting updateRuleDiffs to JSON", e);
|
||||
}
|
||||
|
||||
try {
|
||||
String eventRulesCipher = aesContext.encrypt(
|
||||
JsonConverter.listToJsonArray(eventRuleDiffs).toString());
|
||||
editor.putString(Constants.Defaults.EVENT_RULES_KEY, eventRulesCipher);
|
||||
if (eventRuleDiffs != null && !eventRuleDiffs.isEmpty()) {
|
||||
String eventRulesCipher = aesContext.encrypt(
|
||||
JsonConverter.listToJsonArray(eventRuleDiffs).toString());
|
||||
editor.putString(Constants.Defaults.EVENT_RULES_KEY, eventRulesCipher);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e("Error converting eventRuleDiffs to JSON", e);
|
||||
}
|
||||
@ -424,16 +445,27 @@ public class VarCache {
|
||||
editor.putString(Constants.Defaults.REGIONS_KEY, regionsCipher);
|
||||
|
||||
try {
|
||||
String variantsJson = JsonConverter.listToJsonArray(variants).toString();
|
||||
editor.putString(Constants.Keys.VARIANTS, aesContext.encrypt(variantsJson));
|
||||
if (variants != null && !variants.isEmpty()) {
|
||||
String variantsJson = JsonConverter.listToJsonArray(variants).toString();
|
||||
editor.putString(Constants.Keys.VARIANTS, aesContext.encrypt(variantsJson));
|
||||
}
|
||||
} catch (JSONException e1) {
|
||||
Log.e("Error converting " + variants + " to JSON.\n" + Log.getStackTraceString(e1));
|
||||
}
|
||||
editor.putString(Constants.Params.DEVICE_ID, aesContext.encrypt(Request.deviceId()));
|
||||
editor.putString(Constants.Params.USER_ID, aesContext.encrypt(Request.userId()));
|
||||
|
||||
if (variantDebugInfo != null) {
|
||||
editor.putString(
|
||||
Constants.Keys.VARIANT_DEBUG_INFO,
|
||||
aesContext.encrypt(JsonConverter.toJson(variantDebugInfo)));
|
||||
}
|
||||
|
||||
editor.putString(Constants.Params.DEVICE_ID, aesContext.encrypt(RequestOld.deviceId()));
|
||||
editor.putString(Constants.Params.USER_ID, aesContext.encrypt(RequestOld.userId()));
|
||||
editor.putString(Constants.Keys.LOGGING_ENABLED,
|
||||
aesContext.encrypt(String.valueOf(Constants.loggingEnabled)));
|
||||
SharedPreferencesUtil.commitChanges(editor);
|
||||
|
||||
Leanplum.countAggregator().incrementCount("send_diffs");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -467,9 +499,9 @@ public class VarCache {
|
||||
}
|
||||
String overrideFile = var.stringValue;
|
||||
if (var.isResource && Constants.Kinds.FILE.equals(var.kind()) && overrideFile != null &&
|
||||
!overrideFile.equals(var.defaultValue())) {
|
||||
!overrideFile.equals(var.defaultValue())) {
|
||||
Map<String, Object> variationAttributes = CollectionUtil.uncheckedCast(fileAttributes.get
|
||||
(overrideFile));
|
||||
(overrideFile));
|
||||
InputStream stream = fileStreams.get(overrideFile);
|
||||
if (variationAttributes != null && stream != null) {
|
||||
var.setOverrideResId(getResIdFromPath(var.stringValue()));
|
||||
@ -484,7 +516,8 @@ public class VarCache {
|
||||
List<Map<String, Object>> updateRules,
|
||||
List<Map<String, Object>> eventRules,
|
||||
Map<String, Object> regions,
|
||||
List<Map<String, Object>> variants) {
|
||||
List<Map<String, Object>> variants,
|
||||
Map<String, Object> variantDebugInfo) {
|
||||
if (diffs != null) {
|
||||
VarCache.diffs = diffs;
|
||||
computeMergedDictionary();
|
||||
@ -561,6 +594,10 @@ public class VarCache {
|
||||
VarCache.variants = variants;
|
||||
}
|
||||
|
||||
if (variantDebugInfo != null) {
|
||||
VarCache.setVariantDebugInfo(variantDebugInfo);
|
||||
}
|
||||
|
||||
contentVersion++;
|
||||
|
||||
if (!silent) {
|
||||
@ -575,6 +612,7 @@ public class VarCache {
|
||||
eventsUpdateBlock.updateCache();
|
||||
}
|
||||
}
|
||||
Leanplum.countAggregator().incrementCount("apply_variable_diffs");
|
||||
}
|
||||
|
||||
static void applyUpdateRuleDiffs(List<Map<String, Object>> updateRuleDiffs) {
|
||||
@ -668,7 +706,7 @@ public class VarCache {
|
||||
params.put(Constants.Params.ACTION_DEFINITIONS, JsonConverter.toJson(actionDefinitions));
|
||||
}
|
||||
params.put(Constants.Params.FILE_ATTRIBUTES, JsonConverter.toJson(fileAttributes));
|
||||
Request.post(Constants.Methods.SET_VARS, params).sendIfConnected();
|
||||
RequestOld.post(Constants.Methods.SET_VARS, params).sendIfConnected();
|
||||
}
|
||||
|
||||
return changed;
|
||||
@ -713,7 +751,7 @@ public class VarCache {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put(Constants.Params.DATA, fileData.toString());
|
||||
|
||||
Request.post(Constants.Methods.UPLOAD_FILE, params).sendFilesNow(filenames,
|
||||
RequestOld.post(Constants.Methods.UPLOAD_FILE, params).sendFilesNow(filenames,
|
||||
streams);
|
||||
|
||||
filenames = new ArrayList<>();
|
||||
@ -744,7 +782,7 @@ public class VarCache {
|
||||
if (filenames.size() > 0) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put(Constants.Params.DATA, fileData.toString());
|
||||
Request.post(Constants.Methods.UPLOAD_FILE, params).sendFilesNow(filenames, streams);
|
||||
RequestOld.post(Constants.Methods.UPLOAD_FILE, params).sendFilesNow(filenames, streams);
|
||||
}
|
||||
}
|
||||
|
||||
@ -769,14 +807,17 @@ public class VarCache {
|
||||
|
||||
public static void onUpdate(CacheUpdateBlock block) {
|
||||
updateBlock = block;
|
||||
Leanplum.countAggregator().incrementCount("on_update_varcache");
|
||||
}
|
||||
|
||||
public static void onInterfaceUpdate(CacheUpdateBlock block) {
|
||||
interfaceUpdateBlock = block;
|
||||
Leanplum.countAggregator().incrementCount("on_interface_update");
|
||||
}
|
||||
|
||||
public static void onEventsUpdate(CacheUpdateBlock block) {
|
||||
eventsUpdateBlock = block;
|
||||
Leanplum.countAggregator().incrementCount("on_events_update");
|
||||
}
|
||||
|
||||
public static List<Map<String, Object>> variants() {
|
||||
@ -841,7 +882,7 @@ public class VarCache {
|
||||
if (userAttributes == null) {
|
||||
Context context = Leanplum.getContext();
|
||||
SharedPreferences defaults = context.getSharedPreferences(LEANPLUM, Context.MODE_PRIVATE);
|
||||
AESCrypt aesContext = new AESCrypt(Request.appId(), Request.token());
|
||||
AESCrypt aesContext = new AESCrypt(RequestOld.appId(), RequestOld.token());
|
||||
try {
|
||||
userAttributes = JsonConverter.fromJson(
|
||||
aesContext.decodePreference(defaults, Constants.Defaults.ATTRIBUTES_KEY, "{}"));
|
||||
@ -854,7 +895,7 @@ public class VarCache {
|
||||
}
|
||||
|
||||
public static void saveUserAttributes() {
|
||||
if (Constants.isNoop() || Request.appId() == null || userAttributes == null) {
|
||||
if (Constants.isNoop() || RequestOld.appId() == null || userAttributes == null) {
|
||||
return;
|
||||
}
|
||||
Context context = Leanplum.getContext();
|
||||
@ -862,9 +903,27 @@ public class VarCache {
|
||||
SharedPreferences.Editor editor = defaults.edit();
|
||||
// Crypt functions return input text if there was a problem.
|
||||
String plaintext = JsonConverter.toJson(userAttributes);
|
||||
AESCrypt aesContext = new AESCrypt(Request.appId(), Request.token());
|
||||
AESCrypt aesContext = new AESCrypt(RequestOld.appId(), RequestOld.token());
|
||||
editor.putString(Constants.Defaults.ATTRIBUTES_KEY, aesContext.encrypt(plaintext));
|
||||
SharedPreferencesUtil.commitChanges(editor);
|
||||
|
||||
Leanplum.countAggregator().incrementCount("save_user_attributes");
|
||||
}
|
||||
|
||||
public static void clearUserContent() {
|
||||
vars.clear();
|
||||
variants.clear();
|
||||
variantDebugInfo.clear();
|
||||
|
||||
diffs.clear();
|
||||
messageDiffs.clear();
|
||||
messages = null;
|
||||
userAttributes = null;
|
||||
merged = null;
|
||||
|
||||
devModeValuesFromServer = null;
|
||||
devModeFileAttributesFromServer = null;
|
||||
devModeActionDefinitionsFromServer = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -872,6 +931,7 @@ public class VarCache {
|
||||
*/
|
||||
public static void reset() {
|
||||
vars.clear();
|
||||
variantDebugInfo.clear();
|
||||
fileAttributes.clear();
|
||||
fileStreams.clear();
|
||||
valuesFromClient.clear();
|
||||
|
@ -27,6 +27,14 @@ import android.os.HandlerThread;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
|
||||
import ch.boye.httpclientandroidlib.Header;
|
||||
import ch.boye.httpclientandroidlib.HttpException;
|
||||
import ch.boye.httpclientandroidlib.HttpStatus;
|
||||
import ch.boye.httpclientandroidlib.NameValuePair;
|
||||
import ch.boye.httpclientandroidlib.StatusLine;
|
||||
import ch.boye.httpclientandroidlib.client.HttpResponseException;
|
||||
import ch.boye.httpclientandroidlib.message.BasicLineParser;
|
||||
import ch.boye.httpclientandroidlib.message.BasicNameValuePair;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
@ -45,15 +53,6 @@ import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
|
||||
import ch.boye.httpclientandroidlib.Header;
|
||||
import ch.boye.httpclientandroidlib.HttpException;
|
||||
import ch.boye.httpclientandroidlib.HttpStatus;
|
||||
import ch.boye.httpclientandroidlib.NameValuePair;
|
||||
import ch.boye.httpclientandroidlib.StatusLine;
|
||||
import ch.boye.httpclientandroidlib.client.HttpResponseException;
|
||||
import ch.boye.httpclientandroidlib.message.BasicLineParser;
|
||||
import ch.boye.httpclientandroidlib.message.BasicNameValuePair;
|
||||
|
||||
// Suppressing deprecated apache dependency.
|
||||
@SuppressWarnings("deprecation")
|
||||
class WebSocketClient {
|
||||
|
@ -66,56 +66,6 @@ import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Base dialog used to display the Center Popup, Interstitial, Web Interstitial, HTML template.
|
||||
*
|
||||
* @author Martin Yanakiev, Anna Orlova
|
||||
*/
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.ShapeDrawable;
|
||||
import android.graphics.drawable.shapes.RoundRectShape;
|
||||
import android.graphics.drawable.shapes.Shape;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.text.TextUtils;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.view.animation.AccelerateInterpolator;
|
||||
import android.view.animation.AlphaAnimation;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.leanplum.ActionContext;
|
||||
import com.leanplum.Leanplum;
|
||||
import com.leanplum.utils.BitmapUtil;
|
||||
import com.leanplum.utils.SizeUtil;
|
||||
import com.leanplum.views.BackgroundImageView;
|
||||
import com.leanplum.views.CloseButton;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Base dialog used to display the Center Popup, Interstitial, Web Interstitial, HTML template.
|
||||
*
|
||||
@ -134,7 +84,7 @@ public class BaseMessageDialog extends Dialog {
|
||||
private boolean isClosing = false;
|
||||
|
||||
protected BaseMessageDialog(Activity activity, boolean fullscreen, BaseMessageOptions options,
|
||||
WebInterstitialOptions webOptions, HTMLOptions htmlOptions) {
|
||||
WebInterstitialOptions webOptions, HTMLOptions htmlOptions) {
|
||||
super(activity, getTheme(activity));
|
||||
|
||||
SizeUtil.init(activity);
|
||||
@ -150,7 +100,7 @@ public class BaseMessageDialog extends Dialog {
|
||||
}
|
||||
dialogView = new RelativeLayout(activity);
|
||||
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
|
||||
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||
dialogView.setBackgroundColor(Color.TRANSPARENT);
|
||||
dialogView.setLayoutParams(layoutParams);
|
||||
|
||||
@ -180,9 +130,9 @@ public class BaseMessageDialog extends Dialog {
|
||||
} else {
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
|
||||
window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
|
||||
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
|
||||
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
|
||||
if (htmlOptions != null &&
|
||||
MessageTemplates.Args.HTML_ALIGN_BOTTOM.equals(htmlOptions.getHtmlAlign())) {
|
||||
MessageTemplates.Args.HTML_ALIGN_BOTTOM.equals(htmlOptions.getHtmlAlign())) {
|
||||
dialogView.setGravity(Gravity.BOTTOM);
|
||||
}
|
||||
}
|
||||
@ -261,7 +211,7 @@ public class BaseMessageDialog extends Dialog {
|
||||
CloseButton closeButton = new CloseButton(context);
|
||||
closeButton.setId(103);
|
||||
RelativeLayout.LayoutParams closeLayout = new RelativeLayout.LayoutParams(
|
||||
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||
if (fullscreen) {
|
||||
closeLayout.addRule(RelativeLayout.ALIGN_PARENT_TOP, dialogView.getId());
|
||||
closeLayout.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, dialogView.getId());
|
||||
@ -289,13 +239,13 @@ public class BaseMessageDialog extends Dialog {
|
||||
RelativeLayout.LayoutParams layoutParams;
|
||||
if (fullscreen) {
|
||||
layoutParams = new RelativeLayout.LayoutParams(
|
||||
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||
} else if (isHtml) {
|
||||
int height = SizeUtil.dpToPx(context, htmlOptions.getHtmlHeight());
|
||||
HTMLOptions.Size htmlWidth = htmlOptions.getHtmlWidth();
|
||||
if (htmlWidth == null || TextUtils.isEmpty(htmlWidth.type)) {
|
||||
layoutParams = new RelativeLayout.LayoutParams(
|
||||
LayoutParams.MATCH_PARENT, height);
|
||||
LayoutParams.MATCH_PARENT, height);
|
||||
} else {
|
||||
int width = htmlWidth.value;
|
||||
if ("%".equals(htmlWidth.type)) {
|
||||
@ -361,9 +311,9 @@ public class BaseMessageDialog extends Dialog {
|
||||
|
||||
View message = createMessageView(context);
|
||||
((RelativeLayout.LayoutParams) message.getLayoutParams())
|
||||
.addRule(RelativeLayout.BELOW, title.getId());
|
||||
.addRule(RelativeLayout.BELOW, title.getId());
|
||||
((RelativeLayout.LayoutParams) message.getLayoutParams())
|
||||
.addRule(RelativeLayout.ABOVE, button.getId());
|
||||
.addRule(RelativeLayout.ABOVE, button.getId());
|
||||
view.addView(message, message.getLayoutParams());
|
||||
} else if (isWeb) {
|
||||
WebView webView = createWebView(context);
|
||||
@ -403,7 +353,7 @@ public class BaseMessageDialog extends Dialog {
|
||||
view.setBackgroundDrawable(footerBackground);
|
||||
}
|
||||
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
|
||||
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||
view.setLayoutParams(layoutParams);
|
||||
return view;
|
||||
}
|
||||
@ -411,7 +361,7 @@ public class BaseMessageDialog extends Dialog {
|
||||
private RelativeLayout createTitleView(Context context) {
|
||||
RelativeLayout view = new RelativeLayout(context);
|
||||
view.setLayoutParams(new LayoutParams(
|
||||
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
|
||||
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
|
||||
|
||||
TextView title = new TextView(context);
|
||||
title.setPadding(0, SizeUtil.dp5, 0, SizeUtil.dp5);
|
||||
@ -421,7 +371,7 @@ public class BaseMessageDialog extends Dialog {
|
||||
title.setTextSize(TypedValue.COMPLEX_UNIT_SP, SizeUtil.textSize0);
|
||||
title.setTypeface(null, Typeface.BOLD);
|
||||
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
|
||||
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
|
||||
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
|
||||
layoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
|
||||
layoutParams.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE);
|
||||
title.setLayoutParams(layoutParams);
|
||||
@ -433,7 +383,7 @@ public class BaseMessageDialog extends Dialog {
|
||||
private TextView createMessageView(Context context) {
|
||||
TextView view = new TextView(context);
|
||||
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
|
||||
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||
view.setLayoutParams(layoutParams);
|
||||
view.setGravity(Gravity.CENTER);
|
||||
view.setText(options.getMessageText());
|
||||
@ -445,7 +395,7 @@ public class BaseMessageDialog extends Dialog {
|
||||
private WebView createWebView(Context context) {
|
||||
WebView view = new WebView(context);
|
||||
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
|
||||
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||
view.setLayoutParams(layoutParams);
|
||||
view.setWebViewClient(new WebViewClient() {
|
||||
@SuppressWarnings("deprecation")
|
||||
@ -507,7 +457,6 @@ public class BaseMessageDialog extends Dialog {
|
||||
webViewSettings.setMediaPlaybackRequiresUserGesture(false);
|
||||
}
|
||||
webViewSettings.setAppCacheEnabled(true);
|
||||
webViewSettings.getSaveFormData();
|
||||
webViewSettings.setAllowFileAccess(true);
|
||||
webViewSettings.setJavaScriptEnabled(true);
|
||||
webViewSettings.setDomStorageEnabled(true);
|
||||
@ -525,7 +474,7 @@ public class BaseMessageDialog extends Dialog {
|
||||
webViewSettings.setSupportZoom(false);
|
||||
|
||||
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
|
||||
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||
webView.setLayoutParams(layoutParams);
|
||||
final Dialog currentDialog = this;
|
||||
webView.setWebChromeClient(new WebChromeClient());
|
||||
@ -563,7 +512,7 @@ public class BaseMessageDialog extends Dialog {
|
||||
|
||||
try {
|
||||
paramsMap = ActionContext.mapFromJson(new JSONObject(queryComponentsFromUrl(url,
|
||||
"parameters")));
|
||||
"parameters")));
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
@ -579,7 +528,7 @@ public class BaseMessageDialog extends Dialog {
|
||||
|
||||
// Action URL or track action URL event.
|
||||
if (url.contains(htmlOptions.getActionUrl()) ||
|
||||
url.contains(htmlOptions.getTrackActionUrl())) {
|
||||
url.contains(htmlOptions.getTrackActionUrl())) {
|
||||
cancel();
|
||||
String queryComponentsFromUrl = queryComponentsFromUrl(url, "action");
|
||||
try {
|
||||
@ -638,7 +587,7 @@ public class BaseMessageDialog extends Dialog {
|
||||
private TextView createAcceptButton(Context context) {
|
||||
TextView view = new TextView(context);
|
||||
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
|
||||
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE);
|
||||
layoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
|
||||
layoutParams.setMargins(0, 0, 0, SizeUtil.dp5);
|
||||
@ -650,7 +599,7 @@ public class BaseMessageDialog extends Dialog {
|
||||
view.setTypeface(null, Typeface.BOLD);
|
||||
|
||||
BitmapUtil.stateBackgroundDarkerByPercentage(view,
|
||||
options.getAcceptButtonBackgroundColor(), 30);
|
||||
options.getAcceptButtonBackgroundColor(), 30);
|
||||
|
||||
view.setTextSize(TypedValue.COMPLEX_UNIT_SP, SizeUtil.textSize0_1);
|
||||
view.setOnClickListener(new View.OnClickListener() {
|
||||
@ -667,7 +616,7 @@ public class BaseMessageDialog extends Dialog {
|
||||
|
||||
private static int getTheme(Activity activity) {
|
||||
boolean full = (activity.getWindow().getAttributes().flags &
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN) == WindowManager.LayoutParams.FLAG_FULLSCREEN;
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN) == WindowManager.LayoutParams.FLAG_FULLSCREEN;
|
||||
if (full) {
|
||||
return android.R.style.Theme_Translucent_NoTitleBar_Fullscreen;
|
||||
} else {
|
||||
|
@ -26,6 +26,7 @@ import android.graphics.Point;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.leanplum.ActionContext;
|
||||
import com.leanplum.Leanplum;
|
||||
import com.leanplum.LeanplumActivityHelper;
|
||||
@ -50,32 +51,32 @@ public class HTMLTemplate extends BaseMessageDialog {
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(@NonNull MotionEvent ev) {
|
||||
if (!htmlOptions.isFullScreen()) {
|
||||
Point size = SizeUtil.getDisplaySize(activity);
|
||||
int dialogWidth = webView.getWidth();
|
||||
int left = (size.x - dialogWidth) / 2;
|
||||
int right = (size.x + dialogWidth) / 2;
|
||||
int height = SizeUtil.dpToPx(Leanplum.getContext(), htmlOptions.getHtmlHeight());
|
||||
int statusBarHeight = SizeUtil.getStatusBarHeight(Leanplum.getContext());
|
||||
int htmlYOffset = htmlOptions.getHtmlYOffset(activity);
|
||||
int top;
|
||||
int bottom;
|
||||
if (MessageTemplates.Args.HTML_ALIGN_BOTTOM.equals(htmlOptions.getHtmlAlign())) {
|
||||
top = size.y - height - statusBarHeight - htmlYOffset;
|
||||
bottom = size.y - htmlYOffset - statusBarHeight;
|
||||
} else {
|
||||
top = htmlYOffset + statusBarHeight;
|
||||
bottom = height + statusBarHeight + htmlYOffset;
|
||||
}
|
||||
|
||||
if (ev.getY() < top || ev.getY() > bottom || ev.getX() < left || ev.getX() > right) {
|
||||
if (htmlOptions.isHtmlTabOutsideToClose()) {
|
||||
cancel();
|
||||
}
|
||||
activity.dispatchTouchEvent(ev);
|
||||
}
|
||||
if (!htmlOptions.isFullScreen()) {
|
||||
Point size = SizeUtil.getDisplaySize(activity);
|
||||
int dialogWidth = webView.getWidth();
|
||||
int left = (size.x - dialogWidth) / 2;
|
||||
int right = (size.x + dialogWidth) / 2;
|
||||
int height = SizeUtil.dpToPx(Leanplum.getContext(), htmlOptions.getHtmlHeight());
|
||||
int statusBarHeight = SizeUtil.getStatusBarHeight(Leanplum.getContext());
|
||||
int htmlYOffset = htmlOptions.getHtmlYOffset(activity);
|
||||
int top;
|
||||
int bottom;
|
||||
if (MessageTemplates.Args.HTML_ALIGN_BOTTOM.equals(htmlOptions.getHtmlAlign())) {
|
||||
top = size.y - height - statusBarHeight - htmlYOffset;
|
||||
bottom = size.y - htmlYOffset - statusBarHeight;
|
||||
} else {
|
||||
top = htmlYOffset + statusBarHeight;
|
||||
bottom = height + statusBarHeight + htmlYOffset;
|
||||
}
|
||||
return super.dispatchTouchEvent(ev);
|
||||
|
||||
if (ev.getY() < top || ev.getY() > bottom || ev.getX() < left || ev.getX() > right) {
|
||||
if (htmlOptions.isHtmlTabOutsideToClose()) {
|
||||
cancel();
|
||||
}
|
||||
activity.dispatchTouchEvent(ev);
|
||||
}
|
||||
}
|
||||
return super.dispatchTouchEvent(ev);
|
||||
}
|
||||
|
||||
public static void register() {
|
||||
|
@ -89,18 +89,18 @@ public class MessageTemplates {
|
||||
|
||||
// Open URL.
|
||||
static final String DEFAULT_URL = "http://www.example.com";
|
||||
|
||||
static final String DEFAULT_BASE_URL = "http://leanplum/";
|
||||
// Web interstitial values.
|
||||
static final String DEFAULT_CLOSE_URL = "http://leanplum:close";
|
||||
static final String DEFAULT_CLOSE_URL = DEFAULT_BASE_URL + "close";
|
||||
static final boolean DEFAULT_HAS_DISMISS_BUTTON = true;
|
||||
|
||||
// HTML Template values.
|
||||
public static final String FILE_PREFIX = "__file__";
|
||||
public static final String HTML_TEMPLATE_PREFIX = "__file__Template";
|
||||
static final String DEFAULT_OPEN_URL = "http://leanplum:loadFinished";
|
||||
static final String DEFAULT_TRACK_URL = "http://leanplum:track";
|
||||
static final String DEFAULT_ACTION_URL = "http://leanplum:runAction";
|
||||
static final String DEFAULT_TRACK_ACTION_URL = "http://leanplum:runTrackedAction";
|
||||
static final String DEFAULT_OPEN_URL = DEFAULT_BASE_URL + "loadFinished";
|
||||
static final String DEFAULT_TRACK_URL = DEFAULT_BASE_URL + "track";
|
||||
static final String DEFAULT_ACTION_URL = DEFAULT_BASE_URL + "runAction";
|
||||
static final String DEFAULT_TRACK_ACTION_URL = DEFAULT_BASE_URL + "runTrackedAction";
|
||||
|
||||
}
|
||||
|
||||
|
@ -80,9 +80,8 @@ class OpenURL {
|
||||
for (ResolveInfo resolveInfo : resolveInfoList) {
|
||||
if (resolveInfo != null && resolveInfo.activityInfo != null &&
|
||||
resolveInfo.activityInfo.name != null) {
|
||||
final String contextPackageName = context.getPackageName();
|
||||
if (resolveInfo.activityInfo.name.contains(contextPackageName) ||
|
||||
resolveInfo.activityInfo.packageName.contains(contextPackageName)) {
|
||||
if (resolveInfo.activityInfo.name.contains(
|
||||
context.getPackageName())) {
|
||||
uriIntent.setPackage(resolveInfo.activityInfo.packageName);
|
||||
}
|
||||
}
|
||||
|
19
mobile/android/thirdparty/com/leanplum/models/MessageArchiveData.java
vendored
Normal file
19
mobile/android/thirdparty/com/leanplum/models/MessageArchiveData.java
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
package com.leanplum.models;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class MessageArchiveData {
|
||||
@NonNull public String messageID;
|
||||
@NonNull public String messageBody;
|
||||
@NonNull public String recipientUserID;
|
||||
@NonNull public Date deliveryDateTime;
|
||||
|
||||
public MessageArchiveData(String messageID, String messageBody, String recipientUserID, Date deliveryDateTime) {
|
||||
this.messageID = messageID;
|
||||
this.messageBody = messageBody;
|
||||
this.recipientUserID = recipientUserID;
|
||||
this.deliveryDateTime = deliveryDateTime;
|
||||
}
|
||||
}
|
51
mobile/android/thirdparty/com/leanplum/monitoring/ExceptionHandler.java
vendored
Normal file
51
mobile/android/thirdparty/com/leanplum/monitoring/ExceptionHandler.java
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
package com.leanplum.monitoring;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.leanplum.Leanplum;
|
||||
import com.leanplum.internal.Log;
|
||||
|
||||
public class ExceptionHandler {
|
||||
private static final String LEANPLUM_CRASH_REPORTER_CLASS =
|
||||
"com.leanplum.monitoring.internal.LeanplumExceptionReporter";
|
||||
private static final ExceptionHandler instance = new ExceptionHandler();
|
||||
public ExceptionReporting exceptionReporter = null;
|
||||
|
||||
private ExceptionHandler() {}
|
||||
|
||||
public static ExceptionHandler getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void setContext(Context context) {
|
||||
try {
|
||||
// Class.forName runs the static initializer in LeanplumExceptionReporter
|
||||
// which sets the exceptionReporter on the singleton
|
||||
Class.forName(LEANPLUM_CRASH_REPORTER_CLASS);
|
||||
if (exceptionReporter != null) {
|
||||
try {
|
||||
exceptionReporter.setContext(context);
|
||||
} catch (Throwable t) {
|
||||
Log.e("LeanplumExceptionHandler", t);
|
||||
}
|
||||
}
|
||||
} catch (ClassNotFoundException t) {
|
||||
Log.i("LeanplumExceptionHandler could not initialize Exception Reporting." +
|
||||
"This is expected if you have not included the leanplum-monitoring module");
|
||||
} catch (Throwable t) {
|
||||
Log.e("LeanplumExceptionHandler", t);
|
||||
}
|
||||
}
|
||||
|
||||
public void reportException(Throwable exception) {
|
||||
if (exceptionReporter != null) {
|
||||
try {
|
||||
exceptionReporter.reportException(exception);
|
||||
} catch (Throwable t) {
|
||||
Log.e("LeanplumExceptionHandler", t);
|
||||
}
|
||||
Leanplum.countAggregator().incrementCount("report_exception");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
8
mobile/android/thirdparty/com/leanplum/monitoring/ExceptionReporting.java
vendored
Normal file
8
mobile/android/thirdparty/com/leanplum/monitoring/ExceptionReporting.java
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
package com.leanplum.monitoring;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public interface ExceptionReporting {
|
||||
void setContext(Context context);
|
||||
void reportException(Throwable t);
|
||||
}
|
@ -1,5 +1,3 @@
|
||||
package com.leanplum.utils;
|
||||
|
||||
/*
|
||||
* Copyright 2017, Leanplum, Inc. All rights reserved.
|
||||
*
|
||||
@ -20,6 +18,7 @@ package com.leanplum.utils;
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.leanplum.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
@ -30,28 +29,28 @@ import android.os.Build;
|
||||
* @author Anna Orlova
|
||||
*/
|
||||
public class BuildUtil {
|
||||
private static int targetSdk = -1;
|
||||
private static int targetSdk = -1;
|
||||
|
||||
/**
|
||||
* Whether notification channels are supported.
|
||||
*
|
||||
* @param context The application context.
|
||||
* @return True if notification channels are supported, false otherwise.
|
||||
*/
|
||||
public static boolean isNotificationChannelSupported(Context context) {
|
||||
return Build.VERSION.SDK_INT >= 26 && getTargetSdkVersion(context) >= 26;
|
||||
}
|
||||
/**
|
||||
* Whether notification channels are supported.
|
||||
*
|
||||
* @param context The application context.
|
||||
* @return True if notification channels are supported, false otherwise.
|
||||
*/
|
||||
public static boolean isNotificationChannelSupported(Context context) {
|
||||
return Build.VERSION.SDK_INT >= 26 && getTargetSdkVersion(context) >= 26;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns target SDK version parsed from manifest.
|
||||
*
|
||||
* @param context The application context.
|
||||
* @return Target SDK version.
|
||||
*/
|
||||
private static int getTargetSdkVersion(Context context) {
|
||||
if (targetSdk == -1 && context != null) {
|
||||
targetSdk = context.getApplicationInfo().targetSdkVersion;
|
||||
}
|
||||
return targetSdk;
|
||||
/**
|
||||
* Returns target SDK version parsed from manifest.
|
||||
*
|
||||
* @param context The application context.
|
||||
* @return Target SDK version.
|
||||
*/
|
||||
private static int getTargetSdkVersion(Context context) {
|
||||
if (targetSdk == -1 && context != null) {
|
||||
targetSdk = context.getApplicationInfo().targetSdkVersion;
|
||||
}
|
||||
}
|
||||
return targetSdk;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user