Adds ability to use LauncherPreviewRenderer in Sandbox.

This is mostly a proof-of-concept for future use and
tweaks as desired by UX.

The idea is to better replicate the actual UI.

Screenshot: https://drive.google.com/file/d/1EGDgcrSH2QZuSh6P3zWLBl6L8Xd-gtFY/view?usp=sharing

Bug: 148542211
Change-Id: Ibed4b8118346ef72599463c9fd6a2f71166993fd
This commit is contained in:
Andy Wickham 2020-08-09 21:49:18 -07:00
parent 7144004c56
commit 9e754b008d
10 changed files with 133 additions and 15 deletions

View File

@ -13,7 +13,8 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <com.android.quickstep.interaction.RootSandboxLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?android:attr/colorBackground"> android:background="?android:attr/colorBackground">
@ -93,11 +94,11 @@
style="@style/TextAppearance.GestureTutorial.Feedback" style="@style/TextAppearance.GestureTutorial.Feedback"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_above="@id/gesture_tutorial_fragment_action_button" android:layout_below="@id/gesture_tutorial_fragment_titles_container"
android:layout_centerHorizontal="true" android:layout_centerHorizontal="true"
android:layout_marginStart="@dimen/gesture_tutorial_feedback_margin_start_end" android:layout_marginStart="@dimen/gesture_tutorial_feedback_margin_start_end"
android:layout_marginEnd="@dimen/gesture_tutorial_feedback_margin_start_end" android:layout_marginEnd="@dimen/gesture_tutorial_feedback_margin_start_end"
android:layout_marginBottom="10dp"/> android:layout_marginTop="40dp"/>
<!-- android:stateListAnimator="@null" removes shadow and normal on click behavior (increase <!-- android:stateListAnimator="@null" removes shadow and normal on click behavior (increase
of elevation and shadow) which is replaced by ripple effect in android:foreground --> of elevation and shadow) which is replaced by ripple effect in android:foreground -->
@ -126,4 +127,4 @@
android:background="@null" android:background="@null"
android:foreground="?android:attr/selectableItemBackgroundBorderless" android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:stateListAnimator="@null"/> android:stateListAnimator="@null"/>
</RelativeLayout> </com.android.quickstep.interaction.RootSandboxLayout>

View File

@ -143,6 +143,10 @@ public class EdgeBackGestureHandler implements OnTouchListener {
return false; return false;
} }
boolean onInterceptTouch(MotionEvent motionEvent) {
return isWithinTouchRegion((int) motionEvent.getX(), (int) motionEvent.getY());
}
private boolean isWithinTouchRegion(int x, int y) { private boolean isWithinTouchRegion(int x, int y) {
// Disallow if too far from the edge // Disallow if too far from the edge
if (x > mEdgeWidth + mLeftInset && x < (mDisplaySize.x - mEdgeWidth - mRightInset)) { if (x > mEdgeWidth + mLeftInset && x < (mDisplaySize.x - mEdgeWidth - mRightInset)) {

View File

@ -250,6 +250,12 @@ public class NavBarGestureHandler implements OnTouchListener,
return intercepted; return intercepted;
} }
boolean onInterceptTouch(MotionEvent event) {
return mAssistantLeftRegion.contains(event.getX(), event.getY())
|| mAssistantRightRegion.contains(event.getX(), event.getY())
|| event.getY() >= mDisplaySize.y - mBottomGestureHeight;
}
protected void onMotionPauseDetected() { protected void onMotionPauseDetected() {
VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC); VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
} }

View File

@ -0,0 +1,44 @@
/*
* 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.
*/
package com.android.quickstep.interaction;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.RelativeLayout;
import androidx.fragment.app.FragmentManager;
/** Root layout that TutorialFragment uses to intercept motion events. */
public class RootSandboxLayout extends RelativeLayout {
public RootSandboxLayout(Context context) {
super(context);
}
public RootSandboxLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RootSandboxLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent motionEvent) {
return ((TutorialFragment) FragmentManager.findFragment(this))
.onInterceptTouch(motionEvent);
}
}

View File

@ -0,0 +1,44 @@
/*
* 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.
*/
package com.android.quickstep.interaction;
import android.content.Context;
import android.view.View;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.graphics.LauncherPreviewRenderer;
/** Renders a fake Launcher for use in the Sandbox. */
class SandboxLauncherRenderer extends LauncherPreviewRenderer {
SandboxLauncherRenderer(Context context, InvariantDeviceProfile idp, boolean migrated) {
super(context, idp, migrated);
}
@Override
public boolean shouldShowRealLauncherPreview() {
return false;
}
@Override
public boolean shouldShowQsb() {
return false;
}
@Override
public View.OnLongClickListener getWorkspaceChildOnLongClickListener() {
return null;
}
}

View File

@ -47,6 +47,7 @@ abstract class TutorialController implements BackGestureAttemptCallback,
final TextView mTitleTextView; final TextView mTitleTextView;
final TextView mSubtitleTextView; final TextView mSubtitleTextView;
final TextView mFeedbackView; final TextView mFeedbackView;
final View mLauncherView;
final ClipIconView mFakeIconView; final ClipIconView mFakeIconView;
final View mFakeTaskView; final View mFakeTaskView;
final View mRippleView; final View mRippleView;
@ -68,6 +69,7 @@ abstract class TutorialController implements BackGestureAttemptCallback,
mTitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_title_view); mTitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_title_view);
mSubtitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_subtitle_view); mSubtitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_subtitle_view);
mFeedbackView = rootView.findViewById(R.id.gesture_tutorial_fragment_feedback_view); mFeedbackView = rootView.findViewById(R.id.gesture_tutorial_fragment_feedback_view);
mLauncherView = tutorialFragment.getLauncherView();
mFakeIconView = rootView.findViewById(R.id.gesture_tutorial_fake_icon_view); mFakeIconView = rootView.findViewById(R.id.gesture_tutorial_fake_icon_view);
mFakeTaskView = rootView.findViewById(R.id.gesture_tutorial_fake_task_view); mFakeTaskView = rootView.findViewById(R.id.gesture_tutorial_fake_task_view);
mRippleView = rootView.findViewById(R.id.gesture_tutorial_ripple_view); mRippleView = rootView.findViewById(R.id.gesture_tutorial_ripple_view);
@ -162,8 +164,10 @@ abstract class TutorialController implements BackGestureAttemptCallback,
if (isComplete()) { if (isComplete()) {
hideHandCoachingAnimation(); hideHandCoachingAnimation();
mLauncherView.setVisibility(View.INVISIBLE);
} else { } else {
showHandCoachingAnimation(); showHandCoachingAnimation();
mLauncherView.setVisibility(View.VISIBLE);
} }
} }

View File

@ -31,6 +31,7 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.quickstep.interaction.TutorialController.TutorialType; import com.android.quickstep.interaction.TutorialController.TutorialType;
@ -45,6 +46,7 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
TutorialHandAnimation mHandCoachingAnimation; TutorialHandAnimation mHandCoachingAnimation;
EdgeBackGestureHandler mEdgeBackGestureHandler; EdgeBackGestureHandler mEdgeBackGestureHandler;
NavBarGestureHandler mNavBarGestureHandler; NavBarGestureHandler mNavBarGestureHandler;
private View mLauncherView;
public static TutorialFragment newInstance(TutorialType tutorialType) { public static TutorialFragment newInstance(TutorialType tutorialType) {
TutorialFragment fragment = getFragmentForTutorialType(tutorialType); TutorialFragment fragment = getFragmentForTutorialType(tutorialType);
@ -114,8 +116,11 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
return insets; return insets;
}); });
mRootView.setOnTouchListener(this); mRootView.setOnTouchListener(this);
mHandCoachingAnimation = new TutorialHandAnimation(getContext(), mRootView, mHandCoachingAnimation =
getHandAnimationResId()); new TutorialHandAnimation(getContext(), mRootView, getHandAnimationResId());
InvariantDeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(getContext());
mLauncherView = new SandboxLauncherRenderer(getContext(), dp, true).getRenderedView();
((ViewGroup) mRootView).addView(mLauncherView, 0);
return mRootView; return mRootView;
} }
@ -133,11 +138,17 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
@Override @Override
public boolean onTouch(View view, MotionEvent motionEvent) { public boolean onTouch(View view, MotionEvent motionEvent) {
// Note: Using logical or to ensure both functions get called. // Note: Using logical-or to ensure both functions get called.
return mEdgeBackGestureHandler.onTouch(view, motionEvent) return mEdgeBackGestureHandler.onTouch(view, motionEvent)
| mNavBarGestureHandler.onTouch(view, motionEvent); | mNavBarGestureHandler.onTouch(view, motionEvent);
} }
boolean onInterceptTouch(MotionEvent motionEvent) {
// Note: Using logical-or to ensure both functions get called.
return mEdgeBackGestureHandler.onInterceptTouch(motionEvent)
| mNavBarGestureHandler.onInterceptTouch(motionEvent);
}
void onAttachedToWindow() { void onAttachedToWindow() {
mEdgeBackGestureHandler.setViewGroupParent((ViewGroup) getRootView()); mEdgeBackGestureHandler.setViewGroupParent((ViewGroup) getRootView());
} }
@ -168,6 +179,10 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
return mRootView; return mRootView;
} }
View getLauncherView() {
return mLauncherView;
}
TutorialHandAnimation getHandAnimation() { TutorialHandAnimation getHandAnimation() {
return mHandCoachingAnimation; return mHandCoachingAnimation;
} }

View File

@ -323,11 +323,8 @@ public class CellLayout extends ViewGroup {
@Override @Override
public boolean onInterceptTouchEvent(MotionEvent ev) { public boolean onInterceptTouchEvent(MotionEvent ev) {
if (mTouchHelper != null return mTouchHelper != null
|| (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev))) { || (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev));
return true;
}
return false;
} }
public void enableHardwareLayer(boolean hasLayer) { public void enableHardwareLayer(boolean hasLayer) {

View File

@ -130,12 +130,16 @@ public interface WorkspaceLayoutManager {
} }
child.setHapticFeedbackEnabled(false); child.setHapticFeedbackEnabled(false);
child.setOnLongClickListener(ItemLongClickListener.INSTANCE_WORKSPACE); child.setOnLongClickListener(getWorkspaceChildOnLongClickListener());
if (child instanceof DropTarget) { if (child instanceof DropTarget) {
onAddDropTarget((DropTarget) child); onAddDropTarget((DropTarget) child);
} }
} }
default View.OnLongClickListener getWorkspaceChildOnLongClickListener() {
return ItemLongClickListener.INSTANCE_WORKSPACE;
}
Hotseat getHotseat(); Hotseat getHotseat();
CellLayout getScreenWithId(int screenId); CellLayout getScreenWithId(int screenId);

View File

@ -513,8 +513,7 @@ public class LauncherPreviewRenderer extends ContextThemeWrapper
} }
// Setup search view // Setup search view
SearchUiManager searchUiManager = SearchUiManager searchUiManager = mRootView.findViewById(R.id.search_container_all_apps);
mRootView.findViewById(R.id.search_container_all_apps);
mRootView.findViewById(R.id.apps_view).setTranslationY( mRootView.findViewById(R.id.apps_view).setTranslationY(
mDp.heightPx - searchUiManager.getScrollRangeDelta(mInsets)); mDp.heightPx - searchUiManager.getScrollRangeDelta(mInsets));
ViewGroup searchView = (ViewGroup) searchUiManager; ViewGroup searchView = (ViewGroup) searchUiManager;