diff --git a/CHANGELOG.md b/CHANGELOG.md index e0db5d9f7..77a49d57a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ ## 25.4.4 * Improved disk size calculation in crash reports. +* Mitigated an issue displaying Content on API level 35 and above. + ## 25.4.3 * Improved Health Check metric information. * Improved Content display mechanics. diff --git a/sdk/src/main/java/ly/count/android/sdk/DeviceInfo.java b/sdk/src/main/java/ly/count/android/sdk/DeviceInfo.java index 1ab359190..58fded80d 100644 --- a/sdk/src/main/java/ly/count/android/sdk/DeviceInfo.java +++ b/sdk/src/main/java/ly/count/android/sdk/DeviceInfo.java @@ -39,8 +39,6 @@ of this software and associated documentation files (the "Software"), to deal import android.os.storage.StorageManager; import android.telephony.TelephonyManager; import android.util.DisplayMetrics; -import android.view.Display; -import android.view.WindowManager; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.io.File; @@ -131,19 +129,10 @@ public String getResolution(@NonNull final Context context) { return resolution; } - /** - * Return the display metrics collected from the WindowManager in the specified context. - * @param context context to use to retrieve the current WindowManager - * @return the display metrics of the current default display - */ @NonNull @Override public DisplayMetrics getDisplayMetrics(@NonNull final Context context) { - final WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - final Display display = wm.getDefaultDisplay(); - final DisplayMetrics metrics = new DisplayMetrics(); - display.getMetrics(metrics); - return metrics; + return UtilsDevice.getDisplayMetrics(context); } /** diff --git a/sdk/src/main/java/ly/count/android/sdk/ModuleContent.java b/sdk/src/main/java/ly/count/android/sdk/ModuleContent.java index 47bc6df8f..ddbcce994 100644 --- a/sdk/src/main/java/ly/count/android/sdk/ModuleContent.java +++ b/sdk/src/main/java/ly/count/android/sdk/ModuleContent.java @@ -226,7 +226,6 @@ private TransparentActivityConfig extractOrientationPlacements(@NonNull JSONObje int w = orientationPlacements.optInt("w"); int h = orientationPlacements.optInt("h"); L.d("[ModuleContent] extractOrientationPlacements, orientation: [" + orientation + "], x: [" + x + "], y: [" + y + "], w: [" + w + "], h: [" + h + "]"); - TransparentActivityConfig config = new TransparentActivityConfig((int) Math.ceil(x * density), (int) Math.ceil(y * density), (int) Math.ceil(w * density), (int) Math.ceil(h * density)); config.url = content; return config; diff --git a/sdk/src/main/java/ly/count/android/sdk/TransparentActivity.java b/sdk/src/main/java/ly/count/android/sdk/TransparentActivity.java index 49c4635c7..11a23c716 100644 --- a/sdk/src/main/java/ly/count/android/sdk/TransparentActivity.java +++ b/sdk/src/main/java/ly/count/android/sdk/TransparentActivity.java @@ -2,7 +2,6 @@ import android.annotation.SuppressLint; import android.app.Activity; -import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.graphics.Color; @@ -10,7 +9,6 @@ import android.os.Bundle; import android.util.DisplayMetrics; import android.util.Log; -import android.view.Display; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; @@ -47,7 +45,6 @@ protected void onCreate(Bundle savedInstanceState) { // there is a stripe at the top of the screen for contents // we eliminate it with hiding the system ui - getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE); super.onCreate(savedInstanceState); overridePendingTransition(0, 0); @@ -72,7 +69,7 @@ protected void onCreate(Bundle savedInstanceState) { // Configure window layout parameters WindowManager.LayoutParams params = new WindowManager.LayoutParams(); - params.gravity = Gravity.TOP | Gravity.LEFT; // try out START + params.gravity = Gravity.TOP | Gravity.START; // try out START params.x = config.x; params.y = config.y; params.height = config.height; @@ -94,10 +91,7 @@ protected void onCreate(Bundle savedInstanceState) { } private TransparentActivityConfig setupConfig(@Nullable TransparentActivityConfig config) { - final WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE); - final Display display = wm.getDefaultDisplay(); - final DisplayMetrics metrics = new DisplayMetrics(); // this gets all - display.getMetrics(metrics); + final DisplayMetrics metrics = UtilsDevice.getDisplayMetrics(this); if (config == null) { Log.w(Countly.TAG, "[TransparentActivity] setupConfig, Config is null, using default values with full screen size"); @@ -148,12 +142,12 @@ public void onConfigurationChanged(android.content.res.Configuration newConfig) currentOrientation = newConfig.orientation; } - // CHANGE SCREEN SIZE - final WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE); - final Display display = wm.getDefaultDisplay(); - final DisplayMetrics metrics = new DisplayMetrics(); - display.getMetrics(metrics); + resizeContent(); + } + private void resizeContent() { + // CHANGE SCREEN SIZE + final DisplayMetrics metrics = UtilsDevice.getDisplayMetrics(this); int scaledWidth = (int) Math.ceil(metrics.widthPixels / metrics.density); int scaledHeight = (int) Math.ceil(metrics.heightPixels / metrics.density); @@ -161,6 +155,26 @@ public void onConfigurationChanged(android.content.res.Configuration newConfig) webView.loadUrl("javascript:window.postMessage({type: 'resize', width: " + scaledWidth + ", height: " + scaledHeight + "}, '*');"); } + @Override + public void onDestroy() { + close(new HashMap<>()); + + if (Countly.sharedInstance().isInitialized()) { + Countly.sharedInstance().moduleContent.notifyAfterContentIsClosed(); + } + super.onDestroy(); + } + + private void hideSystemUI() { + getWindow().getDecorView().setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_STABLE | + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN); + } + private void resizeContentInternal() { switch (currentOrientation) { case Configuration.ORIENTATION_LANDSCAPE: @@ -274,11 +288,7 @@ private void resizeMeAction(Map query) { return; } try { - final WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE); - final Display display = wm.getDefaultDisplay(); - final DisplayMetrics metrics = new DisplayMetrics(); - display.getMetrics(metrics); - + final DisplayMetrics metrics = UtilsDevice.getDisplayMetrics(this); float density = metrics.density; JSONObject resizeMeJson = (JSONObject) resizeMe; @@ -414,15 +424,18 @@ private WebView createWebView(TransparentActivityConfig config) { return false; } }); - client.afterPageFinished = (closeIt) -> { - if (closeIt) { - close(new HashMap<>()); + client.afterPageFinished = new WebViewPageLoadedListener() { + @Override public void onPageLoaded(boolean timedOut) { + if (timedOut) { + close(new HashMap<>()); - if (Countly.sharedInstance().isInitialized()) { - Countly.sharedInstance().moduleContent.notifyAfterContentIsClosed(); + if (Countly.sharedInstance().isInitialized()) { + Countly.sharedInstance().moduleContent.notifyAfterContentIsClosed(); + } + } else { + hideSystemUI(); + webView.setVisibility(View.VISIBLE); } - } else { - webView.setVisibility(View.VISIBLE); } }; webView.setWebViewClient(client); diff --git a/sdk/src/main/java/ly/count/android/sdk/UtilsDevice.java b/sdk/src/main/java/ly/count/android/sdk/UtilsDevice.java new file mode 100644 index 000000000..5542ba9a0 --- /dev/null +++ b/sdk/src/main/java/ly/count/android/sdk/UtilsDevice.java @@ -0,0 +1,94 @@ +package ly.count.android.sdk; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Context; +import android.graphics.Insets; +import android.graphics.Rect; +import android.os.Build; +import android.util.DisplayMetrics; +import android.view.Display; +import android.view.WindowInsets; +import android.view.WindowManager; +import android.view.WindowMetrics; +import androidx.annotation.NonNull; + +class UtilsDevice { + private UtilsDevice() { + } + + @NonNull + static DisplayMetrics getDisplayMetrics(@NonNull final Context context) { + final WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + final DisplayMetrics metrics = new DisplayMetrics(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + applyWindowMetrics(context, wm, metrics); + } else { + applyLegacyMetrics(wm, metrics); + } + return metrics; + } + + @TargetApi(Build.VERSION_CODES.R) + private static void applyWindowMetrics(@NonNull Context context, + @NonNull WindowManager wm, + @NonNull DisplayMetrics outMetrics) { + final WindowMetrics windowMetrics = wm.getCurrentWindowMetrics(); + final WindowInsets windowInsets = windowMetrics.getWindowInsets(); + + // Always respect status bar & cutout (they affect safe area even in fullscreen) + int types = 0; + boolean usePhysicalScreenSize = !(context instanceof Activity); + + // If not activity, we can't know system UI visibility, so always use physical screen size + if (!usePhysicalScreenSize) { + // Only subtract navigation bar insets when navigation bar is actually visible + if (windowInsets.isVisible(WindowInsets.Type.navigationBars())) { + types |= WindowInsets.Type.navigationBars(); + } + + if (windowInsets.isVisible(WindowInsets.Type.statusBars())) { + types |= WindowInsets.Type.statusBars(); + } + + boolean drawUnderCutout; + WindowManager.LayoutParams params = ((Activity) context).getWindow().getAttributes(); + drawUnderCutout = params.layoutInDisplayCutoutMode + == WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + + // Only subtract display cutout insets when not allowed to draw under the cutout + if (!drawUnderCutout && windowInsets.isVisible(WindowInsets.Type.displayCutout())) { + types |= WindowInsets.Type.displayCutout(); + } + } + + // Cutout is always respected as safe area for now even in fullscreen mode + if (windowInsets.isVisible(WindowInsets.Type.displayCutout())) { + types |= WindowInsets.Type.displayCutout(); + } + + final Insets insets = windowInsets.getInsets(types); + final Rect bounds = windowMetrics.getBounds(); + final int width = bounds.width() - insets.left - insets.right; + final int height = bounds.height() - insets.top - insets.bottom; + + outMetrics.widthPixels = width; + outMetrics.heightPixels = height; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + outMetrics.density = windowMetrics.getDensity(); + } else { + // Fallback: use resource-based density + outMetrics.density = context.getResources().getDisplayMetrics().density; + } + } + + @SuppressWarnings("deprecation") + private static void applyLegacyMetrics(@NonNull WindowManager wm, + @NonNull DisplayMetrics outMetrics) { + final Display display = wm.getDefaultDisplay(); + display.getRealMetrics(outMetrics); + //getMetrics gives us size minus navigation bar + } +}