diff --git a/packages/SystemUI/res/layout-land/global_screenshot_preview.xml b/packages/SystemUI/res/layout-land/global_screenshot_preview.xml
new file mode 100644
index 0000000000000..b1f4cb7d70de7
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/global_screenshot_preview.xml
@@ -0,0 +1,33 @@
+
+
+
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
index 1dbb38d5dc7a1..d469e0f187e05 100644
--- a/packages/SystemUI/res/layout/global_screenshot.xml
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -71,21 +71,7 @@
android:elevation="@dimen/screenshot_preview_elevation"
android:background="@drawable/screenshot_rounded_corners"
android:adjustViewBounds="true"/>
-
+
+
+
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 79eb433cbb8cb..a3d32c12d1c02 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -314,7 +314,7 @@
8dp
32dp
10dp
- 10dp
+ 16dp
8dp
96dp
8dp
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index d45cedacfdd1a..9cfb1b27cbf34 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -16,6 +16,8 @@
package com.android.systemui.screenshot;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
@@ -61,6 +63,7 @@ import android.util.Slog;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.MotionEvent;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewGroup;
@@ -72,6 +75,7 @@ import android.view.animation.AccelerateInterpolator;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
+import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;
@@ -184,24 +188,25 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
private final Display mDisplay;
private final DisplayMetrics mDisplayMetrics;
- private final View mScreenshotLayout;
- private final ScreenshotSelectorView mScreenshotSelectorView;
- private final ImageView mScreenshotAnimatedView;
- private final ImageView mScreenshotPreview;
- private final ImageView mScreenshotFlash;
- private final ImageView mActionsContainerBackground;
- private final FrameLayout mActionsContainer;
- private final LinearLayout mActionsView;
- private final ImageView mBackgroundProtection;
- private final FrameLayout mDismissButton;
- private final ImageView mDismissImage;
+ private View mScreenshotLayout;
+ private ScreenshotSelectorView mScreenshotSelectorView;
+ private ImageView mScreenshotAnimatedView;
+ private ImageView mScreenshotPreview;
+ private ImageView mScreenshotFlash;
+ private ImageView mActionsContainerBackground;
+ private HorizontalScrollView mActionsContainer;
+ private LinearLayout mActionsView;
+ private ImageView mBackgroundProtection;
+ private FrameLayout mDismissButton;
private Bitmap mScreenBitmap;
private SaveImageInBackgroundTask mSaveInBgTask;
private Animator mScreenshotAnimation;
private Runnable mOnCompleteRunnable;
- private boolean mInDarkMode = false;
private Animator mDismissAnimation;
+ private boolean mInDarkMode = false;
+ private boolean mDirectionLTR = true;
+ private boolean mOrientationPortrait = true;
private float mScreenshotOffsetXPx;
private float mScreenshotOffsetYPx;
@@ -233,57 +238,18 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
*/
@Inject
public GlobalScreenshot(
- Context context, @Main Resources resources, LayoutInflater layoutInflater,
+ Context context, @Main Resources resources,
ScreenshotNotificationsController screenshotNotificationsController,
UiEventLogger uiEventLogger) {
mContext = context;
mNotificationsController = screenshotNotificationsController;
mUiEventLogger = uiEventLogger;
- // Inflate the screenshot layout
- mScreenshotLayout = layoutInflater.inflate(R.layout.global_screenshot, null);
- mScreenshotAnimatedView =
- mScreenshotLayout.findViewById(R.id.global_screenshot_animated_view);
- mScreenshotAnimatedView.setClipToOutline(true);
- mScreenshotAnimatedView.setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setRoundRect(new Rect(0, 0, view.getWidth(), view.getHeight()),
- ROUNDED_CORNER_RADIUS * view.getWidth());
- }
- });
- mScreenshotPreview = mScreenshotLayout.findViewById(R.id.global_screenshot_preview);
- mScreenshotPreview.setClipToOutline(true);
- mScreenshotPreview.setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setRoundRect(new Rect(0, 0, view.getWidth(), view.getHeight()),
- ROUNDED_CORNER_RADIUS * view.getWidth());
- }
- });
-
- mActionsContainerBackground = mScreenshotLayout.findViewById(
- R.id.global_screenshot_actions_container_background);
- mActionsContainer = mScreenshotLayout.findViewById(
- R.id.global_screenshot_actions_container);
- mActionsView = mScreenshotLayout.findViewById(R.id.global_screenshot_actions);
- mBackgroundProtection = mScreenshotLayout.findViewById(
- R.id.global_screenshot_actions_background);
- mDismissButton = mScreenshotLayout.findViewById(R.id.global_screenshot_dismiss_button);
- mDismissButton.setOnClickListener(view -> {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EXPLICIT_DISMISSAL);
- dismissScreenshot("dismiss_button", false);
- mOnCompleteRunnable.run();
- });
- mDismissImage = mDismissButton.findViewById(R.id.global_screenshot_dismiss_image);
-
- mScreenshotFlash = mScreenshotLayout.findViewById(R.id.global_screenshot_flash);
- mScreenshotSelectorView = mScreenshotLayout.findViewById(R.id.global_screenshot_selector);
- mScreenshotLayout.setFocusable(true);
- mScreenshotSelectorView.setFocusable(true);
- mScreenshotSelectorView.setFocusableInTouchMode(true);
- mScreenshotAnimatedView.setPivotX(0);
- mScreenshotAnimatedView.setPivotY(0);
+ reloadAssets();
+ Configuration config = mContext.getResources().getConfiguration();
+ mInDarkMode = config.isNightModeActive();
+ mDirectionLTR = config.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
+ mOrientationPortrait = config.orientation == ORIENTATION_PORTRAIT;
// Setup the window that we are going to use
mWindowLayoutParams = new WindowManager.LayoutParams(
@@ -334,6 +300,121 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
inoutInfo.touchableRegion.set(touchRegion);
}
+ private void onConfigChanged(Configuration newConfig) {
+ boolean needsUpdate = false;
+ // dark mode
+ if (newConfig.isNightModeActive()) {
+ // Night mode is active, we're using dark theme
+ if (!mInDarkMode) {
+ mInDarkMode = true;
+ needsUpdate = true;
+ }
+ } else {
+ // Night mode is not active, we're using the light theme
+ if (mInDarkMode) {
+ mInDarkMode = false;
+ needsUpdate = true;
+ }
+ }
+
+ // RTL configuration
+ switch (newConfig.getLayoutDirection()) {
+ case View.LAYOUT_DIRECTION_LTR:
+ if (!mDirectionLTR) {
+ mDirectionLTR = true;
+ needsUpdate = true;
+ }
+ break;
+ case View.LAYOUT_DIRECTION_RTL:
+ if (mDirectionLTR) {
+ mDirectionLTR = false;
+ needsUpdate = true;
+ }
+ break;
+ }
+
+ // portrait/landscape orientation
+ switch (newConfig.orientation) {
+ case ORIENTATION_PORTRAIT:
+ if (!mOrientationPortrait) {
+ mOrientationPortrait = true;
+ needsUpdate = true;
+ }
+ break;
+ case ORIENTATION_LANDSCAPE:
+ if (mOrientationPortrait) {
+ mOrientationPortrait = false;
+ needsUpdate = true;
+ }
+ break;
+ }
+
+ if (needsUpdate) {
+ reloadAssets();
+ }
+ }
+
+ /**
+ * Update assets (called when the dark theme status changes). We only need to update the dismiss
+ * button and the actions container background, since the buttons are re-inflated on demand.
+ */
+ private void reloadAssets() {
+ boolean wasAttached = mScreenshotLayout != null && mScreenshotLayout.isAttachedToWindow();
+ if (wasAttached) {
+ mWindowManager.removeView(mScreenshotLayout);
+ }
+
+ // Inflate the screenshot layout
+ mScreenshotLayout = LayoutInflater.from(mContext).inflate(R.layout.global_screenshot, null);
+ mScreenshotAnimatedView =
+ mScreenshotLayout.findViewById(R.id.global_screenshot_animated_view);
+ mScreenshotAnimatedView.setClipToOutline(true);
+ mScreenshotAnimatedView.setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRoundRect(new Rect(0, 0, view.getWidth(), view.getHeight()),
+ ROUNDED_CORNER_RADIUS * view.getWidth());
+ }
+ });
+ mScreenshotPreview = mScreenshotLayout.findViewById(R.id.global_screenshot_preview);
+ mScreenshotPreview.setClipToOutline(true);
+ mScreenshotPreview.setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRoundRect(new Rect(0, 0, view.getWidth(), view.getHeight()),
+ ROUNDED_CORNER_RADIUS * view.getWidth());
+ }
+ });
+
+ mActionsContainerBackground = mScreenshotLayout.findViewById(
+ R.id.global_screenshot_actions_container_background);
+ mActionsContainer = mScreenshotLayout.findViewById(
+ R.id.global_screenshot_actions_container);
+ mActionsView = mScreenshotLayout.findViewById(R.id.global_screenshot_actions);
+ mBackgroundProtection = mScreenshotLayout.findViewById(
+ R.id.global_screenshot_actions_background);
+ mDismissButton = mScreenshotLayout.findViewById(R.id.global_screenshot_dismiss_button);
+ mDismissButton.setOnClickListener(view -> {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EXPLICIT_DISMISSAL);
+ dismissScreenshot("dismiss_button", false);
+ mOnCompleteRunnable.run();
+ });
+
+ mScreenshotFlash = mScreenshotLayout.findViewById(R.id.global_screenshot_flash);
+ mScreenshotSelectorView = mScreenshotLayout.findViewById(R.id.global_screenshot_selector);
+ mScreenshotLayout.setFocusable(true);
+ mScreenshotSelectorView.setFocusable(true);
+ mScreenshotSelectorView.setFocusableInTouchMode(true);
+ mScreenshotAnimatedView.setPivotX(0);
+ mScreenshotAnimatedView.setPivotY(0);
+ mActionsContainer.setScrollX(0);
+
+ if (wasAttached) {
+ mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
+ }
+ }
+
+
/**
* Creates a new worker thread and saves the screenshot to the media store.
*/
@@ -383,10 +464,8 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
mScreenBitmap.setHasAlpha(false);
mScreenBitmap.prepareToDraw();
- updateDarkTheme();
+ onConfigChanged(mContext.getResources().getConfiguration());
- mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
- mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
if (mDismissAnimation != null && mDismissAnimation.isRunning()) {
mDismissAnimation.cancel();
@@ -396,7 +475,6 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
}
void takeScreenshot(Consumer finisher, Runnable onComplete) {
- dismissScreenshot("new screenshot requested", true);
mOnCompleteRunnable = onComplete;
mDisplay.getRealMetrics(mDisplayMetrics);
@@ -408,7 +486,6 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
Insets visibleInsets, int taskId, Consumer finisher, Runnable onComplete) {
// TODO use taskId and visibleInsets
- dismissScreenshot("new screenshot requested", true);
mOnCompleteRunnable = onComplete;
takeScreenshot(screenshot, finisher, screenshotScreenBounds);
}
@@ -513,41 +590,6 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
mScreenshotPreview.setTranslationY(0);
}
- /**
- * Update assets (called when the dark theme status changes). We only need to update the
- * dismiss
- * button and the actions container background, since the buttons are re-inflated on demand.
- */
- private void reloadAssets() {
- mDismissImage.setImageDrawable(mContext.getDrawable(R.drawable.screenshot_cancel));
- mActionsContainerBackground.setBackground(
- mContext.getDrawable(R.drawable.action_chip_container_background));
- }
-
- /**
- * Checks the current dark theme status and updates if it has changed.
- */
- private void updateDarkTheme() {
- int currentNightMode = mContext.getResources().getConfiguration().uiMode
- & Configuration.UI_MODE_NIGHT_MASK;
- switch (currentNightMode) {
- case Configuration.UI_MODE_NIGHT_NO:
- // Night mode is not active, we're using the light theme
- if (mInDarkMode) {
- mInDarkMode = false;
- reloadAssets();
- }
- break;
- case Configuration.UI_MODE_NIGHT_YES:
- // Night mode is active, we're using dark theme
- if (!mInDarkMode) {
- mInDarkMode = true;
- reloadAssets();
- }
- break;
- }
- }
-
/**
* Starts the animation after taking the screenshot
*/
@@ -604,17 +646,33 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
}
});
mScreenshotHandler.post(() -> {
- // Play the shutter sound to notify that we've taken a screenshot
- mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
+ if (!mScreenshotLayout.isAttachedToWindow()) {
+ mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
+ }
+ mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+
+ mScreenshotHandler.post(() -> {
+
+ // Play the shutter sound to notify that we've taken a screenshot
+ mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
+
+ mScreenshotPreview.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ mScreenshotPreview.buildLayer();
+ mScreenshotAnimation.start();
+ });
- mScreenshotPreview.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- mScreenshotPreview.buildLayer();
- mScreenshotAnimation.start();
});
+
}
private AnimatorSet createScreenshotDropInAnimation(int width, int height, Rect bounds) {
- float cornerScale = mCornerSizeX / (float) width;
+ int rotation = mContext.getDisplay().getRotation();
+ float cornerScale;
+ if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
+ cornerScale = (mCornerSizeX / (float) height);
+ } else {
+ cornerScale = (mCornerSizeX / (float) width);
+ }
mScreenshotAnimatedView.setScaleX(1);
mScreenshotAnimatedView.setScaleY(1);
@@ -639,8 +697,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
final PointF startPos = new PointF(bounds.centerX(), bounds.centerY());
float finalX;
- if (mContext.getResources().getConfiguration().getLayoutDirection()
- == View.LAYOUT_DIRECTION_LTR) {
+ if (mDirectionLTR) {
finalX = mScreenshotOffsetXPx + width * cornerScale / 2f;
} else {
finalX = width - mScreenshotOffsetXPx - width * cornerScale / 2f;
@@ -720,7 +777,6 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
private ValueAnimator createScreenshotActionsShadeAnimation(SavedImageData imageData) {
LayoutInflater inflater = LayoutInflater.from(mContext);
mActionsView.removeAllViews();
- mActionsContainer.setScrollX(0);
mScreenshotLayout.invalidate();
mScreenshotLayout.requestLayout();
mScreenshotLayout.getViewTreeObserver().dispatchOnGlobalLayout();
@@ -810,14 +866,11 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
animator.setDuration(SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS);
float alphaFraction = (float) SCREENSHOT_ACTIONS_ALPHA_DURATION_MS
/ SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS;
- mActionsContainer.setVisibility(View.VISIBLE);
mActionsContainer.setAlpha(0f);
mActionsContainerBackground.setAlpha(0f);
+ mActionsContainer.setVisibility(View.VISIBLE);
mActionsContainerBackground.setVisibility(View.VISIBLE);
- mActionsContainer.setPivotX(0);
- mActionsContainerBackground.setPivotX(0);
-
animator.addUpdateListener(animation -> {
float t = animation.getAnimatedFraction();
mBackgroundProtection.setAlpha(t);
@@ -832,6 +885,10 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
chip.setAlpha(t);
chip.setScaleX(1 / containerScale); // invert to keep size of children constant
}
+ mActionsContainer.setScrollX(mDirectionLTR ? 0 : mActionsContainer.getWidth());
+ mActionsContainer.setPivotX(mDirectionLTR ? 0 : mActionsContainer.getWidth());
+ mActionsContainerBackground.setPivotX(
+ mDirectionLTR ? 0 : mActionsContainerBackground.getWidth());
});
return animator;
}