From 9149359de77a178cc1771fcadb7f6a547a22cd79 Mon Sep 17 00:00:00 2001 From: Alex Lementuev Date: Mon, 17 Jun 2019 18:46:04 +0300 Subject: [PATCH] Apptentive Android SDK 5.4.3 --- CHANGELOG.md | 7 + README.md | 2 +- .../android/sdk/ApptentiveConfiguration.java | 17 ++ .../android/sdk/ApptentiveInternal.java | 61 +++++-- .../sdk/comm/ApptentiveHttpClient.java | 4 +- .../sdk/conversation/Conversation.java | 4 +- .../sdk/conversation/ConversationManager.java | 21 ++- .../fragment/MessageCenterFragment.java | 2 +- .../module/messagecenter/model/WhoCard.java | 4 - .../android/sdk/storage/DeviceManager.java | 166 ++---------------- .../sdk/storage/DevicePayloadDiff.java | 155 ++++++++++++++++ .../android/sdk/util/Constants.java | 2 +- .../android/sdk/util/StringUtils.java | 14 ++ .../com/apptentive/android/sdk/util/Util.java | 2 +- build.gradle | 2 +- samples/apptentive-example/build.gradle | 2 +- 16 files changed, 280 insertions(+), 185 deletions(-) create mode 100644 apptentive/src/main/java/com/apptentive/android/sdk/storage/DevicePayloadDiff.java diff --git a/CHANGELOG.md b/CHANGELOG.md index f92627973..989625a8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 2019-06-17 - v5.4.3 + +#### Improvements + +* Made Android ID collection optional on pre-Oreo targets. +* Fixed "Who" card in the Message Center. + # 2019-05-14 - v5.4.2 #### Fixes diff --git a/README.md b/README.md index 88012795a..7f2987e64 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ use your app, to talk to them at the right time, and in the right way. ##### [Release Notes](https://learn.apptentive.com/knowledge-base/android-sdk-release-notes/) -##### Binary releases are hosted for Maven [here](http://search.maven.org/#artifactdetails|com.apptentive|apptentive-android|5.4.2|aar) +##### Binary releases are hosted for Maven [here](http://search.maven.org/#artifactdetails|com.apptentive|apptentive-android|5.4.3|aar) #### Reporting Bugs diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/ApptentiveConfiguration.java b/apptentive/src/main/java/com/apptentive/android/sdk/ApptentiveConfiguration.java index 7e32f832f..f8a52d078 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/ApptentiveConfiguration.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/ApptentiveConfiguration.java @@ -20,6 +20,7 @@ public class ApptentiveConfiguration { private boolean shouldSanitizeLogMessages; private boolean troubleshootingModeEnabled; private Encryption encryption; + private boolean shouldCollectAndroidIdOnPreOreoTargets; public ApptentiveConfiguration(@NonNull String apptentiveKey, @NonNull String apptentiveSignature) { if (StringUtils.isNullOrEmpty(apptentiveKey)) { @@ -36,6 +37,7 @@ public ApptentiveConfiguration(@NonNull String apptentiveKey, @NonNull String ap this.shouldEncryptStorage = false; this.shouldSanitizeLogMessages = true; this.troubleshootingModeEnabled = true; + this.shouldCollectAndroidIdOnPreOreoTargets = true; } public String getApptentiveKey() { @@ -122,4 +124,19 @@ public ApptentiveConfiguration setTroubleshootingModeEnabled(boolean troubleshoo this.troubleshootingModeEnabled = troubleshootingModeEnabled; return this; } + + /** + * Overrides if the SDK should collect Android ID on pre Android-O targets. If set to false + * a random value would be generated on the initial SDK launch and provided on each subsequent launch. + */ + public void setShouldCollectAndroidIdOnPreOreoTargets(boolean shouldCollectAndroidIdOnPreOreoTargets) { + this.shouldCollectAndroidIdOnPreOreoTargets = shouldCollectAndroidIdOnPreOreoTargets; + } + + /** + * Indicates if the SDK should collect Android ID on pre Android-O targets. + */ + public boolean shouldCollectAndroidIdOnPreOreoTargets() { + return shouldCollectAndroidIdOnPreOreoTargets; + } } diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/ApptentiveInternal.java b/apptentive/src/main/java/com/apptentive/android/sdk/ApptentiveInternal.java index aab7edc45..f5a5bff7c 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/ApptentiveInternal.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/ApptentiveInternal.java @@ -42,7 +42,6 @@ import com.apptentive.android.sdk.module.engagement.interaction.InteractionManager; import com.apptentive.android.sdk.module.engagement.interaction.model.MessageCenterInteraction; import com.apptentive.android.sdk.module.messagecenter.MessageManager; -import com.apptentive.android.sdk.module.metric.MetricModule; import com.apptentive.android.sdk.module.rating.IRatingProvider; import com.apptentive.android.sdk.module.rating.impl.GooglePlayRatingProvider; import com.apptentive.android.sdk.module.survey.OnSurveyFinishedListener; @@ -153,7 +152,7 @@ public ApptentiveInternal(Context appContext) { appRelease = null; } - private ApptentiveInternal(Application application, ApptentiveConfiguration configuration) { + private ApptentiveInternal(Application application, ApptentiveConfiguration configuration, @NonNull String androidID) { if (configuration == null) { throw new IllegalArgumentException("Configuration is null"); } @@ -174,7 +173,8 @@ private ApptentiveInternal(Application application, ApptentiveConfiguration conf globalSharedPrefs = application.getSharedPreferences(Constants.PREF_NAME, Context.MODE_PRIVATE); apptentiveHttpClient = new ApptentiveHttpClient(apptentiveKey, apptentiveSignature, getEndpointBase(globalSharedPrefs)); - conversationManager = new ConversationManager(appContext, Util.getInternalDir(appContext, CONVERSATIONS_DIR, true), encryption); + DeviceManager deviceManager = new DeviceManager(androidID); + conversationManager = new ConversationManager(appContext, Util.getInternalDir(appContext, CONVERSATIONS_DIR, true), encryption, deviceManager); appRelease = AppReleaseManager.generateCurrentAppRelease(application, this); taskManager = new ApptentiveTaskManager(appContext, apptentiveHttpClient, encryption); @@ -232,17 +232,20 @@ static void createInstance(@NonNull Application application, @NonNull Apptentive synchronized (ApptentiveInternal.class) { if (sApptentiveInternal == null) { - ApptentiveLog.i("Registering Apptentive Android SDK %s", Constants.getApptentiveSdkVersion()); - ApptentiveLog.v("ApptentiveKey=%s ApptentiveSignature=%s", apptentiveKey, apptentiveSignature); - sApptentiveInternal = new ApptentiveInternal(application, configuration); - dispatchOnConversationQueue(new DispatchTask() { - @Override - protected void execute() { - sApptentiveInternal.start(); - } - }); + ApptentiveLog.i("Registering Apptentive Android SDK %s", Constants.getApptentiveSdkVersion()); + ApptentiveLog.v("ApptentiveKey=%s ApptentiveSignature=%s", apptentiveKey, apptentiveSignature); + // resolve Android ID + boolean shouldGenerateRandomAndroidID = Build.VERSION.SDK_INT < Build.VERSION_CODES.O && !configuration.shouldCollectAndroidIdOnPreOreoTargets(); + String androidID = resolveAndroidID(application.getApplicationContext(), shouldGenerateRandomAndroidID); + sApptentiveInternal = new ApptentiveInternal(application, configuration, androidID); + dispatchOnConversationQueue(new DispatchTask() { + @Override + protected void execute() { + sApptentiveInternal.start(); + } + }); - ApptentiveActivityLifecycleCallbacks.register(application); + ApptentiveActivityLifecycleCallbacks.register(application); } else { ApptentiveLog.w("Apptentive instance is already initialized"); } @@ -1191,4 +1194,36 @@ private static void logException(Exception e) { } //endregion + + //region Android ID + + private static final String PREFS_NAME_ANDROID_ID = "com.apptentive.sdk.androidID"; + private static final String PREFS_KEY_NAME_ANDROID_ID = "androidID"; + + private static String resolveAndroidID(Context context, boolean shouldGenerateRandomAndroidID) { + if (shouldGenerateRandomAndroidID) { + String existingAndroidID = loadAndroidID(context); + if (existingAndroidID != null) { + return existingAndroidID; + } + + String androidID = StringUtils.randomAndroidID(); + saveAndroidID(context, androidID); + return androidID; + } + + return Util.getAndroidID(context); + } + + private static String loadAndroidID(Context context) { + SharedPreferences sharedPreferences = context.getSharedPreferences(PREFS_NAME_ANDROID_ID, Context.MODE_PRIVATE); + return sharedPreferences.getString(PREFS_KEY_NAME_ANDROID_ID, null); + } + + private static void saveAndroidID(Context context, String androidID) { + SharedPreferences sharedPreferences = context.getSharedPreferences(PREFS_NAME_ANDROID_ID, Context.MODE_PRIVATE); + sharedPreferences.edit().putString(PREFS_KEY_NAME_ANDROID_ID, androidID).apply(); + } + + //endregion } diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/comm/ApptentiveHttpClient.java b/apptentive/src/main/java/com/apptentive/android/sdk/comm/ApptentiveHttpClient.java index 38c7678b4..37851e640 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/comm/ApptentiveHttpClient.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/comm/ApptentiveHttpClient.java @@ -16,7 +16,7 @@ import com.apptentive.android.sdk.storage.AppRelease; import com.apptentive.android.sdk.storage.AppReleaseManager; import com.apptentive.android.sdk.storage.Device; -import com.apptentive.android.sdk.storage.DeviceManager; +import com.apptentive.android.sdk.storage.DevicePayloadDiff; import com.apptentive.android.sdk.storage.PayloadRequestSender; import com.apptentive.android.sdk.storage.Sdk; import com.apptentive.android.sdk.storage.SdkManager; @@ -168,7 +168,7 @@ public HttpJsonRequest createFirstLoginRequest(String token, AppRelease appRelea ConversationTokenRequest conversationTokenRequest = new ConversationTokenRequest(); conversationTokenRequest.setSdkAndAppRelease(SdkManager.getPayload(sdk), AppReleaseManager.getPayload(appRelease)); - conversationTokenRequest.setDevice(DeviceManager.getDiffPayload(null, device)); + conversationTokenRequest.setDevice(DevicePayloadDiff.getDiffPayload(null, device)); try { conversationTokenRequest.put("token", token); diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/conversation/Conversation.java b/apptentive/src/main/java/com/apptentive/android/sdk/conversation/Conversation.java index e8e73762a..6995ff823 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/conversation/Conversation.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/conversation/Conversation.java @@ -33,7 +33,7 @@ import com.apptentive.android.sdk.storage.DataChangedListener; import com.apptentive.android.sdk.storage.Device; import com.apptentive.android.sdk.storage.DeviceDataChangedListener; -import com.apptentive.android.sdk.storage.DeviceManager; +import com.apptentive.android.sdk.storage.DevicePayloadDiff; import com.apptentive.android.sdk.storage.EncryptedFileSerializer; import com.apptentive.android.sdk.storage.EventData; import com.apptentive.android.sdk.storage.FileSerializer; @@ -507,7 +507,7 @@ protected void execute() { Device lastSentDevice = getLastSentDevice(); Device currentDevice = getDevice(); assertNotNull(currentDevice, "Current device object is null"); - DevicePayload devicePayload = DeviceManager.getDiffPayload(lastSentDevice, currentDevice); + DevicePayload devicePayload = DevicePayloadDiff.getDiffPayload(lastSentDevice, currentDevice); if (devicePayload != null) { addPayload(devicePayload); setLastSentDevice(currentDevice != null ? currentDevice.clone() : null); diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/conversation/ConversationManager.java b/apptentive/src/main/java/com/apptentive/android/sdk/conversation/ConversationManager.java index 514d79b7d..ab233d99c 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/conversation/ConversationManager.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/conversation/ConversationManager.java @@ -33,6 +33,7 @@ import com.apptentive.android.sdk.storage.AppReleaseManager; import com.apptentive.android.sdk.storage.Device; import com.apptentive.android.sdk.storage.DeviceManager; +import com.apptentive.android.sdk.storage.DevicePayloadDiff; import com.apptentive.android.sdk.storage.Sdk; import com.apptentive.android.sdk.storage.SdkManager; import com.apptentive.android.sdk.storage.SerializerException; @@ -89,6 +90,11 @@ public class ConversationManager { */ private final Encryption encryption; + /** + * Responsible for generating device payloads. + */ + private final DeviceManager deviceManager; + /** * Current state of conversation metadata. */ @@ -103,7 +109,7 @@ public class ConversationManager { */ private boolean activeConversationFailedToResolve; // TODO: this is a temporary solution until we restore conversation state - public ConversationManager(@NonNull Context context, @NonNull File conversationsStorageDir, @NonNull Encryption encryption) { + public ConversationManager(@NonNull Context context, @NonNull File conversationsStorageDir, @NonNull Encryption encryption, @NonNull DeviceManager deviceManager) { if (context == null) { throw new IllegalArgumentException("Context is null"); } @@ -116,9 +122,14 @@ public ConversationManager(@NonNull Context context, @NonNull File conversations throw new IllegalArgumentException("Encryption is null"); } + if (deviceManager == null) { + throw new IllegalArgumentException("Device manager is null"); + } + this.contextRef = new WeakReference<>(context.getApplicationContext()); this.conversationsStorageDir = conversationsStorageDir; this.encryption = encryption; + this.deviceManager = deviceManager; ApptentiveNotificationCenter.defaultCenter() .addObserver(NOTIFICATION_APP_ENTERED_FOREGROUND, new ApptentiveNotificationObserver() { @@ -464,11 +475,11 @@ private HttpRequest fetchConversationToken(final Conversation conversation) { ConversationTokenRequest conversationTokenRequest = new ConversationTokenRequest(); // Send the Device and Sdk now, so they are available on the server from the start. - final Device device = DeviceManager.generateNewDevice(context); + final Device device = deviceManager.generateNewDevice(context); final Sdk sdk = SdkManager.generateCurrentSdk(context); final AppRelease appRelease = ApptentiveInternal.getInstance().getAppRelease(); - conversationTokenRequest.setDevice(DeviceManager.getDiffPayload(null, device)); + conversationTokenRequest.setDevice(DevicePayloadDiff.getDiffPayload(null, device)); conversationTokenRequest.setSdkAndAppRelease(SdkManager.getPayload(sdk), AppReleaseManager.getPayload(appRelease)); HttpRequest request = getHttpClient() @@ -957,7 +968,7 @@ public boolean accept(ConversationMetadataItem item) { setActiveConversation(new Conversation(dataFile, messagesFile, conversationEncryption, payloadEncryptionKey)); // TODO: if we don't set these here - device payload would return 4xx error code - activeConversation.setDevice(DeviceManager.generateNewDevice(getContext())); + activeConversation.setDevice(deviceManager.generateNewDevice(getContext())); activeConversation.setAppRelease(ApptentiveInternal.getInstance().getAppRelease()); activeConversation.setSdk(SdkManager.generateCurrentSdk(getContext())); } @@ -998,7 +1009,7 @@ private void sendFirstLoginRequest(final String userId, final String token, fina final AppRelease appRelease = ApptentiveInternal.getInstance().getAppRelease(); final Sdk sdk = SdkManager.generateCurrentSdk(getContext()); - final Device device = DeviceManager.generateNewDevice(getContext()); + final Device device = deviceManager.generateNewDevice(getContext()); HttpJsonRequest request = getHttpClient().createFirstLoginRequest(token, appRelease, sdk, device, new HttpRequest.Listener() { @Override diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/interaction/fragment/MessageCenterFragment.java b/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/interaction/fragment/MessageCenterFragment.java index 5e6912b99..debd7d76c 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/interaction/fragment/MessageCenterFragment.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/interaction/fragment/MessageCenterFragment.java @@ -1034,7 +1034,7 @@ public void onCloseWhoCard(String buttonLabel) { } private boolean shouldOpenComposerAfterClosingWhoCard() { - return interaction.getWhoCard().isRequire() && (recyclerViewContainsItemOfType(MESSAGE_CONTEXT) || recyclerViewContainsItemOfType(MESSAGE_OUTGOING)); + return interaction.getWhoCard().isRequire() && !recyclerViewContainsItemOfType(MESSAGE_OUTGOING); } public void cleanupWhoCard() { diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/module/messagecenter/model/WhoCard.java b/apptentive/src/main/java/com/apptentive/android/sdk/module/messagecenter/model/WhoCard.java index 6fe5aee85..77721ed39 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/module/messagecenter/model/WhoCard.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/module/messagecenter/model/WhoCard.java @@ -70,10 +70,6 @@ public String getTitle() { } public String getNameHint() { - if (isRequire() && isInitial()) { - // The Who Card will show up right when MC is opened in this scenario. Don't ask for the name at this time. - return null; - } return getApplicableConfig().optString(KEY_NAME_HINT, null); } diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/storage/DeviceManager.java b/apptentive/src/main/java/com/apptentive/android/sdk/storage/DeviceManager.java index 538a87e05..5e977d3b7 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/storage/DeviceManager.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/storage/DeviceManager.java @@ -12,21 +12,26 @@ import com.apptentive.android.sdk.ApptentiveLog; import com.apptentive.android.sdk.model.Configuration; -import com.apptentive.android.sdk.model.DevicePayload; import com.apptentive.android.sdk.util.AdvertiserManager; import com.apptentive.android.sdk.util.Constants; -import com.apptentive.android.sdk.util.Util; +import com.apptentive.android.sdk.util.StringUtils; import java.util.Locale; import java.util.TimeZone; import static com.apptentive.android.sdk.debug.ErrorMetrics.logException; -/** - * A helper class with static methods for and diffing information about the current device. - */ public class DeviceManager { - public static Device generateNewDevice(Context context) { + private final String androidID; + + public DeviceManager(String androidID) { + if (StringUtils.isNullOrEmpty(androidID)) { + throw new IllegalArgumentException("Android ID is null or empty"); + } + this.androidID = androidID; + } + + public Device generateNewDevice(Context context) { Device device = new Device(); // First, get all the information we can load from static resources. @@ -41,7 +46,7 @@ public static Device generateNewDevice(Context context) { device.setBrand(Build.BRAND); device.setCpu(Build.CPU_ABI); device.setDevice(Build.DEVICE); - device.setUuid(Util.getAndroidId(context)); + device.setUuid(androidID); device.setBuildType(Build.TYPE); device.setBuildId(Build.ID); @@ -82,150 +87,5 @@ public static Device generateNewDevice(Context context) { device.setUtcOffset(String.valueOf((TimeZone.getDefault().getRawOffset() / 1000))); return device; } - - public static DevicePayload getDiffPayload(com.apptentive.android.sdk.storage.Device oldDevice, com.apptentive.android.sdk.storage.Device newDevice) { - if (newDevice == null) { - return null; - } - - DevicePayload ret = new DevicePayload(); - boolean changed = false; - - if (oldDevice == null || !equal(oldDevice.getUuid(), newDevice.getUuid())) { - ret.setUuid(newDevice.getUuid()); - changed = true; - } - - if (oldDevice == null || !equal(oldDevice.getOsName(), newDevice.getOsName())) { - ret.setOsName(newDevice.getOsName()); - changed = true; - } - - if (oldDevice == null || !equal(oldDevice.getOsVersion(), newDevice.getOsVersion())) { - ret.setOsVersion(newDevice.getOsVersion()); - changed = true; - } - - if (oldDevice == null || !equal(oldDevice.getOsBuild(), newDevice.getOsBuild())) { - ret.setOsBuild(newDevice.getOsBuild()); - changed = true; - } - - if (oldDevice == null || oldDevice.getOsApiLevel() != newDevice.getOsApiLevel()) { - ret.setOsApiLevel(String.valueOf(newDevice.getOsApiLevel())); - changed = true; - } - - if (oldDevice == null || !equal(oldDevice.getManufacturer(), newDevice.getManufacturer())) { - ret.setManufacturer(newDevice.getManufacturer()); - changed = true; - } - - if (oldDevice == null || !equal(oldDevice.getModel(), newDevice.getModel())) { - ret.setModel(newDevice.getModel()); - changed = true; - } - - if (oldDevice == null || !equal(oldDevice.getBoard(), newDevice.getBoard())) { - ret.setBoard(newDevice.getBoard()); - changed = true; - } - - if (oldDevice == null || !equal(oldDevice.getProduct(), newDevice.getProduct())) { - ret.setProduct(newDevice.getProduct()); - changed = true; - } - - if (oldDevice == null || !equal(oldDevice.getBrand(), newDevice.getBrand())) { - ret.setBrand(newDevice.getBrand()); - changed = true; - } - - if (oldDevice == null || !equal(oldDevice.getCpu(), newDevice.getCpu())) { - ret.setCpu(newDevice.getCpu()); - changed = true; - } - - if (oldDevice == null || !equal(oldDevice.getDevice(), newDevice.getDevice())) { - ret.setDevice(newDevice.getDevice()); - changed = true; - } - - if (oldDevice == null || !equal(oldDevice.getCarrier(), newDevice.getCarrier())) { - ret.setCarrier(newDevice.getCarrier()); - changed = true; - } - - if (oldDevice == null || !equal(oldDevice.getCurrentCarrier(), newDevice.getCurrentCarrier())) { - ret.setCurrentCarrier(newDevice.getCurrentCarrier()); - changed = true; - } - - if (oldDevice == null || !equal(oldDevice.getNetworkType(), newDevice.getNetworkType())) { - ret.setNetworkType(newDevice.getNetworkType()); - changed = true; - } - - if (oldDevice == null || !equal(oldDevice.getBuildType(), newDevice.getBuildType())) { - ret.setBuildType(newDevice.getBuildType()); - changed = true; - } - - if (oldDevice == null || !equal(oldDevice.getBuildId(), newDevice.getBuildId())) { - ret.setBuildId(newDevice.getBuildId()); - changed = true; - } - - if (oldDevice == null || !equal(oldDevice.getBootloaderVersion(), newDevice.getBootloaderVersion())) { - ret.setBootloaderVersion(newDevice.getBootloaderVersion()); - changed = true; - } - - if (oldDevice == null || !equal(oldDevice.getRadioVersion(), newDevice.getRadioVersion())) { - ret.setRadioVersion(newDevice.getRadioVersion()); - changed = true; - } - - if (oldDevice == null || !equal(oldDevice.getCustomData(), newDevice.getCustomData())) { - CustomData customData = newDevice.getCustomData(); - ret.setCustomData(customData != null ? customData.toJson() : null); - changed = true; - } - - if (oldDevice == null || !equal(oldDevice.getLocaleCountryCode(), newDevice.getLocaleCountryCode())) { - ret.setLocaleCountryCode(newDevice.getLocaleCountryCode()); - changed = true; - } - - if (oldDevice == null || !equal(oldDevice.getLocaleLanguageCode(), newDevice.getLocaleLanguageCode())) { - ret.setLocaleLanguageCode(newDevice.getLocaleLanguageCode()); - changed = true; - } - - if (oldDevice == null || !equal(oldDevice.getLocaleRaw(), newDevice.getLocaleRaw())) { - ret.setLocaleRaw(newDevice.getLocaleRaw()); - changed = true; - } - - if (oldDevice == null || !equal(oldDevice.getUtcOffset(), newDevice.getUtcOffset())) { - ret.setUtcOffset(newDevice.getUtcOffset()); - changed = true; - } - - if (oldDevice == null || !equal(oldDevice.getAdvertiserId(), newDevice.getAdvertiserId())) { - ret.setAdvertiserId(newDevice.getAdvertiserId()); - changed = true; - } - - if (oldDevice == null || !equal(oldDevice.getIntegrationConfig(), newDevice.getIntegrationConfig())) { - IntegrationConfig integrationConfig = newDevice.getIntegrationConfig(); - ret.setIntegrationConfig(integrationConfig != null ? integrationConfig.toJson() : null); - changed = true; - } - return changed ? ret : null; - } - - private static boolean equal(Object a, Object b) { - return a == null && b == null || a != null && b != null && a.equals(b); - } } + diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/storage/DevicePayloadDiff.java b/apptentive/src/main/java/com/apptentive/android/sdk/storage/DevicePayloadDiff.java new file mode 100644 index 000000000..dcd857b8f --- /dev/null +++ b/apptentive/src/main/java/com/apptentive/android/sdk/storage/DevicePayloadDiff.java @@ -0,0 +1,155 @@ +package com.apptentive.android.sdk.storage; + +import com.apptentive.android.sdk.model.DevicePayload; + +/** + * A helper class with static methods for and diffing information about the current device. + */ +public final class DevicePayloadDiff { + + public static DevicePayload getDiffPayload(Device oldDevice, Device newDevice) { + if (newDevice == null) { + return null; + } + + DevicePayload ret = new DevicePayload(); + boolean changed = false; + + if (oldDevice == null || !equal(oldDevice.getUuid(), newDevice.getUuid())) { + ret.setUuid(newDevice.getUuid()); + changed = true; + } + + if (oldDevice == null || !equal(oldDevice.getOsName(), newDevice.getOsName())) { + ret.setOsName(newDevice.getOsName()); + changed = true; + } + + if (oldDevice == null || !equal(oldDevice.getOsVersion(), newDevice.getOsVersion())) { + ret.setOsVersion(newDevice.getOsVersion()); + changed = true; + } + + if (oldDevice == null || !equal(oldDevice.getOsBuild(), newDevice.getOsBuild())) { + ret.setOsBuild(newDevice.getOsBuild()); + changed = true; + } + + if (oldDevice == null || oldDevice.getOsApiLevel() != newDevice.getOsApiLevel()) { + ret.setOsApiLevel(String.valueOf(newDevice.getOsApiLevel())); + changed = true; + } + + if (oldDevice == null || !equal(oldDevice.getManufacturer(), newDevice.getManufacturer())) { + ret.setManufacturer(newDevice.getManufacturer()); + changed = true; + } + + if (oldDevice == null || !equal(oldDevice.getModel(), newDevice.getModel())) { + ret.setModel(newDevice.getModel()); + changed = true; + } + + if (oldDevice == null || !equal(oldDevice.getBoard(), newDevice.getBoard())) { + ret.setBoard(newDevice.getBoard()); + changed = true; + } + + if (oldDevice == null || !equal(oldDevice.getProduct(), newDevice.getProduct())) { + ret.setProduct(newDevice.getProduct()); + changed = true; + } + + if (oldDevice == null || !equal(oldDevice.getBrand(), newDevice.getBrand())) { + ret.setBrand(newDevice.getBrand()); + changed = true; + } + + if (oldDevice == null || !equal(oldDevice.getCpu(), newDevice.getCpu())) { + ret.setCpu(newDevice.getCpu()); + changed = true; + } + + if (oldDevice == null || !equal(oldDevice.getDevice(), newDevice.getDevice())) { + ret.setDevice(newDevice.getDevice()); + changed = true; + } + + if (oldDevice == null || !equal(oldDevice.getCarrier(), newDevice.getCarrier())) { + ret.setCarrier(newDevice.getCarrier()); + changed = true; + } + + if (oldDevice == null || !equal(oldDevice.getCurrentCarrier(), newDevice.getCurrentCarrier())) { + ret.setCurrentCarrier(newDevice.getCurrentCarrier()); + changed = true; + } + + if (oldDevice == null || !equal(oldDevice.getNetworkType(), newDevice.getNetworkType())) { + ret.setNetworkType(newDevice.getNetworkType()); + changed = true; + } + + if (oldDevice == null || !equal(oldDevice.getBuildType(), newDevice.getBuildType())) { + ret.setBuildType(newDevice.getBuildType()); + changed = true; + } + + if (oldDevice == null || !equal(oldDevice.getBuildId(), newDevice.getBuildId())) { + ret.setBuildId(newDevice.getBuildId()); + changed = true; + } + + if (oldDevice == null || !equal(oldDevice.getBootloaderVersion(), newDevice.getBootloaderVersion())) { + ret.setBootloaderVersion(newDevice.getBootloaderVersion()); + changed = true; + } + + if (oldDevice == null || !equal(oldDevice.getRadioVersion(), newDevice.getRadioVersion())) { + ret.setRadioVersion(newDevice.getRadioVersion()); + changed = true; + } + + if (oldDevice == null || !equal(oldDevice.getCustomData(), newDevice.getCustomData())) { + CustomData customData = newDevice.getCustomData(); + ret.setCustomData(customData != null ? customData.toJson() : null); + changed = true; + } + + if (oldDevice == null || !equal(oldDevice.getLocaleCountryCode(), newDevice.getLocaleCountryCode())) { + ret.setLocaleCountryCode(newDevice.getLocaleCountryCode()); + changed = true; + } + + if (oldDevice == null || !equal(oldDevice.getLocaleLanguageCode(), newDevice.getLocaleLanguageCode())) { + ret.setLocaleLanguageCode(newDevice.getLocaleLanguageCode()); + changed = true; + } + + if (oldDevice == null || !equal(oldDevice.getLocaleRaw(), newDevice.getLocaleRaw())) { + ret.setLocaleRaw(newDevice.getLocaleRaw()); + changed = true; + } + + if (oldDevice == null || !equal(oldDevice.getUtcOffset(), newDevice.getUtcOffset())) { + ret.setUtcOffset(newDevice.getUtcOffset()); + changed = true; + } + + if (oldDevice == null || !equal(oldDevice.getAdvertiserId(), newDevice.getAdvertiserId())) { + ret.setAdvertiserId(newDevice.getAdvertiserId()); + changed = true; + } + + if (oldDevice == null || !equal(oldDevice.getIntegrationConfig(), newDevice.getIntegrationConfig())) { + IntegrationConfig integrationConfig = newDevice.getIntegrationConfig(); + ret.setIntegrationConfig(integrationConfig != null ? integrationConfig.toJson() : null); + changed = true; + } + return changed ? ret : null; + } + + private static boolean equal(Object a, Object b) { + return a == null && b == null || a != null && b != null && a.equals(b); + } +} diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/util/Constants.java b/apptentive/src/main/java/com/apptentive/android/sdk/util/Constants.java index 4a03f626b..45090b7d8 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/util/Constants.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/util/Constants.java @@ -9,7 +9,7 @@ public class Constants { public static final int API_VERSION = 9; - private static final String APPTENTIVE_SDK_VERSION = "5.4.2"; + private static final String APPTENTIVE_SDK_VERSION = "5.4.3"; public static final int DEFAULT_CONNECT_TIMEOUT_MILLIS = 45000; public static final int DEFAULT_READ_TIMEOUT_MILLIS = 45000; diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/util/StringUtils.java b/apptentive/src/main/java/com/apptentive/android/sdk/util/StringUtils.java index 8df2c0b85..9c76ae3aa 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/util/StringUtils.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/util/StringUtils.java @@ -34,6 +34,8 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Random; +import java.util.UUID; /** * A collection of useful string-related functions @@ -234,4 +236,16 @@ public static int parseInt(String value, int defaultValue) { } //endregion + + //region Device identifiers + + public static String randomAndroidID() { + Random random = new Random(); + long lo = ((long) random.nextInt()) & 0xffffffffL; + long hi = ((long) random.nextInt()) << 32L; + long number = hi | lo; + return Long.toHexString(number); + } + + //endregion } diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/util/Util.java b/apptentive/src/main/java/com/apptentive/android/sdk/util/Util.java index 5395c1dca..ac2130421 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/util/Util.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/util/Util.java @@ -1118,7 +1118,7 @@ public static String humanReadableByteCount(long bytes, boolean si) { return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre); } - public static String getAndroidId(Context context) { + public static String getAndroidID(Context context) { if (context == null) { return null; } diff --git a/build.gradle b/build.gradle index 0fd28f57c..a9784273f 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.4.0' + classpath 'com.android.tools.build:gradle:3.4.1' classpath 'com.github.3mph4515:gradle-hockeyapp-plugin:3.7.6' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } diff --git a/samples/apptentive-example/build.gradle b/samples/apptentive-example/build.gradle index 390acd4ab..c18e7b375 100644 --- a/samples/apptentive-example/build.gradle +++ b/samples/apptentive-example/build.gradle @@ -16,7 +16,7 @@ repositories { } dependencies { - implementation 'com.apptentive:apptentive-android:5.3.3' + implementation 'com.apptentive:apptentive-android:5.4.2' implementation 'com.google.firebase:firebase-messaging:17.3.4' }