diff --git a/CHANGELOG.md b/CHANGELOG.md index 260d7f558..7f697b2ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# 2016-05-25 - 3.0.1 + +#### Bugs Fixed + +* Thumbnail in Recents Screen was not using host app's theme when the top Activity was an Apptentive Activity. +* Improve foreground / background detection. +* Fix borderless button styling before API level 23. +* Fix our UI to work better when the host app uses a translucent status bar. +* Fix window panning issue that can result in the keyboard coering part of a survey. + # 2016-04-26 - 3.0.0 #### Improvements diff --git a/README.md b/README.md index c94fbb7b4..db19c54c7 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.0.0|aar) +##### Binary releases are hosted for Maven [here](http://search.maven.org/#artifactdetails|com.apptentive|apptentive-android|3.0.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/build.gradle b/apptentive/build.gradle index 72bac14bc..a3f7057db 100644 --- a/apptentive/build.gradle +++ b/apptentive/build.gradle @@ -7,19 +7,19 @@ repositories { } dependencies { - compile 'com.android.support:support-v4:23.2.1' - compile 'com.android.support:appcompat-v7:23.2.1' - compile 'com.android.support:cardview-v7:23.2.1' - compile 'com.android.support:design:23.2.1' + compile 'com.android.support:support-v4:23.3.0' + compile 'com.android.support:appcompat-v7:23.3.0' + compile 'com.android.support:cardview-v7:23.3.0' + compile 'com.android.support:design:23.3.0' } android { - compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION) - buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION + compileSdkVersion 23 + buildToolsVersion '23.0.3' defaultConfig { - minSdkVersion Integer.parseInt(project.ANDROID_BUILD_MIN_SDK_VERSION) - targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION) + minSdkVersion 14 + targetSdkVersion 23 // BUILD_NUMBER is provided by Jenkins. Default to 1 in dev builds. versionCode System.getenv("BUILD_NUMBER") as Integer ?: 1 versionName project.version 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 996da2b5a..cdb10cc8d 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/ApptentiveInternal.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/ApptentiveInternal.java @@ -107,6 +107,7 @@ public class ApptentiveInternal { ExecutorService cachedExecutor; + // Holds reference to the current foreground activity of the host app private WeakReference currentTaskStackTopActivity; // Used for temporarily holding customData that needs to be sent on the next message the consumer sends. @@ -252,11 +253,11 @@ public Context getApplicationContext() { return appContext; } - /* Get the last running activity from the current application, i.e. at the top of the task - * It is tracked through {@link #onActivityStarted(Activity)} and {@link #onActivityDestroyed(Activity)} + /* Get the foreground activity from the current application, i.e. at the top of the task + * It is tracked through {@link #onActivityStarted(Activity)} and {@link #onActivityStopped(Activity)} * - * If Apptentive interaction is to be launched from a non-activity context, use the last running activity at - * the top of the task. + * If Apptentive interaction is to be launched from a non-activity context, use the current activity at + * the top of the task stack, i.e. the foreground activity. */ public Activity getCurrentTaskStackTopActivity() { if (currentTaskStackTopActivity != null) { @@ -383,12 +384,10 @@ public void onAppExit(final Context appContext) { public void onActivityStarted(Activity activity) { if (activity != null) { + // Set current foreground activity reference whenever a new activity is started currentTaskStackTopActivity = new WeakReference(activity); + messageManager.setCurrentForgroundActivity(activity); } - } - - public void onActivityResumed(Activity activity) { - messageManager.setCurrentForgroundActivity(activity); checkAndUpdateApptentiveConfigurations(); @@ -397,13 +396,13 @@ public void onActivityResumed(Activity activity) { syncPerson(); } - public void onActivityDestroyed(Activity activity) { - if (activity != null && currentTaskStackTopActivity != null) { - Activity currentBottomActivity = currentTaskStackTopActivity.get(); - if (currentBottomActivity != null && currentBottomActivity == activity) { - currentTaskStackTopActivity = null; - } + public void onActivityResumed(Activity activity) { + if (activity != null) { + // Set current foreground activity reference whenever a new activity is started + currentTaskStackTopActivity = new WeakReference(activity); + messageManager.setCurrentForgroundActivity(activity); } + } public void onAppEnterForeground() { @@ -414,6 +413,8 @@ public void onAppEnterForeground() { public void onAppEnterBackground() { appIsInForeground = false; + currentTaskStackTopActivity = null; + messageManager.setCurrentForgroundActivity(null); payloadWorker.appWentToBackground(); messageManager.appWentToBackground(); } 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 eb01a62e9..99ecd6291 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/ApptentiveViewActivity.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/ApptentiveViewActivity.java @@ -7,12 +7,16 @@ package com.apptentive.android.sdk; +import android.app.ActivityManager; import android.content.ComponentName; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Resources; +import android.content.res.TypedArray; import android.graphics.PorterDuff; +import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.Bundle; import android.support.v4.app.FragmentManager; @@ -24,6 +28,7 @@ import android.support.v7.widget.Toolbar; import android.view.MenuItem; import android.view.View; +import android.view.ViewTreeObserver; import android.view.WindowManager; import com.apptentive.android.sdk.adapter.ApptentiveViewPagerAdapter; @@ -47,12 +52,16 @@ public class ApptentiveViewActivity extends AppCompatActivity implements Apptent private int current_tab; + private View decorView; + private View contentView; + protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Bundle bundle = FragmentFactory.addDisplayModeToFragmentBundle(getIntent().getExtras()); + boolean isInteractionModal = bundle.getBoolean(Constants.FragmentConfigKeys.MODAL); // Add theme based on the display mode of the interaction: modal or not - applyApptentiveTheme(bundle.getBoolean(Constants.FragmentConfigKeys.MODAL)); + applyApptentiveTheme(isInteractionModal); ApptentiveBaseFragment newFragment = null; @@ -64,8 +73,8 @@ protected void onCreate(Bundle savedInstanceState) { * failure of retrieval indicate internal error */ if (newFragment == null) { - finish(); - return; + finish(); + return; } } try { @@ -78,7 +87,8 @@ protected void onCreate(Bundle savedInstanceState) { bundle.putInt("toolbarLayoutId", R.id.apptentive_toolbar); if (newFragment == null) { newFragment = FragmentFactory.createFragmentInstance(bundle); - applyApptentiveTheme(newFragment.isShownAsModelDialog()); + isInteractionModal = newFragment.isShownAsModalDialog(); + applyApptentiveTheme(isInteractionModal); } if (newFragment != null) { newFragment.setOnTransitionListener(this); @@ -106,6 +116,12 @@ protected void onCreate(Bundle savedInstanceState) { toolbar = (Toolbar) findViewById(R.id.apptentive_toolbar); setSupportActionBar(toolbar); + + /* Add top padding by the amount of Status Bar height to avoid toolbar being covered when + * status bar is translucent + */ + toolbar.setPadding(0, getToolbarHeightAdjustment(!isInteractionModal), 0, 0); + ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { @@ -142,7 +158,7 @@ protected void onCreate(Bundle savedInstanceState) { @Override public void onPageSelected(int position) { ApptentiveBaseFragment currentFragment = (ApptentiveBaseFragment) viewPager_Adapter.getItem(viewPager.getCurrentItem()); - if (!currentFragment.isShownAsModelDialog()) { + if (!currentFragment.isShownAsModalDialog()) { final String title = currentFragment.getTitle(); toolbar.post(new Runnable() { @@ -237,7 +253,7 @@ public void onSaveInstanceState(Bundle outState) { public void onFragmentTransition(ApptentiveBaseFragment currentFragment) { if (currentFragment != null) { int numberOfPages = viewPager_Adapter.getCount(); - for (int i = 0; i< numberOfPages; ++i) { + for (int i = 0; i < numberOfPages; ++i) { ApptentiveBaseFragment fragment = (ApptentiveBaseFragment) viewPager_Adapter.getItem(i); if (currentFragment == fragment) { if (numberOfPages == 1) { @@ -259,6 +275,13 @@ private void applyApptentiveTheme(boolean isModalInteraction) { apptentiveTheme.applyStyle(R.style.ApptentiveBaseDialogTheme, true); } getTheme().setTo(apptentiveTheme); + + // Change the thumbnail header color in task list + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + int colorPrimary = Util.getThemeColor(apptentiveTheme, R.attr.colorPrimary); + ActivityManager.TaskDescription taskDes = new ActivityManager.TaskDescription(null, null, colorPrimary); + setTaskDescription(taskDes); + } } private void addFragmentToAdapter(ApptentiveBaseFragment f, String title) { @@ -288,4 +311,75 @@ private void startLauncherActivityIfRoot() { } } + /* Android versions starting with API Level 19, setting translucent statusbar would have two implications: + * 1. toolbar of non-model interaction would be partially covered + * 2. Keyboard launch won't resize window. (Bug: https://code.google.com/p/android/issues/detail?id=63777) + * The following method will fix both issues + */ + private int getToolbarHeightAdjustment(boolean bToolbarShown) { + int adjustAmount = 0; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + boolean translucentStatus = false; + // check theme attrs to see if translucent statusbar is set explicitly + int[] attrs = {android.R.attr.windowTranslucentStatus}; + TypedArray a = ApptentiveInternal.getInstance().getApptentiveTheme().obtainStyledAttributes(attrs); + try { + translucentStatus = a.getBoolean(0, false); + } finally { + a.recycle(); + } + + // also check window flags in case translucent statusbar is set implicitly + WindowManager.LayoutParams winParams = getWindow().getAttributes(); + int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; + if ((winParams.flags & bits) != 0) { + translucentStatus = true; + } + + if (translucentStatus) { + if (bToolbarShown) { + int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + adjustAmount = getResources().getDimensionPixelSize(resourceId); + } + } + + /* Add layout listener to ensure keyboard launch resize the screen when android:windowTranslucentStatus=true + * Fixing workaround found here: + * http://stackoverflow.com/questions/8398102/androidwindowsoftinputmode-adjustresize-doesnt-make-any-difference + */ + decorView = getWindow().getDecorView(); + contentView = decorView.findViewById(android.R.id.content); + decorView.getViewTreeObserver().addOnGlobalLayoutListener(keyboardPresencelLayoutListener); + } + } + return adjustAmount; + } + + ViewTreeObserver.OnGlobalLayoutListener keyboardPresencelLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + Rect r = new Rect(); + decorView.getWindowVisibleDisplayFrame(r); + + int height = decorView.getContext().getResources().getDisplayMetrics().heightPixels; + int diff = height - r.bottom; + + // Detect keyboard launch when use-able screen height differs from the total screen height + if (diff != 0) { + //check if the padding is 0 (if yes set the padding for the keyboard) + if (contentView.getPaddingBottom() != diff) { + //set the padding of the contentView for the keyboard + contentView.setPadding(0, 0, 0, diff); + } + } else { + //check if the padding is != 0 (if yes reset the padding) + if (contentView.getPaddingBottom() != 0) { + //reset the padding of the contentView + contentView.setPadding(0, 0, 0, 0); + } + } + } + }; + } diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/lifecycle/ApptentiveActivityLifecycleCallbacks.java b/apptentive/src/main/java/com/apptentive/android/sdk/lifecycle/ApptentiveActivityLifecycleCallbacks.java index 5c4a5e553..f458e0b34 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/lifecycle/ApptentiveActivityLifecycleCallbacks.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/lifecycle/ApptentiveActivityLifecycleCallbacks.java @@ -32,11 +32,6 @@ public ApptentiveActivityLifecycleCallbacks() { @Override public void onActivityStarted(Activity activity) { - ApptentiveInternal.getInstance().onActivityStarted(activity); - } - - @Override - public void onActivityResumed(Activity activity) { boolean wasAppBackground = !isAppForeground; isAppForeground = true; @@ -49,12 +44,30 @@ public void onActivityResumed(Activity activity) { appEnteredForeground(); } + ApptentiveInternal.getInstance().onActivityStarted(activity); + } + + @Override + public void onActivityResumed(Activity activity) { ApptentiveInternal.getInstance().onActivityResumed(activity); } @Override public void onActivityPaused(final Activity activity) { + } + + @Override + public void onActivityDestroyed(final Activity activity) { + } + + @Override + public void onActivityCreated(Activity activity, Bundle savedInstanceState) { + + } + + @Override + public void onActivityStopped(Activity activity) { if (foregroundActivities.decrementAndGet() < 0) { ApptentiveLog.e("Incorrect number of foreground Activities encountered. Resetting to 0."); foregroundActivities.set(0); @@ -63,8 +76,9 @@ public void onActivityPaused(final Activity activity) { if (checkFgBgRoutine != null) { delayedChecker.removeCallbacks(checkFgBgRoutine); } - /* When one activity transits to another one, there is a brief period during which the former - * is paused but the latter has not yet resumed. To prevent false negative, check routine is + + /* When one activity transits to another one, there is a brief period during which the former + * is paused but the latter has not yet resumed. To prevent false negative, check routine is * delayed */ delayedChecker.postDelayed(checkFgBgRoutine = new Runnable() { @@ -77,24 +91,6 @@ public void run() { } }, CHECK_DELAY_SHORT); - - ApptentiveInternal.getInstance().getMessageManager().setCurrentForgroundActivity(null); - - } - - @Override - public void onActivityDestroyed(final Activity activity) { - ApptentiveInternal.getInstance().onActivityDestroyed(activity); - } - - @Override - public void onActivityCreated(Activity activity, Bundle savedInstanceState) { - - } - - @Override - public void onActivityStopped(Activity activity) { - } @Override diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/EngagementModule.java b/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/EngagementModule.java index f9a55494f..2d518cf05 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/EngagementModule.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/EngagementModule.java @@ -85,11 +85,12 @@ public static void launchInteraction(Context context, Interaction interaction) { * ContentProvider, and BroadcastReceiver */ if (!(context instanceof Activity)) { - // check if any activity from the hosting app is running + // check if any activity from the hosting app is running at foreground Activity activity = ApptentiveInternal.getInstance().getCurrentTaskStackTopActivity(); if (activity != null) { context = activity; } else { + // If no foreground activity from the host app, launch Apptentive interaction as a new task intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); } } diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/interaction/fragment/ApptentiveBaseFragment.java b/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/interaction/fragment/ApptentiveBaseFragment.java index 785fa14c0..45c88250d 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/interaction/fragment/ApptentiveBaseFragment.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/interaction/fragment/ApptentiveBaseFragment.java @@ -383,7 +383,7 @@ public Activity getActivity(Fragment fragment) { } } - public boolean isShownAsModelDialog() { + public boolean isShownAsModalDialog() { Bundle bundle = getArguments(); if (bundle != null) { 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 838ed28c0..f22700412 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 @@ -1264,8 +1264,8 @@ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCoun @Override public void OnListViewResize(int w, int h, int oldw, int oldh) { - // detect keyboard launching - if (oldh > h) { + // detect keyboard launching. If height difference is more than 100 pixels, probably due to keyboard + if (oldh > h && oldh - h > 100) { if (composingItem != null) { // When keyboard is up, adjust the scolling such that the cursor is always visible final int firstIndex = messageCenterListView.getFirstVisiblePosition(); 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 75def8ead..9f3c559ea 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 @@ -422,12 +422,12 @@ public void notifyHostUnreadMessagesListeners(int unreadMessages) { } } - // Set when Activity.onResume() is called + // Set when Activity.onStart() and onStop() are called public void setCurrentForgroundActivity(Activity activity) { if (activity != null) { currentForgroundApptentiveActivity = new WeakReference(activity); - } else if (currentForgroundApptentiveActivity != null) { - ApptentiveToastNotificationManager manager = ApptentiveToastNotificationManager.getInstance(currentForgroundApptentiveActivity.get(), false); + } else { + ApptentiveToastNotificationManager manager = ApptentiveToastNotificationManager.getInstance(null, false); if (manager != null) { manager.cleanUp(); } diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/module/messagecenter/view/holder/OutgoingCompoundMessageHolder.java b/apptentive/src/main/java/com/apptentive/android/sdk/module/messagecenter/view/holder/OutgoingCompoundMessageHolder.java index 22ce9ca06..a74bd325b 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/module/messagecenter/view/holder/OutgoingCompoundMessageHolder.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/module/messagecenter/view/holder/OutgoingCompoundMessageHolder.java @@ -19,9 +19,7 @@ import java.util.ArrayList; import java.util.List; -/** - * @author Barry Li - */ + public class OutgoingCompoundMessageHolder extends MessageHolder { public ApptentiveMaterialIndeterminateProgressBar progressBar; public TextView messageBodyView; @@ -31,7 +29,7 @@ public OutgoingCompoundMessageHolder(CompoundMessageView view) { super(view); progressBar = (ApptentiveMaterialIndeterminateProgressBar) view.findViewById(R.id.progressBar); messageBodyView = (TextView) view.findViewById(R.id.apptentive_compound_message_body); - imageBandView =(ApptentiveImageGridView) view.findViewById(R.id.grid); + imageBandView = (ApptentiveImageGridView) view.findViewById(R.id.grid); } public void updateMessage(String datestamp, String status, int statusColor, @@ -62,7 +60,7 @@ public void run() { imageBandView.setVisibility(View.VISIBLE); imageBandView.setAdapterItemSize(viewWidth, desiredColumn); List images = new ArrayList(); - for (StoredFile file: imagesToAttach) { + for (StoredFile file : imagesToAttach) { images.add(new ImageItem(file.getSourceUriOrPath(), file.getLocalFilePath(), file.getMimeType(), file.getCreationTime())); } imageBandView.setData(images); 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 48604160f..b357898a8 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.0.0"; + public static final String APPTENTIVE_SDK_VERSION = "3.0.1"; public static final int REQUEST_CODE_PHOTO_FROM_SYSTEM_PICKER = 10; diff --git a/apptentive/src/main/res/color/apptentive_interaction_button_color_state_list.xml b/apptentive/src/main/res/color/apptentive_interaction_button_color_state_list.xml index 6d82798a1..3069be385 100644 --- a/apptentive/src/main/res/color/apptentive_interaction_button_color_state_list.xml +++ b/apptentive/src/main/res/color/apptentive_interaction_button_color_state_list.xml @@ -13,12 +13,12 @@ will always have apptentiveInteractionButtonColorDefault. However, host app may provide an override color state list style with static values to achieve the desired effects on - both pre-21 and post-21: + both pre-23 and post-23: In ApptentiveThemeOverride.xml: - @style/CutomButtonStyle + @style/CustomButtonStyle Define style as: - Define color as: diff --git a/apptentive/src/main/res/layout/apptentive_dialog_alert.xml b/apptentive/src/main/res/layout/apptentive_dialog_alert.xml index e987a9782..aee1f5e0d 100644 --- a/apptentive/src/main/res/layout/apptentive_dialog_alert.xml +++ b/apptentive/src/main/res/layout/apptentive_dialog_alert.xml @@ -49,7 +49,7 @@