Commit 38cd8305 authored by Yue Zhang's avatar Yue Zhang Committed by Commit Bot

Add components for drag-and-drop IPH

* Add xml files regarding the animated drawable for the IPH.
* Introduce a TabGridIphItemView as the entrance for IPH.
* Add logic in TabListRecyclerView to add a footer to accommodate the
  IPH entrance. This is a workaround before
  https://crrev.com/c/1650522.

Bug: 987043
Change-Id: Ibed6f42c291ebef37277431b910b6ff9aca3da47
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1716138
Commit-Queue: Yue Zhang <yuezhanggg@chromium.org>
Reviewed-by: default avatarWei-Yin Chen (陳威尹) <wychen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#682809}
parent be96700c
......@@ -32,6 +32,7 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogParent.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphItemView.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallback.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetCoordinator.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetMediator.java",
......
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:propertyName="scaleX"
android:startOffset="2350"
android:duration="300"
android:valueFrom="1f"
android:valueTo="0.6f"
android:valueType="floatType" />
<objectAnimator
android:propertyName="scaleY"
android:startOffset="2350"
android:duration="300"
android:valueFrom="1f"
android:valueTo="0.6f"
android:valueType="floatType" />
<objectAnimator
android:propertyName="scaleX"
android:duration="300"
android:startOffset="3650"
android:valueFrom=".6f"
android:valueTo="0.4f"
android:valueType="floatType" />
<objectAnimator
android:propertyName="scaleY"
android:duration="300"
android:startOffset="3650"
android:valueFrom=".6f"
android:valueTo="0.4f"
android:valueType="floatType" />
<objectAnimator
android:propertyName="translateX"
android:startOffset="3650"
android:duration="300"
android:valueFrom="0"
android:valueTo="-55" />
<objectAnimator
android:propertyName="translateY"
android:startOffset="3650"
android:duration="300"
android:valueFrom="0"
android:valueTo="-60" />
</set>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:propertyName="translateX"
android:startOffset="2300"
android:duration="300"
android:valueFrom="0"
android:valueTo="-200" />
<objectAnimator
android:propertyName="translateY"
android:startOffset="2300"
android:duration="300"
android:valueFrom="0"
android:valueTo="100" />
<objectAnimator
android:propertyName="scaleX"
android:duration="300"
android:startOffset="3650"
android:valueFrom="1f"
android:valueTo="0.4f"
android:valueType="floatType" />
<objectAnimator
android:propertyName="scaleY"
android:duration="300"
android:startOffset="3650"
android:valueFrom="1f"
android:valueTo="0.4f"
android:valueType="floatType" />
<objectAnimator
android:propertyName="translateX"
android:duration="300"
android:startOffset="3650"
android:valueFrom="-200"
android:valueTo="-245" />
<objectAnimator
android:propertyName="translateY"
android:duration="300"
android:startOffset="3650"
android:valueFrom="100"
android:valueTo="-60" />
</set>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:propertyName="fillColor"
android:duration="300"
android:startOffset="1000"
android:valueFrom="@color/default_icon_color_white"
android:valueTo="@color/google_blue_50"
android:valueType="intType" />
<objectAnimator
android:propertyName="fillColor"
android:duration="300"
android:startOffset="3650"
android:valueFrom="@color/google_blue_50"
android:valueTo="@color/default_icon_color_white"
android:valueType="intType" />
</set>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:propertyName="fillAlpha"
android:startOffset="700"
android:duration="300"
android:valueFrom="0f"
android:valueTo="0.8f"
android:valueType="floatType" />
<objectAnimator
android:propertyName="fillAlpha"
android:startOffset="3650"
android:duration="300"
android:valueFrom="0.8f"
android:valueTo="0f"
android:valueType="floatType" />
</set>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:propertyName="scaleX"
android:duration="300"
android:startOffset="700"
android:valueFrom="0.75f"
android:valueTo="1f"
android:valueType="floatType" />
<objectAnimator
android:propertyName="scaleY"
android:duration="300"
android:startOffset="700"
android:valueFrom="0.75f"
android:valueTo="1f"
android:valueType="floatType" />
<objectAnimator
android:propertyName="translateX"
android:startOffset="2300"
android:duration="300"
android:valueFrom="0"
android:valueTo="-200" />
<objectAnimator
android:propertyName="translateY"
android:startOffset="2300"
android:duration="300"
android:valueFrom="0"
android:valueTo="100" />
</set>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:propertyName="fillAlpha"
android:startOffset="900"
android:duration="400"
android:valueFrom="0f"
android:valueTo="0.5f"
android:valueType="floatType" />
<objectAnimator
android:propertyName="fillAlpha"
android:startOffset="3650"
android:duration="300"
android:valueFrom="0.5f"
android:valueTo="0f"
android:valueType="floatType" />
</set>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:propertyName="scaleX"
android:duration="400"
android:startOffset="900"
android:valueFrom="0.75f"
android:valueTo="1f"
android:valueType="floatType" />
<objectAnimator
android:propertyName="scaleY"
android:duration="400"
android:startOffset="900"
android:valueFrom="0.75f"
android:valueTo="1f"
android:valueType="floatType" />
<objectAnimator
android:propertyName="translateX"
android:startOffset="2300"
android:duration="300"
android:valueFrom="0"
android:valueTo="-200" />
<objectAnimator
android:propertyName="translateY"
android:startOffset="2300"
android:duration="300"
android:valueFrom="0"
android:valueTo="100" />
</set>
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:drawable="@drawable/iph_drag_and_drop_drawable"
tools:targetApi="21">
<target
android:name="hovered_card"
android:animation="@anim/iph_hovered_card_animation"/>
<target
android:name="selected_card"
android:animation="@anim/iph_selected_card_animation"/>
<target
android:name="selected_card_rectangle"
android:animation="@anim/iph_selected_card_color_change_animation"/>
<target
android:name="touch_point_background"
android:animation="@anim/iph_touch_point_background_animation"/>
<target
android:name="touch_point"
android:animation="@anim/iph_touch_point_animation"/>
<target
android:name="touch_point_circle"
android:animation="@anim/iph_touch_point_alpha_animation"/>
<target
android:name="touch_point_background_circle"
android:animation="@anim/iph_touch_point_background_alpha_animation"/>
</animated-vector>
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:height="45dp"
android:width="70dp"
android:viewportHeight="450"
android:viewportWidth="700"
tools:targetApi="21">
<group android:name="selected_card_background">
<path android:fillColor="#E2E1E1"
android:pathData="
M100,100 h200
a20,20 0 0 1 20,20
v200
a20,20 0 0 1 -20,20
h-200
a20,20 0 0 1 -20,-20
v-200
a20,20 0 0 1 20,-20
z"
android:strokeColor="#E2E1E1"
android:strokeWidth="3"/>
</group>
<group android:name="hovered_card"
android:pivotX="200"
android:pivotY="230">
<path android:fillColor="#FFFFFF"
android:pathData="
M100,100 h200
a20,20 0 0 1 20,20
v200
a20,20 0 0 1 -20,20
h-200
a20,20 0 0 1 -20,-20
v-200
a20,20 0 0 1 20,-20
z"
android:strokeColor="#E2E1E1"
android:strokeWidth="3"/>
</group>
<group android:name="selected_card"
android:pivotX="500"
android:pivotY="230">
<path android:fillColor="#FFFFFF"
android:name="selected_card_rectangle"
android:pathData="
M400,100 h200
a20,20 0 0 1 20,20
v200
a20,20 0 0 1 -20,20
h-200
a20,20 0 0 1 -20,-20
v-200
a20,20 0 0 1 20,-20
z"
android:strokeColor="#E2E1E1"
android:strokeWidth="3"/>
</group>
<group android:name="touch_point_background"
android:pivotX="450"
android:pivotY="200">
<path
android:name="touch_point_background_circle"
android:fillColor="@color/modern_blue_600"
android:pathData="
M 450, 200
m -40, 0
a 40,40 0 1,0 80,0
a 40,40 0 1,0 -80,0
z
"
/>
</group>
<group android:name="touch_point"
android:pivotX="450"
android:pivotY="200">
<path
android:name="touch_point_circle"
android:fillColor="@color/modern_blue_600"
android:pathData="
M 450, 200
m -24, 0
a 24,24 0 1,0 48,0
a 24,24 0 1,0 -48,0
z
"
/>
</group>
</vector>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2018 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. -->
<org.chromium.chrome.browser.tasks.tab_management.TabGridIphItemView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="@dimen/tab_grid_iph_card_height"
android:layout_marginTop="@dimen/tab_grid_iph_card_padding"
android:layout_marginBottom="@dimen/tab_grid_iph_card_padding"
android:background="@color/modern_primary_color">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/popup_bg">
<TextView
android:id="@+id/iph_description"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3"
android:layout_marginStart="@dimen/tab_grid_iph_card_padding"
android:text="@string/iph_drag_and_drop_introduction"
android:textAppearance="@style/TextAppearance.BlackBodyDefault"
android:layout_gravity="center"
android:gravity="center_vertical"/>
<TextView
android:id="@+id/show_me_button"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1.5"
android:text="@string/iph_drag_and_drop_show_me"
android:textAppearance="@style/TextAppearance.BlueTitle2"
android:layout_gravity="center"
android:gravity="center_vertical|end"/>
<org.chromium.ui.widget.ChromeImageView
android:id="@+id/close_iph_button"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
style="@style/BottomToolbarButton"
android:src="@drawable/btn_close"
app:tint="@color/standard_mode_tint"
android:contentDescription="@string/close" />
</LinearLayout>
</org.chromium.chrome.browser.tasks.tab_management.TabGridIphItemView>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2019 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. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="@dimen/tab_grid_iph_dialog_size"
android:layout_height="@dimen/tab_grid_iph_dialog_size"
android:layout_gravity="center"
android:orientation="vertical"
android:background="@drawable/tab_grid_card_background"
android:clickable="true"
android:focusable="true">
<ImageView
android:id="@+id/animation_drawable"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="5"
app:srcCompat="@drawable/iph_drag_and_drop_animated_drawable"
android:contentDescription="@string/iph_drag_and_drop_content" />
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:text="@string/iph_drag_and_drop_title"
android:textAppearance="@style/TextAppearance.BlackTitle1"
android:gravity="center_vertical" />
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:text="@string/iph_drag_and_drop_content"
android:textAppearance="@style/TextAppearance.BlackBodyDefault"
android:gravity="start" />
<TextView
android:id="@+id/iph_drag_and_drop_dialog_close_button"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:text="@string/ok"
android:textAppearance="@style/TextAppearance.BlueTitle2"
android:gravity="end" />
</LinearLayout>
......@@ -12,6 +12,9 @@
<dimen name="tab_grid_close_button_size">18dp</dimen>
<dimen name="tab_grid_dialog_side_margin">16dp</dimen>
<dimen name="tab_grid_dialog_top_margin">85dp</dimen>
<dimen name="tab_grid_iph_card_height">48dp</dimen>
<dimen name="tab_grid_iph_card_padding">8dp</dimen>
<dimen name="tab_grid_iph_dialog_size">370dp</dimen>
<dimen name="tab_grid_merge_threshold">45dp</dimen>
<dimen name="tab_grid_thumbnail_card_default_size">152dp</dimen>
<dimen name="tab_grid_thumbnail_favicon_frame_padding">16dp</dimen>
......
// Copyright 2019 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.tasks.tab_management;
import android.content.Context;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.PopupWindow;
import android.widget.TextView;
import org.chromium.chrome.browser.widget.ScrimView;
import org.chromium.chrome.tab_ui.R;
import org.chromium.ui.widget.ChromeImageView;
/**
* Represents a view that works as the entrance for IPH in {@link TabListRecyclerView}.
*/
public class TabGridIphItemView extends FrameLayout {
private TextView mShowIPHButton;
private ChromeImageView mCloseIphEntranceButton;
private PopupWindow mIphWindow;
private ScrimView mScrimView;
private ScrimView.ScrimParams mScrimParams;
private Drawable mIphDrawable;
public TabGridIphItemView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
FrameLayout backgroundView = new FrameLayout(getContext());
View iphDialogView =
LayoutInflater.from(getContext())
.inflate(R.layout.iph_drag_and_drop_dialog_layout, backgroundView, false);
mShowIPHButton = findViewById(R.id.show_me_button);
mCloseIphEntranceButton = findViewById(R.id.close_iph_button);
mIphDrawable =
((ImageView) iphDialogView.findViewById(R.id.animation_drawable)).getDrawable();
TextView closeIphDialogButton =
iphDialogView.findViewById(R.id.iph_drag_and_drop_dialog_close_button);
backgroundView.addView(iphDialogView);
mIphWindow = new PopupWindow(backgroundView, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
mScrimView = new ScrimView(getContext(), null, backgroundView);
ScrimView.ScrimObserver scrimObserver = new ScrimView.ScrimObserver() {
@Override
public void onScrimClick() {
mIphWindow.dismiss();
mScrimView.hideScrim(true);
}
@Override
public void onScrimVisibilityChanged(boolean visible) {}
};
mScrimParams = new ScrimView.ScrimParams(iphDialogView, false, true, 0, scrimObserver);
closeIphDialogButton.setOnClickListener(View -> scrimObserver.onScrimClick());
}
/**
* Setup the {@link View.OnClickListener} for ShowIPH button.
*/
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
View parent = (View) getParent();
mShowIPHButton.setOnClickListener((View view) -> {
// When show me button is clicked, the entrance should be closed.
mCloseIphEntranceButton.performClick();
mScrimView.showScrim(mScrimParams);
mIphWindow.showAtLocation(parent, Gravity.CENTER, 0, 0);
((Animatable) mIphDrawable).start();
});
}
/**
* Setup the {@link View.OnClickListener} for close button in the entrance of IPH.
*
* @param listener The {@link View.OnClickListener} used to setup close button in IPH entrance.
*/
void setupCloseIphEntranceButtonOnclickListener(View.OnClickListener listener) {
mCloseIphEntranceButton.setOnClickListener(listener);
}
}
......@@ -21,7 +21,9 @@ import android.support.v7.content.res.AppCompatResources;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.FrameLayout;
import android.widget.ImageView;
......@@ -29,6 +31,7 @@ import android.widget.ImageView;
import org.chromium.base.Log;
import org.chromium.chrome.browser.ChromeFeatureList;
import org.chromium.chrome.browser.util.FeatureUtilities;
import org.chromium.chrome.tab_ui.R;
import org.chromium.ui.interpolators.BakedBezierInterpolator;
import org.chromium.ui.resources.dynamics.DynamicResourceLoader;
import org.chromium.ui.resources.dynamics.ViewResourceAdapter;
......@@ -105,6 +108,7 @@ class TabListRecyclerView extends RecyclerView {
private ImageView mShadowImageView;
private int mShadowTopMargin;
private TabListOnScrollListener mScrollListener;
private View mRecyclerViewFooter;
/**
* Basic constructor to use during inflation from xml.
......@@ -441,4 +445,37 @@ class TabListRecyclerView extends RecyclerView {
float left1, float top1, float left2, float top2, float threshold) {
return Math.abs(left1 - left2) < threshold && Math.abs(top1 - top2) < threshold;
}
/**
* This method creates a {@link TabListMediator.IphProvider} that can show IPH for drag-and-drop
* in GridTabSwitcher.
* @return The {@link TabListMediator.IphProvider} that can be used to show IPH.
* TODO(yuezhanggg): Replace this workaround with a footer itemView after we have generic list
* view adapter (crbug: 909779).
*/
TabListMediator.IphProvider setupIphProvider() {
final int height = (int) getResources().getDimension(R.dimen.tab_grid_iph_card_height);
final int padding = (int) getResources().getDimension(R.dimen.tab_grid_iph_card_padding);
// Listener to close the footer.
View.OnClickListener closeListener = view -> {
((ViewGroup) mRecyclerViewFooter.getParent()).removeView(mRecyclerViewFooter);
mRecyclerViewFooter = null;
// Restore the recyclerview to its original state.
setPadding(0, 0, 0, 0);
setScrollBarStyle(SCROLLBARS_INSIDE_OVERLAY);
};
return anchor -> {
if (getChildCount() == 0) return;
if (mRecyclerViewFooter != null) return;
TabGridIphItemView iphView =
(TabGridIphItemView) LayoutInflater.from(getContext())
.inflate(R.layout.iph_card_item_layout, (ViewGroup) anchor, false);
((ViewGroup) anchor).addView(iphView);
mRecyclerViewFooter = iphView;
iphView.setupCloseIphEntranceButtonOnclickListener(closeListener);
setPadding(0, 0, 0, height + padding);
setScrollBarStyle(SCROLLBARS_OUTSIDE_OVERLAY);
};
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment