diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/internal/CoreConstants.kt b/code/core/src/main/java/com/adobe/marketing/mobile/internal/CoreConstants.kt index f85bfddd9..067c3f56c 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/internal/CoreConstants.kt +++ b/code/core/src/main/java/com/adobe/marketing/mobile/internal/CoreConstants.kt @@ -13,7 +13,7 @@ package com.adobe.marketing.mobile.internal internal object CoreConstants { const val LOG_TAG = "MobileCore" - const val VERSION = "2.6.0" + const val VERSION = "2.6.1" object EventDataKeys { /** diff --git a/code/core/src/phone/java/com/adobe/marketing/mobile/services/ui/AEPMessage.java b/code/core/src/phone/java/com/adobe/marketing/mobile/services/ui/AEPMessage.java index c03a761c5..a2f651f0a 100644 --- a/code/core/src/phone/java/com/adobe/marketing/mobile/services/ui/AEPMessage.java +++ b/code/core/src/phone/java/com/adobe/marketing/mobile/services/ui/AEPMessage.java @@ -57,6 +57,8 @@ class AEPMessage implements FullscreenMessage { private static final String FRAGMENT_TAG = "AEPMessageFragment"; private static final String UNEXPECTED_NULL_VALUE = "Unexpected Null Value"; private static final int ANIMATION_DURATION = 300; + + private static final int WEBVIEW_CREATION_MAX_TRIES = 5; private static final String UTF_8 = "UTF-8"; // package private vars @@ -201,7 +203,20 @@ public void show(final boolean withMessagingDelegateControl) { // create the webview if needed if (webView == null) { - webView = createWebView(); + for (int i = 0; i < WEBVIEW_CREATION_MAX_TRIES; i++) { + webView = createWebView(); + + if (webView != null) { + // exit loop if webview creation was successful + break; + } + } + + if (webView == null) { + // unable to create the webview, need to call failure logic and bail. + listener.onShowFailure(); + return; + } } final Activity currentActivity = getCurrentActivity(); @@ -487,37 +502,45 @@ private WebView createWebView() { @SuppressLint("SetJavaScriptEnabled") final Runnable createWebViewRunnable = () -> { - final WebView newWebView = new WebView(getApplicationContext()); - // assign a random resource id to identify this webview - newWebView.setId(Math.abs(new Random().nextInt())); - newWebView.setVerticalScrollBarEnabled(true); - newWebView.setHorizontalScrollBarEnabled(true); - newWebView.setScrollbarFadingEnabled(true); - newWebView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY); - newWebView.setBackgroundColor(Color.TRANSPARENT); - - webViewClient = new MessageWebViewClient(AEPMessage.this); - webViewClient.setLocalAssetsMap(assetMap); - newWebView.setWebViewClient(webViewClient); - - final WebSettings webviewSettings = newWebView.getSettings(); - webviewSettings.setJavaScriptEnabled(true); - webviewSettings.setAllowFileAccess(false); - webviewSettings.setDomStorageEnabled(true); - webviewSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL); - webviewSettings.setDefaultTextEncodingName(UTF_8); - - // Disallow need for a user gesture to play media. - webviewSettings.setMediaPlaybackRequiresUserGesture(false); - - if (ServiceProvider.getInstance() - .getDeviceInfoService() - .getApplicationCacheDir() - != null) { - webviewSettings.setDatabaseEnabled(true); + try { + final WebView newWebView = new WebView(getApplicationContext()); + // assign a random resource id to identify this webview + newWebView.setId(Math.abs(new Random().nextInt())); + newWebView.setVerticalScrollBarEnabled(true); + newWebView.setHorizontalScrollBarEnabled(true); + newWebView.setScrollbarFadingEnabled(true); + newWebView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY); + newWebView.setBackgroundColor(Color.TRANSPARENT); + + webViewClient = new MessageWebViewClient(AEPMessage.this); + webViewClient.setLocalAssetsMap(assetMap); + newWebView.setWebViewClient(webViewClient); + + final WebSettings webviewSettings = newWebView.getSettings(); + webviewSettings.setJavaScriptEnabled(true); + webviewSettings.setAllowFileAccess(false); + webviewSettings.setDomStorageEnabled(true); + webviewSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL); + webviewSettings.setDefaultTextEncodingName(UTF_8); + + // Disallow need for a user gesture to play media. + webviewSettings.setMediaPlaybackRequiresUserGesture(false); + + if (ServiceProvider.getInstance() + .getDeviceInfoService() + .getApplicationCacheDir() + != null) { + webviewSettings.setDatabaseEnabled(true); + } + + webViewAtomicReference.set(newWebView); + } catch (Exception ex) { + Log.warning( + ServiceConstants.LOG_TAG, + TAG, + "Exception thrown inside of createWebViewRunnable: %s", + ex.getLocalizedMessage()); } - - webViewAtomicReference.set(newWebView); }; final RunnableFuture createWebviewTask = @@ -529,12 +552,11 @@ private WebView createWebView() { createWebviewTask.get(1, TimeUnit.SECONDS); return webViewAtomicReference.get(); } catch (final InterruptedException | ExecutionException | TimeoutException exception) { - Log.debug( + Log.warning( ServiceConstants.LOG_TAG, TAG, "Exception occurred when creating the webview: %s", - exception.getLocalizedMessage()); - listener.onShowFailure(); + exception.getMessage()); createWebviewTask.cancel(true); return null; } diff --git a/code/core/src/test/java/com/adobe/marketing/mobile/MobileCoreTests.kt b/code/core/src/test/java/com/adobe/marketing/mobile/MobileCoreTests.kt index 29433c050..33a705275 100644 --- a/code/core/src/test/java/com/adobe/marketing/mobile/MobileCoreTests.kt +++ b/code/core/src/test/java/com/adobe/marketing/mobile/MobileCoreTests.kt @@ -35,7 +35,7 @@ import kotlin.test.assertTrue @RunWith(MockitoJUnitRunner.Silent::class) class MobileCoreTests { - private var EXTENSION_VERSION = "2.6.0" + private var EXTENSION_VERSION = "2.6.1" @Mock private lateinit var mockedEventHub: EventHub diff --git a/code/core/src/test/java/com/adobe/marketing/mobile/internal/configuration/ConfigurationExtensionTests.kt b/code/core/src/test/java/com/adobe/marketing/mobile/internal/configuration/ConfigurationExtensionTests.kt index 8cfe145a2..3bf66bac7 100644 --- a/code/core/src/test/java/com/adobe/marketing/mobile/internal/configuration/ConfigurationExtensionTests.kt +++ b/code/core/src/test/java/com/adobe/marketing/mobile/internal/configuration/ConfigurationExtensionTests.kt @@ -60,7 +60,7 @@ import kotlin.test.assertTrue @RunWith(MockitoJUnitRunner.Silent::class) class ConfigurationExtensionTests { - private var EXTENSION_VERSION = "2.6.0" + private var EXTENSION_VERSION = "2.6.1" @Mock private lateinit var mockServiceProvider: ServiceProvider diff --git a/code/core/src/test/java/com/adobe/marketing/mobile/services/ui/AEPMessageTests.java b/code/core/src/test/java/com/adobe/marketing/mobile/services/ui/AEPMessageTests.java index 8d6a0bfdf..a14e8d99d 100644 --- a/code/core/src/test/java/com/adobe/marketing/mobile/services/ui/AEPMessageTests.java +++ b/code/core/src/test/java/com/adobe/marketing/mobile/services/ui/AEPMessageTests.java @@ -18,6 +18,8 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockConstruction; import static org.mockito.Mockito.never; import static org.mockito.Mockito.when; @@ -30,6 +32,7 @@ import android.view.MotionEvent; import android.view.ViewGroup; import android.view.animation.Animation; +import android.webkit.WebSettings; import android.webkit.WebView; import androidx.cardview.widget.CardView; import com.adobe.marketing.mobile.services.AppContextService; @@ -47,6 +50,7 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentMatchers; import org.mockito.Mock; +import org.mockito.MockedConstruction; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; @@ -176,79 +180,95 @@ public void testCreateAEPMessage_nullMessageDelegate() throws MessageCreationExc // AEPMessage show tests @Test public void aepMessageIsShown_When_NoOtherMessagesAreDisplayed() { - // setup - Mockito.when(mockMessageMonitor.show(any(FullscreenMessage.class), anyBoolean())) - .thenCallRealMethod() - .thenReturn(true); - Mockito.when(mockMessageMonitor.isDisplayed()).thenReturn(false); - Mockito.when( - mockMessagingDelegate.shouldShowMessage( - ArgumentMatchers.any(AEPMessage.class))) - .thenReturn(true); - - try { - message = - new AEPMessage( - "html", - mockFullscreenMessageDelegate, - false, - mockMessageMonitor, - mockAEPMessageSettings, - mockExecutor); - } catch (MessageCreationException ex) { - Assert.fail(ex.getMessage()); + try (MockedConstruction ignored = + mockConstruction( + WebView.class, + (mock, context) -> { + WebSettings mockWebSettings = Mockito.mock(WebSettings.class); + when(mock.getSettings()).thenReturn(mockWebSettings); + })) { + // setup + Mockito.when(mockMessageMonitor.show(any(FullscreenMessage.class), anyBoolean())) + .thenCallRealMethod() + .thenReturn(true); + Mockito.when(mockMessageMonitor.isDisplayed()).thenReturn(false); + Mockito.when( + mockMessagingDelegate.shouldShowMessage( + ArgumentMatchers.any(AEPMessage.class))) + .thenReturn(true); + + try { + message = + new AEPMessage( + "html", + mockFullscreenMessageDelegate, + false, + mockMessageMonitor, + mockAEPMessageSettings, + mockExecutor); + } catch (MessageCreationException ex) { + Assert.fail(ex.getMessage()); + } + Mockito.when(mockViewGroup.getMeasuredWidth()).thenReturn(1000); + Mockito.when(mockViewGroup.getMeasuredHeight()).thenReturn(1000); + setupFragmentTransactionMocks(); + + // test + message.show(); + // verify + Mockito.verify(mockMessageMonitor, Mockito.times(1)) + .show(any(FullscreenMessage.class), eq(true)); + Mockito.verify(mockMessageMonitor, Mockito.times(1)).displayed(); + + // Verify that the message delegates are notified + Mockito.verify(mockFullscreenMessageDelegate).onShow(message); + Mockito.verify(mockMessagingDelegate).onShow(message); } - Mockito.when(mockViewGroup.getMeasuredWidth()).thenReturn(1000); - Mockito.when(mockViewGroup.getMeasuredHeight()).thenReturn(1000); - setupFragmentTransactionMocks(); - - // test - message.show(); - // verify - Mockito.verify(mockMessageMonitor, Mockito.times(1)) - .show(any(FullscreenMessage.class), eq(true)); - Mockito.verify(mockMessageMonitor, Mockito.times(1)).displayed(); - - // Verify that the message delegates are notified - Mockito.verify(mockFullscreenMessageDelegate).onShow(message); - Mockito.verify(mockMessagingDelegate).onShow(message); } @Test public void aepMessageIsNotShown_When_AnotherMessageIsDisplayed() { - // setup - Mockito.when(mockMessageMonitor.isDisplayed()).thenReturn(true); - Mockito.when( - mockMessagingDelegate.shouldShowMessage( - ArgumentMatchers.any(AEPMessage.class))) - .thenReturn(true); - - try { - message = - new AEPMessage( - "html", - mockFullscreenMessageDelegate, - false, - mockMessageMonitor, - mockAEPMessageSettings, - mockExecutor); - } catch (MessageCreationException ex) { - Assert.fail(ex.getMessage()); + try (MockedConstruction ignored = + mockConstruction( + WebView.class, + (mock, context) -> { + WebSettings mockWebSettings = Mockito.mock(WebSettings.class); + when(mock.getSettings()).thenReturn(mockWebSettings); + })) { + // setup + Mockito.when(mockMessageMonitor.isDisplayed()).thenReturn(true); + Mockito.when( + mockMessagingDelegate.shouldShowMessage( + ArgumentMatchers.any(AEPMessage.class))) + .thenReturn(true); + + try { + message = + new AEPMessage( + "html", + mockFullscreenMessageDelegate, + false, + mockMessageMonitor, + mockAEPMessageSettings, + mockExecutor); + } catch (MessageCreationException ex) { + Assert.fail(ex.getMessage()); + } + Mockito.when(mockViewGroup.getMeasuredWidth()).thenReturn(1000); + Mockito.when(mockViewGroup.getMeasuredHeight()).thenReturn(1000); + setupFragmentTransactionMocks(); + + // test + message.show(); + // verify + Mockito.verify(mockMessageMonitor, Mockito.times(1)) + .show(any(FullscreenMessage.class), eq(true)); + Mockito.verify(mockMessageMonitor, Mockito.times(0)).displayed(); + + // Verify that the message delegates are never notified about showing + Mockito.verify(mockFullscreenMessageDelegate, never()).onShow(message); + Mockito.verify(mockMessagingDelegate, never()).onShow(message); } - Mockito.when(mockViewGroup.getMeasuredWidth()).thenReturn(1000); - Mockito.when(mockViewGroup.getMeasuredHeight()).thenReturn(1000); - setupFragmentTransactionMocks(); - - // test - message.show(); - // verify - Mockito.verify(mockMessageMonitor, Mockito.times(1)) - .show(any(FullscreenMessage.class), eq(true)); - Mockito.verify(mockMessageMonitor, Mockito.times(0)).displayed(); - - // Verify that the message delegates are never notified about showing - Mockito.verify(mockFullscreenMessageDelegate, never()).onShow(message); - Mockito.verify(mockMessagingDelegate, never()).onShow(message); } @Test diff --git a/code/gradle.properties b/code/gradle.properties index ab8f6d40f..f52513875 100644 --- a/code/gradle.properties +++ b/code/gradle.properties @@ -6,7 +6,7 @@ android.useAndroidX=true # #Maven artifacts #Core extension -coreExtensionVersion=2.6.0 +coreExtensionVersion=2.6.1 coreExtensionName=core coreExtensionAARName=core-phone-release.aar coreMavenRepoName=AdobeMobileCoreSdk diff --git a/code/testapp/src/main/java/com/adobe/marketing/mobile/services/ui/TestAppUIServices.java b/code/testapp/src/main/java/com/adobe/marketing/mobile/services/ui/TestAppUIServices.java index 761914b34..8f1672b50 100644 --- a/code/testapp/src/main/java/com/adobe/marketing/mobile/services/ui/TestAppUIServices.java +++ b/code/testapp/src/main/java/com/adobe/marketing/mobile/services/ui/TestAppUIServices.java @@ -18,6 +18,8 @@ import com.adobe.marketing.mobile.services.ui.FullscreenMessage; import com.adobe.marketing.mobile.services.ui.NotificationSetting; +import java.net.URI; +import java.net.URISyntaxException; import java.util.Map; public class TestAppUIServices { @@ -49,8 +51,44 @@ public void showLocalNotification(final String identifier, final String content, } public void showFullscreenMessage(final String html) { - FullscreenMessage fullScreenMessage = uiService.createFullscreenMessage(html, null, false, null); - fullScreenMessage.show(); + final MessageSettings messageSettings = new MessageSettings(); + // ACS fullscreen messages are displayed at 100% scale + messageSettings.setHeight(100); + messageSettings.setWidth(100); + messageSettings.setParent(this); + messageSettings.setVerticalAlign(MessageSettings.MessageAlignment.TOP); + messageSettings.setHorizontalAlign(MessageSettings.MessageAlignment.CENTER); + messageSettings.setDisplayAnimation(MessageSettings.MessageAnimation.BOTTOM); + messageSettings.setDismissAnimation(MessageSettings.MessageAnimation.BOTTOM); + messageSettings.setBackdropColor("#FFFFFF"); // html code for white + messageSettings.setBackdropOpacity(1.0f); + messageSettings.setUiTakeover(true); + + FullscreenMessage fullScreenMessage = uiService.createFullscreenMessage(html, new FullscreenMessageDelegate() { + @Override + public void onShow(FullscreenMessage message) {} + @Override + public void onDismiss(FullscreenMessage message) {} + @Override + public boolean overrideUrlLoad(FullscreenMessage message, String url) { + try { + final URI uri = new URI(url); + if (uri.getScheme().equals("adbinapp") && uri.getHost().equals("dismiss")) { + message.dismiss(); + return true; + } + } catch (URISyntaxException ex) { + return false; + } + return false; + } + @Override + public void onShowFailure() {} + }, false, messageSettings); + + if (fullScreenMessage != null) { + fullScreenMessage.show(); + } } public void showUrl(final String url) { diff --git a/code/testapp/src/main/java/com/adobe/testapp/UIServicesFragment.java b/code/testapp/src/main/java/com/adobe/testapp/UIServicesFragment.java index 078bf1c48..3694fc76e 100644 --- a/code/testapp/src/main/java/com/adobe/testapp/UIServicesFragment.java +++ b/code/testapp/src/main/java/com/adobe/testapp/UIServicesFragment.java @@ -62,7 +62,7 @@ public void onClick(View view) { System.currentTimeMillis() / 1000, 0, "myscheme://link", null, "sound.wav", getString(R.string.test_notification_title)); } else if (viewId == R.id.btnShowFullScreenMsg) { - testAppUIServices.showFullscreenMessage("" + getString(R.string.test_fullscreen_html) + ""); + testAppUIServices.showFullscreenMessage("" + getString(R.string.test_fullscreen_html) + "

"); } else if (viewId == R.id.btnShowUrl) { testAppUIServices.showUrl(getString(R.string.test_url)); } else if (viewId == R.id.btnShowFloatingButton) {