From 158fc9198eb56cf02f4eea29b2d718eb3ff2aea5 Mon Sep 17 00:00:00 2001 From: Barry Li Date: Wed, 15 Jun 2016 13:13:56 -0700 Subject: [PATCH 1/5] ANDROID-709 Using api-21 attribute statusbarColor needs to be checked. Also make sure if init() completes pre-maturely, it won't cause run-time exception --- .../android/sdk/ApptentiveInternal.java | 96 +++++++++---------- .../android/sdk/ApptentiveViewActivity.java | 27 +++--- 2 files changed, 61 insertions(+), 62 deletions(-) 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 05c53eb67..42d4c7995 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/ApptentiveInternal.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/ApptentiveInternal.java @@ -96,8 +96,8 @@ public class ApptentiveInternal { // toolbar theme specified in R.attr.apptentiveToolbarTheme Resources.Theme apptentiveToolbarTheme; - // app default theme res id, if specified in app AndroidManifest - int appDefaultThemeId; + // app default appcompat theme res id, if specified in app AndroidManifest + int appDefaultAppCompatThemeId; int statusBarColorDefault; String defaultAppDisplayName = "this app"; @@ -137,9 +137,6 @@ public static PushAction parse(String name) { private static volatile ApptentiveInternal sApptentiveInternal; - ApptentiveInternal() { - } - public static boolean isApptentiveRegistered() { return (sApptentiveInternal != null); @@ -208,7 +205,9 @@ public static ApptentiveInternal getInstance() { synchronized (ApptentiveInternal.class) { if (sApptentiveInternal != null && !isApptentiveInitialized.get()) { isApptentiveInitialized.set(true); - sApptentiveInternal.init(); + if (!sApptentiveInternal.init()) { + ApptentiveLog.e("Apptentive init() failed"); + } } } } @@ -249,11 +248,11 @@ static void setLifeCycleCallback() { synchronized (ApptentiveInternal.class) { if (sApptentiveInternal != null && sApptentiveInternal.lifecycleCallbacks == null && sApptentiveInternal.appContext instanceof Application) { - sApptentiveInternal.lifecycleCallbacks = new ApptentiveActivityLifecycleCallbacks(); - ((Application) sApptentiveInternal.appContext).registerActivityLifecycleCallbacks(sApptentiveInternal.lifecycleCallbacks); - } + sApptentiveInternal.lifecycleCallbacks = new ApptentiveActivityLifecycleCallbacks(); + ((Application) sApptentiveInternal.appContext).registerActivityLifecycleCallbacks(sApptentiveInternal.lifecycleCallbacks); } } + } } /* @@ -453,21 +452,9 @@ public void updateApptentiveInteractionTheme(Resources.Theme interactionTheme, C interactionTheme.applyStyle(R.style.ApptentiveTheme_Base_Versioned, true); } - // Step 2: Inherit app default theme if there is one specified in app's AndroidManifest - if (appDefaultThemeId != 0) { - Resources.Theme appDefaultTheme = context.getResources().newTheme(); - appDefaultTheme.applyStyle(appDefaultThemeId, true); - - // If the app contains colorPrimaryDark, it is using an AppCompat theme. Therefore, we want to use it. - // If it's not using an AppCompat theme, we don't want to apply it to our SDK, and use our default theme instead. - TypedArray a = appDefaultTheme.obtainStyledAttributes(android.support.v7.appcompat.R.styleable.AppCompatTheme); - try { - if (a.hasValue(android.support.v7.appcompat.R.styleable.AppCompatTheme_colorPrimaryDark)) { - interactionTheme.applyStyle(appDefaultThemeId, true); - } - } finally { - a.recycle(); - } + // Step 2: Inherit app default appcompat theme if there is one specified in app's AndroidManifest + if (appDefaultAppCompatThemeId != 0) { + interactionTheme.applyStyle(appDefaultAppCompatThemeId, true); } // Step 3: Restore Apptentive UI window properties that may have been overridden in Step 2. This theme @@ -481,26 +468,34 @@ public void updateApptentiveInteractionTheme(Resources.Theme interactionTheme, C interactionTheme.applyStyle(themeOverrideResId, true); } - int transparentColor = ContextCompat.getColor(context, android.R.color.transparent); - TypedArray a = interactionTheme.obtainStyledAttributes(new int[]{android.R.attr.statusBarColor}); - try { - statusBarColorDefault = a.getColor(0, transparentColor); - } finally { - a.recycle(); + // Step 5: Update statusbar color + /* Obtain the default statusbar color. When Apptentive Modal interaction is shown, + * a translucent overlay would be applied on top of statusBarColorDefault + */ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + int transparentColor = ContextCompat.getColor(context, android.R.color.transparent); + TypedArray a = interactionTheme.obtainStyledAttributes(new int[]{android.R.attr.statusBarColor}); + try { + statusBarColorDefault = a.getColor(0, transparentColor); + } finally { + a.recycle(); + } } + // Step 6: Update toolbar overlay theme int toolbarThemeId = Util.getResourceIdFromAttribute(interactionTheme, R.attr.apptentiveToolbarTheme); apptentiveToolbarTheme.setTo(interactionTheme); apptentiveToolbarTheme.applyStyle(toolbarThemeId, true); } - public void init() { - + public boolean init() { + boolean bRet = true; codePointStore.init(); messageManager.init(); conversationToken = prefs.getString(Constants.PREF_KEY_CONVERSATION_TOKEN, null); conversationId = prefs.getString(Constants.PREF_KEY_CONVERSATION_ID, null); personId = prefs.getString(Constants.PREF_KEY_PERSON_ID, null); + apptentiveToolbarTheme = appContext.getResources().newTheme(); boolean apptentiveDebug = false; String logLevelOverride = null; @@ -519,31 +514,26 @@ public void init() { isAppDebuggable = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; } - /* - * Construct the theme used by Apptentive Ui in the following order - * 1. start with Apptentive default theme : ApptentiveTheme.Base.Versioned - * 2. merge with app default theme, if one is specified and it's a AppCompat theme. - * (merge takes value from app default theme if both define the same attribute) - * 3. apply Apptentive UI specific frame style - * 4. Apply apptentive theme override specified by the app in ApptentiveThemeOverride - */ - appDefaultThemeId = ai.theme; - boolean appHasTheme = appDefaultThemeId != 0; + // Obtain Apptentive theme inheritance metrics + int appDefaultThemeId = ai.theme; boolean appThemeIsAppCompatTheme = false; - apptentiveToolbarTheme = appContext.getResources().newTheme(); - - // Step 2, check if app specifies a default appcompat theme - if (appHasTheme) { + // check if app specifies a default appcompat theme + if (appDefaultThemeId != 0) { Resources.Theme appDefaultTheme = appContext.getResources().newTheme(); appDefaultTheme.applyStyle(appDefaultThemeId, true); - // If the app contains colorPrimaryDark, it is using an AppCompat theme. Therefore, we want to use it. - // If it's not using an AppCompat theme, we don't want to apply it to our SDK, and use our default theme instead. - appThemeIsAppCompatTheme = Util.getThemeColor(appDefaultTheme, R.attr.colorPrimaryDark) != 0; - + TypedArray a = appDefaultTheme.obtainStyledAttributes(android.support.v7.appcompat.R.styleable.AppCompatTheme); + try { + if (a.hasValue(android.support.v7.appcompat.R.styleable.AppCompatTheme_colorPrimaryDark)) { + appThemeIsAppCompatTheme = true; + appDefaultAppCompatThemeId = appDefaultThemeId; + } + } finally { + a.recycle(); + } } - + // check if host app defines an apptentive theme override int themeOverrideResId = appContext.getResources().getIdentifier("ApptentiveThemeOverride", "style", appPackageName); Integer currentVersionCode = packageInfo.versionCode; @@ -555,6 +545,7 @@ public void init() { appRelease.setBuildNumber(String.valueOf(currentVersionCode)); appRelease.setTargetSdkVersion(String.valueOf(packageInfo.applicationInfo.targetSdkVersion)); appRelease.setAppStore(Util.getInstallerPackageName(appContext)); + // Set Apptentive theme inheritance metrics appRelease.setInheritStyle(appThemeIsAppCompatTheme); appRelease.setOverrideStyle(themeOverrideResId != 0); @@ -580,8 +571,10 @@ public void init() { } catch (Exception e) { ApptentiveLog.e("Unexpected error while reading application or package info.", e); + bRet = false; } + // Set debuggable and appropriate log level. if (apptentiveDebug) { ApptentiveLog.i("Apptentive debug logging set to VERBOSE."); @@ -624,6 +617,7 @@ public void init() { ApptentiveLog.d("Android ID: ", androidId); ApptentiveLog.d("Default Locale: %s", Locale.getDefault().toString()); ApptentiveLog.d("Conversation id: %s", prefs.getString(Constants.PREF_KEY_CONVERSATION_ID, "null")); + return bRet; } private void onVersionChanged(Integer previousVersionCode, Integer currentVersionCode, String previousVersionName, String currentVersionName, AppRelease currentAppRelease) { diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/ApptentiveViewActivity.java b/apptentive/src/main/java/com/apptentive/android/sdk/ApptentiveViewActivity.java index bdffba644..73845b9e5 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/ApptentiveViewActivity.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/ApptentiveViewActivity.java @@ -269,18 +269,22 @@ public void onFragmentTransition(ApptentiveBaseFragment currentFragment) { private void applyApptentiveTheme(boolean isModalInteraction) { // Update the activity theme to reflect current attributes - ApptentiveInternal.getInstance().updateApptentiveInteractionTheme(getTheme(), this); + try { + ApptentiveInternal.getInstance().updateApptentiveInteractionTheme(getTheme(), this); - if (isModalInteraction) { - getTheme().applyStyle(R.style.ApptentiveBaseDialogTheme, true); - setStatusBarColor(ApptentiveInternal.getInstance().getDefaultStatusbarColor()); - } + if (isModalInteraction) { + getTheme().applyStyle(R.style.ApptentiveBaseDialogTheme, true); + setStatusBarColor(); + } - // Change the thumbnail header color in task list - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - int colorPrimary = Util.getThemeColor(getTheme(), R.attr.colorPrimary); - ActivityManager.TaskDescription taskDes = new ActivityManager.TaskDescription(null, null, colorPrimary); - setTaskDescription(taskDes); + // Change the thumbnail header color in task list + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + int colorPrimary = Util.getThemeColor(getTheme(), R.attr.colorPrimary); + ActivityManager.TaskDescription taskDes = new ActivityManager.TaskDescription(null, null, colorPrimary); + setTaskDescription(taskDes); + } + } catch (Exception e) { + ApptentiveLog.e("Error apply Apptentive Theme.", e); } } @@ -387,9 +391,10 @@ public void onGlobalLayout() { * color defined by apptentive_activity_frame * @param statusBarDefaultColor the default activity status bar color specified by the app */ - private void setStatusBarColor(int statusBarDefaultColor) { + private void setStatusBarColor() { // Changing status bar color is a post-21 feature if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + int statusBarDefaultColor = ApptentiveInternal.getInstance().getDefaultStatusbarColor(); int overlayColor = ContextCompat.getColor(this, R.color.apptentive_activity_frame_dark); getWindow().setStatusBarColor(Util.alphaMixColors(statusBarDefaultColor, overlayColor)); } From 04ea18d10d5380acef585db697c805ea66f3f31b Mon Sep 17 00:00:00 2001 From: Barry Li Date: Fri, 17 Jun 2016 12:39:02 -0700 Subject: [PATCH 2/5] ANDROID-690 don't poll for message until Message Center feature has been used --- .../android/sdk/ApptentiveInternal.java | 11 ++++++- .../module/messagecenter/MessageManager.java | 30 +++++++++++++++++-- .../messagecenter/MessagePollingWorker.java | 27 +++++++++-------- .../android/sdk/util/Constants.java | 1 + 4 files changed, 52 insertions(+), 17 deletions(-) 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 42d4c7995..9656bb98b 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/ApptentiveInternal.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/ApptentiveInternal.java @@ -491,7 +491,16 @@ public void updateApptentiveInteractionTheme(Resources.Theme interactionTheme, C public boolean init() { boolean bRet = true; codePointStore.init(); - messageManager.init(); + /* If Message Center feature has never been used before, don't initialize message polling thread. + * Message Center feature will be seen as used, if one of the following conditions has been met: + * 1. Message Center has been opened for the first time + * 2. The first Push is received which would open Message Center + * 3. An unreadMessageCountListener() is set up + */ + boolean featureEverUsed = prefs.getBoolean(Constants.PREF_KEY_MESSAGE_CENTER_FEATURE_USED, false); + if (featureEverUsed) { + messageManager.init(); + } conversationToken = prefs.getString(Constants.PREF_KEY_CONVERSATION_TOKEN, null); conversationId = prefs.getString(Constants.PREF_KEY_CONVERSATION_ID, null); personId = prefs.getString(Constants.PREF_KEY_PERSON_ID, null); diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/module/messagecenter/MessageManager.java b/apptentive/src/main/java/com/apptentive/android/sdk/module/messagecenter/MessageManager.java index 9f3c559ea..315eded3f 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/module/messagecenter/MessageManager.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/module/messagecenter/MessageManager.java @@ -10,6 +10,7 @@ import android.app.Notification; import android.app.PendingIntent; import android.content.Context; +import android.content.SharedPreferences; import android.os.AsyncTask; import android.os.Build; import android.os.Handler; @@ -28,6 +29,7 @@ import com.apptentive.android.sdk.module.messagecenter.model.MessageFactory; import com.apptentive.android.sdk.module.metric.MetricModule; import com.apptentive.android.sdk.storage.MessageStore; +import com.apptentive.android.sdk.util.Constants; import com.apptentive.android.sdk.util.Util; import org.json.JSONArray; @@ -38,6 +40,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; /** * @author Sky Kelsey @@ -67,15 +70,17 @@ public class MessageManager { */ private final List> hostUnreadMessagesListeners = new ArrayList>(); + AtomicBoolean appInForeground = new AtomicBoolean(false); + private Handler uiHandler; private MessagePollingWorker pollingWorker; - public MessageManager() { } + // init() will start polling worker. public void init() { if (uiHandler == null) { uiHandler = new Handler(Looper.getMainLooper()) { @@ -104,6 +109,14 @@ public void handleMessage(android.os.Message msg) { } if (pollingWorker == null) { pollingWorker = new MessagePollingWorker(this); + /* Set SharePreference to indicate Message Center feature is desired. It will always be checked + * during Apptentive initialization. + */ + SharedPreferences prefs = ApptentiveInternal.getInstance().getSharedPrefs(); + boolean featureEverUsed = prefs.getBoolean(Constants.PREF_KEY_MESSAGE_CENTER_FEATURE_USED, false); + if (!featureEverUsed) { + prefs.edit().putBoolean(Constants.PREF_KEY_MESSAGE_CENTER_FEATURE_USED, true).apply(); + } } } @@ -112,6 +125,8 @@ public void handleMessage(android.os.Message msg) { * when push is received on the device. */ public void startMessagePreFetchTask() { + // Defer message polling thread creation, if not created yet and host app receives a new message push + init(); AsyncTask task = new AsyncTask() { private Exception e = null; @@ -360,6 +375,7 @@ public void setAfterSendMessageListener(AfterSendMessageListener listener) { public void addInternalOnMessagesUpdatedListener(OnNewIncomingMessagesListener newlistener) { if (newlistener != null) { + init(); for (Iterator> iterator = internalNewMessagesListeners.iterator(); iterator.hasNext(); ) { WeakReference listenerRef = iterator.next(); OnNewIncomingMessagesListener listener = listenerRef.get(); @@ -396,6 +412,8 @@ public void setHostUnreadMessagesListener(UnreadMessagesListener newlistener) { public void addHostUnreadMessagesListener(UnreadMessagesListener newlistener) { if (newlistener != null) { + // Defer message polling thread creation, if not created yet, and host app adds an unread message listener + init(); for (Iterator> iterator = hostUnreadMessagesListeners.iterator(); iterator.hasNext(); ) { WeakReference listenerRef = iterator.next(); UnreadMessagesListener listener = listenerRef.get(); @@ -471,11 +489,17 @@ public void run() { public void appWentToForeground() { - pollingWorker.appWentToForeground(); + appInForeground.set(true); + if (pollingWorker != null) { + pollingWorker.appWentToForeground(); + } } public void appWentToBackground() { - pollingWorker.appWentToBackground(); + appInForeground.set(false); + if (pollingWorker != null) { + pollingWorker.appWentToBackground(); + } } } diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/module/messagecenter/MessagePollingWorker.java b/apptentive/src/main/java/com/apptentive/android/sdk/module/messagecenter/MessagePollingWorker.java index 7efb58e97..f5d4f4052 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/module/messagecenter/MessagePollingWorker.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/module/messagecenter/MessagePollingWorker.java @@ -22,7 +22,6 @@ public class MessagePollingWorker { private MessagePollingThread sPollingThread; // The following booleans will be accessed by both ui thread and worker thread - private AtomicBoolean appInForeground = new AtomicBoolean(false); public AtomicBoolean messageCenterInForeground = new AtomicBoolean(false); private AtomicBoolean threadRunning = new AtomicBoolean(false); @@ -74,7 +73,7 @@ public void run() { try { ApptentiveLog.v("Started %s", toString()); - while (appInForeground.get()) { + while (manager.appInForeground.get()) { MessagePollingThread thread = getAndSetMessagePollingThread(true, false); if (thread != null && thread != MessagePollingThread.this) { return; @@ -112,20 +111,12 @@ private void wakeUp() { // Called from main UI thread to create a new worker thread public void appWentToForeground() { - appInForeground.set(true); - if (threadRunning.compareAndSet(false, true)) { - /* appInForeground was "false", and set to "true" - * thread was not running, and set to be running - */ - getAndSetMessagePollingThread(true, true); - } else { - wakeUp(); - } + startPolling(); } public void appWentToBackground() { - appInForeground.set(false); + // When app goes to background, polling thread will be waken up, and finish and terminate wakeUp(); } @@ -139,8 +130,18 @@ public void appWentToBackground() { public void setMessageCenterInForeground(boolean bInForeground) { if (!messageCenterInForeground.getAndSet(bInForeground) && bInForeground) { /* bInForeground is "true" && messageCenterInForeground was false - * Thread will wake up, then continue the while loop and proceed with fetching */ + startPolling(); + } + } + + private void startPolling() { + if (threadRunning.compareAndSet(false, true)) { + // If polling thread isn't running (either terminated or hasn't been created it), create a new one and run + getAndSetMessagePollingThread(true, true); + } else { + // If polling thread has been created but in sleep, wake it up, then continue the while loop and proceed + // with fetching with shorter interval wakeUp(); } } 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 7f1c04362..8d912acbd 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 @@ -44,6 +44,7 @@ public class Constants { public static final String PREF_KEY_APP_ACTIVITY_STATE_QUEUE = "appActivityStateQueue"; + public static final String PREF_KEY_MESSAGE_CENTER_FEATURE_USED = "messageCenterFeatureUsed"; public static final String PREF_KEY_MESSAGE_CENTER_PENDING_COMPOSING_MESSAGE = "messageCenterPendingComposingMessage"; public static final String PREF_KEY_MESSAGE_CENTER_PENDING_COMPOSING_ATTACHMENTS = "messageCenterPendingComposingAttachments"; public static final String PREF_KEY_MESSAGE_CENTER_SERVER_ERROR_LAST_ATTEMPT = "messageCenterServerErrorLastAttempt"; From f911ddcab32a4fa1215c512ff508c8e8216662a1 Mon Sep 17 00:00:00 2001 From: Barry Li Date: Fri, 17 Jun 2016 14:16:25 -0700 Subject: [PATCH 3/5] ANDROID-710 Add an internal setter method to set the default theme res id whom Apptentive will inherit theme from. --- .../android/sdk/ApptentiveInternal.java | 57 ++++++++++++------- 1 file changed, 37 insertions(+), 20 deletions(-) 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 9656bb98b..d97a71317 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/ApptentiveInternal.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/ApptentiveInternal.java @@ -255,6 +255,39 @@ static void setLifeCycleCallback() { } } + + /* + * Set default theme whom Apptentive UI will inherit theme attributes from + * @param themeResId : resource id of the theme style definition + * @return true if the theme is an AppCompatTheme + */ + public boolean setApplicationDefaultTheme(int themeResId) { + try { + if (themeResId != 0) { + // If passed theme res id does not exist, an exception would be thrown and caught + appContext.getResources().getResourceName(themeResId); + + // Check if the theme to be inheritd from is an AppCompat theme. + Resources.Theme appDefaultTheme = appContext.getResources().newTheme(); + appDefaultTheme.applyStyle(themeResId, true); + + TypedArray a = appDefaultTheme.obtainStyledAttributes(android.support.v7.appcompat.R.styleable.AppCompatTheme); + try { + if (a.hasValue(android.support.v7.appcompat.R.styleable.AppCompatTheme_colorPrimaryDark)) { + // Only set to use if it's an AppCompat theme. See updateApptentiveInteractionTheme() for theme inheritance chain + appDefaultAppCompatThemeId = themeResId; + return true; + } + } finally { + a.recycle(); + } + } + } catch (Resources.NotFoundException e) { + ApptentiveLog.e("Theme Res id not found"); + } + return false; + } + /* * Object getter methods through ApptentiveInternal instance * Usage: ApptentiveInternal.getInstance().getxxxx() @@ -444,8 +477,8 @@ public void onAppEnterBackground() { */ public void updateApptentiveInteractionTheme(Resources.Theme interactionTheme, Context context) { /* Step 1: Apply Apptentive default theme layer. - * If host activity is an Apptentive activity, the base theme already has Apptentive defaults applied, so skip Step 1. - * If parent activity is NOT an Apptentive activity, first apply Apptentive defaults. + * If host activity is an activity, the base theme already has Apptentive defaults applied, so skip Step 1. + * If parent activity is NOT an activity, first apply Apptentive defaults. */ if (!(context instanceof Activity)) { // If host context is not an activity, i.e. application context, treat it as initial theme setup @@ -523,25 +556,9 @@ public boolean init() { isAppDebuggable = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; } - // Obtain Apptentive theme inheritance metrics - int appDefaultThemeId = ai.theme; - boolean appThemeIsAppCompatTheme = false; + // If the app default theme specified in AndroidManifest is an AppCompat theme, use it for Apptentive theme inheritance. + boolean appThemeIsAppCompatTheme = setApplicationDefaultTheme(ai.theme); - // check if app specifies a default appcompat theme - if (appDefaultThemeId != 0) { - Resources.Theme appDefaultTheme = appContext.getResources().newTheme(); - appDefaultTheme.applyStyle(appDefaultThemeId, true); - - TypedArray a = appDefaultTheme.obtainStyledAttributes(android.support.v7.appcompat.R.styleable.AppCompatTheme); - try { - if (a.hasValue(android.support.v7.appcompat.R.styleable.AppCompatTheme_colorPrimaryDark)) { - appThemeIsAppCompatTheme = true; - appDefaultAppCompatThemeId = appDefaultThemeId; - } - } finally { - a.recycle(); - } - } // check if host app defines an apptentive theme override int themeOverrideResId = appContext.getResources().getIdentifier("ApptentiveThemeOverride", "style", appPackageName); From b10aac18b372adafce2953935f9a43bb9166991a Mon Sep 17 00:00:00 2001 From: Barry Li Date: Fri, 17 Jun 2016 14:35:15 -0700 Subject: [PATCH 4/5] ANDROID-710 update comments per PR review --- .../com/apptentive/android/sdk/ApptentiveInternal.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 d97a71317..09d558d14 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/ApptentiveInternal.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/ApptentiveInternal.java @@ -257,9 +257,10 @@ static void setLifeCycleCallback() { /* - * Set default theme whom Apptentive UI will inherit theme attributes from - * @param themeResId : resource id of the theme style definition - * @return true if the theme is an AppCompatTheme + * Set default theme whom Apptentive UI will inherit theme attributes from. Apptentive will only + * inherit from an AppCompat theme + * @param themeResId : resource id of the theme style definition, such as R.style.MyAppTheme + * @return true if the theme is set for inheritance successfully. */ public boolean setApplicationDefaultTheme(int themeResId) { try { From d5626d4333d3c9edaa8c23921801c07023f7d40b Mon Sep 17 00:00:00 2001 From: Barry Li Date: Fri, 17 Jun 2016 15:15:07 -0700 Subject: [PATCH 5/5] Release updates for 3.1.1 --- .idea/runConfigurations.xml | 12 ------------ CHANGELOG.md | 11 +++++++++++ README.md | 2 +- .../com/apptentive/android/sdk/util/Constants.java | 2 +- 4 files changed, 13 insertions(+), 14 deletions(-) delete mode 100644 .idea/runConfigurations.xml diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 7f68460d8..000000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e85a25e6..1a1f52385 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +# 2016-06-20 - 3.1.1 + +#### Improvements + +* Defer message polling task until Message Center is opened, UnreadMessagesListener is registered, or a Push is received. +* Add internal method to set the application theme programmatically from which the Apptentive UI will inherit styles. + +#### Bugs Fixed + +* statusBarColor attribute was causing run-time exception on pre-21 devices. + # 2016-06-08 - 3.1.0 #### Improvements diff --git a/README.md b/README.md index cf48b46ee..e512cad5c 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. ##### Version history is tracked [here](CHANGELOG.md) -##### Binary releases are hosted for Maven [here](http://search.maven.org/#artifactdetails|com.apptentive|apptentive-android|3.1.0|aar) +##### Binary releases are hosted for Maven [here](http://search.maven.org/#artifactdetails|com.apptentive|apptentive-android|3.1.1|aar) #### Reporting Bugs We encourage you to help us find and fix bugs. If you find a bug, please fill in the contributor agreement, then open a [github issue](https://github.com/apptentive/apptentive-android/issues?direction=desc&sort=created&state=open). 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 8d912acbd..ed06dd96e 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 @@ -11,7 +11,7 @@ */ public class Constants { - public static final String APPTENTIVE_SDK_VERSION = "3.1.0"; + public static final String APPTENTIVE_SDK_VERSION = "3.1.1"; public static final int REQUEST_CODE_PHOTO_FROM_SYSTEM_PICKER = 10;