Remove Launcher3GoIconRecents
Bug: 144129431 Test: Build and check that Launcher3QuickStepGo is used instead Change-Id: Ib2a30c9e1e78de7792f34c3505372c8af1cb307d
This commit is contained in:
parent
4f37a5e268
commit
0842cf5f9d
48
Android.mk
48
Android.mk
|
@ -242,53 +242,7 @@ LOCAL_PROGUARD_ENABLED := full
|
|||
LOCAL_PACKAGE_NAME := Launcher3QuickStepGo
|
||||
LOCAL_PRIVILEGED_MODULE := true
|
||||
LOCAL_SYSTEM_EXT_MODULE := true
|
||||
LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep Launcher3GoIconRecents
|
||||
LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
|
||||
|
||||
LOCAL_FULL_LIBS_MANIFEST_FILES := \
|
||||
$(LOCAL_PATH)/go/AndroidManifest.xml \
|
||||
$(LOCAL_PATH)/quickstep/AndroidManifest-launcher.xml \
|
||||
$(LOCAL_PATH)/AndroidManifest-common.xml
|
||||
|
||||
LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml
|
||||
LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.*
|
||||
include $(BUILD_PACKAGE)
|
||||
|
||||
#
|
||||
# Build rule for Launcher3 Go app with quickstep and Go-specific
|
||||
# version of recents for Android Go devices.
|
||||
#
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_USE_AAPT2 := true
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLibLauncherWrapper launcherprotosnano
|
||||
ifneq (,$(wildcard frameworks/base))
|
||||
LOCAL_PRIVATE_PLATFORM_APIS := true
|
||||
else
|
||||
LOCAL_SDK_VERSION := system_current
|
||||
LOCAL_MIN_SDK_VERSION := 26
|
||||
endif
|
||||
LOCAL_STATIC_ANDROID_LIBRARIES := Launcher3CommonDepsLib
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
$(call all-java-files-under, src) \
|
||||
$(call all-java-files-under, quickstep/src) \
|
||||
$(call all-java-files-under, go/src) \
|
||||
$(call all-java-files-under, go/quickstep/src)
|
||||
|
||||
LOCAL_RESOURCE_DIR := \
|
||||
$(LOCAL_PATH)/quickstep/res \
|
||||
$(LOCAL_PATH)/go/res \
|
||||
$(LOCAL_PATH)/go/quickstep/res
|
||||
|
||||
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
|
||||
LOCAL_PROGUARD_ENABLED := full
|
||||
|
||||
LOCAL_PACKAGE_NAME := Launcher3GoIconRecents
|
||||
LOCAL_PRIVILEGED_MODULE := true
|
||||
LOCAL_SYSTEM_EXT_MODULE := true
|
||||
LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3Go Launcher3QuickStep
|
||||
LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep
|
||||
LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
|
||||
|
||||
LOCAL_FULL_LIBS_MANIFEST_FILES := \
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2019 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.
|
||||
-->
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/clear_all_button_bg"/>
|
||||
<corners android:radius="4dp"/>
|
||||
</shape>
|
|
@ -1,22 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2019 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.
|
||||
-->
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@android:color/darker_gray"/>
|
||||
<corners android:radius="@dimen/task_thumbnail_corner_radius"/>
|
||||
</shape>
|
|
@ -1,23 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2019 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.
|
||||
-->
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@android:color/transparent"/>
|
||||
<stroke android:color="@android:color/white" android:width="4px"/>
|
||||
<corners android:radius="2dp"/>
|
||||
</shape>
|
|
@ -1,34 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2019 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.
|
||||
-->
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/clear_all_item_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/clear_all_item_view_height">
|
||||
<Button
|
||||
android:id="@+id/clear_all_button"
|
||||
android:layout_width="@dimen/clear_all_button_width"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:background="@drawable/clear_all_button"
|
||||
android:gravity="center"
|
||||
android:text="@string/recents_clear_all"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@color/clear_all_button_text"
|
||||
android:textSize="14sp"
|
||||
style="@style/TextTitle"/>
|
||||
</FrameLayout>
|
|
@ -1,30 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Copyright (C) 2019 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.
|
||||
-->
|
||||
<com.android.quickstep.fallback.GoRecentsActivityRootView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/drag_layer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<include
|
||||
android:id="@+id/overview_panel"
|
||||
layout="@layout/overview_panel"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:outlineProvider="none"
|
||||
android:theme="@style/HomeScreenElementTheme" />
|
||||
</com.android.quickstep.fallback.GoRecentsActivityRootView>
|
|
@ -1,41 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2019 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.
|
||||
-->
|
||||
<com.android.quickstep.views.IconRecentsView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:clipChildren="false">
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recent_task_recycler_view"
|
||||
android:layout_width="@dimen/recents_list_width"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:scrollbars="none"
|
||||
android:clipToPadding="false"
|
||||
android:clipChildren="false"/>
|
||||
<TextView
|
||||
android:id="@+id/recent_task_empty_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:text="@string/recents_empty_message"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="25sp"
|
||||
style="@style/TextTitle"
|
||||
android:visibility="gone"/>
|
||||
</com.android.quickstep.views.IconRecentsView>
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2019 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.
|
||||
-->
|
||||
<fragment android:name="com.android.quickstep.IconRecentsFragment"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/low_ram_recents_fragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
|
@ -1,46 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2019 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.
|
||||
-->
|
||||
<com.android.quickstep.views.TaskItemView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/task_item_height"
|
||||
android:orientation="horizontal"
|
||||
android:clipChildren="false">
|
||||
<com.android.quickstep.views.TaskThumbnailIconView
|
||||
android:id="@+id/task_icon_and_thumbnail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginHorizontal="@dimen/task_thumbnail_icon_horiz_margin">
|
||||
<ImageView
|
||||
android:id="@+id/task_thumbnail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
<ImageView
|
||||
android:id="@+id/task_icon"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
</com.android.quickstep.views.TaskThumbnailIconView>
|
||||
<TextView
|
||||
android:id="@+id/task_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:singleLine="true"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="24sp"
|
||||
style="@style/TextTitle"/>
|
||||
</com.android.quickstep.views.TaskItemView>
|
|
@ -1,30 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2019 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.
|
||||
-->
|
||||
<resources>
|
||||
<dimen name="recents_list_width">480dp</dimen>
|
||||
|
||||
<dimen name="task_item_height">90dp</dimen>
|
||||
<dimen name="task_item_top_margin">24dp</dimen>
|
||||
<dimen name="task_thumbnail_icon_horiz_margin">24dp</dimen>
|
||||
|
||||
<dimen name="task_thumbnail_corner_radius">4dp</dimen>
|
||||
|
||||
<dimen name="clear_all_item_view_height">52dp</dimen>
|
||||
<dimen name="clear_all_item_view_top_margin">28dp</dimen>
|
||||
<dimen name="clear_all_item_view_bottom_margin">28dp</dimen>
|
||||
<dimen name="clear_all_button_width">160dp</dimen>
|
||||
</resources>
|
|
@ -1,20 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2019 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.
|
||||
-->
|
||||
<resources>
|
||||
<color name="clear_all_button_bg">#FFDADCE0</color>
|
||||
<color name="clear_all_button_text">#FF5F6368</color>
|
||||
</resources>
|
|
@ -1,30 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2019 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.
|
||||
-->
|
||||
<resources>
|
||||
<dimen name="recents_list_width">320dp</dimen>
|
||||
|
||||
<dimen name="task_item_height">60dp</dimen>
|
||||
<dimen name="task_item_top_margin">16dp</dimen>
|
||||
<dimen name="task_thumbnail_icon_horiz_margin">16dp</dimen>
|
||||
|
||||
<dimen name="task_thumbnail_corner_radius">3dp</dimen>
|
||||
|
||||
<dimen name="clear_all_item_view_height">36dp</dimen>
|
||||
<dimen name="clear_all_item_view_top_margin">20dp</dimen>
|
||||
<dimen name="clear_all_item_view_bottom_margin">20dp</dimen>
|
||||
<dimen name="clear_all_button_width">106dp</dimen>
|
||||
</resources>
|
|
@ -1,28 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2018 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.
|
||||
-->
|
||||
|
||||
<!-- Class overrides for Go version of launcher with Go recents. -->
|
||||
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="app_transition_manager_class" translatable="false">com.android.launcher3.GoLauncherAppTransitionManagerImpl</string>
|
||||
|
||||
<string name="instant_app_resolver_class" translatable="false">com.android.quickstep.InstantAppResolverImpl</string>
|
||||
|
||||
<string name="main_process_initializer_class" translatable="false">com.android.quickstep.QuickstepProcessInitializer</string>
|
||||
|
||||
<string name="user_event_dispatcher_class" translatable="false">com.android.quickstep.logging.UserEventDispatcherExtension</string>
|
||||
</resources>
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
package com.android.launcher3;
|
||||
|
||||
import static com.android.launcher3.Utilities.postAsyncCallback;
|
||||
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
|
||||
import static com.android.quickstep.views.IconRecentsView.CONTENT_ALPHA;
|
||||
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
|
||||
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.quickstep.views.IconRecentsView;
|
||||
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
|
||||
/**
|
||||
* A {@link QuickstepAppTransitionManagerImpl} with recents-specific app transitions based off
|
||||
* {@link com.android.quickstep.views.IconRecentsView}.
|
||||
*/
|
||||
public final class GoLauncherAppTransitionManagerImpl extends QuickstepAppTransitionManagerImpl {
|
||||
|
||||
public GoLauncherAppTransitionManagerImpl(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isLaunchingFromRecents(View v, RemoteAnimationTargetCompat[] targets) {
|
||||
return mLauncher.getStateManager().getState().overviewUi;
|
||||
}
|
||||
|
||||
@Override
|
||||
RemoteAnimationRunnerCompat getWallpaperOpenRunner(boolean fromUnlock) {
|
||||
return new GoWallpaperOpenLauncherAnimationRunner(mHandler,
|
||||
false /* startAtFrontOfQueue */, fromUnlock);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void composeRecentsLaunchAnimator(AnimatorSet anim, View v,
|
||||
RemoteAnimationTargetCompat[] appTargets,
|
||||
RemoteAnimationTargetCompat[] wallpaperTargets,
|
||||
boolean launcherClosing) {
|
||||
// Stubbed. Recents launch animation will come from the recents view itself and will not
|
||||
// use remote animations.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Runnable composeViewContentAnimator(AnimatorSet anim, float[] alphas, float[] trans) {
|
||||
IconRecentsView overview = mLauncher.getOverviewPanel();
|
||||
ObjectAnimator alpha = ObjectAnimator.ofFloat(overview,
|
||||
CONTENT_ALPHA, alphas);
|
||||
alpha.setDuration(CONTENT_ALPHA_DURATION);
|
||||
alpha.setInterpolator(LINEAR);
|
||||
anim.play(alpha);
|
||||
|
||||
ObjectAnimator transY = ObjectAnimator.ofFloat(overview, View.TRANSLATION_Y, trans);
|
||||
transY.setInterpolator(AGGRESSIVE_EASE);
|
||||
transY.setDuration(CONTENT_TRANSLATION_DURATION);
|
||||
anim.play(transY);
|
||||
|
||||
return mLauncher.getStateManager()::reapplyState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remote animation runner for animation from app to Launcher. For Go, when going to recents,
|
||||
* we need to ensure that the recents view is ready for remote animation before starting.
|
||||
*/
|
||||
private final class GoWallpaperOpenLauncherAnimationRunner extends
|
||||
WallpaperOpenLauncherAnimationRunner {
|
||||
public GoWallpaperOpenLauncherAnimationRunner(Handler handler, boolean startAtFrontOfQueue,
|
||||
boolean fromUnlock) {
|
||||
super(handler, startAtFrontOfQueue, fromUnlock);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
|
||||
RemoteAnimationTargetCompat[] wallpaperTargets,
|
||||
AnimationResult result) {
|
||||
boolean isGoingToRecents =
|
||||
taskIsATargetWithMode(appTargets, mLauncher.getTaskId(), MODE_OPENING)
|
||||
&& (mLauncher.getStateManager().getState() == LauncherState.OVERVIEW);
|
||||
if (isGoingToRecents) {
|
||||
IconRecentsView recentsView = mLauncher.getOverviewPanel();
|
||||
if (!recentsView.isReadyForRemoteAnim()) {
|
||||
recentsView.setOnReadyForRemoteAnimCallback(() ->
|
||||
postAsyncCallback(mHandler, () -> onCreateAnimation(appTargets,
|
||||
wallpaperTargets, result))
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
super.onCreateAnimation(appTargets, wallpaperTargets, result);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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.launcher3;
|
||||
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
|
||||
import com.android.quickstep.RecentsToActivityHelper;
|
||||
|
||||
/**
|
||||
* {@link RecentsToActivityHelper} for when the recents implementation is contained in
|
||||
* {@link Launcher}.
|
||||
*/
|
||||
public final class LauncherRecentsToActivityHelper implements RecentsToActivityHelper {
|
||||
|
||||
private final Launcher mLauncher;
|
||||
|
||||
public LauncherRecentsToActivityHelper(Launcher launcher) {
|
||||
mLauncher = launcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void leaveRecents() {
|
||||
mLauncher.getStateManager().goToState(NORMAL);
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2019 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.launcher3.uioverrides;
|
||||
|
||||
import com.android.launcher3.BaseQuickstepLauncher;
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.LandscapeStatesTouchController;
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
|
||||
import com.android.launcher3.util.TouchController;
|
||||
import com.android.quickstep.SysUINavigationMode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class QuickstepLauncher extends BaseQuickstepLauncher {
|
||||
|
||||
public static final boolean GO_LOW_RAM_RECENTS_ENABLED = true;
|
||||
|
||||
@Override
|
||||
public TouchController[] createTouchControllers() {
|
||||
ArrayList<TouchController> list = new ArrayList<>();
|
||||
list.add(getDragController());
|
||||
|
||||
if (getDeviceProfile().isVerticalBarLayout()) {
|
||||
list.add(new LandscapeStatesTouchController(this));
|
||||
list.add(new LandscapeEdgeSwipeController(this));
|
||||
} else {
|
||||
boolean allowDragToOverview = SysUINavigationMode.INSTANCE.get(this)
|
||||
.getMode().hasGestures;
|
||||
list.add(new PortraitStatesTouchController(this, allowDragToOverview));
|
||||
}
|
||||
return list.toArray(new TouchController[list.size()]);
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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.launcher3.uioverrides;
|
||||
|
||||
import static com.android.quickstep.views.IconRecentsView.CONTENT_ALPHA;
|
||||
|
||||
import android.util.FloatProperty;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherRecentsToActivityHelper;
|
||||
import com.android.quickstep.views.IconRecentsView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* State handler for Go's {@link IconRecentsView}.
|
||||
*/
|
||||
public final class RecentsViewStateController extends
|
||||
BaseRecentsViewStateController<IconRecentsView> {
|
||||
|
||||
public RecentsViewStateController(@NonNull Launcher launcher) {
|
||||
super(launcher);
|
||||
launcher.<IconRecentsView>getOverviewPanel().setRecentsToActivityHelper(
|
||||
new LauncherRecentsToActivityHelper(launcher));
|
||||
}
|
||||
|
||||
@Override
|
||||
FloatProperty<IconRecentsView> getContentAlphaProperty() {
|
||||
return CONTENT_ALPHA;
|
||||
}
|
||||
}
|
|
@ -1,159 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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.launcher3.uioverrides.states;
|
||||
|
||||
import static android.view.View.VISIBLE;
|
||||
|
||||
import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
|
||||
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
|
||||
import static com.android.launcher3.anim.Interpolators.ACCEL;
|
||||
import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
|
||||
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
|
||||
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7;
|
||||
import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.anim.AnimatorSetBuilder;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.quickstep.SysUINavigationMode;
|
||||
import com.android.quickstep.views.IconRecentsView;
|
||||
|
||||
/**
|
||||
* Definition for overview state
|
||||
*/
|
||||
public class OverviewState extends LauncherState {
|
||||
|
||||
// Scale recents takes before animating in
|
||||
private static final float RECENTS_PREPARE_SCALE = 1.33f;
|
||||
|
||||
private static final int STATE_FLAGS = FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED
|
||||
| FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_DISABLE_ACCESSIBILITY;
|
||||
|
||||
public OverviewState(int id) {
|
||||
this(id, OVERVIEW_TRANSITION_MS, STATE_FLAGS);
|
||||
}
|
||||
|
||||
protected OverviewState(int id, int transitionDuration, int stateFlags) {
|
||||
super(id, LauncherLogProto.ContainerType.TASKSWITCHER, transitionDuration, stateFlags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScaleAndTranslation getOverviewScaleAndTranslation(Launcher launcher) {
|
||||
return new ScaleAndTranslation(1f, 0f, 0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateEnabled(Launcher launcher) {
|
||||
IconRecentsView recentsView = launcher.getOverviewPanel();
|
||||
recentsView.onBeginTransitionToOverview();
|
||||
recentsView.setShowStatusBarForegroundScrim(true);
|
||||
// Request orientation be set to unspecified, letting the system decide the best
|
||||
// orientation.
|
||||
launcher.getRotationHelper().setCurrentStateRequest(REQUEST_ROTATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateDisabled(Launcher launcher) {
|
||||
IconRecentsView recentsView = launcher.getOverviewPanel();
|
||||
recentsView.setShowStatusBarForegroundScrim(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
|
||||
return new PageAlphaProvider(DEACCEL_2) {
|
||||
@Override
|
||||
public float getPageAlpha(int pageIndex) {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVisibleElements(Launcher launcher) {
|
||||
return NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getWorkspaceScrimAlpha(Launcher launcher) {
|
||||
return 0.5f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription(Launcher launcher) {
|
||||
return launcher.getString(R.string.accessibility_desc_recent_apps);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed(Launcher launcher) {
|
||||
// TODO: Add logic to go back to task if coming from a currently running task.
|
||||
super.onBackPressed(launcher);
|
||||
}
|
||||
|
||||
public static float getDefaultSwipeHeight(Launcher launcher) {
|
||||
return getDefaultSwipeHeight(launcher, launcher.getDeviceProfile());
|
||||
}
|
||||
|
||||
public static float getDefaultSwipeHeight(Context context, DeviceProfile dp) {
|
||||
return dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState,
|
||||
AnimatorSetBuilder builder) {
|
||||
if (fromState == NORMAL && this == OVERVIEW) {
|
||||
if (SysUINavigationMode.getMode(launcher) == SysUINavigationMode.Mode.NO_BUTTON) {
|
||||
builder.setInterpolator(ANIM_WORKSPACE_SCALE, ACCEL);
|
||||
builder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL);
|
||||
} else {
|
||||
builder.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2);
|
||||
}
|
||||
builder.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
|
||||
builder.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
|
||||
builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7);
|
||||
builder.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
|
||||
|
||||
View overview = launcher.getOverviewPanel();
|
||||
if (overview.getVisibility() != VISIBLE) {
|
||||
SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static OverviewState newBackgroundState(int id) {
|
||||
return new OverviewState(id);
|
||||
}
|
||||
|
||||
public static OverviewState newPeekState(int id) {
|
||||
return new OverviewState(id);
|
||||
}
|
||||
|
||||
public static OverviewState newSwitchState(int id) {
|
||||
return new OverviewState(id);
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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.launcher3.uioverrides.touchcontrollers;
|
||||
|
||||
import static com.android.launcher3.LauncherState.ALL_APPS;
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
|
||||
/**
|
||||
* Touch controller for landscape mode.
|
||||
*/
|
||||
public final class LandscapeStatesTouchController extends PortraitStatesTouchController {
|
||||
|
||||
public LandscapeStatesTouchController(Launcher l) {
|
||||
super(l, true /* allowDragToOverview */);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canInterceptTouch(MotionEvent ev) {
|
||||
if (mCurrentAnimation != null) {
|
||||
// If we are already animating from a previous state, we can intercept.
|
||||
return true;
|
||||
}
|
||||
if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
|
||||
return false;
|
||||
}
|
||||
if (mLauncher.isInState(ALL_APPS)) {
|
||||
// In all-apps only listen if the container cannot scroll itself
|
||||
return mLauncher.getAppsView().shouldContainerScroll(ev);
|
||||
} else if (mLauncher.isInState(NORMAL)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
|
||||
if (fromState == ALL_APPS && !isDragTowardPositive) {
|
||||
return NORMAL;
|
||||
} else if (isDragTowardPositive) {
|
||||
return ALL_APPS;
|
||||
}
|
||||
return fromState;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLogContainerTypeForNormalState(MotionEvent ev) {
|
||||
return LauncherLogProto.ContainerType.WORKSPACE;
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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.launcher3.uioverrides.touchcontrollers;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.util.PendingAnimation;
|
||||
|
||||
/**
|
||||
* Helper class for {@link PortraitStatesTouchController} that determines swipeable regions and
|
||||
* animations on the overview state that depend on the recents implementation.
|
||||
*/
|
||||
public final class PortraitOverviewStateTouchHelper {
|
||||
|
||||
public PortraitOverviewStateTouchHelper(Launcher launcher) {}
|
||||
|
||||
/**
|
||||
* Whether or not {@link PortraitStatesTouchController} should intercept the touch when on the
|
||||
* overview state.
|
||||
*
|
||||
* @param ev the motion event
|
||||
* @return true if we should intercept the motion event
|
||||
*/
|
||||
boolean canInterceptTouch(MotionEvent ev) {
|
||||
// Go does not support swiping to all-apps from recents.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not swiping down to leave overview state should return to the currently running
|
||||
* task app.
|
||||
*
|
||||
* @return true if going back should take the user to the currently running task
|
||||
*/
|
||||
boolean shouldSwipeDownReturnToApp() {
|
||||
// Go does not support swiping tasks down to launch tasks from recents.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the animation for going from overview to the task app via swiping.
|
||||
*
|
||||
* @param duration how long the animation should be
|
||||
* @return the animation
|
||||
*/
|
||||
PendingAnimation createSwipeDownToTaskAppAnimation(long duration) {
|
||||
// Go does not support swiping tasks down to launch tasks from recents.
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,203 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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;
|
||||
|
||||
import static com.android.launcher3.Utilities.postAsyncCallback;
|
||||
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
|
||||
import static com.android.quickstep.views.IconRecentsView.REMOTE_APP_TO_OVERVIEW_DURATION;
|
||||
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
|
||||
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
|
||||
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
|
||||
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.app.ActivityOptions;
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.LauncherAnimationRunner;
|
||||
import com.android.quickstep.util.RemoteAnimationProvider;
|
||||
import com.android.quickstep.views.IconRecentsView;
|
||||
import com.android.systemui.shared.system.ActivityOptionsCompat;
|
||||
import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
|
||||
/**
|
||||
* Provider for the atomic remote window animation from the app to the overview.
|
||||
*
|
||||
* @param <T> activity that contains the overview
|
||||
*/
|
||||
final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> implements
|
||||
RemoteAnimationProvider {
|
||||
private static final String TAG = "AppToOverviewAnimationProvider";
|
||||
|
||||
private final BaseActivityInterface<T> mActivityInterface;
|
||||
private final int mTargetTaskId;
|
||||
private IconRecentsView mRecentsView;
|
||||
private AppToOverviewAnimationListener mAnimationReadyListener;
|
||||
|
||||
AppToOverviewAnimationProvider(BaseActivityInterface<T> activityInterface, int targetTaskId) {
|
||||
mActivityInterface = activityInterface;
|
||||
mTargetTaskId = targetTaskId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set listener to various points in the animation preparing to animate.
|
||||
*
|
||||
* @param listener listener
|
||||
*/
|
||||
void setAnimationListener(AppToOverviewAnimationListener listener) {
|
||||
mAnimationReadyListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the activity is ready/initialized.
|
||||
*
|
||||
* @param wasVisible true if it was visible before
|
||||
*/
|
||||
boolean onActivityReady(Boolean wasVisible) {
|
||||
T activity = mActivityInterface.getCreatedActivity();
|
||||
if (mAnimationReadyListener != null) {
|
||||
mAnimationReadyListener.onActivityReady(activity);
|
||||
}
|
||||
BaseActivityInterface.AnimationFactory factory =
|
||||
mActivityInterface.prepareRecentsUI(wasVisible,
|
||||
false /* animate activity */, (controller) -> {
|
||||
controller.dispatchOnStart();
|
||||
ValueAnimator anim = controller.getAnimationPlayer()
|
||||
.setDuration(getRecentsLaunchDuration());
|
||||
anim.setInterpolator(FAST_OUT_SLOW_IN);
|
||||
anim.start();
|
||||
});
|
||||
factory.onRemoteAnimationReceived(null);
|
||||
factory.createActivityInterface(getRecentsLaunchDuration());
|
||||
mRecentsView = activity.getOverviewPanel();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create remote window animation from the currently running app to the overview panel. Should
|
||||
* be called after {@link #onActivityReady}.
|
||||
*
|
||||
* @param appTargets the target apps
|
||||
* @return animation from app to overview
|
||||
*/
|
||||
@Override
|
||||
public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
|
||||
RemoteAnimationTargetCompat[] wallpaperTargets) {
|
||||
if (mAnimationReadyListener != null) {
|
||||
mAnimationReadyListener.onWindowAnimationCreated();
|
||||
}
|
||||
AnimatorSet anim = new AnimatorSet();
|
||||
if (mRecentsView == null) {
|
||||
if (Log.isLoggable(TAG, Log.WARN)) {
|
||||
Log.w(TAG, "No recents view. Using stub animation.");
|
||||
}
|
||||
anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration()));
|
||||
return anim;
|
||||
}
|
||||
|
||||
RemoteAnimationTargets targets =
|
||||
new RemoteAnimationTargets(appTargets, wallpaperTargets, MODE_CLOSING);
|
||||
mRecentsView.setTransitionedFromApp(!targets.isAnimatingHome());
|
||||
|
||||
RemoteAnimationTargetCompat recentsTarget = null;
|
||||
RemoteAnimationTargetCompat closingAppTarget = null;
|
||||
|
||||
for (RemoteAnimationTargetCompat target : appTargets) {
|
||||
if (target.mode == MODE_OPENING) {
|
||||
recentsTarget = target;
|
||||
} else if (target.mode == MODE_CLOSING && target.taskId == mTargetTaskId) {
|
||||
closingAppTarget = target;
|
||||
}
|
||||
}
|
||||
|
||||
if (closingAppTarget == null) {
|
||||
if (Log.isLoggable(TAG, Log.WARN)) {
|
||||
Log.w(TAG, "No closing app target. Using stub animation.");
|
||||
}
|
||||
anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration()));
|
||||
return anim;
|
||||
}
|
||||
if (recentsTarget == null) {
|
||||
if (Log.isLoggable(TAG, Log.WARN)) {
|
||||
Log.w(TAG, "No recents target. Using stub animation.");
|
||||
}
|
||||
anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration()));
|
||||
return anim;
|
||||
}
|
||||
|
||||
if (closingAppTarget.activityType == ACTIVITY_TYPE_HOME) {
|
||||
mRecentsView.playRemoteHomeToRecentsAnimation(anim, closingAppTarget, recentsTarget);
|
||||
} else {
|
||||
mRecentsView.playRemoteAppToRecentsAnimation(anim, closingAppTarget, recentsTarget);
|
||||
}
|
||||
|
||||
return anim;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActivityOptions toActivityOptions(Handler handler, long duration, Context context) {
|
||||
LauncherAnimationRunner runner = new LauncherAnimationRunner(handler,
|
||||
false /* startAtFrontOfQueue */) {
|
||||
|
||||
@Override
|
||||
public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
|
||||
RemoteAnimationTargetCompat[] wallpaperTargets,
|
||||
AnimationResult result) {
|
||||
IconRecentsView recentsView = mRecentsView;
|
||||
if (!recentsView.isReadyForRemoteAnim()) {
|
||||
recentsView.setOnReadyForRemoteAnimCallback(() -> postAsyncCallback(handler,
|
||||
() -> onCreateAnimation(appTargets, wallpaperTargets, result))
|
||||
);
|
||||
return;
|
||||
}
|
||||
result.setAnimation(createWindowAnimation(appTargets, wallpaperTargets), context);
|
||||
}
|
||||
};
|
||||
return ActivityOptionsCompat.makeRemoteAnimation(
|
||||
new RemoteAnimationAdapterCompat(runner, duration,
|
||||
0 /* statusBarTransitionDelay */));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get duration of animation from app to overview.
|
||||
*
|
||||
* @return duration of animation
|
||||
*/
|
||||
long getRecentsLaunchDuration() {
|
||||
return REMOTE_APP_TO_OVERVIEW_DURATION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for various points in the app to overview animation preparing to animate.
|
||||
*/
|
||||
interface AppToOverviewAnimationListener {
|
||||
/**
|
||||
* Logic for when activity we're animating to is ready
|
||||
*
|
||||
* @param activity activity to animate to
|
||||
*/
|
||||
void onActivityReady(BaseDraggingActivity activity);
|
||||
|
||||
/**
|
||||
* Logic for when we've created the app to recents animation.
|
||||
*/
|
||||
void onWindowAnimationCreated();
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
||||
|
||||
/**
|
||||
* Holder for clear all button view in task recycler view.
|
||||
*/
|
||||
final class ClearAllHolder extends ViewHolder {
|
||||
public ClearAllHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
}
|
||||
}
|
|
@ -1,281 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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;
|
||||
|
||||
import static android.view.View.ALPHA;
|
||||
|
||||
import static com.android.quickstep.TaskAdapter.CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT;
|
||||
import static com.android.quickstep.views.TaskItemView.CONTENT_TRANSITION_PROGRESS;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator;
|
||||
|
||||
import com.android.quickstep.views.TaskItemView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An item animator that is only set and used for the transition from the empty loading UI to
|
||||
* the filled task content UI. The animation starts from the bottom to top, changing all valid
|
||||
* empty item views to be filled and removing all extra empty views.
|
||||
*/
|
||||
public final class ContentFillItemAnimator extends SimpleItemAnimator {
|
||||
|
||||
private static final class PendingAnimation {
|
||||
ViewHolder viewHolder;
|
||||
int animType;
|
||||
|
||||
PendingAnimation(ViewHolder vh, int type) {
|
||||
viewHolder = vh;
|
||||
animType = type;
|
||||
}
|
||||
}
|
||||
|
||||
private static final int ANIM_TYPE_REMOVE = 0;
|
||||
private static final int ANIM_TYPE_CHANGE = 1;
|
||||
|
||||
private static final int ITEM_BETWEEN_DELAY = 40;
|
||||
private static final int ITEM_CHANGE_DURATION = 150;
|
||||
private static final int ITEM_REMOVE_DURATION = 150;
|
||||
|
||||
/**
|
||||
* Animations that have been registered to occur together at the next call of
|
||||
* {@link #runPendingAnimations()} but have not started.
|
||||
*/
|
||||
private final ArrayList<PendingAnimation> mPendingAnims = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Animations that have started and are running.
|
||||
*/
|
||||
private final ArrayList<ObjectAnimator> mRunningAnims = new ArrayList<>();
|
||||
|
||||
private Runnable mOnFinishRunnable;
|
||||
|
||||
/**
|
||||
* Set runnable to run after the content fill animation is fully completed.
|
||||
*
|
||||
* @param runnable runnable to run on end
|
||||
*/
|
||||
public void setOnAnimationFinishedRunnable(Runnable runnable) {
|
||||
mOnFinishRunnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChangeDuration(long changeDuration) {
|
||||
throw new UnsupportedOperationException("Cascading item animator cannot have animation "
|
||||
+ "duration changed.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRemoveDuration(long removeDuration) {
|
||||
throw new UnsupportedOperationException("Cascading item animator cannot have animation "
|
||||
+ "duration changed.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean animateRemove(ViewHolder holder) {
|
||||
PendingAnimation pendAnim = new PendingAnimation(holder, ANIM_TYPE_REMOVE);
|
||||
mPendingAnims.add(pendAnim);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void animateRemoveImpl(ViewHolder holder, long startDelay) {
|
||||
final View view = holder.itemView;
|
||||
if (holder.itemView.getAlpha() == 0) {
|
||||
// View is already visually removed. We can just get rid of it now.
|
||||
view.setAlpha(1.0f);
|
||||
dispatchRemoveFinished(holder);
|
||||
dispatchFinishedWhenDone();
|
||||
return;
|
||||
}
|
||||
final ObjectAnimator anim = ObjectAnimator.ofFloat(
|
||||
holder.itemView, ALPHA, holder.itemView.getAlpha(), 0.0f);
|
||||
anim.setDuration(ITEM_REMOVE_DURATION).setStartDelay(startDelay);
|
||||
anim.addListener(
|
||||
new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
dispatchRemoveStarting(holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
view.setAlpha(1);
|
||||
dispatchRemoveFinished(holder);
|
||||
mRunningAnims.remove(anim);
|
||||
dispatchFinishedWhenDone();
|
||||
}
|
||||
}
|
||||
);
|
||||
anim.start();
|
||||
mRunningAnims.add(anim);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean animateAdd(ViewHolder holder) {
|
||||
dispatchAddFinished(holder);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean animateMove(ViewHolder holder, int fromX, int fromY, int toX,
|
||||
int toY) {
|
||||
dispatchMoveFinished(holder);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean animateChange(ViewHolder oldHolder,
|
||||
ViewHolder newHolder, int fromLeft, int fromTop, int toLeft, int toTop) {
|
||||
// Only support changes where the holders are the same
|
||||
if (oldHolder == newHolder) {
|
||||
PendingAnimation pendAnim = new PendingAnimation(oldHolder, ANIM_TYPE_CHANGE);
|
||||
mPendingAnims.add(pendAnim);
|
||||
return true;
|
||||
}
|
||||
dispatchChangeFinished(oldHolder, true /* oldItem */);
|
||||
dispatchChangeFinished(newHolder, false /* oldItem */);
|
||||
return false;
|
||||
}
|
||||
|
||||
private void animateChangeImpl(ViewHolder viewHolder, long startDelay) {
|
||||
TaskItemView itemView = (TaskItemView) viewHolder.itemView;
|
||||
if (itemView.getAlpha() == 0) {
|
||||
// View is still not visible, so we can finish the change immediately.
|
||||
CONTENT_TRANSITION_PROGRESS.set(itemView, 1.0f);
|
||||
dispatchChangeFinished(viewHolder, true /* oldItem */);
|
||||
dispatchFinishedWhenDone();
|
||||
return;
|
||||
}
|
||||
final ObjectAnimator anim =
|
||||
ObjectAnimator.ofFloat(itemView, CONTENT_TRANSITION_PROGRESS, 0.0f, 1.0f);
|
||||
anim.setDuration(ITEM_CHANGE_DURATION).setStartDelay(startDelay);
|
||||
anim.addListener(
|
||||
new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
dispatchChangeStarting(viewHolder, true /* oldItem */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
CONTENT_TRANSITION_PROGRESS.set(itemView, 1.0f);
|
||||
dispatchChangeFinished(viewHolder, true /* oldItem */);
|
||||
mRunningAnims.remove(anim);
|
||||
dispatchFinishedWhenDone();
|
||||
}
|
||||
}
|
||||
);
|
||||
anim.start();
|
||||
mRunningAnims.add(anim);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runPendingAnimations() {
|
||||
// Run animations bottom to top.
|
||||
mPendingAnims.sort(Comparator.comparingInt(o -> -o.viewHolder.itemView.getBottom()));
|
||||
int delay = 0;
|
||||
while (!mPendingAnims.isEmpty()) {
|
||||
PendingAnimation curAnim = mPendingAnims.remove(0);
|
||||
ViewHolder vh = curAnim.viewHolder;
|
||||
switch (curAnim.animType) {
|
||||
case ANIM_TYPE_REMOVE:
|
||||
animateRemoveImpl(vh, delay);
|
||||
break;
|
||||
case ANIM_TYPE_CHANGE:
|
||||
animateChangeImpl(vh, delay);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
delay += ITEM_BETWEEN_DELAY;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endAnimation(@NonNull ViewHolder item) {
|
||||
for (int i = mPendingAnims.size() - 1; i >= 0; i--) {
|
||||
endPendingAnimation(mPendingAnims.get(i));
|
||||
mPendingAnims.remove(i);
|
||||
}
|
||||
dispatchFinishedWhenDone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endAnimations() {
|
||||
if (!isRunning()) {
|
||||
return;
|
||||
}
|
||||
for (int i = mPendingAnims.size() - 1; i >= 0; i--) {
|
||||
endPendingAnimation(mPendingAnims.get(i));
|
||||
mPendingAnims.remove(i);
|
||||
}
|
||||
for (int i = mRunningAnims.size() - 1; i >= 0; i--) {
|
||||
ObjectAnimator anim = mRunningAnims.get(i);
|
||||
// This calls the on end animation callback which will set values to their end target.
|
||||
anim.cancel();
|
||||
}
|
||||
dispatchFinishedWhenDone();
|
||||
}
|
||||
|
||||
private void endPendingAnimation(PendingAnimation pendAnim) {
|
||||
ViewHolder item = pendAnim.viewHolder;
|
||||
switch (pendAnim.animType) {
|
||||
case ANIM_TYPE_REMOVE:
|
||||
item.itemView.setAlpha(1.0f);
|
||||
dispatchRemoveFinished(item);
|
||||
break;
|
||||
case ANIM_TYPE_CHANGE:
|
||||
CONTENT_TRANSITION_PROGRESS.set(item.itemView, 1.0f);
|
||||
dispatchChangeFinished(item, true /* oldItem */);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return !mPendingAnims.isEmpty() || !mRunningAnims.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
|
||||
@NonNull List<Object> payloads) {
|
||||
if (!payloads.isEmpty()
|
||||
&& (int) payloads.get(0) == CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT) {
|
||||
return true;
|
||||
}
|
||||
return super.canReuseUpdatedViewHolder(viewHolder, payloads);
|
||||
}
|
||||
|
||||
private void dispatchFinishedWhenDone() {
|
||||
if (!isRunning()) {
|
||||
dispatchAnimationsFinished();
|
||||
if (mOnFinishRunnable != null) {
|
||||
mOnFinishRunnable.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,121 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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;
|
||||
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
import static com.android.quickstep.views.IconRecentsView.CONTENT_ALPHA;
|
||||
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.graphics.Rect;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.quickstep.util.ActivityInitListener;
|
||||
import com.android.quickstep.views.IconRecentsView;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* {@link BaseActivityInterface} for recents when the default launcher is different than the
|
||||
* currently running one and apps should interact with the {@link RecentsActivity} as opposed
|
||||
* to the in-launcher one.
|
||||
*/
|
||||
public final class FallbackActivityInterface extends
|
||||
GoActivityInterface<RecentsActivity> {
|
||||
|
||||
public FallbackActivityInterface() { }
|
||||
|
||||
@Override
|
||||
public AnimationFactory prepareRecentsUI(boolean activityVisible,
|
||||
boolean animateActivity, Consumer<AnimatorPlaybackController> callback) {
|
||||
if (activityVisible) {
|
||||
return (transitionLength) -> { };
|
||||
}
|
||||
|
||||
RecentsActivity activity = getCreatedActivity();
|
||||
IconRecentsView rv = activity.getOverviewPanel();
|
||||
rv.setUsingRemoteAnimation(true);
|
||||
rv.setAlpha(0);
|
||||
|
||||
return new AnimationFactory() {
|
||||
|
||||
boolean isAnimatingToRecents = false;
|
||||
|
||||
@Override
|
||||
public void onRemoteAnimationReceived(RemoteAnimationTargets targets) {
|
||||
isAnimatingToRecents = targets != null && targets.isAnimatingHome();
|
||||
if (!isAnimatingToRecents) {
|
||||
rv.setAlpha(1);
|
||||
}
|
||||
createActivityInterface(getSwipeUpDestinationAndLength(
|
||||
activity.getDeviceProfile(), activity, new Rect()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createActivityInterface(long transitionLength) {
|
||||
if (!isAnimatingToRecents) {
|
||||
return;
|
||||
}
|
||||
|
||||
ObjectAnimator anim = ObjectAnimator.ofFloat(rv, CONTENT_ALPHA, 0, 1);
|
||||
anim.setDuration(transitionLength).setInterpolator(LINEAR);
|
||||
AnimatorSet animatorSet = new AnimatorSet();
|
||||
animatorSet.play(anim);
|
||||
callback.accept(AnimatorPlaybackController.wrap(animatorSet, transitionLength));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActivityInitListener createActivityInitListener(
|
||||
Predicate<Boolean> onInitListener) {
|
||||
return new ActivityInitListener<>((activity, alreadyOnHome) ->
|
||||
onInitListener.test(alreadyOnHome), RecentsActivity.ACTIVITY_TRACKER);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public RecentsActivity getCreatedActivity() {
|
||||
return RecentsActivity.ACTIVITY_TRACKER.getCreatedActivity();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IconRecentsView getVisibleRecentsView() {
|
||||
RecentsActivity activity = getCreatedActivity();
|
||||
if (activity != null && activity.hasWindowFocus()) {
|
||||
return activity.getOverviewPanel();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean switchToRecentsIfVisible(Runnable onCompleteCallback) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerType() {
|
||||
return LauncherLogProto.ContainerType.SIDELOADED_LAUNCHER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLaunchTaskSuccess() { }
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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;
|
||||
|
||||
/**
|
||||
* {@link RecentsToActivityHelper} for when we are using the fallback recents in
|
||||
* {@link BaseRecentsActivity}.
|
||||
*/
|
||||
public final class FallbackRecentsToActivityHelper implements RecentsToActivityHelper {
|
||||
|
||||
BaseRecentsActivity mActivity;
|
||||
|
||||
public FallbackRecentsToActivityHelper(BaseRecentsActivity activity) {
|
||||
mActivity = activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void leaveRecents() {
|
||||
mActivity.startHome();
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
package com.android.quickstep;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
|
||||
/**
|
||||
* Base activity control helper for Go that stubs out most of the functionality that is not needed
|
||||
* for Go.
|
||||
*
|
||||
* @param <T> activity that contains the overview
|
||||
*/
|
||||
public abstract class GoActivityInterface<T extends BaseDraggingActivity> implements
|
||||
BaseActivityInterface<T> {
|
||||
|
||||
@Override
|
||||
public void onTransitionCancelled(boolean activityVisible) {
|
||||
// Go transitions to overview are all atomic.
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) {
|
||||
// TODO Implement outRect depending on where the task should animate to.
|
||||
// Go does not support swipe up gesture.
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwipeUpToRecentsComplete() {
|
||||
// Go does not support swipe up gesture.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAssistantVisibilityChanged(float visibility) {
|
||||
// Go does not support assistant visibility transitions.
|
||||
}
|
||||
|
||||
@Override
|
||||
public HomeAnimationFactory prepareHomeUI() {
|
||||
// Go does not support gestures from app to home.
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
|
||||
// Go does not support gestures to overview.
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldMinimizeSplitScreen() {
|
||||
// Go does not support split screen.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInLiveTileMode() {
|
||||
// Go does not support live tiles.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLaunchTaskFailed() {
|
||||
// Go does not support gestures from one task to another.
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
|
||||
public class IconRecentsFragment extends Fragment {
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.icon_recents_root_view, container, false);
|
||||
}
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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;
|
||||
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.LauncherInitListener;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.quickstep.views.IconRecentsView;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* {@link BaseActivityInterface} for the in-launcher recents.
|
||||
* TODO: Implement the app to overview animation functionality
|
||||
*/
|
||||
public final class LauncherActivityInterface extends GoActivityInterface<Launcher> {
|
||||
|
||||
@Override
|
||||
public AnimationFactory prepareRecentsUI(boolean activityVisible, boolean animateActivity,
|
||||
Consumer<AnimatorPlaybackController> callback) {
|
||||
Launcher launcher = getCreatedActivity();
|
||||
LauncherState fromState = launcher.getStateManager().getState();
|
||||
launcher.<IconRecentsView>getOverviewPanel().setUsingRemoteAnimation(true);
|
||||
//TODO: Implement this based off where the recents view needs to be for app => recents anim.
|
||||
return new AnimationFactory() {
|
||||
public void createActivityInterface(long transitionLength) {
|
||||
callback.accept(launcher.getStateManager().createAnimationToNewWorkspace(
|
||||
fromState, OVERVIEW, transitionLength));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransitionCancelled() {}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public LauncherInitListener createActivityInitListener(Predicate<Boolean> onInitListener) {
|
||||
return new LauncherInitListener((activity, alreadyOnHome) ->
|
||||
onInitListener.test(alreadyOnHome));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Launcher getCreatedActivity() {
|
||||
LauncherAppState app = LauncherAppState.getInstanceNoCreate();
|
||||
if (app == null) {
|
||||
return null;
|
||||
}
|
||||
return (Launcher) app.getModel().getCallback();
|
||||
}
|
||||
|
||||
private Launcher getVisibleLauncher() {
|
||||
Launcher launcher = getCreatedActivity();
|
||||
return (launcher != null) && launcher.isStarted() && launcher.hasWindowFocus() ?
|
||||
launcher : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IconRecentsView getVisibleRecentsView() {
|
||||
Launcher launcher = getVisibleLauncher();
|
||||
return launcher != null && launcher.getStateManager().getState().overviewUi
|
||||
? launcher.getOverviewPanel() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean switchToRecentsIfVisible(Runnable onCompleteCallback) {
|
||||
Launcher launcher = getVisibleLauncher();
|
||||
if (launcher == null) {
|
||||
return false;
|
||||
}
|
||||
launcher.<IconRecentsView>getOverviewPanel().setUsingRemoteAnimation(false);
|
||||
launcher.getUserEventDispatcher().logActionCommand(
|
||||
LauncherLogProto.Action.Command.RECENTS_BUTTON,
|
||||
getContainerType(),
|
||||
LauncherLogProto.ContainerType.TASKSWITCHER);
|
||||
launcher.getStateManager().goToState(OVERVIEW,
|
||||
launcher.getStateManager().shouldAnimateStateChange(), onCompleteCallback);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerType() {
|
||||
final Launcher launcher = getVisibleLauncher();
|
||||
return launcher != null ? launcher.getStateManager().getState().containerType
|
||||
: LauncherLogProto.ContainerType.APP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLaunchTaskSuccess() {
|
||||
Launcher launcher = getCreatedActivity();
|
||||
launcher.getStateManager().moveToRestState();
|
||||
}
|
||||
}
|
|
@ -1,180 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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;
|
||||
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.SystemClock;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.logging.UserEventDispatcher;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.quickstep.AppToOverviewAnimationProvider.AppToOverviewAnimationListener;
|
||||
import com.android.quickstep.util.ActivityInitListener;
|
||||
import com.android.quickstep.views.IconRecentsView;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.LatencyTrackerCompat;
|
||||
|
||||
/**
|
||||
* Helper class to handle various atomic commands for switching between Overview.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.P)
|
||||
public class OverviewCommandHelper {
|
||||
|
||||
private final Context mContext;
|
||||
private final RecentsAnimationDeviceState mDeviceState;
|
||||
private final RecentsModel mRecentsModel;
|
||||
private final OverviewComponentObserver mOverviewComponentObserver;
|
||||
|
||||
private long mLastToggleTime;
|
||||
|
||||
public OverviewCommandHelper(Context context, RecentsAnimationDeviceState deviceState,
|
||||
OverviewComponentObserver observer) {
|
||||
mContext = context;
|
||||
mDeviceState = deviceState;
|
||||
mRecentsModel = RecentsModel.INSTANCE.get(mContext);
|
||||
mOverviewComponentObserver = observer;
|
||||
}
|
||||
|
||||
public void onOverviewToggle() {
|
||||
// If currently screen pinning, do not enter overview
|
||||
if (mDeviceState.isScreenPinningActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ActivityManagerWrapper.getInstance()
|
||||
.closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
|
||||
MAIN_EXECUTOR.execute(new RecentsActivityCommand<>());
|
||||
}
|
||||
|
||||
public void onOverviewShown(boolean triggeredFromAltTab) {
|
||||
MAIN_EXECUTOR.execute(new ShowRecentsCommand());
|
||||
}
|
||||
|
||||
public void onOverviewHidden() {
|
||||
MAIN_EXECUTOR.execute(new HideRecentsCommand());
|
||||
}
|
||||
|
||||
public void onTip(int actionType, int viewType) {
|
||||
MAIN_EXECUTOR.execute(() ->
|
||||
UserEventDispatcher.newInstance(mContext).logActionTip(actionType, viewType));
|
||||
}
|
||||
|
||||
private class ShowRecentsCommand extends RecentsActivityCommand {
|
||||
|
||||
@Override
|
||||
protected boolean handleCommand(long elapsedTime) {
|
||||
return mHelper.getVisibleRecentsView() != null;
|
||||
}
|
||||
}
|
||||
|
||||
private class HideRecentsCommand extends RecentsActivityCommand {
|
||||
|
||||
@Override
|
||||
protected boolean handleCommand(long elapsedTime) {
|
||||
IconRecentsView recents = (IconRecentsView) mHelper.getVisibleRecentsView();
|
||||
if (recents == null) {
|
||||
return false;
|
||||
}
|
||||
recents.handleOverviewCommand();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private class RecentsActivityCommand<T extends BaseDraggingActivity> implements Runnable {
|
||||
|
||||
protected final BaseActivityInterface<T> mHelper;
|
||||
private final long mCreateTime;
|
||||
|
||||
private final long mToggleClickedTime = SystemClock.uptimeMillis();
|
||||
private boolean mUserEventLogged;
|
||||
private ActivityInitListener<T> mListener;
|
||||
|
||||
public RecentsActivityCommand() {
|
||||
mHelper = mOverviewComponentObserver.getActivityInterface();
|
||||
mCreateTime = SystemClock.elapsedRealtime();
|
||||
|
||||
// Preload the plan
|
||||
mRecentsModel.getTasks(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
long elapsedTime = mCreateTime - mLastToggleTime;
|
||||
mLastToggleTime = mCreateTime;
|
||||
|
||||
if (handleCommand(elapsedTime)) {
|
||||
// Command already handled.
|
||||
return;
|
||||
}
|
||||
|
||||
if (mHelper.switchToRecentsIfVisible(null /* onCompleteCallback */)) {
|
||||
// If successfully switched, then return
|
||||
return;
|
||||
}
|
||||
|
||||
AppToOverviewAnimationProvider<T> provider =
|
||||
new AppToOverviewAnimationProvider<>(mHelper, RecentsModel.getRunningTaskId());
|
||||
provider.setAnimationListener(
|
||||
new AppToOverviewAnimationListener() {
|
||||
@Override
|
||||
public void onActivityReady(BaseDraggingActivity activity) {
|
||||
if (!mUserEventLogged) {
|
||||
activity.getUserEventDispatcher().logActionCommand(
|
||||
LauncherLogProto.Action.Command.RECENTS_BUTTON,
|
||||
mHelper.getContainerType(),
|
||||
LauncherLogProto.ContainerType.TASKSWITCHER);
|
||||
mUserEventLogged = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowAnimationCreated() {
|
||||
if (LatencyTrackerCompat.isEnabled(mContext)) {
|
||||
LatencyTrackerCompat.logToggleRecents(
|
||||
(int) (SystemClock.uptimeMillis() - mToggleClickedTime));
|
||||
}
|
||||
|
||||
mListener.unregister();
|
||||
}
|
||||
});
|
||||
|
||||
// Otherwise, start overview.
|
||||
mListener = mHelper.createActivityInitListener(provider::onActivityReady);
|
||||
mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(),
|
||||
provider, mContext, MAIN_EXECUTOR.getHandler(),
|
||||
provider.getRecentsLaunchDuration());
|
||||
}
|
||||
|
||||
protected boolean handleCommand(long elapsedTime) {
|
||||
IconRecentsView recents = mHelper.getVisibleRecentsView();
|
||||
if (recents != null) {
|
||||
recents.handleOverviewCommand();
|
||||
return true;
|
||||
} else if (elapsedTime < ViewConfiguration.getDoubleTapTimeout()) {
|
||||
// The user tried to launch back into overview too quickly, either after
|
||||
// launching an app, or before overview has actually shown, just ignore for now
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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;
|
||||
|
||||
import android.app.ActivityOptions;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.views.BaseDragLayer;
|
||||
import com.android.quickstep.fallback.GoRecentsActivityRootView;
|
||||
import com.android.quickstep.views.IconRecentsView;
|
||||
|
||||
/**
|
||||
* A recents activity that displays recent tasks with an icon and small snapshot.
|
||||
*/
|
||||
public final class RecentsActivity extends BaseRecentsActivity {
|
||||
|
||||
private GoRecentsActivityRootView mRecentsRootView;
|
||||
private IconRecentsView mIconRecentsView;
|
||||
|
||||
@Override
|
||||
protected void initViews() {
|
||||
setContentView(R.layout.fallback_recents_activity);
|
||||
mRecentsRootView = findViewById(R.id.drag_layer);
|
||||
mIconRecentsView = findViewById(R.id.overview_panel);
|
||||
mIconRecentsView.setRecentsToActivityHelper(new FallbackRecentsToActivityHelper(this));
|
||||
mIconRecentsView.setShowStatusBarForegroundScrim(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reapplyUi() {
|
||||
// No-op. Insets are automatically re-applied in the root view.
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseDragLayer getDragLayer() {
|
||||
return mRecentsRootView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getRootView() {
|
||||
return mRecentsRootView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends View> T getOverviewPanel() {
|
||||
return (T) mIconRecentsView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActivityOptions getActivityLaunchOptions(View v) {
|
||||
// Stubbed. Recents launch animation will come from the recents view itself.
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
mIconRecentsView.onBeginTransitionToOverview();
|
||||
super.onResume();
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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;
|
||||
|
||||
/**
|
||||
* Generic interface providing methods to the recents implementation that allow it to callback to
|
||||
* the containing activity.
|
||||
*/
|
||||
public interface RecentsToActivityHelper {
|
||||
|
||||
/**
|
||||
* The default action to take when leaving/closing recents. In general, this should be used to
|
||||
* go to the appropriate home state.
|
||||
*/
|
||||
void leaveRecents();
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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;
|
||||
|
||||
import static com.android.quickstep.TaskAdapter.TASKS_START_POSITION;
|
||||
import static com.android.quickstep.TaskUtils.getLaunchComponentKeyForTask;
|
||||
|
||||
import android.app.ActivityOptions;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.launcher3.logging.StatsLogManager;
|
||||
import com.android.quickstep.views.TaskItemView;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
import com.android.systemui.shared.recents.model.Task.TaskKey;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
|
||||
/**
|
||||
* Controller that provides logic for task-related commands on recents and updating the model/view
|
||||
* as appropriate.
|
||||
*/
|
||||
public final class TaskActionController {
|
||||
|
||||
private final TaskListLoader mLoader;
|
||||
private final TaskAdapter mAdapter;
|
||||
private final StatsLogManager mStatsLogManager;
|
||||
|
||||
public TaskActionController(TaskListLoader loader, TaskAdapter adapter,
|
||||
StatsLogManager logManager) {
|
||||
mLoader = loader;
|
||||
mAdapter = adapter;
|
||||
mStatsLogManager = logManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch the task associated with the task holder, animating into the app from the task view.
|
||||
*
|
||||
* @param viewHolder the task view holder to launch
|
||||
*/
|
||||
public void launchTaskFromView(@NonNull TaskHolder viewHolder) {
|
||||
if (!viewHolder.getTask().isPresent()) {
|
||||
return;
|
||||
}
|
||||
TaskItemView itemView = (TaskItemView) (viewHolder.itemView);
|
||||
View v = itemView.getThumbnailView();
|
||||
int left = 0;
|
||||
int top = 0;
|
||||
int width = v.getMeasuredWidth();
|
||||
int height = v.getMeasuredHeight();
|
||||
|
||||
TaskKey key = viewHolder.getTask().get().key;
|
||||
ActivityOptions opts = ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
|
||||
ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(key, opts,
|
||||
null /* resultCallback */, null /* resultCallbackHandler */);
|
||||
mStatsLogManager.logTaskLaunch(null /* view */, getLaunchComponentKeyForTask(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch the task directly with a basic animation.
|
||||
*
|
||||
* @param task the task to launch
|
||||
*/
|
||||
public void launchTask(@NonNull Task task) {
|
||||
ActivityOptions opts = ActivityOptions.makeBasic();
|
||||
ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(task.key, opts,
|
||||
null /* resultCallback */, null /* resultCallbackHandler */);
|
||||
mStatsLogManager.logTaskLaunch(null /* view */, getLaunchComponentKeyForTask(task.key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the task holder and the task, updating the model and the view.
|
||||
*
|
||||
* @param viewHolder the task view holder to remove
|
||||
*/
|
||||
public void removeTask(TaskHolder viewHolder) {
|
||||
if (!viewHolder.getTask().isPresent()) {
|
||||
return;
|
||||
}
|
||||
int position = viewHolder.getAdapterPosition();
|
||||
Task task = viewHolder.getTask().get();
|
||||
ActivityManagerWrapper.getInstance().removeTask(task.key.id);
|
||||
mLoader.removeTask(task);
|
||||
mAdapter.notifyItemRemoved(position);
|
||||
// TODO(b/131840601): Add logging point for removal.
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all tasks and updates the model and view.
|
||||
*/
|
||||
public void clearAllTasks() {
|
||||
int count = mAdapter.getItemCount();
|
||||
ActivityManagerWrapper.getInstance().removeAllRecentTasks();
|
||||
mLoader.clearAllTasks();
|
||||
mAdapter.notifyItemRangeRemoved(TASKS_START_POSITION /* positionStart */, count);
|
||||
}
|
||||
}
|
|
@ -1,179 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView.Adapter;
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.quickstep.views.TaskItemView;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Recycler view adapter that dynamically inflates and binds {@link TaskHolder} instances with the
|
||||
* appropriate {@link Task} from the recents task list.
|
||||
*/
|
||||
public final class TaskAdapter extends Adapter<ViewHolder> {
|
||||
|
||||
public static final int CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT = 0;
|
||||
public static final int MAX_TASKS_TO_DISPLAY = 6;
|
||||
public static final int TASKS_START_POSITION = 1;
|
||||
|
||||
public static final int ITEM_TYPE_TASK = 0;
|
||||
public static final int ITEM_TYPE_CLEAR_ALL = 1;
|
||||
|
||||
private static final String TAG = "TaskAdapter";
|
||||
private final TaskListLoader mLoader;
|
||||
private TaskActionController mTaskActionController;
|
||||
private OnClickListener mClearAllListener;
|
||||
private boolean mIsShowingLoadingUi;
|
||||
|
||||
public TaskAdapter(@NonNull TaskListLoader loader) {
|
||||
mLoader = loader;
|
||||
}
|
||||
|
||||
public void setActionController(TaskActionController taskActionController) {
|
||||
mTaskActionController = taskActionController;
|
||||
}
|
||||
|
||||
public void setOnClearAllClickListener(OnClickListener listener) {
|
||||
mClearAllListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all positions in the task adapter to loading views, binding new views if necessary.
|
||||
* This changes the task adapter's view of the data, so the appropriate notify events should be
|
||||
* called in addition to this method to reflect the changes.
|
||||
*
|
||||
* @param isShowingLoadingUi true to bind loading task views to all positions, false to return
|
||||
* to the real data
|
||||
*/
|
||||
public void setIsShowingLoadingUi(boolean isShowingLoadingUi) {
|
||||
mIsShowingLoadingUi = isShowingLoadingUi;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
switch (viewType) {
|
||||
case ITEM_TYPE_TASK:
|
||||
TaskItemView itemView = (TaskItemView) LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.task_item_view, parent, false);
|
||||
TaskHolder taskHolder = new TaskHolder(itemView);
|
||||
itemView.setOnClickListener(
|
||||
view -> mTaskActionController.launchTaskFromView(taskHolder));
|
||||
return taskHolder;
|
||||
case ITEM_TYPE_CLEAR_ALL:
|
||||
View clearView = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.clear_all_button, parent, false);
|
||||
ClearAllHolder clearAllHolder = new ClearAllHolder(clearView);
|
||||
Button clearViewButton = clearView.findViewById(R.id.clear_all_button);
|
||||
clearViewButton.setOnClickListener(mClearAllListener);
|
||||
return clearAllHolder;
|
||||
default:
|
||||
throw new IllegalArgumentException("No known holder for item type: " + viewType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||
onBindViewHolderInternal(holder, position, false /* willAnimate */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position,
|
||||
@NonNull List<Object> payloads) {
|
||||
if (payloads.isEmpty()) {
|
||||
super.onBindViewHolder(holder, position, payloads);
|
||||
return;
|
||||
}
|
||||
int changeType = (int) payloads.get(0);
|
||||
if (changeType == CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT) {
|
||||
// Bind in preparation for animation
|
||||
onBindViewHolderInternal(holder, position, true /* willAnimate */);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Payload content is not a valid change event type: "
|
||||
+ changeType);
|
||||
}
|
||||
}
|
||||
|
||||
private void onBindViewHolderInternal(@NonNull ViewHolder holder, int position,
|
||||
boolean willAnimate) {
|
||||
int itemType = getItemViewType(position);
|
||||
switch (itemType) {
|
||||
case ITEM_TYPE_TASK:
|
||||
TaskHolder taskHolder = (TaskHolder) holder;
|
||||
if (mIsShowingLoadingUi) {
|
||||
taskHolder.bindEmptyUi();
|
||||
return;
|
||||
}
|
||||
List<Task> tasks = mLoader.getCurrentTaskList();
|
||||
int taskPos = position - TASKS_START_POSITION;
|
||||
if (taskPos >= tasks.size()) {
|
||||
// Task list has updated.
|
||||
return;
|
||||
}
|
||||
Task task = tasks.get(taskPos);
|
||||
taskHolder.bindTask(task, willAnimate /* willAnimate */);
|
||||
mLoader.loadTaskIconAndLabel(task, () -> {
|
||||
// Ensure holder still has the same task.
|
||||
if (Objects.equals(Optional.of(task), taskHolder.getTask())) {
|
||||
taskHolder.getTaskItemView().setIcon(task.icon);
|
||||
taskHolder.getTaskItemView().setLabel(task.titleDescription);
|
||||
}
|
||||
});
|
||||
mLoader.loadTaskThumbnail(task, () -> {
|
||||
if (Objects.equals(Optional.of(task), taskHolder.getTask())) {
|
||||
taskHolder.getTaskItemView().setThumbnail(task.thumbnail);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case ITEM_TYPE_CLEAR_ALL:
|
||||
// Nothing to bind.
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("No known holder for item type: " + itemType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
// Bottom is always clear all button.
|
||||
return (position == 0) ? ITEM_TYPE_CLEAR_ALL : ITEM_TYPE_TASK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
int itemCount = TASKS_START_POSITION;
|
||||
if (mIsShowingLoadingUi) {
|
||||
// Show loading version of all items.
|
||||
itemCount += MAX_TASKS_TO_DISPLAY;
|
||||
} else {
|
||||
itemCount += Math.min(mLoader.getCurrentTaskList().size(), MAX_TASKS_TO_DISPLAY);
|
||||
}
|
||||
return itemCount;
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
||||
|
||||
import com.android.quickstep.views.TaskItemView;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A recycler view holder that holds the task view and binds {@link Task} content (app title, icon,
|
||||
* etc.) to the view.
|
||||
*/
|
||||
public final class TaskHolder extends ViewHolder {
|
||||
|
||||
private final TaskItemView mTaskItemView;
|
||||
private Task mTask;
|
||||
|
||||
public TaskHolder(TaskItemView itemView) {
|
||||
super(itemView);
|
||||
mTaskItemView = itemView;
|
||||
}
|
||||
|
||||
public TaskItemView getTaskItemView() {
|
||||
return mTaskItemView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind the task model to the holder. This will take the current task content in the task
|
||||
* object (i.e. icon, thumbnail, label) and either apply the content immediately or simply bind
|
||||
* the content to animate to at a later time. If the task does not have all its content loaded,
|
||||
* the view will prepare appropriate default placeholders and it is the callers responsibility
|
||||
* to change them at a later time.
|
||||
*
|
||||
* Regardless of whether it is animating, input handlers will be bound immediately (see
|
||||
* {@link TaskActionController}).
|
||||
*
|
||||
* @param task the task to bind to the view
|
||||
* @param willAnimate true if UI should animate in later, false if it should apply immediately
|
||||
*/
|
||||
public void bindTask(@NonNull Task task, boolean willAnimate) {
|
||||
mTask = task;
|
||||
if (willAnimate) {
|
||||
mTaskItemView.startContentAnimation(task.icon, task.thumbnail, task.titleDescription);
|
||||
} else {
|
||||
mTaskItemView.setIcon(task.icon);
|
||||
mTaskItemView.setThumbnail(task.thumbnail);
|
||||
mTaskItemView.setLabel(task.titleDescription);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a generic empty UI to the holder to make it clear that the item is loading/unbound and
|
||||
* should not be expected to react to user input.
|
||||
*/
|
||||
public void bindEmptyUi() {
|
||||
mTask = null;
|
||||
mTaskItemView.resetToEmptyUi();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the task currently bound to this view. May be null if task holder is in a loading state.
|
||||
*
|
||||
* @return the current task
|
||||
*/
|
||||
public Optional<Task> getTask() {
|
||||
return Optional.ofNullable(mTask);
|
||||
}
|
||||
}
|
|
@ -1,140 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* This class is responsible for maintaining the list of tasks and the task content. The list must
|
||||
* be updated explicitly with {@link #loadTaskList} whenever the list needs to be
|
||||
* up-to-date.
|
||||
*/
|
||||
public final class TaskListLoader {
|
||||
|
||||
private final RecentsModel mRecentsModel;
|
||||
|
||||
private ArrayList<Task> mTaskList = new ArrayList<>();
|
||||
private int mTaskListChangeId;
|
||||
|
||||
public TaskListLoader(Context context) {
|
||||
mRecentsModel = RecentsModel.INSTANCE.get(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current task list as of the last completed load (see {@link #loadTaskList}) as a
|
||||
* read-only list. This list of tasks is not guaranteed to have all content loaded.
|
||||
*
|
||||
* @return the current list of tasks
|
||||
*/
|
||||
public List<Task> getCurrentTaskList() {
|
||||
return Collections.unmodifiableList(mTaskList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the loader needs to load data to be up to date. This can return true if the
|
||||
* task list is already up to date OR there is already a load in progress for the task list to
|
||||
* become up to date.
|
||||
*
|
||||
* @return true if already up to date or load in progress, false otherwise
|
||||
*/
|
||||
public boolean needsToLoad() {
|
||||
return !mRecentsModel.isTaskListValid(mTaskListChangeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the most recent tasks and updates the task list asynchronously. This call does not
|
||||
* provide guarantees the task content (icon, thumbnail, label) are loaded but will fill in
|
||||
* what it has. May run the callback immediately if there have been no changes in the task
|
||||
* list since the start of the last load.
|
||||
*
|
||||
* @param onLoadedCallback callback to run when task list is loaded
|
||||
*/
|
||||
public void loadTaskList(@Nullable Consumer<ArrayList<Task>> onLoadedCallback) {
|
||||
if (!needsToLoad()) {
|
||||
if (onLoadedCallback != null) {
|
||||
onLoadedCallback.accept(mTaskList);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// TODO: Look into error checking / more robust handling for when things go wrong.
|
||||
mTaskListChangeId = mRecentsModel.getTasks(loadedTasks -> {
|
||||
ArrayList<Task> tasks = new ArrayList<>(loadedTasks);
|
||||
// Reverse tasks to put most recent at the bottom of the view
|
||||
Collections.reverse(tasks);
|
||||
// Load task content
|
||||
for (Task task : tasks) {
|
||||
int loadedPos = mTaskList.indexOf(task);
|
||||
if (loadedPos == -1) {
|
||||
continue;
|
||||
}
|
||||
Task loadedTask = mTaskList.get(loadedPos);
|
||||
task.icon = loadedTask.icon;
|
||||
task.titleDescription = loadedTask.titleDescription;
|
||||
task.thumbnail = loadedTask.thumbnail;
|
||||
}
|
||||
mTaskList = tasks;
|
||||
onLoadedCallback.accept(tasks);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load task icon and label asynchronously if it is not already loaded in the task. If the task
|
||||
* already has an icon, this calls the callback immediately.
|
||||
*
|
||||
* @param task task to update with icon + label
|
||||
* @param onLoadedCallback callback to run when task has icon and label
|
||||
*/
|
||||
public void loadTaskIconAndLabel(Task task, @Nullable Runnable onLoadedCallback) {
|
||||
mRecentsModel.getIconCache().updateIconInBackground(task,
|
||||
loadedTask -> onLoadedCallback.run());
|
||||
}
|
||||
|
||||
/**
|
||||
* Load thumbnail asynchronously if not already loaded in the task. If the task already has a
|
||||
* thumbnail or if the thumbnail is cached, this calls the callback immediately.
|
||||
*
|
||||
* @param task task to update with the thumbnail
|
||||
* @param onLoadedCallback callback to run when task has thumbnail
|
||||
*/
|
||||
public void loadTaskThumbnail(Task task, @Nullable Runnable onLoadedCallback) {
|
||||
mRecentsModel.getThumbnailCache().updateThumbnailInBackground(task,
|
||||
thumbnail -> onLoadedCallback.run());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the task from the current task list.
|
||||
*/
|
||||
void removeTask(Task task) {
|
||||
mTaskList.remove(task);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the current task list.
|
||||
*/
|
||||
void clearAllTasks() {
|
||||
mTaskList.clear();
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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;
|
||||
|
||||
import static androidx.recyclerview.widget.ItemTouchHelper.RIGHT;
|
||||
|
||||
import static com.android.quickstep.TaskAdapter.ITEM_TYPE_CLEAR_ALL;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Callback for swipe input on {@link TaskHolder} views in the recents view.
|
||||
*/
|
||||
public final class TaskSwipeCallback extends ItemTouchHelper.SimpleCallback {
|
||||
|
||||
private final Consumer<TaskHolder> mOnTaskSwipeCallback;
|
||||
|
||||
public TaskSwipeCallback(Consumer<TaskHolder> onTaskSwipeCallback) {
|
||||
super(0 /* dragDirs */, RIGHT);
|
||||
mOnTaskSwipeCallback = onTaskSwipeCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder,
|
||||
ViewHolder target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwiped(ViewHolder viewHolder, int direction) {
|
||||
if (direction == RIGHT) {
|
||||
mOnTaskSwipeCallback.accept((TaskHolder) viewHolder);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView,
|
||||
@NonNull ViewHolder viewHolder, float dX, float dY, int actionState,
|
||||
boolean isCurrentlyActive) {
|
||||
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
|
||||
float alpha = 1.0f - dX / (float) viewHolder.itemView.getWidth();
|
||||
viewHolder.itemView.setAlpha(alpha);
|
||||
}
|
||||
super.onChildDraw(c, recyclerView, viewHolder, dX, dY,
|
||||
actionState, isCurrentlyActive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSwipeDirs(@NonNull RecyclerView recyclerView,
|
||||
@NonNull ViewHolder viewHolder) {
|
||||
if (viewHolder.getItemViewType() == ITEM_TYPE_CLEAR_ALL) {
|
||||
// Clear all button should not be swipable.
|
||||
return 0;
|
||||
}
|
||||
return super.getSwipeDirs(recyclerView, viewHolder);
|
||||
}
|
||||
}
|
|
@ -1,145 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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;
|
||||
|
||||
import static android.graphics.Shader.TileMode.CLAMP;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapShader;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
|
||||
/**
|
||||
* Bitmap backed drawable that supports rotating the thumbnail bitmap depending on if the
|
||||
* orientation the thumbnail was taken in matches the desired orientation. In addition, the
|
||||
* thumbnail always fills into the containing bounds.
|
||||
*/
|
||||
public final class ThumbnailDrawable extends Drawable {
|
||||
|
||||
private final Paint mPaint = new Paint();
|
||||
private final Matrix mMatrix = new Matrix();
|
||||
private final ThumbnailData mThumbnailData;
|
||||
private final BitmapShader mShader;
|
||||
private final RectF mDestRect = new RectF();
|
||||
private final int mCornerRadius;
|
||||
private int mRequestedOrientation;
|
||||
|
||||
public ThumbnailDrawable(Resources res, @NonNull ThumbnailData thumbnailData,
|
||||
int requestedOrientation) {
|
||||
mThumbnailData = thumbnailData;
|
||||
mRequestedOrientation = requestedOrientation;
|
||||
mCornerRadius = (int) res.getDimension(R.dimen.task_thumbnail_corner_radius);
|
||||
mShader = new BitmapShader(mThumbnailData.thumbnail, CLAMP, CLAMP);
|
||||
mPaint.setShader(mShader);
|
||||
mPaint.setAntiAlias(true);
|
||||
updateMatrix();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the requested orientation.
|
||||
*
|
||||
* @param orientation the orientation we want the thumbnail to be in
|
||||
*/
|
||||
public void setRequestedOrientation(int orientation) {
|
||||
if (mRequestedOrientation != orientation) {
|
||||
mRequestedOrientation = orientation;
|
||||
updateMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
if (mThumbnailData.thumbnail == null) {
|
||||
return;
|
||||
}
|
||||
canvas.drawRoundRect(mDestRect, mCornerRadius, mCornerRadius, mPaint);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBoundsChange(Rect bounds) {
|
||||
super.onBoundsChange(bounds);
|
||||
mDestRect.set(bounds);
|
||||
updateMatrix();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
final int oldAlpha = mPaint.getAlpha();
|
||||
if (alpha != oldAlpha) {
|
||||
mPaint.setAlpha(alpha);
|
||||
invalidateSelf();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAlpha() {
|
||||
return mPaint.getAlpha();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter colorFilter) {
|
||||
mPaint.setColorFilter(colorFilter);
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorFilter getColorFilter() {
|
||||
return mPaint.getColorFilter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return PixelFormat.TRANSLUCENT;
|
||||
}
|
||||
|
||||
private void updateMatrix() {
|
||||
if (mThumbnailData.thumbnail == null) {
|
||||
return;
|
||||
}
|
||||
mMatrix.reset();
|
||||
float scaleX;
|
||||
float scaleY;
|
||||
Rect bounds = getBounds();
|
||||
Bitmap thumbnail = mThumbnailData.thumbnail;
|
||||
if (mRequestedOrientation != mThumbnailData.orientation) {
|
||||
// Rotate and translate so that top left is the same.
|
||||
mMatrix.postRotate(90, 0, 0);
|
||||
mMatrix.postTranslate(thumbnail.getHeight(), 0);
|
||||
|
||||
scaleX = (float) bounds.width() / thumbnail.getHeight();
|
||||
scaleY = (float) bounds.height() / thumbnail.getWidth();
|
||||
} else {
|
||||
scaleX = (float) bounds.width() / thumbnail.getWidth();
|
||||
scaleY = (float) bounds.height() / thumbnail.getHeight();
|
||||
}
|
||||
// Scale to fill.
|
||||
mMatrix.postScale(scaleX, scaleY);
|
||||
mShader.setLocalMatrix(mMatrix);
|
||||
}
|
||||
}
|
|
@ -1,167 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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;
|
||||
|
||||
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Region;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.android.systemui.shared.recents.IOverviewProxy;
|
||||
import com.android.systemui.shared.recents.ISystemUiProxy;
|
||||
|
||||
/**
|
||||
* Service connected by system-UI for handling touch interaction.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
public class TouchInteractionService extends Service {
|
||||
|
||||
private static final String TAG = "GoTouchInteractionService";
|
||||
|
||||
private final IBinder mMyBinder = new IOverviewProxy.Stub() {
|
||||
|
||||
@Override
|
||||
public void onActiveNavBarRegionChanges(Region region) throws RemoteException { }
|
||||
|
||||
@Override
|
||||
public void onInitialize(Bundle bundle) throws RemoteException {
|
||||
ISystemUiProxy iSystemUiProxy = ISystemUiProxy.Stub
|
||||
.asInterface(bundle.getBinder(KEY_EXTRA_SYSUI_PROXY));
|
||||
SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(iSystemUiProxy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOverviewToggle() {
|
||||
if (mDeviceState.isUserUnlocked()) {
|
||||
mOverviewCommandHelper.onOverviewToggle();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOverviewShown(boolean triggeredFromAltTab) {
|
||||
if (mDeviceState.isUserUnlocked()) {
|
||||
mOverviewCommandHelper.onOverviewShown(triggeredFromAltTab);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
|
||||
if (mDeviceState.isUserUnlocked() && triggeredFromAltTab && !triggeredFromHomeKey) {
|
||||
// onOverviewShownFromAltTab hides the overview and ends at the target app
|
||||
mOverviewCommandHelper.onOverviewHidden();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTip(int actionType, int viewType) {
|
||||
if (mDeviceState.isUserUnlocked()) {
|
||||
mOverviewCommandHelper.onTip(actionType, viewType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAssistantAvailable(boolean available) {
|
||||
// TODO handle assistant
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAssistantVisibilityChanged(float visibility) {
|
||||
// TODO handle assistant
|
||||
}
|
||||
|
||||
public void onBackAction(boolean completed, int downX, int downY, boolean isButton,
|
||||
boolean gestureSwipeLeft) {
|
||||
}
|
||||
|
||||
public void onSystemUiStateChanged(int stateFlags) {
|
||||
// To be implemented
|
||||
}
|
||||
|
||||
/** Deprecated methods **/
|
||||
public void onQuickStep(MotionEvent motionEvent) { }
|
||||
|
||||
public void onQuickScrubEnd() { }
|
||||
|
||||
public void onQuickScrubProgress(float progress) { }
|
||||
|
||||
public void onQuickScrubStart() { }
|
||||
|
||||
public void onPreMotionEvent(int downHitTarget) { }
|
||||
|
||||
public void onMotionEvent(MotionEvent ev) { }
|
||||
|
||||
public void onBind(ISystemUiProxy iSystemUiProxy) {
|
||||
SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(iSystemUiProxy);
|
||||
}
|
||||
};
|
||||
|
||||
private static boolean sConnected = false;;
|
||||
|
||||
public static boolean isConnected() {
|
||||
return sConnected;
|
||||
}
|
||||
|
||||
private RecentsModel mRecentsModel;
|
||||
private OverviewComponentObserver mOverviewComponentObserver;
|
||||
private OverviewCommandHelper mOverviewCommandHelper;
|
||||
private RecentsAnimationDeviceState mDeviceState;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
mDeviceState = new RecentsAnimationDeviceState(this);
|
||||
mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
|
||||
|
||||
sConnected = true;
|
||||
}
|
||||
|
||||
public void onUserUnlocked() {
|
||||
mRecentsModel = RecentsModel.INSTANCE.get(this);
|
||||
mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState);
|
||||
mOverviewCommandHelper = new OverviewCommandHelper(this, mDeviceState,
|
||||
mOverviewComponentObserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (mDeviceState.isUserUnlocked()) {
|
||||
mOverviewComponentObserver.onDestroy();
|
||||
}
|
||||
mDeviceState.destroy();
|
||||
sConnected = false;
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||
Log.d(TAG, "Touch service connected");
|
||||
}
|
||||
return mMyBinder;
|
||||
}
|
||||
|
||||
public static boolean isInitialized() {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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.fallback;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.WindowInsets;
|
||||
|
||||
import com.android.launcher3.util.TouchController;
|
||||
import com.android.launcher3.views.BaseDragLayer;
|
||||
import com.android.quickstep.RecentsActivity;
|
||||
|
||||
/**
|
||||
* Minimal implementation of {@link BaseDragLayer} for Go's fallback recents activity.
|
||||
*/
|
||||
public final class GoRecentsActivityRootView extends BaseDragLayer<RecentsActivity> {
|
||||
public GoRecentsActivityRootView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs, 1 /* alphaChannelCount */);
|
||||
// Go leaves touch control to the view itself.
|
||||
mControllers = new TouchController[0];
|
||||
setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInsets(Rect insets) {
|
||||
if (insets.equals(mInsets)) {
|
||||
return;
|
||||
}
|
||||
super.setInsets(insets);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
|
||||
Insets sysInsets = insets.getSystemWindowInsets();
|
||||
setInsets(new Rect(sysInsets.left, sysInsets.top, sysInsets.right, sysInsets.bottom));
|
||||
return insets.consumeSystemWindowInsets();
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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.util;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
|
||||
/** Empty class, only exists so that lowRamWithQuickstepIconRecentsDebug compiles. */
|
||||
public class ShelfPeekAnim {
|
||||
public ShelfPeekAnim(Launcher launcher) {
|
||||
}
|
||||
|
||||
public enum ShelfAnimState {
|
||||
}
|
||||
|
||||
public boolean isPeeking() {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,962 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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.views;
|
||||
|
||||
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
|
||||
|
||||
import static androidx.recyclerview.widget.LinearLayoutManager.VERTICAL;
|
||||
|
||||
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
|
||||
import static com.android.quickstep.TaskAdapter.CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT;
|
||||
import static com.android.quickstep.TaskAdapter.ITEM_TYPE_CLEAR_ALL;
|
||||
import static com.android.quickstep.TaskAdapter.ITEM_TYPE_TASK;
|
||||
import static com.android.quickstep.TaskAdapter.MAX_TASKS_TO_DISPLAY;
|
||||
import static com.android.quickstep.TaskAdapter.TASKS_START_POSITION;
|
||||
import static com.android.quickstep.util.RemoteAnimationProvider.getLayer;
|
||||
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.UserHandle;
|
||||
import android.util.ArraySet;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.FloatProperty;
|
||||
import android.view.View;
|
||||
import android.view.ViewDebug;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.animation.PathInterpolator;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.interpolator.view.animation.LinearOutSlowInInterpolator;
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver;
|
||||
import androidx.recyclerview.widget.RecyclerView.ItemDecoration;
|
||||
import androidx.recyclerview.widget.RecyclerView.OnChildAttachStateChangeListener;
|
||||
|
||||
import com.android.launcher3.BaseActivity;
|
||||
import com.android.launcher3.Insettable;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.quickstep.ContentFillItemAnimator;
|
||||
import com.android.quickstep.RecentsModel;
|
||||
import com.android.quickstep.RecentsModel.TaskVisualsChangeListener;
|
||||
import com.android.quickstep.RecentsToActivityHelper;
|
||||
import com.android.quickstep.TaskActionController;
|
||||
import com.android.quickstep.TaskAdapter;
|
||||
import com.android.quickstep.TaskHolder;
|
||||
import com.android.quickstep.TaskListLoader;
|
||||
import com.android.quickstep.TaskSwipeCallback;
|
||||
import com.android.quickstep.util.MultiValueUpdateListener;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
|
||||
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Root view for the icon recents view. Acts as the main interface to the rest of the Launcher code
|
||||
* base.
|
||||
*/
|
||||
public final class IconRecentsView extends FrameLayout
|
||||
implements Insettable, TaskVisualsChangeListener {
|
||||
|
||||
public static final FloatProperty<IconRecentsView> CONTENT_ALPHA =
|
||||
new FloatProperty<IconRecentsView>("contentAlpha") {
|
||||
@Override
|
||||
public void setValue(IconRecentsView view, float v) {
|
||||
ALPHA.set(view, v);
|
||||
if (view.getVisibility() != VISIBLE && v > 0) {
|
||||
view.setVisibility(VISIBLE);
|
||||
} else if (view.getVisibility() != GONE && v == 0){
|
||||
view.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float get(IconRecentsView view) {
|
||||
return ALPHA.get(view);
|
||||
}
|
||||
};
|
||||
private static final long CROSSFADE_DURATION = 300;
|
||||
private static final long LAYOUT_ITEM_ANIMATE_IN_DURATION = 150;
|
||||
private static final long LAYOUT_ITEM_ANIMATE_IN_DELAY_BETWEEN = 40;
|
||||
private static final long ITEM_ANIMATE_OUT_DURATION = 150;
|
||||
private static final long ITEM_ANIMATE_OUT_DELAY_BETWEEN = 40;
|
||||
private static final float ITEM_ANIMATE_OUT_TRANSLATION_X_RATIO = .25f;
|
||||
private static final long CLEAR_ALL_FADE_DELAY = 120;
|
||||
|
||||
private static final long REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION = 300;
|
||||
private static final long REMOTE_TO_RECENTS_VERTICAL_EASE_IN_DURATION = 400;
|
||||
private static final long REMOTE_TO_RECENTS_ITEM_FADE_START_DELAY = 200;
|
||||
private static final long REMOTE_TO_RECENTS_ITEM_FADE_DURATION = 217;
|
||||
private static final long REMOTE_TO_RECENTS_ITEM_FADE_BETWEEN_DELAY = 33;
|
||||
|
||||
private static final PathInterpolator FAST_OUT_SLOW_IN_1 =
|
||||
new PathInterpolator(.4f, 0f, 0f, 1f);
|
||||
private static final PathInterpolator FAST_OUT_SLOW_IN_2 =
|
||||
new PathInterpolator(.5f, 0f, 0f, 1f);
|
||||
private static final LinearOutSlowInInterpolator OUT_SLOW_IN =
|
||||
new LinearOutSlowInInterpolator();
|
||||
|
||||
public static final long REMOTE_APP_TO_OVERVIEW_DURATION =
|
||||
REMOTE_TO_RECENTS_VERTICAL_EASE_IN_DURATION;
|
||||
|
||||
/**
|
||||
* A ratio representing the view's relative placement within its padded space. For example, 0
|
||||
* is top aligned and 0.5 is centered vertically.
|
||||
*/
|
||||
@ViewDebug.ExportedProperty(category = "launcher")
|
||||
|
||||
private final Context mContext;
|
||||
private final TaskListLoader mTaskLoader;
|
||||
private final TaskAdapter mTaskAdapter;
|
||||
private final LinearLayoutManager mTaskLayoutManager;
|
||||
private final TaskActionController mTaskActionController;
|
||||
private final DefaultItemAnimator mDefaultItemAnimator = new DefaultItemAnimator();
|
||||
private final ContentFillItemAnimator mLoadingContentItemAnimator =
|
||||
new ContentFillItemAnimator();
|
||||
private final BaseActivity mActivity;
|
||||
private final Drawable mStatusBarForegroundScrim;
|
||||
|
||||
private RecentsToActivityHelper mActivityHelper;
|
||||
private RecyclerView mTaskRecyclerView;
|
||||
private View mShowingContentView;
|
||||
private View mEmptyView;
|
||||
private View mContentView;
|
||||
private boolean mTransitionedFromApp;
|
||||
private boolean mUsingRemoteAnimation;
|
||||
private boolean mStartedEnterAnimation;
|
||||
private boolean mShowStatusBarForegroundScrim;
|
||||
private AnimatorSet mLayoutAnimation;
|
||||
private final ArraySet<View> mLayingOutViews = new ArraySet<>();
|
||||
private Rect mInsets;
|
||||
|
||||
public IconRecentsView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mActivity = BaseActivity.fromContext(context);
|
||||
mContext = context;
|
||||
mStatusBarForegroundScrim =
|
||||
Themes.getAttrDrawable(mContext, R.attr.workspaceStatusBarScrim);
|
||||
mTaskLoader = new TaskListLoader(mContext);
|
||||
mTaskAdapter = new TaskAdapter(mTaskLoader);
|
||||
mTaskAdapter.setOnClearAllClickListener(view -> animateClearAllTasks());
|
||||
mTaskActionController = new TaskActionController(mTaskLoader, mTaskAdapter,
|
||||
mActivity.getStatsLogManager());
|
||||
mTaskAdapter.setActionController(mTaskActionController);
|
||||
mTaskLayoutManager = new LinearLayoutManager(mContext, VERTICAL, true /* reverseLayout */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData) {
|
||||
ArrayList<TaskItemView> itemViews = getTaskViews();
|
||||
for (int i = 0, size = itemViews.size(); i < size; i++) {
|
||||
TaskItemView taskView = itemViews.get(i);
|
||||
TaskHolder taskHolder = (TaskHolder) mTaskRecyclerView.getChildViewHolder(taskView);
|
||||
Optional<Task> optTask = taskHolder.getTask();
|
||||
if (optTask.filter(task -> task.key.id == taskId).isPresent()) {
|
||||
Task task = optTask.get();
|
||||
// Update thumbnail on the task.
|
||||
task.thumbnail = thumbnailData;
|
||||
taskView.setThumbnail(thumbnailData);
|
||||
return task;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskIconChanged(String pkg, UserHandle user) { }
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
if (mTaskRecyclerView == null) {
|
||||
mTaskRecyclerView = findViewById(R.id.recent_task_recycler_view);
|
||||
mTaskRecyclerView.setAdapter(mTaskAdapter);
|
||||
mTaskRecyclerView.setLayoutManager(mTaskLayoutManager);
|
||||
ItemTouchHelper helper = new ItemTouchHelper(
|
||||
new TaskSwipeCallback(holder -> {
|
||||
mTaskActionController.removeTask(holder);
|
||||
if (mTaskLoader.getCurrentTaskList().isEmpty()) {
|
||||
mActivityHelper.leaveRecents();
|
||||
}
|
||||
}));
|
||||
helper.attachToRecyclerView(mTaskRecyclerView);
|
||||
mTaskRecyclerView.addOnChildAttachStateChangeListener(
|
||||
new OnChildAttachStateChangeListener() {
|
||||
@Override
|
||||
public void onChildViewAttachedToWindow(@NonNull View view) {
|
||||
if (mLayoutAnimation != null && !mLayingOutViews.contains(view)) {
|
||||
// Child view was added that is not part of current layout animation
|
||||
// so restart the animation.
|
||||
animateFadeInLayoutAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChildViewDetachedFromWindow(@NonNull View view) { }
|
||||
});
|
||||
mTaskRecyclerView.setItemAnimator(mDefaultItemAnimator);
|
||||
mLoadingContentItemAnimator.setOnAnimationFinishedRunnable(
|
||||
() -> mTaskRecyclerView.setItemAnimator(new DefaultItemAnimator()));
|
||||
ItemDecoration marginDecorator = new ItemDecoration() {
|
||||
@Override
|
||||
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view,
|
||||
@NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
|
||||
// TODO: Determine if current margins cause off screen item to be fully off
|
||||
// screen and if so, modify them so that it is partially off screen.
|
||||
int itemType = parent.getChildViewHolder(view).getItemViewType();
|
||||
Resources res = getResources();
|
||||
switch (itemType) {
|
||||
case ITEM_TYPE_CLEAR_ALL:
|
||||
outRect.top = (int) res.getDimension(
|
||||
R.dimen.clear_all_item_view_top_margin);
|
||||
outRect.bottom = (int) res.getDimension(
|
||||
R.dimen.clear_all_item_view_bottom_margin);
|
||||
break;
|
||||
case ITEM_TYPE_TASK:
|
||||
int desiredTopMargin = (int) res.getDimension(
|
||||
R.dimen.task_item_top_margin);
|
||||
if (mTaskRecyclerView.getChildAdapterPosition(view) ==
|
||||
state.getItemCount() - 1) {
|
||||
// Only add top margin to top task view if insets aren't enough.
|
||||
if (mInsets.top < desiredTopMargin) {
|
||||
outRect.top = desiredTopMargin - mInsets.bottom;
|
||||
}
|
||||
return;
|
||||
}
|
||||
outRect.top = desiredTopMargin;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
};
|
||||
mTaskRecyclerView.addItemDecoration(marginDecorator);
|
||||
|
||||
mEmptyView = findViewById(R.id.recent_task_empty_view);
|
||||
mContentView = mTaskRecyclerView;
|
||||
mTaskAdapter.registerAdapterDataObserver(new AdapterDataObserver() {
|
||||
@Override
|
||||
public void onChanged() {
|
||||
updateContentViewVisibility();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemRangeRemoved(int positionStart, int itemCount) {
|
||||
updateContentViewVisibility();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
super.setEnabled(enabled);
|
||||
int childCount = mTaskRecyclerView.getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
mTaskRecyclerView.getChildAt(i).setEnabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set activity helper for the view to callback to.
|
||||
*
|
||||
* @param helper the activity helper
|
||||
*/
|
||||
public void setRecentsToActivityHelper(@NonNull RecentsToActivityHelper helper) {
|
||||
mActivityHelper = helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logic for when we know we are going to overview/recents and will be putting up the recents
|
||||
* view. This should be used to prepare recents (e.g. load any task data, etc.) before it
|
||||
* becomes visible.
|
||||
*/
|
||||
public void onBeginTransitionToOverview() {
|
||||
mStartedEnterAnimation = false;
|
||||
if (mContext.getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE) {
|
||||
// Scroll to bottom of task in landscape mode. This is a non-issue in portrait mode as
|
||||
// all tasks should be visible to fill up the screen in portrait mode and the view will
|
||||
// not be scrollable.
|
||||
mTaskLayoutManager.scrollToPositionWithOffset(TASKS_START_POSITION, 0 /* offset */);
|
||||
}
|
||||
if (!mUsingRemoteAnimation) {
|
||||
scheduleFadeInLayoutAnimation();
|
||||
}
|
||||
// Load any task changes
|
||||
if (!mTaskLoader.needsToLoad()) {
|
||||
return;
|
||||
}
|
||||
mTaskAdapter.setIsShowingLoadingUi(true);
|
||||
mTaskAdapter.notifyDataSetChanged();
|
||||
mTaskLoader.loadTaskList(tasks -> {
|
||||
int numEmptyItems = mTaskAdapter.getItemCount() - TASKS_START_POSITION;
|
||||
mTaskAdapter.setIsShowingLoadingUi(false);
|
||||
int numActualItems = mTaskAdapter.getItemCount() - TASKS_START_POSITION;
|
||||
if (numEmptyItems < numActualItems) {
|
||||
throw new IllegalStateException("There are less empty item views than the number "
|
||||
+ "of items to animate to.");
|
||||
}
|
||||
// Possible that task list loads faster than adapter changes propagate to layout so
|
||||
// only start content fill animation if there aren't any pending adapter changes and
|
||||
// we've started the on enter layout animation.
|
||||
boolean needsContentFillAnimation =
|
||||
!mTaskRecyclerView.hasPendingAdapterUpdates() && mStartedEnterAnimation;
|
||||
if (needsContentFillAnimation) {
|
||||
// Set item animator for content filling animation. The item animator will switch
|
||||
// back to the default on completion
|
||||
mTaskRecyclerView.setItemAnimator(mLoadingContentItemAnimator);
|
||||
mTaskAdapter.notifyItemRangeRemoved(TASKS_START_POSITION + numActualItems,
|
||||
numEmptyItems - numActualItems);
|
||||
mTaskAdapter.notifyItemRangeChanged(TASKS_START_POSITION, numActualItems,
|
||||
CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT);
|
||||
} else {
|
||||
// Notify change without animating.
|
||||
mTaskAdapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether we transitioned to recents from the most recent app.
|
||||
*
|
||||
* @param transitionedFromApp true if transitioned from the most recent app, false otherwise
|
||||
*/
|
||||
public void setTransitionedFromApp(boolean transitionedFromApp) {
|
||||
mTransitionedFromApp = transitionedFromApp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether we're using a custom remote animation. If so, we will not do the default layout
|
||||
* animation when entering recents and instead wait for the remote app surface to be ready to
|
||||
* use.
|
||||
*
|
||||
* @param usingRemoteAnimation true if doing a remote animation, false o/w
|
||||
*/
|
||||
public void setUsingRemoteAnimation(boolean usingRemoteAnimation) {
|
||||
mUsingRemoteAnimation = usingRemoteAnimation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles input from the overview button. Launch the most recent task unless we just came from
|
||||
* the app. In that case, we launch the next most recent.
|
||||
*/
|
||||
public void handleOverviewCommand() {
|
||||
List<Task> tasks = mTaskLoader.getCurrentTaskList();
|
||||
int tasksSize = tasks.size();
|
||||
if (tasksSize == 0) {
|
||||
// Do nothing
|
||||
return;
|
||||
}
|
||||
Task taskToLaunch;
|
||||
if (mTransitionedFromApp && tasksSize > 1) {
|
||||
// Launch the next most recent app
|
||||
taskToLaunch = tasks.get(1);
|
||||
} else {
|
||||
// Launch the most recent app
|
||||
taskToLaunch = tasks.get(0);
|
||||
}
|
||||
|
||||
// See if view for this task is attached, and if so, animate launch from that view.
|
||||
ArrayList<TaskItemView> itemViews = getTaskViews();
|
||||
for (int i = 0, size = itemViews.size(); i < size; i++) {
|
||||
TaskItemView taskView = itemViews.get(i);
|
||||
TaskHolder holder = (TaskHolder) mTaskRecyclerView.getChildViewHolder(taskView);
|
||||
if (Objects.equals(holder.getTask(), Optional.of(taskToLaunch))) {
|
||||
mTaskActionController.launchTaskFromView(holder);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, just use a basic launch animation.
|
||||
mTaskActionController.launchTask(taskToLaunch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not to show the scrim in between the view and the top insets. This only works
|
||||
* if the view is being insetted in the first place.
|
||||
*
|
||||
* The scrim is added to the activity's root view to prevent animations on this view
|
||||
* affecting the scrim. As a result, it is the activity's responsibility to show/hide this
|
||||
* scrim as appropriate.
|
||||
*
|
||||
* @param showStatusBarForegroundScrim true to show the scrim, false to hide
|
||||
*/
|
||||
public void setShowStatusBarForegroundScrim(boolean showStatusBarForegroundScrim) {
|
||||
mShowStatusBarForegroundScrim = showStatusBarForegroundScrim;
|
||||
if (mShowStatusBarForegroundScrim != showStatusBarForegroundScrim) {
|
||||
updateStatusBarScrim();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateStatusBarScrim() {
|
||||
boolean shouldShow = mInsets.top != 0 && mShowStatusBarForegroundScrim;
|
||||
mActivity.getDragLayer().setForeground(shouldShow ? mStatusBarForegroundScrim : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bottom most task view to animate to.
|
||||
*
|
||||
* @return the task view
|
||||
*/
|
||||
private @Nullable TaskItemView getBottomTaskView() {
|
||||
int childCount = mTaskRecyclerView.getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View view = mTaskRecyclerView.getChildAt(i);
|
||||
if (mTaskRecyclerView.getChildViewHolder(view).getItemViewType() == ITEM_TYPE_TASK) {
|
||||
return (TaskItemView) view;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this view has processed all data changes and is ready to animate from the app to
|
||||
* the overview.
|
||||
*
|
||||
* @return true if ready to animate app to overview, false otherwise
|
||||
*/
|
||||
public boolean isReadyForRemoteAnim() {
|
||||
return !mTaskRecyclerView.hasPendingAdapterUpdates();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a callback for whenever this view is ready to do a remote animation from the app to
|
||||
* overview. See {@link #isReadyForRemoteAnim()}.
|
||||
*
|
||||
* @param callback callback to run when view is ready to animate
|
||||
*/
|
||||
public void setOnReadyForRemoteAnimCallback(onReadyForRemoteAnimCallback callback) {
|
||||
mTaskRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(
|
||||
new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
if (isReadyForRemoteAnim()) {
|
||||
callback.onReadyForRemoteAnim();
|
||||
mTaskRecyclerView.getViewTreeObserver().
|
||||
removeOnGlobalLayoutListener(this);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all tasks and animate out.
|
||||
*/
|
||||
private void animateClearAllTasks() {
|
||||
setEnabled(false);
|
||||
ArrayList<TaskItemView> itemViews = getTaskViews();
|
||||
|
||||
AnimatorSet clearAnim = new AnimatorSet();
|
||||
long currentDelay = 0;
|
||||
|
||||
// Animate each item view to the right and fade out.
|
||||
for (int i = 0, size = itemViews.size(); i < size; i++) {
|
||||
TaskItemView itemView = itemViews.get(i);
|
||||
PropertyValuesHolder transXproperty = PropertyValuesHolder.ofFloat(TRANSLATION_X,
|
||||
0, itemView.getWidth() * ITEM_ANIMATE_OUT_TRANSLATION_X_RATIO);
|
||||
PropertyValuesHolder alphaProperty = PropertyValuesHolder.ofFloat(ALPHA, 1.0f, 0f);
|
||||
ObjectAnimator itemAnim = ObjectAnimator.ofPropertyValuesHolder(itemView,
|
||||
transXproperty, alphaProperty);
|
||||
itemAnim.setDuration(ITEM_ANIMATE_OUT_DURATION);
|
||||
itemAnim.setStartDelay(currentDelay);
|
||||
|
||||
clearAnim.play(itemAnim);
|
||||
currentDelay += ITEM_ANIMATE_OUT_DELAY_BETWEEN;
|
||||
}
|
||||
|
||||
// Animate view fading and leave recents when faded enough.
|
||||
ValueAnimator contentAlpha = ValueAnimator.ofFloat(1.0f, 0f)
|
||||
.setDuration(CROSSFADE_DURATION);
|
||||
contentAlpha.setStartDelay(CLEAR_ALL_FADE_DELAY);
|
||||
contentAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
private boolean mLeftRecents = false;
|
||||
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator valueAnimator) {
|
||||
mContentView.setAlpha((float) valueAnimator.getAnimatedValue());
|
||||
// Leave recents while fading out.
|
||||
if ((float) valueAnimator.getAnimatedValue() < .5f && !mLeftRecents) {
|
||||
mActivityHelper.leaveRecents();
|
||||
mLeftRecents = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
clearAnim.play(contentAlpha);
|
||||
clearAnim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
for (int i = 0, size = itemViews.size(); i < size; i++) {
|
||||
TaskItemView itemView = itemViews.get(i);
|
||||
itemView.setTranslationX(0);
|
||||
itemView.setAlpha(1.0f);
|
||||
}
|
||||
setEnabled(true);
|
||||
mContentView.setVisibility(GONE);
|
||||
mTaskActionController.clearAllTasks();
|
||||
}
|
||||
});
|
||||
clearAnim.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attached task item views ordered by most recent.
|
||||
*
|
||||
* @return array list of attached task item views
|
||||
*/
|
||||
private ArrayList<TaskItemView> getTaskViews() {
|
||||
int taskCount = mTaskRecyclerView.getChildCount();
|
||||
ArrayList<TaskItemView> itemViews = new ArrayList<>();
|
||||
for (int i = 0; i < taskCount; i ++) {
|
||||
View child = mTaskRecyclerView.getChildAt(i);
|
||||
if (child instanceof TaskItemView) {
|
||||
itemViews.add((TaskItemView) child);
|
||||
}
|
||||
}
|
||||
return itemViews;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the content view so that the appropriate view is shown based off the current list
|
||||
* of tasks.
|
||||
*/
|
||||
private void updateContentViewVisibility() {
|
||||
int taskListSize = mTaskAdapter.getItemCount() - TASKS_START_POSITION;
|
||||
if (mShowingContentView != mEmptyView && taskListSize == 0) {
|
||||
mShowingContentView = mEmptyView;
|
||||
crossfadeViews(mEmptyView, mContentView);
|
||||
}
|
||||
if (mShowingContentView != mContentView && taskListSize > 0) {
|
||||
mShowingContentView = mContentView;
|
||||
crossfadeViews(mContentView, mEmptyView);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Animate views so that one view fades in while the other fades out.
|
||||
*
|
||||
* @param fadeInView view that should fade in
|
||||
* @param fadeOutView view that should fade out
|
||||
*/
|
||||
private void crossfadeViews(View fadeInView, View fadeOutView) {
|
||||
fadeInView.animate().cancel();
|
||||
fadeInView.setVisibility(VISIBLE);
|
||||
fadeInView.setAlpha(0f);
|
||||
fadeInView.animate()
|
||||
.alpha(1f)
|
||||
.setDuration(CROSSFADE_DURATION)
|
||||
.setListener(null);
|
||||
|
||||
fadeOutView.animate().cancel();
|
||||
fadeOutView.animate()
|
||||
.alpha(0f)
|
||||
.setDuration(CROSSFADE_DURATION)
|
||||
.setListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
fadeOutView.setVisibility(GONE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a one-shot layout animation on the next layout. Separate from
|
||||
* {@link #scheduleLayoutAnimation()} as the animation is {@link Animator} based and acts on the
|
||||
* view properties themselves, allowing more controllable behavior and making it easier to
|
||||
* manage when the animation conflicts with another animation.
|
||||
*/
|
||||
private void scheduleFadeInLayoutAnimation() {
|
||||
mTaskRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(
|
||||
new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
animateFadeInLayoutAnimation();
|
||||
mTaskRecyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Start animating the layout animation where items fade in.
|
||||
*/
|
||||
private void animateFadeInLayoutAnimation() {
|
||||
if (mLayoutAnimation != null) {
|
||||
// If layout animation still in progress, cancel and restart.
|
||||
mLayoutAnimation.cancel();
|
||||
}
|
||||
ArrayList<TaskItemView> views = getTaskViews();
|
||||
int delay = 0;
|
||||
mLayoutAnimation = new AnimatorSet();
|
||||
for (int i = 0, size = views.size(); i < size; i++) {
|
||||
TaskItemView view = views.get(i);
|
||||
view.setAlpha(0.0f);
|
||||
Animator alphaAnim = ObjectAnimator.ofFloat(view, ALPHA, 0.0f, 1.0f);
|
||||
alphaAnim.setDuration(LAYOUT_ITEM_ANIMATE_IN_DURATION).setStartDelay(delay);
|
||||
alphaAnim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
view.setAlpha(1.0f);
|
||||
mLayingOutViews.remove(view);
|
||||
}
|
||||
});
|
||||
delay += LAYOUT_ITEM_ANIMATE_IN_DELAY_BETWEEN;
|
||||
mLayoutAnimation.play(alphaAnim);
|
||||
mLayingOutViews.add(view);
|
||||
}
|
||||
mLayoutAnimation.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mLayoutAnimation = null;
|
||||
}
|
||||
});
|
||||
mLayoutAnimation.start();
|
||||
mStartedEnterAnimation = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Play remote app to recents animation when the app is the home activity. We use a simple
|
||||
* cross-fade here. Note this is only used if the home activity is a separate app than the
|
||||
* recents activity.
|
||||
*
|
||||
* @param anim animator set
|
||||
* @param homeTarget the home surface thats closing
|
||||
* @param recentsTarget the surface containing recents
|
||||
*/
|
||||
public void playRemoteHomeToRecentsAnimation(@NonNull AnimatorSet anim,
|
||||
@NonNull RemoteAnimationTargetCompat homeTarget,
|
||||
@NonNull RemoteAnimationTargetCompat recentsTarget) {
|
||||
SyncRtSurfaceTransactionApplierCompat surfaceApplier =
|
||||
new SyncRtSurfaceTransactionApplierCompat(this);
|
||||
|
||||
SurfaceParams[] params = new SurfaceParams[2];
|
||||
int boostedMode = MODE_CLOSING;
|
||||
|
||||
ValueAnimator remoteHomeAnim = ValueAnimator.ofFloat(0, 1);
|
||||
remoteHomeAnim.setDuration(REMOTE_APP_TO_OVERVIEW_DURATION);
|
||||
|
||||
remoteHomeAnim.addUpdateListener(valueAnimator -> {
|
||||
float val = (float) valueAnimator.getAnimatedValue();
|
||||
float alpha;
|
||||
RemoteAnimationTargetCompat visibleTarget;
|
||||
RemoteAnimationTargetCompat invisibleTarget;
|
||||
if (val < .5f) {
|
||||
visibleTarget = homeTarget;
|
||||
invisibleTarget = recentsTarget;
|
||||
alpha = 1 - (val * 2);
|
||||
} else {
|
||||
visibleTarget = recentsTarget;
|
||||
invisibleTarget = homeTarget;
|
||||
alpha = (val - .5f) * 2;
|
||||
}
|
||||
params[0] = new SurfaceParams(visibleTarget.leash, alpha, null /* matrix */,
|
||||
null /* windowCrop */, getLayer(visibleTarget, boostedMode),
|
||||
0 /* cornerRadius */);
|
||||
params[1] = new SurfaceParams(invisibleTarget.leash, 0.0f, null /* matrix */,
|
||||
null /* windowCrop */, getLayer(invisibleTarget, boostedMode),
|
||||
0 /* cornerRadius */);
|
||||
surfaceApplier.scheduleApply(params);
|
||||
});
|
||||
anim.play(remoteHomeAnim);
|
||||
animateFadeInLayoutAnimation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Play remote animation from app to recents. This should scale the currently closing app down
|
||||
* to the recents thumbnail.
|
||||
*
|
||||
* @param anim animator set
|
||||
* @param appTarget the app surface thats closing
|
||||
* @param recentsTarget the surface containing recents
|
||||
*/
|
||||
public void playRemoteAppToRecentsAnimation(@NonNull AnimatorSet anim,
|
||||
@NonNull RemoteAnimationTargetCompat appTarget,
|
||||
@NonNull RemoteAnimationTargetCompat recentsTarget) {
|
||||
TaskItemView bottomView = getBottomTaskView();
|
||||
if (bottomView == null) {
|
||||
// This can be null if there were previously 0 tasks and the recycler view has not had
|
||||
// enough time to take in the data change, bind a new view, and lay out the new view.
|
||||
// TODO: Have a fallback to animate to
|
||||
anim.play(ValueAnimator.ofInt(0, 1).setDuration(REMOTE_APP_TO_OVERVIEW_DURATION));
|
||||
return;
|
||||
}
|
||||
final Matrix appMatrix = new Matrix();
|
||||
playRemoteTransYAnim(anim, appMatrix);
|
||||
playRemoteAppScaleDownAnim(anim, appMatrix, appTarget, recentsTarget,
|
||||
bottomView.getThumbnailView());
|
||||
playRemoteTaskListFadeIn(anim, bottomView);
|
||||
mStartedEnterAnimation = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Play translation Y animation for the remote app to recents animation. Animates over all task
|
||||
* views as well as the closing app, easing them into their final vertical positions.
|
||||
*
|
||||
* @param anim animator set to play on
|
||||
* @param appMatrix transformation matrix for the closing app surface
|
||||
*/
|
||||
private void playRemoteTransYAnim(@NonNull AnimatorSet anim, @NonNull Matrix appMatrix) {
|
||||
final ArrayList<TaskItemView> views = getTaskViews();
|
||||
|
||||
// Start Y translation from about halfway through the tasks list to the bottom thumbnail.
|
||||
float taskHeight = getResources().getDimension(R.dimen.task_item_height);
|
||||
float totalTransY = -(MAX_TASKS_TO_DISPLAY / 2.0f - 1) * taskHeight;
|
||||
for (int i = 0, size = views.size(); i < size; i++) {
|
||||
views.get(i).setTranslationY(totalTransY);
|
||||
}
|
||||
|
||||
ValueAnimator transYAnim = ValueAnimator.ofFloat(totalTransY, 0);
|
||||
transYAnim.setDuration(REMOTE_TO_RECENTS_VERTICAL_EASE_IN_DURATION);
|
||||
transYAnim.setInterpolator(FAST_OUT_SLOW_IN_2);
|
||||
transYAnim.addUpdateListener(valueAnimator -> {
|
||||
float transY = (float) valueAnimator.getAnimatedValue();
|
||||
for (int i = 0, size = views.size(); i < size; i++) {
|
||||
views.get(i).setTranslationY(transY);
|
||||
}
|
||||
appMatrix.postTranslate(0, transY - totalTransY);
|
||||
});
|
||||
transYAnim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
for (int i = 0, size = views.size(); i < size; i++) {
|
||||
views.get(i).setTranslationY(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
anim.play(transYAnim);
|
||||
}
|
||||
|
||||
/**
|
||||
* Play the scale down animation for the remote app to recents animation where the app surface
|
||||
* scales down to where the thumbnail is.
|
||||
*
|
||||
* @param anim animator set to play on
|
||||
* @param appMatrix transformation matrix for the app surface
|
||||
* @param appTarget closing app target
|
||||
* @param recentsTarget opening recents target
|
||||
* @param thumbnailView thumbnail view to animate to
|
||||
*/
|
||||
private void playRemoteAppScaleDownAnim(@NonNull AnimatorSet anim, @NonNull Matrix appMatrix,
|
||||
@NonNull RemoteAnimationTargetCompat appTarget,
|
||||
@NonNull RemoteAnimationTargetCompat recentsTarget,
|
||||
@NonNull View thumbnailView) {
|
||||
// Identify where the entering remote app should animate to.
|
||||
Rect endRect = new Rect();
|
||||
thumbnailView.getGlobalVisibleRect(endRect);
|
||||
Rect appBounds = appTarget.sourceContainerBounds;
|
||||
RectF currentAppRect = new RectF();
|
||||
|
||||
SyncRtSurfaceTransactionApplierCompat surfaceApplier =
|
||||
new SyncRtSurfaceTransactionApplierCompat(this);
|
||||
|
||||
// Keep recents visible throughout the animation.
|
||||
SurfaceParams[] params = new SurfaceParams[2];
|
||||
// Closing app should stay on top.
|
||||
int boostedMode = MODE_CLOSING;
|
||||
params[0] = new SurfaceParams(recentsTarget.leash, 1f, null /* matrix */,
|
||||
null /* windowCrop */, getLayer(recentsTarget, boostedMode), 0 /* cornerRadius */);
|
||||
|
||||
ValueAnimator remoteAppAnim = ValueAnimator.ofInt(0, 1);
|
||||
remoteAppAnim.setDuration(REMOTE_TO_RECENTS_VERTICAL_EASE_IN_DURATION);
|
||||
remoteAppAnim.addUpdateListener(new MultiValueUpdateListener() {
|
||||
private final FloatProp mScaleX;
|
||||
private final FloatProp mScaleY;
|
||||
private final FloatProp mTranslationX;
|
||||
private final FloatProp mTranslationY;
|
||||
private final FloatProp mAlpha;
|
||||
|
||||
{
|
||||
// Scale down and move to view location.
|
||||
float endScaleX = ((float) endRect.width()) / appBounds.width();
|
||||
mScaleX = new FloatProp(1f, endScaleX, 0, REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION,
|
||||
FAST_OUT_SLOW_IN_1);
|
||||
float endScaleY = ((float) endRect.height()) / appBounds.height();
|
||||
mScaleY = new FloatProp(1f, endScaleY, 0, REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION,
|
||||
FAST_OUT_SLOW_IN_1);
|
||||
float endTranslationX = endRect.left -
|
||||
(appBounds.width() - thumbnailView.getWidth()) / 2.0f;
|
||||
mTranslationX = new FloatProp(0, endTranslationX, 0,
|
||||
REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION, FAST_OUT_SLOW_IN_1);
|
||||
float endTranslationY = endRect.top -
|
||||
(appBounds.height() - thumbnailView.getHeight()) / 2.0f;
|
||||
mTranslationY = new FloatProp(0, endTranslationY, 0,
|
||||
REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION, FAST_OUT_SLOW_IN_2);
|
||||
mAlpha = new FloatProp(1.0f, 0, 0, REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION,
|
||||
ACCEL_2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(float percent) {
|
||||
Matrix m = new Matrix();
|
||||
m.preScale(mScaleX.value, mScaleY.value,
|
||||
appBounds.width() / 2.0f, appBounds.height() / 2.0f);
|
||||
m.postTranslate(mTranslationX.value, mTranslationY.value);
|
||||
appMatrix.preConcat(m);
|
||||
params[1] = new SurfaceParams(appTarget.leash, mAlpha.value, appMatrix,
|
||||
null /* windowCrop */, getLayer(appTarget, boostedMode),
|
||||
0 /* cornerRadius */);
|
||||
surfaceApplier.scheduleApply(params);
|
||||
|
||||
m.mapRect(currentAppRect, new RectF(appBounds));
|
||||
setViewToRect(thumbnailView, new RectF(endRect), currentAppRect);
|
||||
appMatrix.reset();
|
||||
}
|
||||
});
|
||||
remoteAppAnim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
thumbnailView.setTranslationY(0);
|
||||
thumbnailView.setTranslationX(0);
|
||||
thumbnailView.setScaleX(1);
|
||||
thumbnailView.setScaleY(1);
|
||||
}
|
||||
});
|
||||
anim.play(remoteAppAnim);
|
||||
}
|
||||
|
||||
/**
|
||||
* Play task list fade in animation as part of remote app to recents animation. This animation
|
||||
* ensures that the task views in the recents list fade in from bottom to top.
|
||||
*
|
||||
* @param anim animator set to play on
|
||||
* @param appTaskView the task view associated with the remote app closing
|
||||
*/
|
||||
private void playRemoteTaskListFadeIn(@NonNull AnimatorSet anim,
|
||||
@NonNull TaskItemView appTaskView) {
|
||||
long delay = REMOTE_TO_RECENTS_ITEM_FADE_START_DELAY;
|
||||
int childCount = mTaskRecyclerView.getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
ValueAnimator fadeAnim = ValueAnimator.ofFloat(0, 1.0f);
|
||||
fadeAnim.setDuration(REMOTE_TO_RECENTS_ITEM_FADE_DURATION).setInterpolator(OUT_SLOW_IN);
|
||||
fadeAnim.setStartDelay(delay);
|
||||
View view = mTaskRecyclerView.getChildAt(i);
|
||||
if (Objects.equals(view, appTaskView)) {
|
||||
// Only animate icon and text for the view with snapshot animating in
|
||||
final View icon = appTaskView.getIconView();
|
||||
final View label = appTaskView.getLabelView();
|
||||
|
||||
icon.setAlpha(0.0f);
|
||||
label.setAlpha(0.0f);
|
||||
|
||||
fadeAnim.addUpdateListener(alphaVal -> {
|
||||
float val = alphaVal.getAnimatedFraction();
|
||||
|
||||
icon.setAlpha(val);
|
||||
label.setAlpha(val);
|
||||
});
|
||||
fadeAnim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
icon.setAlpha(1.0f);
|
||||
label.setAlpha(1.0f);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Otherwise, fade in the entire view.
|
||||
view.setAlpha(0.0f);
|
||||
fadeAnim.addUpdateListener(alphaVal -> {
|
||||
float val = alphaVal.getAnimatedFraction();
|
||||
view.setAlpha(val);
|
||||
});
|
||||
fadeAnim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
view.setAlpha(1.0f);
|
||||
}
|
||||
});
|
||||
}
|
||||
anim.play(fadeAnim);
|
||||
|
||||
int itemType = mTaskRecyclerView.getChildViewHolder(view).getItemViewType();
|
||||
if (itemType == ITEM_TYPE_CLEAR_ALL) {
|
||||
// Don't add delay. Clear all should animate at same time as next view.
|
||||
continue;
|
||||
}
|
||||
delay += REMOTE_TO_RECENTS_ITEM_FADE_BETWEEN_DELAY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set view properties so that the view fits to the target rect.
|
||||
*
|
||||
* @param view view to set
|
||||
* @param origRect original rect that view was located
|
||||
* @param targetRect rect to set to
|
||||
*/
|
||||
private void setViewToRect(View view, RectF origRect, RectF targetRect) {
|
||||
float dX = targetRect.left - origRect.left;
|
||||
float dY = targetRect.top - origRect.top;
|
||||
view.setTranslationX(dX);
|
||||
view.setTranslationY(dY);
|
||||
|
||||
float scaleX = targetRect.width() / origRect.width();
|
||||
float scaleY = targetRect.height() / origRect.height();
|
||||
view.setPivotX(0);
|
||||
view.setPivotY(0);
|
||||
view.setScaleX(scaleX);
|
||||
view.setScaleY(scaleY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInsets(Rect insets) {
|
||||
mInsets = insets;
|
||||
mTaskRecyclerView.setPadding(insets.left, insets.top, insets.right, insets.bottom);
|
||||
mTaskRecyclerView.invalidateItemDecorations();
|
||||
if (mInsets.top != 0) {
|
||||
updateStatusBarScrim();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when this view is ready for a remote animation from app to overview.
|
||||
*/
|
||||
public interface onReadyForRemoteAnimCallback {
|
||||
|
||||
void onReadyForRemoteAnim();
|
||||
}
|
||||
}
|
|
@ -1,216 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.FloatProperty;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.quickstep.ThumbnailDrawable;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
|
||||
/**
|
||||
* View representing an individual task item with the icon + thumbnail adjacent to the task label.
|
||||
*/
|
||||
public final class TaskItemView extends LinearLayout {
|
||||
|
||||
private static final String EMPTY_LABEL = "";
|
||||
private static final String DEFAULT_LABEL = "...";
|
||||
private final Drawable mDefaultIcon;
|
||||
private final Drawable mDefaultThumbnail;
|
||||
private final TaskLayerDrawable mIconDrawable;
|
||||
private final TaskLayerDrawable mThumbnailDrawable;
|
||||
private View mTaskIconThumbnailView;
|
||||
private TextView mLabelView;
|
||||
private ImageView mIconView;
|
||||
private ImageView mThumbnailView;
|
||||
private float mContentTransitionProgress;
|
||||
private int mDisplayedOrientation;
|
||||
|
||||
/**
|
||||
* Property representing the content transition progress of the view. 1.0f represents that the
|
||||
* currently bound icon, thumbnail, and label are fully animated in and visible.
|
||||
*/
|
||||
public static FloatProperty CONTENT_TRANSITION_PROGRESS =
|
||||
new FloatProperty<TaskItemView>("taskContentTransitionProgress") {
|
||||
@Override
|
||||
public void setValue(TaskItemView view, float progress) {
|
||||
view.setContentTransitionProgress(progress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float get(TaskItemView view) {
|
||||
return view.mContentTransitionProgress;
|
||||
}
|
||||
};
|
||||
|
||||
public TaskItemView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
Resources res = context.getResources();
|
||||
mDefaultIcon = res.getDrawable(android.R.drawable.sym_def_app_icon, context.getTheme());
|
||||
mDefaultThumbnail = res.getDrawable(R.drawable.default_thumbnail, context.getTheme());
|
||||
mIconDrawable = new TaskLayerDrawable(context);
|
||||
mThumbnailDrawable = new TaskLayerDrawable(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mLabelView = findViewById(R.id.task_label);
|
||||
mTaskIconThumbnailView = findViewById(R.id.task_icon_and_thumbnail);
|
||||
mThumbnailView = findViewById(R.id.task_thumbnail);
|
||||
mIconView = findViewById(R.id.task_icon);
|
||||
|
||||
mThumbnailView.setImageDrawable(mThumbnailDrawable);
|
||||
mIconView.setImageDrawable(mIconDrawable);
|
||||
|
||||
resetToEmptyUi();
|
||||
CONTENT_TRANSITION_PROGRESS.setValue(this, 1.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets task item view to empty, loading UI.
|
||||
*/
|
||||
public void resetToEmptyUi() {
|
||||
mIconDrawable.resetDrawable();
|
||||
mThumbnailDrawable.resetDrawable();
|
||||
setLabel(EMPTY_LABEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the label for the task item. Sets to a default label if null.
|
||||
*
|
||||
* @param label task label
|
||||
*/
|
||||
public void setLabel(@Nullable String label) {
|
||||
mLabelView.setText(getSafeLabel(label));
|
||||
// TODO: Animation for label
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the icon for the task item. Sets to a default icon if null.
|
||||
*
|
||||
* @param icon task icon
|
||||
*/
|
||||
public void setIcon(@Nullable Drawable icon) {
|
||||
// TODO: Scale the icon up based off the padding on the side
|
||||
// The icon proper is actually smaller than the drawable and has "padding" on the side for
|
||||
// the purpose of drawing the shadow, allowing the icon to pop up, so we need to scale the
|
||||
// view if we want the icon to be flush with the bottom of the thumbnail.
|
||||
mIconDrawable.setCurrentDrawable(getSafeIcon(icon));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the task thumbnail for the task. Sets to a default thumbnail if null.
|
||||
*
|
||||
* @param thumbnailData task thumbnail data for the task
|
||||
*/
|
||||
public void setThumbnail(@Nullable ThumbnailData thumbnailData) {
|
||||
mThumbnailDrawable.setCurrentDrawable(getSafeThumbnail(thumbnailData));
|
||||
}
|
||||
|
||||
public View getThumbnailView() {
|
||||
return mThumbnailView;
|
||||
}
|
||||
|
||||
public View getIconView() {
|
||||
return mIconView;
|
||||
}
|
||||
|
||||
public View getLabelView() {
|
||||
return mLabelView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a new animation from the current task content to the specified new content. The caller
|
||||
* is responsible for the actual animation control via the property
|
||||
* {@link #CONTENT_TRANSITION_PROGRESS}.
|
||||
*
|
||||
* @param endIcon the icon to animate to
|
||||
* @param endThumbnail the thumbnail to animate to
|
||||
* @param endLabel the label to animate to
|
||||
*/
|
||||
public void startContentAnimation(@Nullable Drawable endIcon,
|
||||
@Nullable ThumbnailData endThumbnail, @Nullable String endLabel) {
|
||||
mIconDrawable.startNewTransition(getSafeIcon(endIcon));
|
||||
mThumbnailDrawable.startNewTransition(getSafeThumbnail(endThumbnail));
|
||||
// TODO: Animation for label
|
||||
|
||||
setContentTransitionProgress(0.0f);
|
||||
}
|
||||
|
||||
private void setContentTransitionProgress(float progress) {
|
||||
mContentTransitionProgress = progress;
|
||||
mIconDrawable.setTransitionProgress(progress);
|
||||
mThumbnailDrawable.setTransitionProgress(progress);
|
||||
// TODO: Animation for label
|
||||
}
|
||||
|
||||
private @NonNull Drawable getSafeIcon(@Nullable Drawable icon) {
|
||||
return (icon != null) ? icon : mDefaultIcon;
|
||||
}
|
||||
|
||||
private @NonNull Drawable getSafeThumbnail(@Nullable ThumbnailData thumbnailData) {
|
||||
if (thumbnailData == null || thumbnailData.thumbnail == null) {
|
||||
return mDefaultThumbnail;
|
||||
}
|
||||
int orientation = getResources().getConfiguration().orientation;
|
||||
return new ThumbnailDrawable(getResources(), thumbnailData,
|
||||
orientation /* requestedOrientation */);
|
||||
}
|
||||
|
||||
private @NonNull String getSafeLabel(@Nullable String label) {
|
||||
return (label != null) ? label : DEFAULT_LABEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
onOrientationChanged(getResources().getConfiguration().orientation);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
onOrientationChanged(newConfig.orientation);
|
||||
}
|
||||
|
||||
private void onOrientationChanged(int newOrientation) {
|
||||
if (mDisplayedOrientation == newOrientation) {
|
||||
return;
|
||||
}
|
||||
mDisplayedOrientation = newOrientation;
|
||||
int layerCount = mThumbnailDrawable.getNumberOfLayers();
|
||||
for (int i = 0; i < layerCount; i++) {
|
||||
Drawable drawable = mThumbnailDrawable.getDrawable(i);
|
||||
if (drawable instanceof ThumbnailDrawable) {
|
||||
((ThumbnailDrawable) drawable).setRequestedOrientation(newOrientation);
|
||||
}
|
||||
}
|
||||
mTaskIconThumbnailView.forceLayout();
|
||||
}
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.LayerDrawable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
|
||||
/**
|
||||
* A layer drawable for task content that transitions between two drawables by crossfading. Similar
|
||||
* to {@link android.graphics.drawable.TransitionDrawable} but allows callers to control transition
|
||||
* progress and provides a default, empty drawable.
|
||||
*/
|
||||
public final class TaskLayerDrawable extends LayerDrawable {
|
||||
private final Drawable mEmptyDrawable;
|
||||
private float mProgress;
|
||||
|
||||
public TaskLayerDrawable(Context context) {
|
||||
super(new Drawable[0]);
|
||||
|
||||
// Use empty drawable for both layers initially.
|
||||
mEmptyDrawable = context.getResources().getDrawable(
|
||||
R.drawable.empty_content_box, context.getTheme());
|
||||
addLayer(mEmptyDrawable);
|
||||
addLayer(mEmptyDrawable);
|
||||
setTransitionProgress(1.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Immediately set the front-most drawable layer.
|
||||
*
|
||||
* @param drawable drawable to set
|
||||
*/
|
||||
public void setCurrentDrawable(@NonNull Drawable drawable) {
|
||||
setDrawable(0, drawable);
|
||||
applyTransitionProgress(mProgress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Immediately reset the drawable to showing the empty drawable.
|
||||
*/
|
||||
public void resetDrawable() {
|
||||
setCurrentDrawable(mEmptyDrawable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare to start animating the transition by pushing the current drawable to the back and
|
||||
* setting a new drawable to the front layer and making it invisible.
|
||||
*
|
||||
* @param endDrawable drawable to animate to
|
||||
*/
|
||||
public void startNewTransition(@NonNull Drawable endDrawable) {
|
||||
Drawable oldDrawable = getDrawable(0);
|
||||
setDrawable(1, oldDrawable);
|
||||
setDrawable(0, endDrawable);
|
||||
setTransitionProgress(0.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the progress of the transition animation to crossfade the two drawables.
|
||||
*
|
||||
* @param progress current transition progress between 0 (front view invisible) and 1
|
||||
* (front view visible)
|
||||
*/
|
||||
public void setTransitionProgress(float progress) {
|
||||
if (progress > 1 || progress < 0) {
|
||||
throw new IllegalArgumentException("Transition progress should be between 0 and 1");
|
||||
}
|
||||
mProgress = progress;
|
||||
applyTransitionProgress(progress);
|
||||
}
|
||||
|
||||
private void applyTransitionProgress(float progress) {
|
||||
int drawableAlpha = (int) (progress * 255);
|
||||
getDrawable(0).setAlpha(drawableAlpha);
|
||||
if (getDrawable(0) != getDrawable(1)) {
|
||||
// Only do this if it's a different drawable so that it fades out.
|
||||
// Otherwise, we'd just be overwriting the front drawable's alpha.
|
||||
getDrawable(1).setAlpha(255 - drawableAlpha);
|
||||
}
|
||||
invalidateSelf();
|
||||
}
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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.views;
|
||||
|
||||
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
|
||||
import static android.view.View.MeasureSpec.makeMeasureSpec;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
|
||||
/**
|
||||
* Square view that holds thumbnail and icon and shrinks them appropriately so that both fit nicely
|
||||
* within the view. Side length is determined by height.
|
||||
*/
|
||||
final class TaskThumbnailIconView extends ViewGroup {
|
||||
private final Rect mTmpFrameRect = new Rect();
|
||||
private final Rect mTmpChildRect = new Rect();
|
||||
private View mThumbnailView;
|
||||
private View mIconView;
|
||||
private static final float SUBITEM_FRAME_RATIO = .6f;
|
||||
|
||||
public TaskThumbnailIconView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mThumbnailView = findViewById(R.id.task_thumbnail);
|
||||
mIconView = findViewById(R.id.task_icon);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
int height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
|
||||
int width = height;
|
||||
setMeasuredDimension(width, height);
|
||||
|
||||
int subItemSize = (int) (SUBITEM_FRAME_RATIO * height);
|
||||
if (mThumbnailView.getVisibility() != GONE) {
|
||||
boolean isPortrait =
|
||||
(getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT);
|
||||
int thumbnailHeightSpec =
|
||||
makeMeasureSpec(isPortrait ? height : subItemSize, MeasureSpec.EXACTLY);
|
||||
int thumbnailWidthSpec =
|
||||
makeMeasureSpec(isPortrait ? subItemSize : width, MeasureSpec.EXACTLY);
|
||||
measureChild(mThumbnailView, thumbnailWidthSpec, thumbnailHeightSpec);
|
||||
}
|
||||
if (mIconView.getVisibility() != GONE) {
|
||||
int iconHeightSpec = makeMeasureSpec(subItemSize, MeasureSpec.EXACTLY);
|
||||
int iconWidthSpec = makeMeasureSpec(subItemSize, MeasureSpec.EXACTLY);
|
||||
measureChild(mIconView, iconWidthSpec, iconHeightSpec);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
mTmpFrameRect.left = getPaddingLeft();
|
||||
mTmpFrameRect.right = right - left - getPaddingRight();
|
||||
mTmpFrameRect.top = getPaddingTop();
|
||||
mTmpFrameRect.bottom = bottom - top - getPaddingBottom();
|
||||
|
||||
// Layout the thumbnail to the top-start corner of the view
|
||||
if (mThumbnailView.getVisibility() != GONE) {
|
||||
final int width = mThumbnailView.getMeasuredWidth();
|
||||
final int height = mThumbnailView.getMeasuredHeight();
|
||||
|
||||
final int thumbnailGravity = Gravity.TOP | Gravity.START;
|
||||
Gravity.apply(thumbnailGravity, width, height, mTmpFrameRect, mTmpChildRect);
|
||||
|
||||
mThumbnailView.layout(mTmpChildRect.left, mTmpChildRect.top,
|
||||
mTmpChildRect.right, mTmpChildRect.bottom);
|
||||
}
|
||||
|
||||
// Layout the icon to the bottom-end corner of the view
|
||||
if (mIconView.getVisibility() != GONE) {
|
||||
final int width = mIconView.getMeasuredWidth();
|
||||
final int height = mIconView.getMeasuredHeight();
|
||||
|
||||
int thumbnailGravity = Gravity.BOTTOM | Gravity.END;
|
||||
Gravity.apply(thumbnailGravity, width, height, mTmpFrameRect, mTmpChildRect);
|
||||
|
||||
mIconView.layout(mTmpChildRect.left, mTmpChildRect.top,
|
||||
mTmpChildRect.right, mTmpChildRect.bottom);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue