Remaining APIs for freeform window caption.
The APIs are:
* setting a listener for the area of the caption where we display
control buttons; this is intended for apps that overlay the caption on
their content, so they don't display the content in that area and clash
with the buttons;
* specifying the color pallete of controls in the caption; by default we
try to infer the best color from the theme (dark buttons for light theme
and vice versa), but in case we get it wrong, we allow the developer to
overwrite it;
* specifying the caption background during the resizing; by default we
try to infer a good color from the theme, but in case we do it wrong, we
allow the developer to overwrite this.
The CL also includes merging of layouts decor_caption_dark and
decor_caption_light into a single one. Not only it reduces code
duplication, but also is necessary for allowing adjustment of the
controls color pallete after the layout was created.
Bug: 25486369
Bug: 25818398
Change-Id: Ib87fe849b07df341893ec7873982bf7ab932f6d5
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index 75ca639..1b44ff3 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -63,17 +63,18 @@
private boolean mReportNextDraw;
private Drawable mCaptionBackgroundDrawable;
+ private Drawable mUserCaptionBackgroundDrawable;
private Drawable mResizingBackgroundDrawable;
private ColorDrawable mStatusBarColor;
public BackdropFrameRenderer(DecorView decorView, ThreadedRenderer renderer, Rect initialBounds,
Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawable,
- int statusBarColor) {
+ Drawable userCaptionBackgroundDrawable, int statusBarColor) {
setName("ResizeFrame");
mRenderer = renderer;
onResourcesLoaded(decorView, resizingBackgroundDrawable, captionBackgroundDrawable,
- statusBarColor);
+ userCaptionBackgroundDrawable, statusBarColor);
// Create a render node for the content and frame backdrop
// which can be resized independently from the content.
@@ -92,10 +93,12 @@
}
void onResourcesLoaded(DecorView decorView, Drawable resizingBackgroundDrawable,
- Drawable captionBackgroundDrawableDrawable, int statusBarColor) {
+ Drawable captionBackgroundDrawableDrawable, Drawable userCaptionBackgroundDrawable,
+ int statusBarColor) {
mDecorView = decorView;
mResizingBackgroundDrawable = resizingBackgroundDrawable;
mCaptionBackgroundDrawable = captionBackgroundDrawableDrawable;
+ mUserCaptionBackgroundDrawable = userCaptionBackgroundDrawable;
if (statusBarColor != 0) {
mStatusBarColor = new ColorDrawable(statusBarColor);
addSystemBarNodeIfNeeded();
@@ -281,8 +284,10 @@
// Draw the caption and content backdrops in to our render node.
DisplayListCanvas canvas = mFrameAndBackdropNode.start(width, height);
- mCaptionBackgroundDrawable.setBounds(0, 0, left + width, top + mLastCaptionHeight);
- mCaptionBackgroundDrawable.draw(canvas);
+ final Drawable drawable = mUserCaptionBackgroundDrawable != null
+ ? mUserCaptionBackgroundDrawable : mCaptionBackgroundDrawable;
+ drawable.setBounds(0, 0, left + width, top + mLastCaptionHeight);
+ drawable.draw(canvas);
// The backdrop: clear everything with the background. Clipping is done elsewhere.
mResizingBackgroundDrawable.setBounds(0, mLastCaptionHeight, left + width, top + height);
@@ -324,4 +329,8 @@
mChoreographer.postFrameCallback(this);
}
}
+
+ void setUserCaptionBackgroundDrawable(Drawable userCaptionBackgroundDrawable) {
+ mUserCaptionBackgroundDrawable = userCaptionBackgroundDrawable;
+ }
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 531ba2f..e405564 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -74,6 +74,8 @@
import static android.view.View.MeasureSpec.getMode;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.view.Window.DECOR_CAPTION_SHADE_DARK;
+import static android.view.Window.DECOR_CAPTION_SHADE_LIGHT;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
@@ -184,9 +186,10 @@
private boolean mWindowResizeCallbacksAdded = false;
- BackdropFrameRenderer mBackdropFrameRenderer = null;
+ private BackdropFrameRenderer mBackdropFrameRenderer = null;
private Drawable mResizingBackgroundDrawable;
private Drawable mCaptionBackgroundDrawable;
+ private Drawable mUserCaptionBackgroundDrawable;
DecorView(Context context, int featureId, PhoneWindow window) {
super(context);
@@ -1580,18 +1583,20 @@
initializeElevation();
}
- View onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
+ void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
mStackId = getStackId();
mResizingBackgroundDrawable = getResizingBackgroundDrawable(
mWindow.mBackgroundResource, mWindow.mBackgroundFallbackResource);
- mCaptionBackgroundDrawable =
- getContext().getDrawable(R.drawable.decor_caption_title_focused);
+ if (mCaptionBackgroundDrawable == null) {
+ mCaptionBackgroundDrawable = getContext().getDrawable(
+ R.drawable.decor_caption_title_focused);
+ }
if (mBackdropFrameRenderer != null) {
mBackdropFrameRenderer.onResourcesLoaded(
this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
- getCurrentColor(mStatusColorViewState));
+ mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState));
}
mDecorCaptionView = createDecorCaptionView(inflater);
@@ -1608,17 +1613,16 @@
}
mContentRoot = (ViewGroup) root;
initializeElevation();
- return root;
}
// Free floating overlapping windows require a caption.
private DecorCaptionView createDecorCaptionView(LayoutInflater inflater) {
- DecorCaptionView DecorCaptionView = null;
- for (int i = getChildCount() - 1; i >= 0 && DecorCaptionView == null; i--) {
+ DecorCaptionView decorCaptionView = null;
+ for (int i = getChildCount() - 1; i >= 0 && decorCaptionView == null; i--) {
View view = getChildAt(i);
if (view instanceof DecorCaptionView) {
// The decor was most likely saved from a relaunch - so reuse it.
- DecorCaptionView = (DecorCaptionView) view;
+ decorCaptionView = (DecorCaptionView) view;
removeViewAt(i);
}
}
@@ -1630,27 +1634,72 @@
&& ActivityManager.StackId.hasWindowDecor(mStackId)) {
// Dependent on the brightness of the used title we either use the
// dark or the light button frame.
- if (DecorCaptionView == null) {
- Context context = getContext();
- TypedValue value = new TypedValue();
- context.getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
- inflater = inflater.from(context);
- if (Color.luminance(value.data) < 0.5) {
- DecorCaptionView = (DecorCaptionView) inflater.inflate(
- R.layout.decor_caption_dark, null);
- } else {
- DecorCaptionView = (DecorCaptionView) inflater.inflate(
- R.layout.decor_caption_light, null);
- }
+ if (decorCaptionView == null) {
+ decorCaptionView = inflateDecorCaptionView(inflater);
}
- DecorCaptionView.setPhoneWindow(mWindow, true /*showDecor*/);
+ decorCaptionView.setPhoneWindow(mWindow, true /*showDecor*/);
} else {
- DecorCaptionView = null;
+ decorCaptionView = null;
}
// Tell the decor if it has a visible caption.
- enableCaption(DecorCaptionView != null);
- return DecorCaptionView;
+ enableCaption(decorCaptionView != null);
+ return decorCaptionView;
+ }
+
+ private DecorCaptionView inflateDecorCaptionView(LayoutInflater inflater) {
+ final Context context = getContext();
+ // We make a copy of the inflater, so it has the right context associated with it.
+ inflater = inflater.from(context);
+ final DecorCaptionView view = (DecorCaptionView) inflater.inflate(R.layout.decor_caption,
+ null);
+ setDecorCaptionShade(context, view);
+ return view;
+ }
+
+ private void setDecorCaptionShade(Context context, DecorCaptionView view) {
+ final int shade = mWindow.getDecorCaptionShade();
+ switch (shade) {
+ case DECOR_CAPTION_SHADE_LIGHT:
+ setLightDecorCaptionShade(view);
+ break;
+ case DECOR_CAPTION_SHADE_DARK:
+ setDarkDecorCaptionShade(view);
+ break;
+ default: {
+ TypedValue value = new TypedValue();
+ context.getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
+ // We invert the shade depending on brightness of the theme. Dark shade for light
+ // theme and vice versa. Thanks to this the buttons should be visible on the
+ // background.
+ if (Color.luminance(value.data) < 0.5) {
+ setLightDecorCaptionShade(view);
+ } else {
+ setDarkDecorCaptionShade(view);
+ }
+ break;
+ }
+ }
+ }
+
+ void updateDecorCaptionShade() {
+ if (mDecorCaptionView != null) {
+ setDecorCaptionShade(getContext(), mDecorCaptionView);
+ }
+ }
+
+ private void setLightDecorCaptionShade(DecorCaptionView view) {
+ view.findViewById(R.id.maximize_window).setBackgroundResource(
+ R.drawable.decor_maximize_button_light);
+ view.findViewById(R.id.close_window).setBackgroundResource(
+ R.drawable.decor_close_button_light);
+ }
+
+ private void setDarkDecorCaptionShade(DecorCaptionView view) {
+ view.findViewById(R.id.maximize_window).setBackgroundResource(
+ R.drawable.decor_maximize_button_dark);
+ view.findViewById(R.id.close_window).setBackgroundResource(
+ R.drawable.decor_close_button_dark);
}
/**
@@ -1735,11 +1784,11 @@
if (mBackdropFrameRenderer != null) {
return;
}
- final ThreadedRenderer renderer = (ThreadedRenderer) getHardwareRenderer();
+ final ThreadedRenderer renderer = getHardwareRenderer();
if (renderer != null) {
mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer,
initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
- getCurrentColor(mStatusColorViewState));
+ mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState));
// Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
// If we want to get the shadow shown while resizing, we would need to elevate a new
@@ -1849,6 +1898,16 @@
getResources().getDisplayMetrics());
}
+ /**
+ * Provide an override of the caption background drawable.
+ */
+ void setUserCaptionBackgroundDrawable(Drawable drawable) {
+ mUserCaptionBackgroundDrawable = drawable;
+ if (mBackdropFrameRenderer != null) {
+ mBackdropFrameRenderer.setUserCaptionBackgroundDrawable(drawable);
+ }
+ }
+
private static class ColorViewState {
View view = null;
int targetVisibility = View.INVISIBLE;
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 337bb69..86bd782 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -94,7 +94,6 @@
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
-import android.widget.LinearLayout;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -273,6 +272,8 @@
private boolean mIsStartingWindow;
private int mTheme = -1;
+ private int mDecorCaptionShade = DECOR_CAPTION_SHADE_AUTO;
+
static class WindowManagerHolder {
static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
@@ -2527,7 +2528,7 @@
}
mDecor.startChanging();
- final View in = mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
+ mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
@@ -3720,4 +3721,21 @@
}
}
}
+
+ @Override
+ public void setResizingCaptionDrawable(Drawable drawable) {
+ mDecor.setUserCaptionBackgroundDrawable(drawable);
+ }
+
+ @Override
+ public void setDecorCaptionShade(int decorCaptionShade) {
+ mDecorCaptionShade = decorCaptionShade;
+ if (mDecor != null) {
+ mDecor.updateDecorCaptionShade();
+ }
+ }
+
+ int getDecorCaptionShade() {
+ return mDecorCaptionShade;
+ }
}
diff --git a/core/java/com/android/internal/widget/DecorCaptionView.java b/core/java/com/android/internal/widget/DecorCaptionView.java
index d747686..c3fe9e7 100644
--- a/core/java/com/android/internal/widget/DecorCaptionView.java
+++ b/core/java/com/android/internal/widget/DecorCaptionView.java
@@ -319,6 +319,10 @@
captionHeight + mContent.getMeasuredHeight());
}
}
+
+ // This assumes that the caption bar is at the top.
+ mOwner.notifyRestrictedCaptionAreaCallback(mMaximize.getLeft(), mMaximize.getTop(),
+ mClose.getRight(), mClose.getBottom());
}
/**
* Determine if the workspace is entirely covered by the window.