Merge "Update RTL/dark theme/orientation dynamically" into rvc-dev am: 3a9ceed26b

Change-Id: Icf417d597641d41d8922bfbb3b9986a3056e01ae
This commit is contained in:
Miranda Kephart
2020-05-15 23:04:05 +00:00
committed by Automerger Merge Worker
5 changed files with 235 additions and 126 deletions

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2020 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/global_screenshot_preview"
android:layout_width="wrap_content"
android:layout_height="@dimen/global_screenshot_x_scale"
android:layout_gravity="center"
android:layout_marginStart="@dimen/screenshot_offset_x"
android:layout_marginBottom="@dimen/screenshot_offset_y"
android:scaleType="fitStart"
android:elevation="@dimen/screenshot_preview_elevation"
android:visibility="gone"
android:background="@drawable/screenshot_rounded_corners"
android:adjustViewBounds="true"
android:contentDescription="@string/screenshot_preview_description"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/>

View File

@@ -71,21 +71,7 @@
android:elevation="@dimen/screenshot_preview_elevation"
android:background="@drawable/screenshot_rounded_corners"
android:adjustViewBounds="true"/>
<ImageView
android:id="@+id/global_screenshot_preview"
android:layout_width="@dimen/global_screenshot_x_scale"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="@dimen/screenshot_offset_x"
android:layout_marginBottom="@dimen/screenshot_offset_y"
android:scaleType="fitEnd"
android:elevation="@dimen/screenshot_preview_elevation"
android:visibility="gone"
android:background="@drawable/screenshot_rounded_corners"
android:adjustViewBounds="true"
android:contentDescription="@string/screenshot_preview_description"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<include layout="@layout/global_screenshot_preview"/>
<FrameLayout
android:id="@+id/global_screenshot_dismiss_button"
android:layout_width="@dimen/screenshot_dismiss_button_tappable_size"

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2011 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/global_screenshot_preview"
android:layout_width="@dimen/global_screenshot_x_scale"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="@dimen/screenshot_offset_x"
android:layout_marginBottom="@dimen/screenshot_offset_y"
android:scaleType="fitEnd"
android:elevation="@dimen/screenshot_preview_elevation"
android:visibility="gone"
android:background="@drawable/screenshot_rounded_corners"
android:adjustViewBounds="true"
android:contentDescription="@string/screenshot_preview_description"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/>

View File

@@ -314,7 +314,7 @@
<dimen name="screenshot_dismiss_button_margin">8dp</dimen>
<dimen name="screenshot_action_container_offset_y">32dp</dimen>
<dimen name="screenshot_action_container_corner_radius">10dp</dimen>
<dimen name="screenshot_action_container_padding_vertical">10dp</dimen>
<dimen name="screenshot_action_container_padding_vertical">16dp</dimen>
<dimen name="screenshot_action_container_margin_horizontal">8dp</dimen>
<dimen name="screenshot_action_container_padding_left">96dp</dimen>
<dimen name="screenshot_action_container_padding_right">8dp</dimen>

View File

@@ -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<Uri> 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<Uri> 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;
}