Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2362)

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/infobar/ContentWrapperView.java

Issue 24109002: [InfoBar] Upstram basic infobar flow for Android. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@upstream_infobar_full
Patch Set: Fix License header in two more files Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/java/src/org/chromium/chrome/browser/infobar/ContentWrapperView.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/ContentWrapperView.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/ContentWrapperView.java
new file mode 100644
index 0000000000000000000000000000000000000000..79e6d902056112dce37fb4d783c0ba9f87cadc6f
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/ContentWrapperView.java
@@ -0,0 +1,254 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.infobar;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.widget.FrameLayout;
+
+import org.chromium.chrome.R;
+
+import org.chromium.base.ApiCompatibilityUtils;
+
+import java.util.ArrayList;
+
+/**
+ * A wrapper class designed to:
+ * - consume all touch events. This way the parent view (the FrameLayout ContentView) won't
+ * have its onTouchEvent called. If it does, ContentView will process the touch click.
+ * We don't want web content responding to clicks on the InfoBars.
+ * - allow swapping out of children Views for animations.
+ *
+ * Once an InfoBar has been hidden and removed from the InfoBarContainer, it cannot be reused
+ * because the main panel is discarded after the hiding animation.
+ */
+public class ContentWrapperView extends FrameLayout {
+ private static final String TAG = "ContentWrapperView";
+
+ // Index of the child View that will get swapped out during transitions.
+ private static final int CONTENT_INDEX = 0;
+
+ private final int mGravity;
+ private final boolean mInfoBarsFromTop;
+ private final InfoBar mInfoBar;
+
+ private View mViewToHide;
+ private View mViewToShow;
+
+ /**
+ * Constructs a ContentWrapperView object.
+ * @param context The context to create this View with.
+ */
+ public ContentWrapperView(Context context, InfoBar infoBar, int backgroundType, View panel,
+ boolean infoBarsFromTop) {
+ // Set up this ViewGroup.
+ super(context);
+ mInfoBar = infoBar;
+ mGravity = infoBarsFromTop ? Gravity.BOTTOM : Gravity.TOP;
+ mInfoBarsFromTop = infoBarsFromTop;
+
+ // Pull out resources we need for the backgrounds. Defaults to the INFO type.
+ int separatorBackground = R.color.infobar_info_background_separator;
+ int layoutBackground = R.drawable.infobar_info_background;
+ if (backgroundType == InfoBar.BACKGROUND_TYPE_WARNING) {
+ layoutBackground = R.drawable.infobar_warning_background;
+ separatorBackground = R.color.infobar_warning_background_separator;
+ }
+
+ // Set up this view.
+ Resources resources = context.getResources();
+ LayoutParams wrapParams = new LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.WRAP_CONTENT);
+ setLayoutParams(wrapParams);
+ ApiCompatibilityUtils.setBackgroundForView(this, resources.getDrawable(layoutBackground));
+
+ // Add a separator line that delineates different InfoBars.
+ View separator = new View(context);
+ separator.setBackgroundColor(resources.getColor(separatorBackground));
+ addView(separator, new LayoutParams(LayoutParams.MATCH_PARENT, getBoundaryHeight(context),
+ mGravity));
+
+ // Add the InfoBar content.
+ addChildView(panel);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ return !mInfoBar.areControlsEnabled();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ // Consume all motion events so they do not reach the ContentView.
+ return true;
+ }
+
+ /**
+ * Calculates how tall the InfoBar boundary should be in pixels.
+ * XHDPI devices and above get a double-tall boundary.
+ * @return The height of the boundary.
+ */
+ private int getBoundaryHeight(Context context) {
+ float density = context.getResources().getDisplayMetrics().density;
+ return density < 2.0f ? 1 : 2;
+ }
+
+ /**
+ * @return the current View representing the InfoBar.
+ */
+ public boolean hasChildView() {
+ // If there's a View that can be replaced, there will be at least two children for the View.
+ // One of the Views will always be the InfoBar separator.
+ return getChildCount() > 1;
+ }
+
+ /**
+ * Detaches the View currently being shown and returns it for reparenting.
+ * @return the View that is currently being shown.
+ */
+ public View detachCurrentView() {
+ assert getChildCount() > 1;
+ View view = getChildAt(CONTENT_INDEX);
+ removeView(view);
+ return view;
+ }
+
+ /**
+ * Adds a View to this layout, before the InfoBar separator.
+ * @param viewToAdd The View to add.
+ */
+ private void addChildView(View viewToAdd) {
+ addView(viewToAdd, CONTENT_INDEX, new FrameLayout.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, mGravity));
+ }
+
+ /**
+ * Prepares the animation needed to hide the current View and show the new one.
+ * @param viewToShow View that will replace the currently shown child of this FrameLayout.
+ */
+ public void prepareTransition(View viewToShow) {
+ assert mViewToHide == null && mViewToShow == null;
+
+ // If it exists, the View that is being replaced will be the non-separator child and will
+ // we in the second position.
+ assert getChildCount() <= 2;
+ if (hasChildView()) {
+ mViewToHide = getChildAt(CONTENT_INDEX);
+ }
+
+ mViewToShow = viewToShow;
+ assert mViewToHide != null || mViewToShow != null;
+ assert mViewToHide != mViewToShow;
+ }
+
+ /**
+ * Called when the animation is starting.
+ */
+ public void startTransition() {
+ if (mViewToShow != null) {
+ // Move the View to this container.
+ ViewParent parent = mViewToShow.getParent();
+ assert parent != null && parent instanceof ViewGroup;
+ ((ViewGroup) parent).removeView(mViewToShow);
+ addChildView(mViewToShow);
+
+ // We're transitioning between two views; set the alpha so it doesn't pop in.
+ if (mViewToHide != null) mViewToShow.setAlpha(0.0f);
+
+ // Because of layout scheduling, we need to move the child Views downward before it
+ // occurs. Failure to do so results in the Views being located incorrectly during the
+ // first few frames of the animation.
+ if (mInfoBarsFromTop && getViewToShowHeight() > getViewToHideHeight()) {
+ getLayoutParams().height = getViewToShowHeight();
+
+ int translation = getTransitionHeightDifference();
+ for (int i = 0; i < getChildCount(); ++i) {
+ View v = getChildAt(i);
+ v.setTop(v.getTop() + translation);
+ v.setBottom(v.getBottom() + translation);
+ }
+ }
+ }
+ }
+
+ /**
+ * Called when the animation is done.
+ * At this point, we can get rid of the View that used to represent the InfoBar and re-enable
+ * controls.
+ */
+ public void finishTransition() {
+ if (mViewToHide != null) {
+ removeView(mViewToHide);
+ }
+ getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
+ requestLayout();
+
+ mViewToHide = null;
+ mViewToShow = null;
+ mInfoBar.setControlsEnabled(true);
+ }
+
+ /**
+ * Returns the height of the View being shown.
+ * If no new View is going to replace the current one (i.e. the InfoBar is being hidden), the
+ * height is 0.
+ */
+ private int getViewToShowHeight() {
+ return mViewToShow == null ? 0 : mViewToShow.getHeight();
+ }
+
+ /**
+ * Returns the height of the View being hidden.
+ * If there wasn't a View in the container (i.e. the InfoBar is being animated onto the screen),
+ * then the height is 0.
+ */
+ private int getViewToHideHeight() {
+ return mViewToHide == null ? 0 : mViewToHide.getHeight();
+ }
+
+ /**
+ * @return the difference in height between the View being shown and the View being hidden.
+ */
+ public int getTransitionHeightDifference() {
+ return getViewToShowHeight() - getViewToHideHeight();
+ }
+
+ /**
+ * Creates animations for transitioning between the two Views.
+ * @param animators ArrayList to append the transition Animators to.
+ */
+ public void getAnimationsForTransition(ArrayList<Animator> animators) {
+ if (mViewToHide != null && mViewToShow != null) {
+ ObjectAnimator hideAnimator;
+ hideAnimator = ObjectAnimator.ofFloat((Object)mViewToHide, "alpha", 1.0f, 0.0f);
+ animators.add(hideAnimator);
+
+ ObjectAnimator showAnimator;
+ showAnimator = ObjectAnimator.ofFloat((Object)mViewToShow, "alpha", 0.0f, 1.0f);
+ animators.add(showAnimator);
+ }
+ }
+
+ /**
+ * Calculates a Rect that prevents this ContentWrapperView from overlapping its siblings.
+ * Because of the way the InfoBarContainer stores its children, Android will cause the InfoBars
+ * to overlap when a bar is slid towards the top of the screen. This calculates a bounding box
+ * around this ContentWrapperView that clips the InfoBar to be drawn solely in the space it was
+ * occupying before being translated anywhere.
+ * @return the calculated bounding box
+ */
+ public Rect getClippingRect() {
+ int maxHeight = Math.max(getViewToHideHeight(), getViewToShowHeight());
+ return new Rect(getLeft(), getTop(), getRight(), getTop() + maxHeight);
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698