[automerger] Also reset mBadgeScale to 0 when recycling view. am: 48043ee0d6
Change-Id: I45edb03913e7cad7f670be5e5331896c30662bcc
diff --git a/Android.mk b/Android.mk
index d41e184..1e40835 100644
--- a/Android.mk
+++ b/Android.mk
@@ -26,7 +26,6 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-v4 \
android-support-v7-recyclerview \
- android-support-v7-palette \
android-support-dynamic-animation
LOCAL_SRC_FILES := \
@@ -72,7 +71,6 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-v4 \
android-support-v7-recyclerview \
- android-support-v7-palette \
android-support-dynamic-animation
LOCAL_SRC_FILES := \
@@ -114,22 +112,58 @@
include $(BUILD_PACKAGE)
#
-# Launcher proto buffer jar used for development
+# Build rule for Quickstep app.
#
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-proto-files-under, protos) $(call all-proto-files-under, proto_overrides)
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-v4 \
+ android-support-v7-recyclerview \
+ android-support-dynamic-animation
+
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src) \
+ $(call all-java-files-under, src_config) \
+ $(call all-java-files-under, quickstep/src) \
+ $(call all-java-files-under, quickstep/src_flags) \
+ $(call all-proto-files-under, protos) \
+ $(call all-proto-files-under, proto_overrides)
+
+LOCAL_RESOURCE_DIR := \
+ $(LOCAL_PATH)/quickstep/res \
+ $(LOCAL_PATH)/res \
+ prebuilts/sdk/current/support/v7/recyclerview/res \
+
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
LOCAL_PROTOC_OPTIMIZE_TYPE := nano
LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/protos/ --proto_path=$(LOCAL_PATH)/proto_overrides/
LOCAL_PROTO_JAVA_OUTPUT_PARAMS := enum_style=java
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE := launcher_proto_lib
-LOCAL_IS_HOST_MODULE := true
-LOCAL_STATIC_JAVA_LIBRARIES := host-libprotobuf-java-nano
+LOCAL_AAPT_FLAGS := \
+ --auto-add-overlay \
+ --extra-packages android.support.v7.recyclerview \
-include $(BUILD_HOST_JAVA_LIBRARY)
+LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 21
+LOCAL_PACKAGE_NAME := Launcher3QuickStep
+LOCAL_PRIVILEGED_MODULE := true
+LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3
+
+LOCAL_FULL_LIBS_MANIFEST_FILES := \
+ $(LOCAL_PATH)/AndroidManifest.xml \
+ $(LOCAL_PATH)/AndroidManifest-common.xml
+
+LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml
+
+LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.*
+
+include $(BUILD_PACKAGE)
+
+
+
# ==================================================
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index ad404c0..bb03f50 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -43,6 +43,7 @@
<uses-permission android:name="android.permission.BIND_APPWIDGET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+ <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<application
android:backupAgent="com.android.launcher3.LauncherBackupAgent"
@@ -80,12 +81,6 @@
</intent-filter>
</receiver>
- <service android:name="com.android.launcher3.dynamicui.ColorExtractionService"
- android:exported="false"
- android:process=":wallpaper_chooser"
- android:permission="android.permission.BIND_JOB_SERVICE">
- </service>
-
<service
android:name="com.android.launcher3.compat.WallpaperManagerCompatVL$ColorExtractionService"
android:exported="false"
diff --git a/build.gradle b/build.gradle
index 886ccac..9b0989c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -4,7 +4,7 @@
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.3.1'
+ classpath 'com.android.tools.build:gradle:2.3.3'
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.0'
}
}
@@ -13,12 +13,12 @@
apply plugin: 'com.google.protobuf'
android {
- compileSdkVersion 26
- buildToolsVersion '26.0.0'
+ compileSdkVersion 27
+ buildToolsVersion '27.0.0'
defaultConfig {
minSdkVersion 21
- targetSdkVersion 26
+ targetSdkVersion 27
versionCode 1
versionName "1.0"
@@ -40,7 +40,20 @@
applicationId 'com.android.launcher3'
testApplicationId 'com.android.launcher3.tests'
}
+
+ quickstep {
+ applicationId 'com.android.launcher3'
+ testApplicationId 'com.android.launcher3.tests'
+ }
}
+
+ // Disable release builds for now
+ android.variantFilter { variant ->
+ if (variant.buildType.name.endsWith('release')) {
+ variant.setIgnore(true);
+ }
+ }
+
sourceSets {
main {
res.srcDirs = ['res']
@@ -52,31 +65,34 @@
}
}
+ debug {
+ manifest.srcFile "AndroidManifest.xml"
+ }
+
androidTest {
res.srcDirs = ['tests/res']
java.srcDirs = ['tests/src']
manifest.srcFile "tests/AndroidManifest-common.xml"
}
- aosp {
- java.srcDirs = ['src_flags']
- manifest.srcFile "AndroidManifest.xml"
+ androidTestDebug {
+ manifest.srcFile "tests/AndroidManifest.xml"
}
- aospAndroidTest {
- manifest.srcFile "tests/AndroidManifest.xml"
+ aosp {
+ java.srcDirs = ['src_flags']
}
l3go {
res.srcDirs = ['go/res']
java.srcDirs = ['go/src_flags']
- // Note: we are using the Launcher3 manifest here because the gradle manifest-merger uses
- // different attributes than the build system.
- manifest.srcFile "AndroidManifest.xml"
+ manifest.srcFile "go/AndroidManifest.xml"
}
- l3goAndroidTest {
- manifest.srcFile "tests/AndroidManifest.xml"
+ quickstep {
+ res.srcDirs = ['quickstep/res']
+ java.srcDirs = ['quickstep/src_flags', 'quickstep/src']
+ manifest.srcFile "quickstep/AndroidManifest.xml"
}
}
}
@@ -86,12 +102,11 @@
jcenter()
}
-final String SUPPORT_LIBS_VERSION = '26.0.0-SNAPSHOT'
+final String SUPPORT_LIBS_VERSION = '27.0.0-SNAPSHOT'
dependencies {
compile "com.android.support:support-v4:${SUPPORT_LIBS_VERSION}"
compile "com.android.support:support-dynamic-animation:${SUPPORT_LIBS_VERSION}"
compile "com.android.support:recyclerview-v7:${SUPPORT_LIBS_VERSION}"
- compile "com.android.support:palette-v7:${SUPPORT_LIBS_VERSION}"
compile 'com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-7'
testCompile 'junit:junit:4.12'
diff --git a/go/AndroidManifest.xml b/go/AndroidManifest.xml
index ed8e0ad..fbaf981 100644
--- a/go/AndroidManifest.xml
+++ b/go/AndroidManifest.xml
@@ -42,7 +42,7 @@
android:excludeFromRecents="true"
android:autoRemoveFromRecents="true"
android:label="@string/action_add_to_workspace"
- tools:merge="override" >
+ tools:node="replace" >
<intent-filter>
<action android:name="android.content.pm.action.CONFIRM_PIN_SHORTCUT" />
</intent-filter>
diff --git a/go/res/values-az-rAZ/strings.xml b/go/res/values-az-rAZ/strings.xml
deleted file mode 100644
index c4b8cb7..0000000
--- a/go/res/values-az-rAZ/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 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 xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Qısayolu seçmək üçün toxunub saxlayın."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Qısayolu seçmək üçün iki dəfə basıb saxlayın və ya fərdi əməliyyatlardan istifadə edin."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Qısayollar"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> qısayolları"</string>
-</resources>
diff --git a/go/res/values-bs-rBA/strings.xml b/go/res/values-bs-rBA/strings.xml
deleted file mode 100644
index 7042468..0000000
--- a/go/res/values-bs-rBA/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 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 xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Dodirnite i držite da uzmete prečicu."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Dvaput dodirnite i držite da uzmete prečicu ili koristite prilagođene akcije."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Prečice"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Prečice aplikacije <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-ka-rGE/strings.xml b/go/res/values-ka-rGE/strings.xml
deleted file mode 100644
index 1b46534..0000000
--- a/go/res/values-ka-rGE/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 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 xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"შეეხეთ და დააყოვნეთ მალსახმობის ასარჩევად."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"ორმაგად შეეხეთ და გეჭიროთ მალსახმობის ასარჩევად ან მორგებული მოქმედებების გამოსაყენებლად."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"მალსახმობები"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>-ის მალსახმობები"</string>
-</resources>
diff --git a/go/res/values-ky-rKG/strings.xml b/go/res/values-ky-rKG/strings.xml
deleted file mode 100644
index 4c7e973..0000000
--- a/go/res/values-ky-rKG/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 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 xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Кыска жолду тандоо үчүн басып туруңуз."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Кыска жолду тандоо үчүн эки жолу таптап, кармап туруңуз же ыңгайлаштырылган аракеттерди колдонуңуз."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Кыска жолдор"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> кыска жол"</string>
-</resources>
diff --git a/go/res/values-lo-rLA/strings.xml b/go/res/values-lo-rLA/strings.xml
deleted file mode 100644
index 7864884..0000000
--- a/go/res/values-lo-rLA/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 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 xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"ແຕະຄ້າງໄວ້ເພື່ອຮັບປຸ່ມລັດ."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"ແຕະສອງເທື່ອຄ້າງໄວ້ເພື່ອຮັບປຸ່ມລັດ ຫຼື ໃຊ້ຄຳສັ່ງແບບກຳນົດເອງ."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"ປຸ່ມລັດ"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"ປຸ່ມລັດ <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-si-rLK/strings.xml b/go/res/values-si-rLK/strings.xml
deleted file mode 100644
index 4b25c90..0000000
--- a/go/res/values-si-rLK/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 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 xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"කෙටි මගක් තෝරා ගැනීමට ස්පර්ශ කර අල්ලාගෙන සිටින්න."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"විජට් එකක් තෝරා ගැනීමට හෝ අභිරුචි භාවිත කිරීමට දෙවරක් තට්ටු කර අල්ලා ගෙන සිටින්න."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"කෙටි මං"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"කෙටි මං <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-uz-rUZ/strings.xml b/go/res/values-uz-rUZ/strings.xml
deleted file mode 100644
index 318bc15..0000000
--- a/go/res/values-uz-rUZ/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 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 xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Yorliqni tanlab olish uchun bosib turing."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Ikki marta bosib va bosib turgan holatda yorliqni tanlang yoki maxsus amaldan foydalaning."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Yorliqlar"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> ilovasi yorliqlari"</string>
-</resources>
diff --git a/proguard.flags b/proguard.flags
index 51abcca..a315cdc 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -86,13 +86,15 @@
public <init>(...);
}
+# The support library contains references to newer platform versions.
+# Don't warn about those in case this app is linking against an older
+# platform version. We know about them, and they are safe.
+-dontwarn android.support.**
+
# Proguard will strip methods required for talkback to properly scroll to
# next row when focus is on the last item of last row when using a RecyclerView
# Keep optimized and shrunk proguard to prevent issues like this when using
# support jar.
-#-keep,allowoptimization,allowshrinking class android.support.** {
-# *;
-#}
-keep class android.support.v7.widget.RecyclerView { *; }
-keep interface com.android.launcher3.userevent.nano.LauncherLogProto.** {
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
index 0bbec18..de74fce 100644
--- a/protos/launcher_log.proto
+++ b/protos/launcher_log.proto
@@ -132,7 +132,9 @@
// not using the HOME_INTENT
CANCEL = 3; // Indicates that a confirmation screen was cancelled
CONFIRM = 4; // Indicates thata confirmation screen was accepted
+ STOP = 5; // Indicates onStop() was called (screen time out, power off)
}
+
optional Type type = 1;
optional Touch touch = 2;
optional Direction dir = 3;
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
new file mode 100644
index 0000000..eb05864
--- /dev/null
+++ b/quickstep/AndroidManifest.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2017, 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.
+*/
+-->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.launcher3" >
+
+ <uses-sdk android:targetSdkVersion="23" android:minSdkVersion="21"/>
+
+ <application
+ android:backupAgent="com.android.launcher3.LauncherBackupAgent"
+ android:fullBackupOnly="true"
+ android:fullBackupContent="@xml/backupscheme"
+ android:hardwareAccelerated="true"
+ android:icon="@drawable/ic_launcher_home"
+ android:label="@string/derived_app_name"
+ android:theme="@style/LauncherTheme"
+ android:largeHeap="@bool/config_largeHeap"
+ android:restoreAnyVersion="true"
+ android:supportsRtl="true" >
+
+ <service android:name="com.android.quickstep.TouchInteractionService"
+ android:exported="false" />
+
+ </application>
+
+</manifest>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
new file mode 100644
index 0000000..ef61226
--- /dev/null
+++ b/quickstep/res/values/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright (C) 2017 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>
+
+ <!-- Application name -->
+ <string name="derived_app_name" translatable="false">Quickstep</string>
+</resources>
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
new file mode 100644
index 0000000..091ab54
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 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.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+/**
+ * Service connected by system-UI for handling touch interaction.
+ */
+public class TouchInteractionService extends Service {
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+}
diff --git a/quickstep/src_flags/com/android/launcher3/config/FeatureFlags.java b/quickstep/src_flags/com/android/launcher3/config/FeatureFlags.java
new file mode 100644
index 0000000..1edf592
--- /dev/null
+++ b/quickstep/src_flags/com/android/launcher3/config/FeatureFlags.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 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.config;
+
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.states.OverviewState;
+
+/**
+ * Defines a set of flags used to control various launcher behaviors
+ */
+public final class FeatureFlags extends BaseFlags {
+
+ private FeatureFlags() {}
+
+ public static LauncherState createOverviewState(int id) {
+ return new OverviewState(id);
+ }
+}
diff --git a/res/drawable/all_apps_handle_landscape.xml b/res/drawable/all_apps_handle_landscape.xml
new file mode 100644
index 0000000..23826ab
--- /dev/null
+++ b/res/drawable/all_apps_handle_landscape.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="13dp"
+ android:height="13dp"
+ android:viewportWidth="13.0"
+ android:viewportHeight="13.0" >
+
+ <path
+ android:pathData="M2 8.5L6.5 4L11 8.5"
+ android:strokeColor="?attr/workspaceAmbientShadowColor"
+ android:strokeWidth="4"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round" />
+
+ <path
+ android:pathData="M2 8.5L6.5 4L11 8.5"
+ android:strokeColor="?attr/workspaceTextColor"
+ android:strokeWidth="2"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round" />
+
+</vector>
diff --git a/res/drawable/all_apps_handle_portrait.xml b/res/drawable/all_apps_handle_portrait.xml
new file mode 100644
index 0000000..75aa448
--- /dev/null
+++ b/res/drawable/all_apps_handle_portrait.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="16dp"
+ android:height="13dp"
+ android:viewportWidth="16.0"
+ android:viewportHeight="13.0" >
+
+ <path
+ android:pathData="M2 6h12"
+ android:strokeColor="?attr/workspaceAmbientShadowColor"
+ android:strokeWidth="4"
+ android:strokeLineCap="round" />
+
+ <path
+ android:pathData="M2 6h12"
+ android:strokeColor="?attr/workspaceTextColor"
+ android:strokeWidth="2"
+ android:strokeLineCap="round" />
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_install_no_shadow.xml b/res/drawable/ic_install_no_shadow.xml
new file mode 100644
index 0000000..ffce22a
--- /dev/null
+++ b/res/drawable/ic_install_no_shadow.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:fillColor="?android:attr/textColorPrimary"
+ android:pathData="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z" />
+ <path
+ android:pathData="M0 0h24v24H0z" />
+</vector>
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index ac440fc..4ea32b4 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -42,6 +42,19 @@
android:layout_gravity="center"
launcher:pageIndicator="@id/page_indicator" />
+ <com.android.launcher3.pageindicators.PageIndicatorLandscape
+ android:id="@+id/page_indicator"
+ android:theme="@style/HomeScreenElementTheme"
+ android:layout_width="@dimen/dynamic_grid_min_page_indicator_size"
+ android:layout_height="@dimen/dynamic_grid_min_page_indicator_size"
+ android:layout_gravity="bottom|left">
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/all_apps_handle_landscape"
+ android:layout_gravity="center" />
+ </com.android.launcher3.pageindicators.PageIndicatorLandscape>
+
<include layout="@layout/gradient_bg" />
<!-- DO NOT CHANGE THE ID -->
@@ -60,25 +73,12 @@
android:id="@+id/overview_panel"
android:visibility="gone" />
- <include layout="@layout/widgets_view"
- android:id="@+id/widgets_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="invisible" />
-
<include layout="@layout/all_apps"
android:id="@+id/apps_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
- <com.android.launcher3.pageindicators.PageIndicatorCaretLandscape
- android:id="@+id/page_indicator"
- android:theme="@style/HomeScreenElementTheme"
- android:layout_width="@dimen/dynamic_grid_min_page_indicator_size"
- android:layout_height="@dimen/dynamic_grid_min_page_indicator_size"
- android:layout_gravity="bottom|left"/>
-
</com.android.launcher3.dragndrop.DragLayer>
</com.android.launcher3.LauncherRootView>
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index c41a6e3..f58a87e 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -66,12 +66,6 @@
android:id="@+id/drop_target_bar"
layout="@layout/drop_target_bar_horz" />
- <include layout="@layout/widgets_view"
- android:id="@+id/widgets_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="invisible" />
-
<include layout="@layout/all_apps"
android:id="@+id/apps_view"
android:layout_width="match_parent"
diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml
index 03e42bc..660d0ed 100644
--- a/res/layout-sw720dp/launcher.xml
+++ b/res/layout-sw720dp/launcher.xml
@@ -65,12 +65,6 @@
<include layout="@layout/page_indicator"
android:id="@+id/page_indicator" />
- <include layout="@layout/widgets_view"
- android:id="@+id/widgets_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="invisible" />
-
<include layout="@layout/all_apps"
android:id="@+id/apps_view"
android:layout_width="match_parent"
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index 39df2b1..6f837ae 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -18,60 +18,39 @@
will bake the left/right padding into that view's background itself. -->
<com.android.launcher3.allapps.AllAppsContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:id="@+id/apps_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical"
- launcher:revealBackground="@drawable/round_rect_primary">
+ android:clipChildren="true"
+ android:clipToPadding="false"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:saveEnabled="false" >
- <View
- android:id="@+id/reveal_view"
+ <!-- DO NOT CHANGE THE ID -->
+ <com.android.launcher3.allapps.AllAppsRecyclerView
+ android:id="@+id/apps_list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_gravity="center"
- android:focusable="false"
- android:visibility="invisible" />
-
-
- <com.android.launcher3.allapps.AllAppsRecyclerViewContainerView
- android:id="@+id/main_content"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:focusable="true"
+ android:layout_below="@id/search_container_all_apps"
+ android:layout_gravity="center_horizontal|top"
android:clipToPadding="false"
- android:clipChildren="true"
- android:focusableInTouchMode="true"
- android:saveEnabled="false"
- android:visibility="gone">
+ android:descendantFocusability="afterDescendants"
+ android:focusable="true"
+ android:overScrollMode="never" />
- <!-- DO NOT CHANGE THE ID -->
- <com.android.launcher3.allapps.AllAppsRecyclerView
- android:id="@+id/apps_list_view"
- android:layout_below="@id/search_container_all_apps"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center_horizontal|top"
- android:clipToPadding="false"
- android:overScrollMode="never"
- android:descendantFocusability="afterDescendants"
- android:focusable="true" />
+ <!-- Note: we are reusing/repurposing a system attribute for search layout, because of a
+ platform bug, which prevents using custom attributes in <include> tag -->
+ <include
+ android:id="@id/search_container_all_apps"
+ layout="?android:attr/keyboardLayout" />
- <!-- Note: we are reusing/repurposing a system attribute for search layout, because of a
- platform bug, which prevents using custom attributes in <include> tag -->
- <include
- layout="?android:attr/keyboardLayout"
- android:id="@id/search_container_all_apps" />
+ <include layout="@layout/all_apps_fast_scroller" />
- <include layout="@layout/all_apps_fast_scroller" />
-
- </com.android.launcher3.allapps.AllAppsRecyclerViewContainerView>
<View
android:id="@+id/nav_bar_bg"
- android:background="?attr/allAppsNavBarScrimColor"
android:layout_width="match_parent"
android:layout_height="0dp"
- android:layout_gravity="bottom"
- android:focusable="false" />
+ android:layout_alignParentBottom="true"
+ android:background="?attr/allAppsNavBarScrimColor" />
</com.android.launcher3.allapps.AllAppsContainerView>
\ No newline at end of file
diff --git a/res/layout/app_widget_resize_frame.xml b/res/layout/app_widget_resize_frame.xml
index 874fecc..12561b6 100644
--- a/res/layout/app_widget_resize_frame.xml
+++ b/res/layout/app_widget_resize_frame.xml
@@ -21,42 +21,47 @@
android:background="@drawable/widget_resize_shadow"
android:foreground="@drawable/widget_resize_frame"
android:foregroundTint="?attr/workspaceTextColor"
- android:padding="0dp" >
+ android:padding="0dp">
- <!-- Left -->
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="left|center_vertical"
- android:layout_marginLeft="@dimen/widget_handle_margin"
- android:src="@drawable/ic_widget_resize_handle"
- android:tint="?attr/workspaceTextColor" />
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
- <!-- Top -->
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top|center_horizontal"
- android:layout_marginTop="@dimen/widget_handle_margin"
- android:src="@drawable/ic_widget_resize_handle"
- android:tint="?attr/workspaceTextColor" />
+ <!-- Left -->
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left|center_vertical"
+ android:layout_marginLeft="@dimen/widget_handle_margin"
+ android:src="@drawable/ic_widget_resize_handle"
+ android:tint="?attr/workspaceTextColor" />
- <!-- Right -->
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right|center_vertical"
- android:layout_marginRight="@dimen/widget_handle_margin"
- android:src="@drawable/ic_widget_resize_handle"
- android:tint="?attr/workspaceTextColor" />
+ <!-- Top -->
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|center_horizontal"
+ android:layout_marginTop="@dimen/widget_handle_margin"
+ android:src="@drawable/ic_widget_resize_handle"
+ android:tint="?attr/workspaceTextColor" />
- <!-- Bottom -->
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom|center_horizontal"
- android:layout_marginBottom="@dimen/widget_handle_margin"
- android:src="@drawable/ic_widget_resize_handle"
- android:tint="?attr/workspaceTextColor" />
+ <!-- Right -->
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right|center_vertical"
+ android:layout_marginRight="@dimen/widget_handle_margin"
+ android:src="@drawable/ic_widget_resize_handle"
+ android:tint="?attr/workspaceTextColor" />
+ <!-- Bottom -->
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|center_horizontal"
+ android:layout_marginBottom="@dimen/widget_handle_margin"
+ android:src="@drawable/ic_widget_resize_handle"
+ android:tint="?attr/workspaceTextColor" />
+
+ </FrameLayout>
</com.android.launcher3.AppWidgetResizeFrame>
\ No newline at end of file
diff --git a/res/layout/gradient_bg.xml b/res/layout/gradient_bg.xml
index db448d7..6c6626c 100644
--- a/res/layout/gradient_bg.xml
+++ b/res/layout/gradient_bg.xml
@@ -20,5 +20,4 @@
android:id="@+id/gradient_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:visibility="gone"
launcher:layout_ignoreInsets="true" />
\ No newline at end of file
diff --git a/res/layout/page_indicator.xml b/res/layout/page_indicator.xml
index 92f52d6..2df511b 100644
--- a/res/layout/page_indicator.xml
+++ b/res/layout/page_indicator.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
-<com.android.launcher3.pageindicators.PageIndicatorLineCaret
+<com.android.launcher3.pageindicators.PageIndicatorLine
xmlns:android="http://schemas.android.com/apk/res/android"
android:theme="@style/HomeScreenElementTheme"
android:layout_width="match_parent"
@@ -23,6 +23,7 @@
android:id="@+id/all_apps_handle"
android:layout_width="48dp"
android:layout_height="@dimen/dynamic_grid_min_page_indicator_size"
+ android:src="@drawable/all_apps_handle_portrait"
android:layout_gravity="top|center"
android:scaleType="centerInside"/>
-</com.android.launcher3.pageindicators.PageIndicatorLineCaret>
+</com.android.launcher3.pageindicators.PageIndicatorLine>
diff --git a/res/layout/widgets_full_sheet.xml b/res/layout/widgets_full_sheet.xml
new file mode 100644
index 0000000..1535299
--- /dev/null
+++ b/res/layout/widgets_full_sheet.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.launcher3.widget.WidgetsFullSheet
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:theme="?attr/widgetsTheme" >
+
+ <com.android.launcher3.graphics.GradientView
+ android:id="@+id/gradient_bg"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <FrameLayout
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="?android:attr/colorPrimary"
+ android:elevation="4dp">
+
+ <com.android.launcher3.widget.WidgetsRecyclerView
+ android:id="@+id/widgets_list_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipToPadding="false"
+ />
+
+ <!-- Fast scroller popup -->
+ <TextView
+ android:id="@+id/fast_scroller_popup"
+ style="@style/FastScrollerPopup"
+ android:layout_gravity="top|end"
+ android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
+
+ <com.android.launcher3.views.RecyclerViewFastScroller
+ android:id="@+id/fast_scroller"
+ android:layout_width="@dimen/fastscroll_width"
+ android:layout_height="match_parent"
+ android:layout_gravity="end"
+ android:layout_marginEnd="@dimen/fastscroll_end_margin" />
+
+ <View
+ android:id="@+id/nav_bar_bg"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_gravity="bottom"
+ android:background="?attr/allAppsNavBarScrimColor"
+ android:focusable="false" />
+ </FrameLayout>
+</com.android.launcher3.widget.WidgetsFullSheet>
\ No newline at end of file
diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml
deleted file mode 100644
index 4f3c7c8..0000000
--- a/res/layout/widgets_view.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 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.
--->
-<!-- The top and bottom paddings are defined in this container, but since we want
- the list view to span the full width (for touch interception purposes), we
- will bake the left/right padding into that view's background itself. -->
-<com.android.launcher3.widget.WidgetsContainerView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto"
- android:id="@+id/widgets_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:descendantFocusability="afterDescendants"
- android:theme="?attr/widgetsTheme"
- launcher:revealBackground="@drawable/round_rect_primary">
-
- <View
- android:id="@+id/reveal_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:elevation="2dp"
- android:focusable="false"
- android:visibility="invisible" />
-
- <FrameLayout
- android:id="@+id/main_content"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:elevation="15dp"
- android:visibility="gone">
-
- <com.android.launcher3.widget.WidgetsRecyclerView
- android:id="@+id/widgets_list_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
- <!-- Fast scroller popup -->
- <TextView
- android:id="@+id/fast_scroller_popup"
- style="@style/FastScrollerPopup"
- android:layout_gravity="top|end"
- android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
-
- <ProgressBar
- android:id="@+id/loader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center" />
-
- <com.android.launcher3.views.RecyclerViewFastScroller
- android:id="@+id/fast_scroller"
- android:layout_width="@dimen/fastscroll_width"
- android:layout_height="match_parent"
- android:layout_gravity="end"
- android:layout_marginEnd="@dimen/fastscroll_end_margin" />
-
- </FrameLayout>
-
-</com.android.launcher3.widget.WidgetsContainerView>
\ No newline at end of file
diff --git a/res/layout/zzz_dummy_widget.xml b/res/layout/zzz_dummy_widget.xml
deleted file mode 100644
index a0fa8fc..0000000
--- a/res/layout/zzz_dummy_widget.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal">
-
- <FrameLayout
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:background="#ffff0000" />
-
- <FrameLayout
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:background="#ff00ff00" />
-
-</LinearLayout>
\ No newline at end of file
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index d71b4c7..5a776a6 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Verwyder"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deïnstalleer"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Programinligting"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Installeer"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installeer kortpaaie"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Laat \'n program toe om kortpaaie by te voeg sonder gebruikerinmenging."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"lees Tuis-instellings en -kortpaaie"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 293b452..5225dba 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"አስወግድ"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"አራግፍ"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"የመተግበሪያ መረጃ"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"ጫን"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"አቋራጮችን ይጭናል"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"መተግበሪያው ያለተጠቃሚ ጣልቃ ገብነት አቋራጭ እንዲያክል ያስችለዋል።"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"የመነሻ ቅንብሮች እና አቋራጮችን ያነባል"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 6447bf0..5f2d58d 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"إزالة"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"إلغاء التثبيت"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"معلومات عن التطبيق"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"تثبيت"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"تثبيت اختصارات"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"للسماح لتطبيق ما بإضافة اختصارات بدون تدخل المستخدم."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"قراءة إعدادات واختصارات الشاشة الرئيسية"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 18f87bc..dddb6fa 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Silin"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Sistemdən sil"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Tətbiq məlumatı"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Quraşdırın"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"qısayolları quraşdır"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Tətbiqə istifadəçi müdaxiləsi olmadan qısayolları əlavə etməyə icazə verir."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Əsas Səhifə ayarlarını və qısayolları oxuyun"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 777512a..4fa86d2 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Ukloni"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deinstaliraj"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Inform. o aplikaciji"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Instaliraj"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instaliranje prečica"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Dozvoljava aplikaciji da dodaje prečice bez intervencije korisnika."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"čitanje podešavanja i prečica na početnom ekranu"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 0a31ddf..1d7797d 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Выдаліць"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Выдаліць"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Звесткі пра праграмы"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Усталяваць"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"усталёўваць ярлыкі"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Дазваляе праграмам дадаваць ярлыкі без умяшання карыстальніка."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"счытваць налады і ярлыкі на Галоўнай старонцы"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 3e05e0b..b429492 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Премахване"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Деинсталиране"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Данни за прилож."</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Инсталиране"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"инсталиране на преки пътища"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Разрешава на приложението да добавя преки пътища без намеса на потребителя."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"четене на настройките и преките пътища в Начало"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 4d601e0..897a8db 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"সরান"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"আনইনস্টল করুন"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"অ্যাপের তথ্য"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"ইনস্টল করুন"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"শর্টকাটগুলি ইনস্টল করে"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"একটি অ্যাপ্লিকেশানকে ব্যবহারকারীর হস্তক্ষেপ ছাড়াই শর্টকাটগুলি যোগ করার অনুমতি দেয়৷"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"হোম সেটিংস এবং শর্টকাটগুলি পড়ে"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index a3da365..b062b88 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Ukloni"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deinstaliraj"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Informacije o aplikaciji"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Instaliraj"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instaliraj prečice"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Dopušta aplikaciji dodavanje prečica bez posredovanja korisnika."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"čitaj postavke na početnom ekranu i prečice"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index b1a1f85..008531a 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Suprimeix"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstal·la"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Dades de l\'aplicació"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Instal·la"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instal·la dreceres"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permet que una aplicació afegeixi dreceres sense la intervenció de l\'usuari."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"llegeix la configuració i les dreceres de la pantalla d\'inici"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 5a3b4c0..7cd71c2 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Odstranit"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odinstalovat"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"O aplikaci"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Nainstalovat"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalace zástupce"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Umožňuje aplikaci přidat zástupce bez zásahu uživatele."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"čtení nastavení a odkazů plochy"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 849be0d..9a91bc4 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Fjern"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Afinstaller"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Appinfo"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Installer"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installere genveje"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Tillader, at en app tilføjer genveje uden brugerens indgriben."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"læs indstillinger og genveje for startskærmen"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 1c4cd7a..6bc0875 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Entfernen"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deinstallieren"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"App-Details"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Installieren"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Verknüpfungen installieren"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Ermöglicht einer App das Hinzufügen von Verknüpfungen ohne Eingreifen des Nutzers"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Einstellungen und Verknüpfungen auf dem Startbildschirm lesen"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 25c8866..cdb255d 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Κατάργηση"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Απεγκατάσταση"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Πληροφορίες εφαρμογής"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Εγκατάσταση"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"εγκατάσταση συντομεύσεων"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Επιτρέπει σε μια εφαρμογή την προσθήκη συντομεύσεων χωρίς την παρέμβαση του χρήστη."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ανάγνωση ρυθμίσεων και συντομεύσεων αρχικής οθόνης"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index f3f10cc..afd6757 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Remove"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstall"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"App info"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Install"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"read Home settings and shortcuts"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index f3f10cc..afd6757 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Remove"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstall"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"App info"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Install"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"read Home settings and shortcuts"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index f3f10cc..afd6757 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Remove"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstall"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"App info"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Install"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"read Home settings and shortcuts"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index f60e186..05f1e06 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Quitar"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Información de app"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Instalar"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar accesos directos"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que una aplicación agregue accesos directos sin que el usuario intervenga."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"leer configuración y accesos directos de la pantalla principal"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 8e637ff..e7e1cf8 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Quitar"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Datos de aplicación"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Instalar"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar accesos directos"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que una aplicación añada accesos directos sin intervención del usuario."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"leer información de accesos directos y de ajustes de la pantalla de inicio"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 9d9991d..ccc85a3 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Eemalda"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalli"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Rakenduse teave"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Installimine"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installi otseteed"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Võimaldab rakendusel lisada otseteid kasutaja sekkumiseta."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"loe avaekraani seadeid ja otseteid"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index b6082fad..68478de 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Kendu"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalatu"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Aplikazioaren datuak"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Instalatu"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Instalatu lasterbideak"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Erabiltzaileak ezer egin gabe lasterbideak gehitzea baimentzen die aplikazioei."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Irakurri hasierako ezarpenak eta lasterbideak"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 8fe874b..8051b85 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"برداشتن"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"حذف نصب"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"اطلاعات برنامه"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"نصب"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"نصب میانبرها"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"به برنامه اجازه میدهد میانبرها را بدون دخالت کاربر اضافه کند."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"خواندن تنظیمات و میانبرهای صفحه اصلی"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 016dbbc..5e05bbe 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Poista"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Poista asennus"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Sovelluksen tiedot"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Asenna"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"asenna pikakuvakkeita"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Antaa sovelluksen lisätä pikakuvakkeita itsenäisesti ilman käyttäjän valintaa."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"lue aloitusruudun asetuksia ja pikakuvakkeita"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index cd4181e..da403a0 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Supprimer"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Désinstaller"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Détails de l\'appli"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Installer"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installer des raccourcis"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permet à une application d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"lire les paramètres et les raccourcis de la page d\'accueil"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index dde4392..bc98cd2 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Supprimer"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Désinstaller"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Infos sur l\'appli"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Installer"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installer des raccourcis"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permettre à une application d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"lire les paramètres et les raccourcis de l\'écran d\'accueil"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index d55514d..54f9604 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Eliminar"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Info. da aplicación"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Instalar"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atallos"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite a unha aplicación engadir atallos sen intervención do usuario."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ler a configuración e os atallos da pantalla de inicio"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 462f0a0..9734781 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"દૂર કરો"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"અનઇન્સ્ટોલ કરો"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"ઍપ્લિકેશન માહિતી"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"ઇન્સ્ટૉલ કરો"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"શોર્ટકટ્સ ઇન્સ્ટોલ કરો"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"એપ્લિકેશનને વપરાશકર્તા હસ્તક્ષેપ વગર શોર્ટકટ્સ ઉમેરવાની મંજૂરી આપે છે."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"હોમ સેટિંગ્સ અને શોર્ટકટ્સ વાંચો"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 61f7364..2387404 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"निकालें"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"अनइंस्टॉल करें"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"ऐप की जानकारी"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"इंस्टॉल करें"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"शॉर्टकट इंस्टॉल करें"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ऐप को उपयोगकर्ता के हस्तक्षेप के बिना शॉर्टकट जोड़ने देती है."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"होम पेज की सेटिंग और शॉर्टकट पढ़ें"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index aaae258..f533015 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Ukloni"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deinstaliraj"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Info o aplikaciji"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Instaliraj"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instaliranje prečaca"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Aplikaciji omogućuje dodavanje prečaca bez intervencije korisnika."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"čitanje postavki početnog zaslona i prečaca"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 2da64b0..e38da35 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Törlés"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Eltávolítás"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Alkalmazásinformáció"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Telepítés"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"parancsikonok telepítése"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Lehetővé teszi egy alkalmazás számára, hogy felhasználói beavatkozás nélkül adjon hozzá parancsikonokat."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Főoldal beállításainak és parancsikonjainak beolvasása"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index ffb14b0..4901dc5 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Հեռացնել"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Հեռացնել"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Հավելվածի տվյալներ"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Տեղադրել"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"տեղադրել դյուրանցումներ"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Ծրագրին թույլ է տալիս ավելացնել դյուրանցումներ՝ առանց օգտագործողի միջամտության:"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"կարդալ հիմնաէջի կարգավորումներն ու դյուրանցումները"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index b503c88..5112223 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Hapus"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstal"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Info aplikasi"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Instal"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"memasang pintasan"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Mengizinkan aplikasi menambahkan pintasan tanpa campur tangan pengguna."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"membaca setelan dan pintasan layar Utama"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index cf178a5..e1b04e2 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Fjarlægja"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Fjarlægja"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Forritsupplýsingar"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Setja upp"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"setja upp flýtileiðir"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Leyfir forriti að bæta við flýtileiðum án íhlutunar notanda."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"lesa stillingar og flýtileiðir heimaskjás"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index a52aee4..a22a4ce 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Rimuovi"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Disinstalla"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Informazioni app"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Installa"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"aggiunta di scorciatoie"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Consente a un\'app di aggiungere scorciatoie automaticamente."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"lettura di impostazioni e scorciatoie in Home"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 8ffa105..d7905b4 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"הסר"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"הסר התקנה"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"פרטי אפליקציה"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"התקנה"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"התקן קיצורי דרך"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"מאפשר לאפליקציה להוסיף קיצורי דרך ללא התערבות המשתמש."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"קרא הגדרות וקיצורי דרך של דף הבית"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 12815cd..4b6c3ef 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"削除"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"アンインストール"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"アプリ情報"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"インストール"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ショートカットのインストール"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ユーザー操作なしでショートカットを追加することをアプリに許可します。"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ホームの設定とショートカットの読み取り"</string>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 7e8b46c..ec60583 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"ამოშლა"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"დეინსტალაცია"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"აპის შესახებ"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"ინსტალაცია"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"მალსახმობების დაყენება"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"აპისთვის მალსახმობების დამოუკიდებლად დამატების უფლების მიცემა."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"მთავარი ეკრანის პარამეტრებისა და მალსახმობების წაკითხვა"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 50ed9aa..115b752 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Жою"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Жою"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Қолданба ақпараты"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Орнату"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"төте пернелерді орнату"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Қолданбаға пайдаланушының қатысуынсыз төте пернелерді қосу мүмкіндігін береді."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Негізгі экрандағы параметрлер мен төте пернелерді оқу"</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 7028f7e..9040811 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"យកចេញ"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"លុបការដំឡើង"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"ព័ត៌មានកម្មវិធី"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"ដំឡើង"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ដំឡើងផ្លូវកាត់"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"អនុញ្ញាតឲ្យកម្មវិធីបន្ថែមផ្លូវកាត់ ដោយមិនចាំបាច់អំពើពីអ្នកប្រើ។"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"អានការកំណត់ និងផ្លូវកាត់អេក្រង់ដើម"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 5c8b5ac..4156a21 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"ತೆಗೆದುಹಾಕಿ"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"ಅನ್ಇನ್ಸ್ಟಾಲ್"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"ಅಪ್ಲಿಕೇಶನ್ ಮಾಹಿತಿ"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"ಸ್ಥಾಪಿಸಿ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ಸ್ಥಾಪಿಸಿ"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ಬಳಕೆದಾರರ ಹಸ್ತಕ್ಷೇಪವಿಲ್ಲದೆ ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ಸೇರಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ಮುಖಪುಟದ ಸೆಟ್ಟಿಂಗ್ಗಳು ಮತ್ತು ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ಓದಿ"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index e3511d4..d9d8da6 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"삭제"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"제거"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"앱 정보"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"설치"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"바로가기 설치"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"앱이 사용자의 작업 없이 바로가기를 추가할 수 있도록 합니다."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"홈 설정 및 바로가기 읽기"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 85ba8be..0808214 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Алып салуу"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Чыгарып салуу"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Колдонмо тууралуу"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Орнотуу"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"тез чакырмаларды орнотуу"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Колдонмого колдонуучуга кайрылбастан тез чакырма кошууга уруксат берет."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Үйдүн тууралоолорун жана тез чакырмаларын окуу"</string>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 9a50c4b..c1a1e71 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"ເອົາອອກ"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"ຖອນການຕິດຕັ້ງ"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"ຂໍ້ມູນແອັບ"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"ຕິດຕັ້ງ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ຕິດຕັ້ງທາງລັດ"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ອະນຸຍາດໃຫ້ແອັບຯ ເພີ່ມທາງລັດໂດຍບໍ່ຕ້ອງຮັບການຢືນຢັນຈາກຜູ່ໃຊ້."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ອ່ານການຕັ້ງຄ່າໜ້າຫຼັກ ແລະທາງລັດ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index d415e4d..ca45583 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Ištrinti"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Pašalinti"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Programos inform."</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Įdiegti"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"įdiegti sparčiuosius klavišus"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Programai leidžiama pridėti sparčiuosius klavišus be naudotojo įsikišimo."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"skaityti pagrindinio puslapio nustatymus ir sparčiuosius klavišus"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 563a063..7a4903a 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Noņemt"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Atinstalēt"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Par lietotni"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Instalēt"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalēt saīsnes"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Ļauj lietotnei pievienot saīsnes, nejautājot lietotājam."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"lasīt sākuma ekrāna iestatījumus un saīsnes"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 458f0fe..0aaed62 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Отстрани"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Деинсталирај"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Инф. за апликација"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Инсталирај"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"инсталирај кратенки"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Овозможува апликацијата да додава кратенки без интервенција на корисникот."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"чита поставки и кратенки на почетна страница"</string>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 0cc7aa0..299fc45 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"നീക്കംചെയ്യുക"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"അൺഇൻസ്റ്റാളുചെയ്യുക"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"ആപ്പ് വിവരം"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"ഇൻസ്റ്റാൾ ചെയ്യുക"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"കുറുക്കുവഴികൾ ഇൻസ്റ്റാളുചെയ്യുക"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ഉപയോക്തൃ ഇടപെടൽ ഇല്ലാതെ കുറുക്കുവഴികൾ ചേർക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ഹോം ക്രമീകരണങ്ങളും കുറുക്കുവഴികളും റീഡുചെയ്യുക"</string>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 22479cc..8921c77 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Арилгах"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Устгах"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Апп-н мэдээлэл"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Суулгах"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"товчлол суулгах"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Апп нь хэрэглэгчийн оролцоогүйгээр товчлолыг нэмэж чадна"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Нүүрний тохиргоо болон товчлолыг унших"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index ecc5e39..ca9a402 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"काढा"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"अनइंस्टॉल करा"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"अॅप माहिती"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"इंस्टॉल करा"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"शॉर्टकट स्थापित करा"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"वापरकर्ता हस्तक्षेपाशिवाय शॉर्टकट जोडण्यास अॅप ला अनुमती देते."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"होम सेटिंग्ज आणि शॉर्टकट वाचा"</string>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index f94b167..6aeac8b 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Alih keluar"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Nyahpasang"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Maklumat apl"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Pasang"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"pasang pintasan"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Membenarkan apl menambah pintasan tanpa campur tangan pengguna."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"baca tetapan dan pintasan Laman Utama"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 74574b9..0b2e0d5 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"ဖယ်ရှားမည်"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"ဖယ်ထုတ်မည်"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"အက်ပ်အချက်အလက်များ"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"ထည့်သွင်းရန်"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"အတိုကောက်မှတ်သားမှုများအား ထည့်သွင်းခြင်း"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"အသုံးပြုသူ လုပ်ဆောင်မှုမရှိပဲ အပ်ပလီကေးရှင်းကို အတိုကောက်မှတ်သားမှုများ ပြုလုပ်ခွင့် ပေးခြင်း"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ပင်မမျက်နှာစာ အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများအား ဖတ်ခြင်း"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 65247c1..0a6276b 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Fjern"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Avinstaller"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Info om appen"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Installer"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installere snarveier"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Gir apper tillatelse til å legge til snarveier uten innblanding fra brukeren."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"lese startsideinnstillinger og -snarveier"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 904dad4..41e43a7 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"हटाउनुहोस्"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"विस्थापित गर्नुहोस्"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"अनुप्रयोग जानकारी"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"स्थापना गर्नुहोस्"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"सर्टकट स्थापना गर्नेहोस्"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"प्रयोगकर्ताको हस्तक्षेप बिना एउटा अनुप्रयोगलाई सर्टकटमा थप्नको लागि अनुमति दिनुहोस्।"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"गृह सेटिङहरू र सर्टकटहरू पढ्नुहोस्"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 7e8def4..16a79ae 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Verwijderen"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deïnstalleren"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"App-info"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Installeren"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Snelle links instellen"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Een app toestaan snelkoppelingen toe te voegen zonder tussenkomst van de gebruiker."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"instellingen en snelkoppelingen op de homepage lezen"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 5deb2a9..525f2f6 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"ਹਟਾਓ"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"ਅਣਸਥਾਪਤ ਕਰੋ"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"ਐਪ ਜਾਣਕਾਰੀ"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"ਸਥਾਪਤ ਕਰੋ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ਸ਼ਾਰਟਕੱਟ ਸਥਾਪਤ ਕਰੋ"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ਇੱਕ ਐਪ ਨੂੰ ਵਰਤੋਂਕਾਰ ਦੇ ਦਖ਼ਲ ਤੋਂ ਬਿਨਾਂ ਸ਼ਾਰਟਕੱਟ ਸ਼ਾਮਲ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ਹੋਮ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਪੜ੍ਹੋ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index c07f7de..2a01d04 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Usuń"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odinstaluj"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"O aplikacji"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Zainstaluj"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalowanie skrótów"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Pozwala aplikacji dodawać skróty bez interwencji użytkownika."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"odczytywanie ustawień i skrótów na ekranie głównym"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 100f07b..ee4bf08 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Remover"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Inf. da aplicação"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Instalar"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atalhos"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite a uma aplicação adicionar atalhos sem a intervenção do utilizador."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ler definições e atalhos do Ecrã Principal"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index d75159f..b550f6f 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Remover"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Informações do app"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Instalar"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atalhos"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que um app adicione atalhos sem intervenção do usuário."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ler configurações e atalhos da tela inicial"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 512d96d..9cf1cc6 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Eliminați"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Dezinstalați"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Informații aplicație"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Instalați"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalează comenzi rapide"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite unei aplicații să adauge comenzi rapide fără intervenția utilizatorului."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"citește setări și comenzi rapide pentru ecranul de pornire"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 923f357..bfd014e 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Убрать"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Удалить"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"О приложении"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Установить"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Создание ярлыков"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Приложение сможет самостоятельно добавлять ярлыки."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Доступ к настройкам и ярлыкам главного экрана"</string>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index f6c42b2..349b7ca 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"ඉවත් කරන්න"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"අස්ථාපනය කරන්න"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"යෙදුම් තොරතුරු"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"ස්ථාපනය කරන්න"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"කෙටිමං ස්ථාපනය කරන්න"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"පරිශීලක මැදිහත්වීමෙන් තොරව කෙටිමං එක් කිරීමට යෙදුමකට අවසර දෙයි."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"මුල් පිටු සැකසීම් සහ කෙටිමං කියවන්න"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 4dbc407..ba84811 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Odstrániť"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odinštalovať"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Info o aplikácii"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Inštalovať"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"inštalovať odkazy"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Povoľuje aplikácii pridať odkazy bez zásahu používateľa."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"čítanie nastavení a odkazov plochy"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 0b7d36e..c317c0e 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Odstrani"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odstrani"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Podatki o aplikaciji"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Namesti"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"namestitev bližnjic"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Aplikaciji dovoli dodajanje bližnjic brez posredovanja uporabnika."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"branje nastavitev in bližnjic na začetnem zaslonu"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 3e6afee..ed07912 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Hiqe"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Çinstalo"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Informacion mbi aplikacionin"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Instalo"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalo shkurtore"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Lejon një aplikacion të shtojë shkurtore pa ndërhyrjen e përdoruesit."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"lexo cilësimet dhe shkurtoret e ekranit bazë"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index f41e02d..f40366f 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Уклони"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Деинсталирај"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Информ. о апликацији"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Инсталирај"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"инсталирање пречица"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Дозвољава апликацији да додаје пречице без интервенције корисника."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"читање подешавања и пречица на почетном екрану"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 6c598ff..ff6b4e8 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Ta bort"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Avinstallera"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Info om appen"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Installera"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installera genvägar"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Tillåter att en app lägger till genvägar utan åtgärd från användaren."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"läsa inställningar och genvägar för startsidan"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index fd9e4e3..402f3a6 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Ondoa"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Ondoa"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Maelezo ya programu"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Sakinisha"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"kuweka njia za mkato"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Huruhusu programu kuongeza njia za mkato bila mtumiaji kuingilia kati."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"soma mipangilio ya Mwanzo na njia za mkato"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 738abaf..5d72030 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"அகற்று"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"நிறுவல் நீக்கு"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"ஆப்ஸ் தகவல்"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"நிறுவு"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"குறுக்குவழிகளை நிறுவுதல்"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"பயனரின் அனுமதி இல்லாமல் குறுக்குவழிகளைச் சேர்க்கப் பயன்பாட்டை அனுமதிக்கிறது."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"முகப்பின் அமைப்பு மற்றும் குறுக்குவழிகளைப் படித்தல்"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 3155e3d..4db5352 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"తీసివేయి"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"అన్ఇన్స్టాల్ చేయి"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"యాప్ సమాచారం"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"ఇన్స్టాల్ చేయండి"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"సత్వరమార్గాలను ఇన్స్టాల్ చేయడం"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"వినియోగదారు ప్రమేయం లేకుండా సత్వరమార్గాలను జోడించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"హోమ్ సెట్టింగ్లు మరియు సత్వరమార్గాలను చదవడం"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index b370099..ae5aeb5 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"นำออก"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"ถอนการติดตั้ง"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"ข้อมูลแอป"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"ติดตั้ง"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ติดตั้งทางลัด"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"อนุญาตให้แอปเพิ่มทางลัดโดยไม่ต้องให้ผู้ใช้จัดการ"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"อ่านการตั้งค่าและทางลัดหน้าแรกแล้ว"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 54135c9..11f13b0 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Alisin"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"I-uninstall"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Impormasyon ng app"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"I-install"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"i-install ang mga shortcut"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Pinapayagan ang isang app na magdagdag ng mga shortcut nang walang panghihimasok ng user."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"basahin ang mga setting at shortcut ng Home"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index cb8b50a..f19cd78 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Kaldır"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Yüklemeyi kaldır"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Uygulama bilgileri"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Yükle"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"kısayolları yükle"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Uygulamaya, kullanıcı müdahalesi olmadan kısayol ekleme izni verir."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Ana ekran ayarlarını ve kısayollarını oku"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index a9e0109..08f1575 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Видалити"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Видалити"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Про додаток"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Установити"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"створення ярликів"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Дозволяє програмі самостійно додавати ярлики."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"читати налаштування та ярлики головного екрана"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index a704fab..99b8c80 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"ہٹائیں"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"اَن انسٹال کریں"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"ایپ کی معلومات"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"انسٹال کریں"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"شارٹ کٹس انسٹال کریں"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"کسی ایپ کو صارف کی مداخلت کے بغیر شارٹ کٹس شامل کرنے کی اجازت دیتا ہے۔"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ہوم ترتیبات اور شارٹ کٹس کو پڑھیں"</string>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index ed2d342..5167670 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Olib tashlash"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"O‘chirib tashlash"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Ilova haqida"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"O‘rnatish"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"yorliqlar yaratish"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Ilovalarga foydalanuvchidan so‘ramasdan yorliqlar qo‘shishga ruxsat beradi."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Uy sozlamalari va yorliqlarini o‘qish"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 83e1cea..c0e1454 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Xóa"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Gỡ cài đặt"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Thông tin ứng dụng"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Cài đặt"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"cài đặt lối tắt"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Cho phép ứng dụng thêm lối tắt mà không cần sự can thiệp của người dùng."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"đọc cài đặt và lối tắt trên Màn hình chính"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index bef12fb..2342133 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"移除"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"卸载"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"应用信息"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"安装"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"安装快捷方式"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"允许应用自行添加快捷方式。"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"读取主屏幕设置和快捷方式"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 7ac5553..f87ff7f 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"移除"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"解除安裝"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"應用程式資料"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"安裝"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"安裝捷徑"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"允許應用程式無需使用者許可也可新增捷徑。"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"讀取主畫面的設定和捷徑"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index e0c4c99..814a6e4 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"移除"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"解除安裝"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"應用程式資訊"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"安裝"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"安裝捷徑"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"允許應用程式自動新增捷徑。"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"讀取主螢幕的設定和捷徑"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index ef6fdeb..35fa1c7 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -47,6 +47,7 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"Susa"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Khipha"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Ulwazi lohlelo lokusebenza"</string>
+ <string name="install_drop_target_label" msgid="2539096853673231757">"Faka"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"faka izinqamuleli"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Ivumela uhlelo lokusebenza ukufaka izinqamuleli ngaphandle kokungenelela komsebenzisi."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"funda izilungiselelo zokuthi Ikhaya nezinqamuleli"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 5aee715..ad5f0b8 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -63,13 +63,6 @@
<attr name="pageIndicator" format="reference" />
</declare-styleable>
- <!-- BaseContainerView specific attributes. These attributes are used to customize
- AllApps view and WidgetsView in xml. -->
- <declare-styleable name="BaseContainerView">
- <!-- Drawable to use for the reveal animation -->
- <attr name="revealBackground" format="reference" />
- </declare-styleable>
-
<!-- XML attributes used by default_workspace.xml -->
<declare-styleable name="Favorite">
<attr name="className" format="string" />
@@ -144,4 +137,19 @@
<declare-styleable name="RecyclerViewFastScroller">
<attr name="canThumbDetach" format="boolean" />
</declare-styleable>
+
+ <declare-styleable name="CustomAppWidgetProviderInfo">
+ <attr name="providerId" format="integer" />
+
+ <attr name="android:label" />
+ <attr name="android:initialLayout" />
+ <attr name="android:icon" />
+ <attr name="android:previewImage" />
+ <attr name="android:resizeMode" />
+
+ <attr name="numRows" />
+ <attr name="numColumns" />
+ <attr name="numMinRows" format="integer" />
+ <attr name="numMinColumns" format="integer" />
+ </declare-styleable>
</resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index 10b612b..7a33ae6 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -46,21 +46,10 @@
<!-- AllApps & Launcher transitions -->
<!-- The alpha of the AppsCustomize bg in spring loaded mode -->
- <integer name="config_workspaceScrimAlpha">30</integer>
- <integer name="config_allAppsTransitionTime">100</integer>
- <integer name="config_overviewTransitionTime">250</integer>
+ <integer name="config_workspaceScrimAlpha">76</integer>
<!-- Out of 100, the percent to shrink the workspace during spring loaded mode. -->
<integer name="config_workspaceSpringLoadShrinkPercentage">90</integer>
- <!-- Out of 100, the percent to shrink the workspace during overview mode. -->
- <integer name="config_workspaceOverviewShrinkPercentage">70</integer>
-
- <!-- Fade/zoom in/out duration & scale in a Launcher overlay transition.
- Note: This should be less than the config_overlayTransitionTime as they happen together. -->
- <integer name="config_overlayRevealTime">220</integer>
- <integer name="config_overlaySlideRevealTime">320</integer>
- <integer name="config_overlayTransitionTime">300</integer>
- <integer name="config_overlayItemsAlphaStagger">60</integer>
<!-- This constant stores the ratio of the all apps button drawable which
is used for internal (baked-in) padding -->
@@ -86,9 +75,7 @@
<integer name="config_dropAnimMaxDuration">500</integer>
<!-- The duration of the UserFolder opening and closing animation -->
- <integer name="config_folderExpandDuration">120</integer>
<integer name="config_materialFolderExpandDuration">200</integer>
- <integer name="config_materialFolderExpandStagger">60</integer>
<integer name="config_folderDelay">30</integer>
<!-- The distance at which the animation should take the max duration -->
@@ -128,9 +115,6 @@
<!-- View ID used by cell layout to jail its content -->
<item type="id" name="cell_layout_jail_id" />
- <!-- View ID used by PreviewImageView to cache its instance -->
- <item type="id" name="preview_image_id" />
-
<!-- Popup items -->
<integer name="config_popupOpenCloseDuration">150</integer>
<integer name="config_popupArrowOpenDuration">80</integer>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index b1f9d63..eb12dc8 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -54,7 +54,6 @@
<dimen name="vert_drop_target_horizontal_gap">14dp</dimen>
<!-- App Widget resize frame -->
- <dimen name="default_widget_padding">8dp</dimen>
<dimen name="widget_handle_margin">13dp</dimen>
<dimen name="resize_frame_background_padding">24dp</dimen>
@@ -87,9 +86,6 @@
<dimen name="all_apps_empty_search_bg_top_offset">144dp</dimen>
<dimen name="all_apps_background_canvas_width">700dp</dimen>
<dimen name="all_apps_background_canvas_height">475dp</dimen>
- <dimen name="all_apps_caret_stroke_width">2dp</dimen>
- <dimen name="all_apps_caret_shadow_spread">1dp</dimen>
- <dimen name="all_apps_caret_size">13dp</dimen>
<dimen name="all_apps_caret_workspace_offset">18dp</dimen>
<!-- Search bar in All Apps -->
@@ -233,8 +229,4 @@
<dimen name="horizontal_ellipsis_offset">19dp</dimen>
<dimen name="popup_item_divider_height">0.5dp</dimen>
<dimen name="swipe_helper_falsing_threshold">70dp</dimen>
-
-<!-- Other -->
- <!-- Approximates the system status bar height. Not guaranteed to be always be correct. -->
- <dimen name="status_bar_height">24dp</dimen>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1197b1c..e9b00f6 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -90,6 +90,8 @@
<string name="uninstall_drop_target_label">Uninstall</string>
<!-- Label for app info drop target. [CHAR_LIMIT=20] -->
<string name="app_info_drop_target_label">App info</string>
+ <!-- Label for install drop target. [CHAR_LIMIT=20] -->
+ <string name="install_drop_target_label">Install</string>
<!-- Permissions: -->
<skip />
diff --git a/res/xml/custom_widgets.xml b/res/xml/custom_widgets.xml
new file mode 100644
index 0000000..4b54386
--- /dev/null
+++ b/res/xml/custom_widgets.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 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.
+-->
+
+<widgets>
+ <!-- Sample widget definition
+ <widget
+ android:label="My custom widget"
+ android:initialLayout="@layout/sample_widget_layout"
+ android:icon="@drawable/ic_launcher_home"
+ android:resizeMode="horizontal|vertical"
+ launcher:numRows="2"
+ launcher:numColumns="3"
+ launcher:numMinRows="1"
+ launcher:numMinColumns="2"
+ launcher:providerId="1" />
+ -->
+</widgets>
\ No newline at end of file
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 597e937..62e0fb1 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -20,11 +20,14 @@
import android.content.Context;
import android.support.annotation.IntDef;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
+import com.android.launcher3.util.TouchController;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -32,18 +35,25 @@
/**
* Base class for a View which shows a floating UI on top of the launcher UI.
*/
-public abstract class AbstractFloatingView extends LinearLayout {
+public abstract class AbstractFloatingView extends LinearLayout implements TouchController {
@IntDef(flag = true, value = {
TYPE_FOLDER,
- TYPE_POPUP_CONTAINER_WITH_ARROW,
- TYPE_WIDGETS_BOTTOM_SHEET
+ TYPE_ACTION_POPUP,
+ TYPE_WIDGETS_BOTTOM_SHEET,
+ TYPE_WIDGET_RESIZE_FRAME,
+ TYPE_WIDGETS_FULL_SHEET
})
@Retention(RetentionPolicy.SOURCE)
public @interface FloatingViewType {}
public static final int TYPE_FOLDER = 1 << 0;
- public static final int TYPE_POPUP_CONTAINER_WITH_ARROW = 1 << 1;
+ public static final int TYPE_ACTION_POPUP = 1 << 1;
public static final int TYPE_WIDGETS_BOTTOM_SHEET = 1 << 2;
+ public static final int TYPE_WIDGET_RESIZE_FRAME = 1 << 3;
+ public static final int TYPE_WIDGETS_FULL_SHEET = 1 << 4;
+
+ public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
+ | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET;
protected boolean mIsOpen;
@@ -72,21 +82,7 @@
protected abstract void handleClose(boolean animate);
- /**
- * If the view is current handling keyboard, return the active target, null otherwise
- */
- public ExtendedEditText getActiveTextView() {
- return null;
- }
-
-
- /**
- * Any additional view (outside of this container) where touch should be allowed while this
- * view is visible.
- */
- public View getExtendedTouchView() {
- return null;
- }
+ public abstract void logActionCommand(int command);
public final boolean isOpen() {
return mIsOpen;
@@ -97,6 +93,16 @@
protected abstract boolean isOfType(@FloatingViewType int type);
+ public void onBackPressed() {
+ logActionCommand(Action.Command.BACK);
+ close(true);
+ }
+
+ @Override
+ public boolean onControllerTouchEvent(MotionEvent ev) {
+ return false;
+ }
+
protected static <T extends AbstractFloatingView> T getOpenView(
Launcher launcher, @FloatingViewType int type) {
DragLayer dragLayer = launcher.getDragLayer();
@@ -121,26 +127,31 @@
}
}
- public static void closeAllOpenViews(Launcher launcher, boolean animate) {
+ public static void closeOpenViews(Launcher launcher, boolean animate,
+ @FloatingViewType int type) {
DragLayer dragLayer = launcher.getDragLayer();
// Iterate in reverse order. AbstractFloatingView is added later to the dragLayer,
// and will be one of the last views.
for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) {
View child = dragLayer.getChildAt(i);
if (child instanceof AbstractFloatingView) {
- ((AbstractFloatingView) child).close(animate);
+ AbstractFloatingView abs = (AbstractFloatingView) child;
+ if (abs.isOfType(type)) {
+ abs.close(animate);
+ }
}
}
}
+ public static void closeAllOpenViews(Launcher launcher, boolean animate) {
+ closeOpenViews(launcher, animate, TYPE_ALL);
+ }
+
public static void closeAllOpenViews(Launcher launcher) {
closeAllOpenViews(launcher, true);
}
public static AbstractFloatingView getTopOpenView(Launcher launcher) {
- return getOpenView(launcher, TYPE_FOLDER | TYPE_POPUP_CONTAINER_WITH_ARROW
- | TYPE_WIDGETS_BOTTOM_SHEET);
+ return getOpenView(launcher, TYPE_ALL);
}
-
- public abstract int getLogContainerType();
}
diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java
index 8ac8570..5eb6cc7 100644
--- a/src/com/android/launcher3/AllAppsList.java
+++ b/src/com/android/launcher3/AllAppsList.java
@@ -154,7 +154,7 @@
for (int i = data.size() - 1; i >= 0; i--) {
AppInfo info = data.get(i);
if (matcher.matches(info, info.componentName)) {
- info.isDisabled = op.apply(info.isDisabled);
+ info.runtimeStatusFlags = op.apply(info.runtimeStatusFlags);
modified.add(info);
}
}
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index 7d2f753..a5422aa 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -32,10 +32,6 @@
*/
public class AppInfo extends ItemInfoWithIcon {
- public static final int FLAG_SYSTEM_UNKNOWN = 0;
- public static final int FLAG_SYSTEM_YES = 1 << 0;
- public static final int FLAG_SYSTEM_NO = 1 << 1;
-
/**
* The intent used to start the application.
*/
@@ -43,16 +39,6 @@
public ComponentName componentName;
- /**
- * {@see ShortcutInfo#isDisabled}
- */
- public int isDisabled = ShortcutInfo.DEFAULT;
-
- /**
- * Stores if the app is a system app or not.
- */
- public int isSystemApp;
-
public AppInfo() {
itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
}
@@ -74,15 +60,14 @@
this.container = ItemInfo.NO_ID;
this.user = user;
if (PackageManagerHelper.isAppSuspended(info.getApplicationInfo())) {
- isDisabled |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
+ runtimeStatusFlags |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
}
if (quietModeEnabled) {
- isDisabled |= ShortcutInfo.FLAG_DISABLED_QUIET_USER;
+ runtimeStatusFlags |= ShortcutInfo.FLAG_DISABLED_QUIET_USER;
}
intent = makeLaunchIntent(info);
-
- isSystemApp = (info.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) == 0
+ runtimeStatusFlags |= (info.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) == 0
? FLAG_SYSTEM_NO : FLAG_SYSTEM_YES;
}
@@ -92,8 +77,6 @@
componentName = info.componentName;
title = Utilities.trim(info.title);
intent = new Intent(info.intent);
- isDisabled = info.isDisabled;
- isSystemApp = info.isSystemApp;
}
@Override
@@ -119,9 +102,4 @@
.setComponent(cn)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
}
-
- @Override
- public boolean isDisabled() {
- return isDisabled != 0;
- }
}
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index a486a3a..1e95333 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -8,22 +8,19 @@
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
-import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
-import android.widget.FrameLayout;
+import android.view.ViewGroup;
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.util.FocusLogic;
-import com.android.launcher3.util.TouchController;
-public class AppWidgetResizeFrame extends FrameLayout
- implements View.OnKeyListener, TouchController {
+public class AppWidgetResizeFrame extends AbstractFloatingView implements View.OnKeyListener {
private static final int SNAP_DURATION = 150;
private static final float DIMMED_HANDLE_ALPHA = 0f;
private static final float RESIZE_THRESHOLD = 0.66f;
@@ -109,12 +106,28 @@
protected void onFinishInflate() {
super.onFinishInflate();
+ ViewGroup content = (ViewGroup) getChildAt(0);
for (int i = 0; i < HANDLE_COUNT; i ++) {
- mDragHandles[i] = getChildAt(i);
+ mDragHandles[i] = content.getChildAt(i);
}
}
- public void setupForWidget(LauncherAppWidgetHostView widgetView, CellLayout cellLayout,
+ public static void showForWidget(LauncherAppWidgetHostView widget, CellLayout cellLayout) {
+ Launcher launcher = Launcher.getLauncher(cellLayout.getContext());
+ AbstractFloatingView.closeAllOpenViews(launcher);
+
+ DragLayer dl = launcher.getDragLayer();
+ AppWidgetResizeFrame frame = (AppWidgetResizeFrame) launcher.getLayoutInflater()
+ .inflate(R.layout.app_widget_resize_frame, dl, false);
+ frame.setupForWidget(widget, cellLayout, dl);
+ ((DragLayer.LayoutParams) frame.getLayoutParams()).customPosition = true;
+
+ dl.addView(frame);
+ frame.mIsOpen = true;
+ frame.snapToWidget(false);
+ }
+
+ private void setupForWidget(LauncherAppWidgetHostView widgetView, CellLayout cellLayout,
DragLayer dragLayer) {
mCellLayout = cellLayout;
mWidgetView = widgetView;
@@ -126,14 +139,8 @@
mMinHSpan = info.minSpanX;
mMinVSpan = info.minSpanY;
- if (!info.isCustomWidget) {
- mWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(getContext(),
- widgetView.getAppWidgetInfo().provider, null);
- } else {
- Resources r = getContext().getResources();
- int padding = r.getDimensionPixelSize(R.dimen.default_widget_padding);
- mWidgetPadding = new Rect(padding, padding, padding, padding);
- }
+ mWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(getContext(),
+ widgetView.getAppWidgetInfo().provider, null);
if (mResizeMode == AppWidgetProviderInfo.RESIZE_HORIZONTAL) {
mDragHandles[INDEX_TOP].setVisibility(GONE);
@@ -391,7 +398,7 @@
out.bottom = out.top + height;
}
- public void snapToWidget(boolean animate) {
+ private void snapToWidget(boolean animate) {
getSnappedRectRelativeToDragLayer(sTmpRect);
int newWidth = sTmpRect.width();
int newHeight = sTmpRect.height();
@@ -455,7 +462,7 @@
public boolean onKey(View v, int keyCode, KeyEvent event) {
// Clear the frame and give focus to the widget host view when a directional key is pressed.
if (FocusLogic.shouldConsume(keyCode)) {
- mDragLayer.clearResizeFrame();
+ close(false);
mWidgetView.requestFocus();
return true;
}
@@ -505,9 +512,25 @@
if (ev.getAction() == MotionEvent.ACTION_DOWN && handleTouchDown(ev)) {
return true;
}
+ close(false);
return false;
}
+ @Override
+ protected void handleClose(boolean animate) {
+ mDragLayer.removeView(this);
+ }
+
+ @Override
+ public void logActionCommand(int command) {
+ // TODO: Log this case.
+ }
+
+ @Override
+ protected boolean isOfType(int type) {
+ return (type & TYPE_WIDGET_RESIZE_FRAME) != 0;
+ }
+
/**
* A mutable class for describing the range of two int values.
*/
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index d82579b..162aa08 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -28,7 +28,9 @@
import android.database.sqlite.SQLiteDatabase;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
+import android.os.Process;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -433,8 +435,10 @@
return -1;
}
- mValues.put(LauncherSettings.Favorites.ICON,
- Utilities.flattenBitmap(LauncherIcons.createIconBitmap(icon, mContext)));
+ // Auto installs should always support the current platform version.
+ mValues.put(LauncherSettings.Favorites.ICON, Utilities.flattenBitmap(
+ LauncherIcons.createBadgedIconBitmap(
+ icon, Process.myUserHandle(), mContext, Build.VERSION.SDK_INT)));
mValues.put(Favorites.ICON_PACKAGE, mIconRes.getResourcePackageName(iconId));
mValues.put(Favorites.ICON_RESOURCE, mIconRes.getResourceName(iconId));
diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java
deleted file mode 100644
index 82175b7..0000000
--- a/src/com/android/launcher3/BaseContainerView.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * Copyright (C) 2015 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 android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.InsetDrawable;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.widget.FrameLayout;
-
-import com.android.launcher3.allapps.AllAppsContainerView;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.TransformingTouchDelegate;
-
-/**
- * A base container view, which supports resizing.
- */
-public abstract class BaseContainerView extends FrameLayout
- implements DeviceProfile.LauncherLayoutChangeListener {
-
- private static final Rect sBgPaddingRect = new Rect();
-
- protected final Drawable mBaseDrawable;
-
- private View mRevealView;
- private View mContent;
-
- private TransformingTouchDelegate mTouchDelegate;
-
- private final PointF mLastTouchDownPosPx = new PointF(-1.0f, -1.0f);
-
- public BaseContainerView(Context context) {
- this(context, null);
- }
-
- public BaseContainerView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public BaseContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
-
- if (this instanceof AllAppsContainerView) {
- mBaseDrawable = new ColorDrawable();
- } else {
- TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.BaseContainerView, defStyleAttr, 0);
- mBaseDrawable = a.getDrawable(R.styleable.BaseContainerView_revealBackground);
- a.recycle();
- }
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
- grid.addLauncherLayoutChangedListener(this);
-
- View touchDelegateTargetView = getTouchDelegateTargetView();
- if (touchDelegateTargetView != null) {
- mTouchDelegate = new TransformingTouchDelegate(touchDelegateTargetView);
- ((View) touchDelegateTargetView.getParent()).setTouchDelegate(mTouchDelegate);
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
-
- DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
- grid.removeLauncherLayoutChangedListener(this);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- mContent = findViewById(R.id.main_content);
- mRevealView = findViewById(R.id.reveal_view);
-
- updatePaddings();
- }
-
- @Override
- public void onLauncherLayoutChanged() {
- updatePaddings();
- }
-
- /**
- * Calculate the background padding as it can change due to insets/content padding change.
- */
- private void updatePaddings() {
- DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
- int[] padding = grid.getContainerPadding();
-
- int paddingLeft = padding[0];
- int paddingRight = padding[1];
- int paddingTop = 0;
- int paddingBottom = 0;
-
- if (!grid.isVerticalBarLayout()) {
- paddingLeft += grid.edgeMarginPx;
- paddingRight += grid.edgeMarginPx;
- paddingTop = paddingBottom = grid.edgeMarginPx;
- }
- updateBackground(paddingLeft, paddingTop, paddingRight, paddingBottom);
- }
-
- /**
- * Update the background for the reveal view and content view based on the background padding.
- */
- protected void updateBackground(int paddingLeft, int paddingTop,
- int paddingRight, int paddingBottom) {
- mRevealView.setBackground(new InsetDrawable(mBaseDrawable,
- paddingLeft, paddingTop, paddingRight, paddingBottom));
- mContent.setBackground(new InsetDrawable(mBaseDrawable,
- paddingLeft, paddingTop, paddingRight, paddingBottom));
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
-
- View touchDelegateTargetView = getTouchDelegateTargetView();
- if (touchDelegateTargetView != null) {
- getRevealView().getBackground().getPadding(sBgPaddingRect);
- mTouchDelegate.setBounds(
- touchDelegateTargetView.getLeft() - sBgPaddingRect.left,
- touchDelegateTargetView.getTop() - sBgPaddingRect.top,
- touchDelegateTargetView.getRight() + sBgPaddingRect.right,
- touchDelegateTargetView.getBottom() + sBgPaddingRect.bottom);
- }
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- return handleTouchEvent(ev);
- }
-
- @SuppressLint("ClickableViewAccessibility")
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- return handleTouchEvent(ev);
- }
-
- public void setRevealDrawableColor(int color) {
- ((ColorDrawable) mBaseDrawable).setColor(color);
- }
-
- public final View getContentView() {
- return mContent;
- }
-
- public final View getRevealView() {
- return mRevealView;
- }
-
-
- /**
- * Handles the touch events that shows the workspace when clicking outside the bounds of the
- * touch delegate target view.
- */
- private boolean handleTouchEvent(MotionEvent ev) {
- switch (ev.getAction()) {
- case MotionEvent.ACTION_DOWN:
- // Check if the touch is outside touch delegate target view
- View touchDelegateTargetView = getTouchDelegateTargetView();
- float leftBoundPx = touchDelegateTargetView.getLeft();
- if (ev.getX() < leftBoundPx ||
- ev.getX() > (touchDelegateTargetView.getWidth() + leftBoundPx)) {
- mLastTouchDownPosPx.set((int) ev.getX(), (int) ev.getY());
- }
- break;
- case MotionEvent.ACTION_UP:
- if (mLastTouchDownPosPx.x > -1) {
- ViewConfiguration viewConfig = ViewConfiguration.get(getContext());
- float dx = ev.getX() - mLastTouchDownPosPx.x;
- float dy = ev.getY() - mLastTouchDownPosPx.y;
- float distance = PointF.length(dx, dy);
- if (distance < viewConfig.getScaledTouchSlop()) {
- // The background was clicked, so just go home
- Launcher.getLauncher(getContext()).showWorkspace(true);
- return true;
- }
- }
- // Fall through
- case MotionEvent.ACTION_CANCEL:
- mLastTouchDownPosPx.set(-1, -1);
- break;
- }
- return false;
- }
-
- public abstract View getTouchDelegateTargetView();
-}
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index 3ee6e51..afb83be 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -21,6 +21,7 @@
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.MotionEvent;
+import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
@@ -99,11 +100,15 @@
// DO NOT REMOVE, NEEDED IMPLEMENTATION FOR M BUILDS
}
+ public int getScrollBarTop() {
+ return getPaddingTop();
+ }
+
/**
* Returns the height of the fast scroll bar
*/
public int getScrollbarTrackHeight() {
- return getHeight() - getPaddingTop() - getPaddingBottom();
+ return getHeight() - getScrollBarTop() - getPaddingBottom();
}
/**
@@ -121,13 +126,6 @@
return availableScrollBarHeight;
}
- /**
- * Returns the scrollbar for this recycler view.
- */
- public RecyclerViewFastScroller getScrollBar() {
- return mScrollbar;
- }
-
@Override
protected void dispatchDraw(Canvas canvas) {
onUpdateScrollbar(0);
@@ -160,6 +158,28 @@
}
/**
+ * Returns whether the view itself will handle the touch event or not.
+ * @param ev MotionEvent in {@param eventSource}
+ */
+ public boolean shouldContainerScroll(MotionEvent ev, View eventSource) {
+ int[] point = new int[2];
+ point[0] = (int) ev.getX();
+ point[1] = (int) ev.getY();
+ Utilities.mapCoordInSelfToDescendant(mScrollbar, eventSource, point);
+ // IF the MotionEvent is inside the thumb, container should not be pulled down.
+ if (mScrollbar.shouldBlockIntercept(point[0], point[1])) {
+ return false;
+ }
+
+ // IF scroller is at the very top OR there is no scroll bar because there is probably not
+ // enough items to scroll, THEN it's okay for the container to be pulled down.
+ if (getCurrentScrollY() == 0) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
* @return whether fast scrolling is supported in the current state.
*/
public boolean supportsFastScrolling() {
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 5bdc4ed..a590504 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -42,6 +42,7 @@
import com.android.launcher3.IconCache.IconLoadRequest;
import com.android.launcher3.IconCache.ItemInfoUpdateReceiver;
+import com.android.launcher3.Launcher.OnResumeCallback;
import com.android.launcher3.badge.BadgeInfo;
import com.android.launcher3.badge.BadgeRenderer;
import com.android.launcher3.folder.FolderIcon;
@@ -59,7 +60,7 @@
* because we want to make the bubble taller than the text and TextView's clip is
* too aggressive.
*/
-public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
+public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback {
private static final int DISPLAY_WORKSPACE = 0;
private static final int DISPLAY_ALL_APPS = 1;
@@ -340,6 +341,13 @@
refreshDrawableState();
}
+ @Override
+ public void onLauncherResume() {
+ // Reset the pressed state of icon that was locked in the press state while activity
+ // was launching
+ setStayPressed(false);
+ }
+
void clearPressedBackground() {
setPressed(false);
setStayPressed(false);
@@ -506,11 +514,17 @@
public PreloadIconDrawable applyProgressLevel(int progressLevel) {
if (getTag() instanceof ItemInfoWithIcon) {
ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
- setContentDescription(progressLevel > 0
- ? getContext().getString(R.string.app_downloading_title, info.title,
- NumberFormat.getPercentInstance().format(progressLevel * 0.01))
- : getContext().getString(R.string.app_waiting_download_title, info.title));
-
+ if (progressLevel >= 100) {
+ setContentDescription(info.contentDescription != null
+ ? info.contentDescription : "");
+ } else if (progressLevel > 0) {
+ setContentDescription(getContext()
+ .getString(R.string.app_downloading_title, info.title,
+ NumberFormat.getPercentInstance().format(progressLevel * 0.01)));
+ } else {
+ setContentDescription(getContext()
+ .getString(R.string.app_waiting_download_title, info.title));
+ }
if (mIcon != null) {
final PreloadIconDrawable preloadDrawable;
if (mIcon instanceof PreloadIconDrawable) {
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 632e490..d8c4efa 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -16,6 +16,8 @@
package com.android.launcher3;
+import static com.android.launcher3.LauncherState.NORMAL;
+
import android.animation.AnimatorSet;
import android.animation.FloatArrayEvaluator;
import android.animation.ObjectAnimator;
@@ -35,10 +37,9 @@
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.LinearInterpolator;
import android.widget.TextView;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
@@ -178,7 +179,7 @@
@Override
public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
- mActive = supportsDrop(dragObject.dragSource, dragObject.dragInfo);
+ mActive = supportsDrop(dragObject.dragInfo);
mDrawable.setColorFilter(null);
if (mCurrentColorAnim != null) {
mCurrentColorAnim.cancel();
@@ -194,10 +195,14 @@
@Override
public final boolean acceptDrop(DragObject dragObject) {
- return supportsDrop(dragObject.dragSource, dragObject.dragInfo);
+ return supportsDrop(dragObject.dragInfo);
}
- protected abstract boolean supportsDrop(DragSource source, ItemInfo info);
+ protected abstract boolean supportsDrop(ItemInfo info);
+
+ public boolean supportsAccessibilityDrop(ItemInfo info) {
+ return supportsDrop(info);
+ }
@Override
public boolean isDropEnabled() {
@@ -215,7 +220,7 @@
* On drop animate the dropView to the icon.
*/
@Override
- public void onDrop(final DragObject d) {
+ public void onDrop(final DragObject d, final DragOptions options) {
final DragLayer dragLayer = mLauncher.getDragLayer();
final Rect from = new Rect();
dragLayer.getViewRectRelativeToSelf(d.dragView, from);
@@ -229,19 +234,22 @@
public void run() {
completeDrop(d);
mDropTargetBar.onDragEnd();
- mLauncher.exitSpringLoadedDragModeDelayed(true, 0, null);
+ mLauncher.getStateManager().goToState(NORMAL);
}
};
dragLayer.animateView(d.dragView, from, to, scale, 1f, 1f, 0.1f, 0.1f,
DRAG_VIEW_DROP_DURATION,
- new DecelerateInterpolator(2),
- new LinearInterpolator(), onAnimationEndRunnable,
+ Interpolators.DEACCEL_2, Interpolators.LINEAR, onAnimationEndRunnable,
DragLayer.ANIMATION_END_DISAPPEAR, null);
}
+ public abstract int getAccessibilityAction();
+
@Override
public void prepareAccessibilityDrop() { }
+ public abstract void onAccessibilityDrop(View view, ItemInfo item);
+
public abstract void completeDrop(DragObject d);
@Override
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index a75c6d1..3162286 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -44,15 +44,15 @@
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.DecelerateInterpolator;
+
import com.android.launcher3.BubbleTextView.BubbleTextShadowHandler;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
import com.android.launcher3.accessibility.FolderAccessibilityHelper;
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.folder.PreviewBackground;
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.util.CellAndSpan;
@@ -60,6 +60,7 @@
import com.android.launcher3.util.ParcelableSparseArray;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -89,8 +90,6 @@
private int mCountY;
private boolean mDropPending = false;
- private boolean mIsDragTarget = true;
- private boolean mJailContent = true;
// These are temporary variables to prevent having to allocate a new object just to
// return an (x, y) value from helper functions. Do NOT use them to maintain other state.
@@ -106,8 +105,6 @@
private final ArrayList<PreviewBackground> mFolderBackgrounds = new ArrayList<>();
final PreviewBackground mFolderLeaveBehind = new PreviewBackground();
- private float mBackgroundAlpha;
-
private static final int[] BACKGROUND_STATE_ACTIVE = new int[] { android.R.attr.state_active };
private static final int[] BACKGROUND_STATE_DEFAULT = new int[0];
private final Drawable mBackground;
@@ -223,12 +220,12 @@
mBackground = res.getDrawable(R.drawable.bg_celllayout);
mBackground.setCallback(this);
- mBackground.setAlpha((int) (mBackgroundAlpha * 255));
+ mBackground.setAlpha(0);
mReorderPreviewAnimationMagnitude = (REORDER_PREVIEW_MAGNITUDE * grid.iconSizePx);
// Initialize the data structures used for the drag visualization.
- mEaseOutInterpolator = new DecelerateInterpolator(2.5f); // Quint ease out
+ mEaseOutInterpolator = Interpolators.DEACCEL_2_5; // Quint ease out
mDragCell[0] = mDragCell[1] = -1;
for (int i = 0; i < mDragOutlines.length; i++) {
mDragOutlines[i] = new Rect(-1, -1, -1, -1);
@@ -346,7 +343,7 @@
// the home screen mode, however, once in overview mode stylus button press should be
// enabled to allow rearranging the different home screens. So check what mode
// the workspace is in, and only perform stylus button presses while in overview mode.
- if (mLauncher.mWorkspace.isInOverviewMode()
+ if (mLauncher.isInState(LauncherState.OVERVIEW)
&& mStylusEventHelper.onMotionEvent(ev)) {
return true;
}
@@ -357,10 +354,6 @@
mShortcutsAndWidgets.setLayerType(hasLayer ? LAYER_TYPE_HARDWARE : LAYER_TYPE_NONE, sPaint);
}
- public void buildHardwareLayer() {
- mShortcutsAndWidgets.buildLayer();
- }
-
public void setCellDimensions(int width, int height) {
mFixedCellWidth = mCellWidth = width;
mFixedCellHeight = mCellHeight = height;
@@ -404,14 +397,6 @@
}
}
- void disableDragTarget() {
- mIsDragTarget = false;
- }
-
- public boolean isDragTarget() {
- return mIsDragTarget;
- }
-
void setIsDragOverlapping(boolean isDragOverlapping) {
if (mIsDragOverlapping != isDragOverlapping) {
mIsDragOverlapping = isDragOverlapping;
@@ -421,26 +406,22 @@
}
}
- public void disableJailContent() {
- mJailContent = false;
- }
-
@Override
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
- if (mJailContent) {
- ParcelableSparseArray jail = getJailedArray(container);
- super.dispatchSaveInstanceState(jail);
- container.put(R.id.cell_layout_jail_id, jail);
- } else {
- super.dispatchSaveInstanceState(container);
- }
+ ParcelableSparseArray jail = getJailedArray(container);
+ super.dispatchSaveInstanceState(jail);
+ container.put(R.id.cell_layout_jail_id, jail);
}
@Override
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
- super.dispatchRestoreInstanceState(mJailContent ? getJailedArray(container) : container);
+ super.dispatchRestoreInstanceState(getJailedArray(container));
}
+ /**
+ * Wrap the SparseArray in another Parcelable so that the item ids do not conflict with our
+ * our internal resource ids
+ */
private ParcelableSparseArray getJailedArray(SparseArray<Parcelable> container) {
final Parcelable parcelable = container.get(R.id.cell_layout_jail_id);
return parcelable instanceof ParcelableSparseArray ?
@@ -453,16 +434,12 @@
@Override
protected void onDraw(Canvas canvas) {
- if (!mIsDragTarget) {
- return;
- }
-
// When we're large, we are either drawn in a "hover" state (ie when dragging an item to
// a neighboring page) or with just a normal background (if backgroundAlpha > 0.0f)
// When we're small, we are either drawn normally or in the "accepts drops" state (during
// a drag). However, we also drag the mini hover background *over* one of those two
// backgrounds
- if (mBackgroundAlpha > 0.0f) {
+ if (mBackground.getAlpha() > 0) {
mBackground.draw(canvas);
}
@@ -838,16 +815,10 @@
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
- boolean isFullscreen = mShortcutsAndWidgets.getChildCount() > 0 &&
- ((LayoutParams) mShortcutsAndWidgets.getChildAt(0).getLayoutParams()).isFullscreen;
int left = getPaddingLeft();
- if (!isFullscreen) {
- left += (int) Math.ceil(getUnusedHorizontalSpace() / 2f);
- }
+ left += (int) Math.ceil(getUnusedHorizontalSpace() / 2f);
int right = r - l - getPaddingRight();
- if (!isFullscreen) {
- right -= (int) Math.ceil(getUnusedHorizontalSpace() / 2f);
- }
+ right -= (int) Math.ceil(getUnusedHorizontalSpace() / 2f);
int top = getPaddingTop();
int bottom = b - t - getPaddingBottom();
@@ -875,20 +846,13 @@
return getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - (mCountX * mCellWidth);
}
- public float getBackgroundAlpha() {
- return mBackgroundAlpha;
- }
-
- public void setBackgroundAlpha(float alpha) {
- if (mBackgroundAlpha != alpha) {
- mBackgroundAlpha = alpha;
- mBackground.setAlpha((int) (mBackgroundAlpha * 255));
- }
+ public Drawable getScrimBackground() {
+ return mBackground;
}
@Override
protected boolean verifyDrawable(Drawable who) {
- return super.verifyDrawable(who) || (mIsDragTarget && who == mBackground);
+ return super.verifyDrawable(who) || (who == mBackground);
}
public void setShortcutAndWidgetAlpha(float alpha) {
@@ -2654,11 +2618,6 @@
public boolean isLockedToGrid = true;
/**
- * Indicates that this item should use the full extents of its parent.
- */
- public boolean isFullscreen = false;
-
- /**
* Indicates whether this item can be reordered. Always true except in the case of the
* the AllApps button and QSB place holder.
*/
diff --git a/src/com/android/launcher3/CustomAppWidget.java b/src/com/android/launcher3/CustomAppWidget.java
deleted file mode 100644
index 1b4ed79..0000000
--- a/src/com/android/launcher3/CustomAppWidget.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.android.launcher3;
-
-public interface CustomAppWidget {
- public String getLabel();
- public int getPreviewImage();
- public int getIcon();
- public int getWidgetLayout();
-
- public int getSpanX();
- public int getSpanY();
- public int getMinSpanX();
- public int getMinSpanY();
- public int getResizeMode();
-}
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 4dcb64f..c12ea57 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -21,6 +21,7 @@
import android.util.AttributeSet;
import android.view.View;
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.folder.Folder;
@@ -46,27 +47,35 @@
@Override
public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
super.onDragStart(dragObject, options);
- setTextBasedOnDragSource(dragObject.dragSource);
+ setTextBasedOnDragSource(dragObject.dragInfo);
}
- /** @return true for items that should have a "Remove" action in accessibility. */
- public static boolean supportsAccessibleDrop(ItemInfo info) {
+ /**
+ * @return true for items that should have a "Remove" action in accessibility.
+ */
+ @Override
+ public boolean supportsAccessibilityDrop(ItemInfo info) {
return (info instanceof ShortcutInfo)
|| (info instanceof LauncherAppWidgetInfo)
|| (info instanceof FolderInfo);
}
@Override
- protected boolean supportsDrop(DragSource source, ItemInfo info) {
+ public int getAccessibilityAction() {
+ return LauncherAccessibilityDelegate.REMOVE;
+ }
+
+ @Override
+ protected boolean supportsDrop(ItemInfo info) {
return true;
}
/**
- * Set the drop target's text to either "Remove" or "Cancel" depending on the drag source.
+ * Set the drop target's text to either "Remove" or "Cancel" depending on the drag item.
*/
- public void setTextBasedOnDragSource(DragSource dragSource) {
+ private void setTextBasedOnDragSource(ItemInfo item) {
if (!TextUtils.isEmpty(mText)) {
- mText = getResources().getString(dragSource.supportsDeleteDropTarget()
+ mText = getResources().getString(item.id != ItemInfo.NO_ID
? R.string.remove_drop_target_label
: android.R.string.cancel);
requestLayout();
@@ -77,19 +86,21 @@
public void completeDrop(DragObject d) {
ItemInfo item = d.dragInfo;
if ((d.dragSource instanceof Workspace) || (d.dragSource instanceof Folder)) {
- removeWorkspaceOrFolderItem(mLauncher, item, null);
+ onAccessibilityDrop(null, item);
}
}
/**
* Removes the item from the workspace. If the view is not null, it also removes the view.
*/
- public static void removeWorkspaceOrFolderItem(Launcher launcher, ItemInfo item, View view) {
+ @Override
+ public void onAccessibilityDrop(View view, ItemInfo item) {
// Remove the item from launcher and the db, we can ignore the containerInfo in this call
// because we already remove the drag view from the folder (if the drag originated from
// a folder) in Folder.beginDrag()
- launcher.removeItem(view, item, true /* deleteFromDb */);
- launcher.getWorkspace().stripEmptyScreens();
- launcher.getDragLayer().announceForAccessibility(launcher.getString(R.string.item_removed));
+ mLauncher.removeItem(view, item, true /* deleteFromDb */);
+ mLauncher.getWorkspace().stripEmptyScreens();
+ mLauncher.getDragLayer()
+ .announceForAccessibility(getContext().getString(R.string.item_removed));
}
}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 69ee03e..8aaad13 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -277,8 +277,12 @@
DeviceProfile profile = new DeviceProfile(context, inv, mwSize, mwSize, mwSize.x, mwSize.y,
isLandscape);
- // Hide labels on the workspace.
- profile.adjustToHideWorkspaceLabels();
+ // If there isn't enough vertical cell padding with the labels displayed, hide the labels.
+ float workspaceCellPaddingY = profile.getCellSize().y - profile.iconSizePx
+ - iconDrawablePaddingPx - profile.iconTextSizePx;
+ if (workspaceCellPaddingY < profile.iconDrawablePaddingPx * 2) {
+ profile.adjustToHideWorkspaceLabels();
+ }
// We use these scales to measure and layout the widgets using their full invariant profile
// sizes and then draw them scaled and centered to fit in their multi-window mode cellspans.
@@ -566,7 +570,7 @@
}
}
- int getOverviewModeButtonBarHeight() {
+ public int getOverviewModeButtonBarHeight() {
int zoneHeight = (int) (overviewModeIconZoneRatio * availableHeightPx);
return Utilities.boundToRange(zoneHeight,
overviewModeMinIconZoneHeightPx,
diff --git a/src/com/android/launcher3/DragSource.java b/src/com/android/launcher3/DragSource.java
index dcd8f58..93f865c 100644
--- a/src/com/android/launcher3/DragSource.java
+++ b/src/com/android/launcher3/DragSource.java
@@ -27,24 +27,8 @@
public interface DragSource extends LogContainerProvider {
/**
- * @return whether items dragged from this source supports 'App Info'
- */
- boolean supportsAppInfoDropTarget();
-
- /**
- * @return whether items dragged from this source supports 'Delete' drop target (e.g. to remove
- * a shortcut.) If this returns false, the drop target will say "Cancel" instead of "Remove."
- */
- boolean supportsDeleteDropTarget();
-
- /*
- * @return the scale of the icons over the workspace icon size
- */
- float getIntrinsicIconScaleFactor();
-
- /**
* A callback made back to the source after an item from this source has been dropped on a
* DropTarget.
*/
- void onDropCompleted(View target, DragObject d, boolean isFlingToDelete, boolean success);
+ void onDropCompleted(View target, DragObject d, boolean success);
}
diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java
index 7d047d7..4d30479 100644
--- a/src/com/android/launcher3/DropTarget.java
+++ b/src/com/android/launcher3/DropTarget.java
@@ -19,6 +19,7 @@
import android.graphics.Rect;
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
+import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
/**
@@ -58,9 +59,6 @@
/** The object is part of an accessible drag operation */
public boolean accessibleDrag;
- /** Post drag animation runnable */
- public Runnable postAnimationRunnable = null;
-
/** Indicates that the drag operation was cancelled */
public boolean cancelled = false;
@@ -104,9 +102,16 @@
boolean isDropEnabled();
/**
- * Handle an object being dropped on the DropTarget
+ * Handle an object being dropped on the DropTarget.
+ *
+ * This will be called only if this target previously returned true for {@link #acceptDrop}. It
+ * is the responsibility of this target to exit out of the spring loaded mode (either
+ * immediately or after any pending animations).
+ *
+ * If the drop was cancelled for some reason, onDrop will never get called, the UI will
+ * automatically exit out of this mode.
*/
- void onDrop(DragObject dragObject);
+ void onDrop(DragObject dragObject, DragOptions options);
void onDragEnter(DragObject dragObject);
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index 29a1349..3eca5cd 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -16,6 +16,9 @@
package com.android.launcher3;
+import static com.android.launcher3.AlphaUpdateListener.updateVisibility;
+import static com.android.launcher3.Utilities.isAccessibilityEnabled;
+
import android.animation.TimeInterpolator;
import android.content.Context;
import android.util.AttributeSet;
@@ -23,29 +26,27 @@
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
-import android.view.accessibility.AccessibilityManager;
-import android.view.animation.AccelerateInterpolator;
import android.widget.LinearLayout;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions;
+import java.util.ArrayList;
+
/*
* The top bar containing various drop targets: Delete/App Info/Uninstall.
*/
public class DropTargetBar extends LinearLayout implements DragController.DragListener {
protected static final int DEFAULT_DRAG_FADE_DURATION = 175;
- protected static final TimeInterpolator DEFAULT_INTERPOLATOR = new AccelerateInterpolator();
+ protected static final TimeInterpolator DEFAULT_INTERPOLATOR = Interpolators.ACCEL;
private final Runnable mFadeAnimationEndRunnable = new Runnable() {
@Override
public void run() {
- AccessibilityManager am = (AccessibilityManager)
- getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
- boolean accessibilityEnabled = am.isEnabled();
- AlphaUpdateListener.updateVisibility(DropTargetBar.this, accessibilityEnabled);
+ updateVisibility(DropTargetBar.this, isAccessibilityEnabled(getContext()));
}
};
@@ -55,6 +56,7 @@
@ViewDebug.ExportedProperty(category = "launcher")
protected boolean mVisible = false;
+ private ButtonDropTarget[] mDropTargets;
private ViewPropertyAnimator mCurrentAnimation;
public DropTargetBar(Context context, AttributeSet attrs) {
@@ -75,7 +77,27 @@
public void setup(DragController dragController) {
dragController.addDragListener(this);
- setupButtonDropTarget(this, dragController);
+ ArrayList<ButtonDropTarget> outList = new ArrayList<>();
+ findDropTargets(this, outList);
+
+ mDropTargets = new ButtonDropTarget[outList.size()];
+ for (int i = 0; i < mDropTargets.length; i++) {
+ mDropTargets[i] = outList.get(i);
+ mDropTargets[i].setDropTargetBar(this);
+ dragController.addDragListener(mDropTargets[i]);
+ dragController.addDropTarget(mDropTargets[i]);
+ }
+ }
+
+ private static void findDropTargets(View view, ArrayList<ButtonDropTarget> outTargets) {
+ if (view instanceof ButtonDropTarget) {
+ outTargets.add((ButtonDropTarget) view);
+ } else if (view instanceof ViewGroup) {
+ ViewGroup vg = (ViewGroup) view;
+ for (int i = vg.getChildCount() - 1; i >= 0; i--) {
+ findDropTargets(vg.getChildAt(i), outTargets);
+ }
+ }
}
@Override
@@ -130,20 +152,6 @@
return result;
}
- private void setupButtonDropTarget(View view, DragController dragController) {
- if (view instanceof ButtonDropTarget) {
- ButtonDropTarget bdt = (ButtonDropTarget) view;
- bdt.setDropTargetBar(this);
- dragController.addDragListener(bdt);
- dragController.addDropTarget(bdt);
- } else if (view instanceof ViewGroup) {
- ViewGroup vg = (ViewGroup) view;
- for (int i = vg.getChildCount() - 1; i >= 0; i--) {
- setupButtonDropTarget(vg.getChildAt(i), dragController);
- }
- }
- }
-
private void animateToVisibility(boolean isVisible) {
if (mVisible != isVisible) {
mVisible = isVisible;
@@ -190,4 +198,8 @@
mDeferOnDragEnd = false;
}
}
+
+ public ButtonDropTarget[] getDropTargets() {
+ return mDropTargets;
+ }
}
diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java
index 596aa8f..403c8b8 100644
--- a/src/com/android/launcher3/ExtendedEditText.java
+++ b/src/com/android/launcher3/ExtendedEditText.java
@@ -16,12 +16,16 @@
package com.android.launcher3;
import android.content.Context;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.DragEvent;
import android.view.KeyEvent;
+import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
+import com.android.launcher3.util.UiThreadHelper;
+
/**
* The edit text that reports back when the back key has been pressed.
@@ -102,8 +106,7 @@
}
public void dispatchBackKey() {
- ((InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE))
- .hideSoftInputFromWindow(getWindowToken(), 0);
+ UiThreadHelper.hideKeyboardAsync(getContext(), getWindowToken());
if (mBackKeyListener != null) {
mBackKeyListener.onBackKey();
}
@@ -121,4 +124,17 @@
public boolean isSuggestionsEnabled() {
return !mForceDisableSuggestions && super.isSuggestionsEnabled();
}
+
+ public void reset() {
+ if (!TextUtils.isEmpty(getText())) {
+ setText("");
+ }
+ if (isFocused()) {
+ View nextFocus = focusSearch(View.FOCUS_DOWN);
+ if (nextFocus != null) {
+ nextFocus.requestFocus();
+ }
+ }
+ UiThreadHelper.hideKeyboardAsync(getContext(), getWindowToken());
+ }
}
diff --git a/src/com/android/launcher3/FirstFrameAnimatorHelper.java b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
index 3cbc989..cea7e43 100644
--- a/src/com/android/launcher3/FirstFrameAnimatorHelper.java
+++ b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
@@ -24,6 +24,7 @@
import android.view.ViewPropertyAnimator;
import android.view.ViewTreeObserver;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.util.TraceHelper;
/*
* This is a helper class that listens to updates from the corresponding animation.
@@ -71,15 +72,12 @@
if (sGlobalDrawListener != null) {
view.getViewTreeObserver().removeOnDrawListener(sGlobalDrawListener);
}
+
+ TraceHelper.beginSection("TICK");
sGlobalDrawListener = new ViewTreeObserver.OnDrawListener() {
- private long mTime = System.currentTimeMillis();
public void onDraw() {
sGlobalFrameCounter++;
- if (DEBUG) {
- long newTime = System.currentTimeMillis();
- Log.d(TAG, "TICK " + (newTime - mTime));
- mTime = newTime;
- }
+ TraceHelper.partitionSection("TICK", "Frame drawn");
}
};
view.getViewTreeObserver().addOnDrawListener(sGlobalDrawListener);
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index fe7acda..1f18ea1 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -288,25 +288,11 @@
case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN:
// Go to the previous page but keep the focus on the same hotseat icon.
workspace.snapToPage(pageIndex - 1);
- // If the page we are going to is fullscreen, have it take the focus from hotseat.
- CellLayout prevPage = (CellLayout) workspace.getPageAt(pageIndex - 1);
- boolean isPrevPageFullscreen = ((CellLayout.LayoutParams) prevPage
- .getShortcutsAndWidgets().getChildAt(0).getLayoutParams()).isFullscreen;
- if (isPrevPageFullscreen) {
- workspace.getPageAt(pageIndex - 1).requestFocus();
- }
break;
case FocusLogic.NEXT_PAGE_LEFT_COLUMN:
case FocusLogic.NEXT_PAGE_RIGHT_COLUMN:
// Go to the next page but keep the focus on the same hotseat icon.
workspace.snapToPage(pageIndex + 1);
- // If the page we are going to is fullscreen, have it take the focus from hotseat.
- CellLayout nextPage = (CellLayout) workspace.getPageAt(pageIndex + 1);
- boolean isNextPageFullscreen = ((CellLayout.LayoutParams) nextPage
- .getShortcutsAndWidgets().getChildAt(0).getLayoutParams()).isFullscreen;
- if (isNextPageFullscreen) {
- workspace.getPageAt(pageIndex + 1).requestFocus();
- }
break;
}
if (parent == iconParent && newIconIndex >= iconParent.getChildCount()) {
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index a6d80e3..09f9e82 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -16,16 +16,9 @@
package com.android.launcher3;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ArgbEvaluator;
-import android.animation.ValueAnimator;
import android.content.Context;
-import android.graphics.Color;
import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.support.v4.graphics.ColorUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -35,11 +28,9 @@
import android.widget.TextView;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.dynamicui.ExtractedColors;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-import com.android.launcher3.util.Themes;
public class Hotseat extends FrameLayout
implements UserEventDispatcher.LogContainerProvider {
@@ -51,12 +42,6 @@
@ViewDebug.ExportedProperty(category = "launcher")
private final boolean mHasVerticalHotseat;
- @ViewDebug.ExportedProperty(category = "launcher")
- private int mBackgroundColor;
- @ViewDebug.ExportedProperty(category = "launcher")
- private ColorDrawable mBackground;
- private ValueAnimator mBackgroundColorAnimator;
-
public Hotseat(Context context) {
this(context, null);
}
@@ -69,12 +54,6 @@
super(context, attrs, defStyle);
mLauncher = Launcher.getLauncher(context);
mHasVerticalHotseat = mLauncher.getDeviceProfile().isVerticalBarLayout();
- mBackgroundColor = ColorUtils.setAlphaComponent(
- Themes.getAttrColor(context, android.R.attr.colorPrimary), 0);
- mBackground = new ColorDrawable(mBackgroundColor);
- if (!FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
- setBackground(mBackground);
- }
}
public CellLayout getLayout() {
@@ -177,49 +156,4 @@
target.gridY = info.cellY;
targetParent.containerType = ContainerType.HOTSEAT;
}
-
- public void updateColor(ExtractedColors extractedColors, boolean animate) {
- if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
- // not hotseat visible
- return;
- }
- if (!mHasVerticalHotseat) {
- int color = extractedColors.getColor(ExtractedColors.HOTSEAT_INDEX);
- if (mBackgroundColorAnimator != null) {
- mBackgroundColorAnimator.cancel();
- }
- if (!animate) {
- setBackgroundColor(color);
- } else {
- mBackgroundColorAnimator = ValueAnimator.ofInt(mBackgroundColor, color);
- mBackgroundColorAnimator.setEvaluator(new ArgbEvaluator());
- mBackgroundColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mBackground.setColor((Integer) animation.getAnimatedValue());
- }
- });
- mBackgroundColorAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mBackgroundColorAnimator = null;
- }
- });
- mBackgroundColorAnimator.start();
- }
- mBackgroundColor = color;
- }
- }
-
- public void setBackgroundTransparent(boolean enable) {
- if (enable) {
- mBackground.setAlpha(0);
- } else {
- mBackground.setAlpha(255);
- }
- }
-
- public int getBackgroundDrawableColor() {
- return mBackgroundColor;
- }
}
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index f919dd0..289242f 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -26,6 +26,7 @@
import android.util.Log;
import android.widget.Toast;
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.util.Themes;
@@ -49,28 +50,29 @@
}
@Override
- public void completeDrop(DragObject d) {
- DropTargetResultCallback callback = d.dragSource instanceof DropTargetResultCallback
- ? (DropTargetResultCallback) d.dragSource : null;
- startDetailsActivityForInfo(d.dragInfo, mLauncher, callback);
+ protected ComponentName performDropAction(ItemInfo item) {
+ return performDropAction(mLauncher, item, null, null);
}
/**
* @return Whether the activity was started.
*/
public static boolean startDetailsActivityForInfo(
- ItemInfo info, Launcher launcher, DropTargetResultCallback callback) {
- return startDetailsActivityForInfo(info, launcher, callback, null, null);
+ ItemInfo info, Launcher launcher, Rect sourceBounds, Bundle opts) {
+ return performDropAction(launcher, info, sourceBounds, opts) != null;
}
- public static boolean startDetailsActivityForInfo(ItemInfo info, Launcher launcher,
- DropTargetResultCallback callback, Rect sourceBounds, Bundle opts) {
+ /**
+ * Performs the drop action and returns the target component for the dragObject or null if
+ * the action was not performed.
+ */
+ private static ComponentName performDropAction(Context context, ItemInfo info,
+ Rect sourceBounds, Bundle opts) {
if (info instanceof PromiseAppInfo) {
PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
- launcher.startActivity(promiseAppInfo.getMarketIntent());
- return true;
+ context.startActivity(promiseAppInfo.getMarketIntent());
+ return null;
}
- boolean result = false;
ComponentName componentName = null;
if (info instanceof AppInfo) {
componentName = ((AppInfo) info).componentName;
@@ -83,29 +85,27 @@
}
if (componentName != null) {
try {
- LauncherAppsCompat.getInstance(launcher)
+ LauncherAppsCompat.getInstance(context)
.showAppDetailsForProfile(componentName, info.user, sourceBounds, opts);
- result = true;
+ return componentName;
} catch (SecurityException | ActivityNotFoundException e) {
- Toast.makeText(launcher, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+ Toast.makeText(context, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Unable to launch settings", e);
}
}
-
- if (callback != null) {
- sendUninstallResult(launcher, result, componentName, info.user, callback);
- }
- return result;
+ return null;
}
@Override
- protected boolean supportsDrop(DragSource source, ItemInfo info) {
- return source.supportsAppInfoDropTarget() && supportsDrop(getContext(), info);
+ public int getAccessibilityAction() {
+ return LauncherAccessibilityDelegate.INFO;
}
- public static boolean supportsDrop(Context context, ItemInfo info) {
+ @Override
+ protected boolean supportsDrop(ItemInfo info) {
// Only show the App Info drop target if developer settings are enabled.
- boolean developmentSettingsEnabled = Settings.Global.getInt(context.getContentResolver(),
+ boolean developmentSettingsEnabled = Settings.Global.getInt(
+ getContext().getContentResolver(),
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1;
if (!developmentSettingsEnabled) {
return false;
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 0370777..df1eec6 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -27,7 +27,9 @@
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.os.Handler;
import android.os.Looper;
+import android.os.Message;
import android.os.Parcelable;
import android.os.Process;
import android.os.UserHandle;
@@ -61,6 +63,9 @@
public class InstallShortcutReceiver extends BroadcastReceiver {
+ private static final int MSG_ADD_TO_QUEUE = 1;
+ private static final int MSG_FLUSH_QUEUE = 2;
+
public static final int FLAG_ACTIVITY_PAUSED = 1;
public static final int FLAG_LOADER_RUNNING = 2;
public static final int FLAG_DRAG_AND_DROP = 4;
@@ -93,73 +98,98 @@
public static final int NEW_SHORTCUT_BOUNCE_DURATION = 450;
public static final int NEW_SHORTCUT_STAGGER_DELAY = 85;
- private static final Object sLock = new Object();
+ private static final Handler sHandler = new Handler(LauncherModel.getWorkerLooper()) {
- private static void addToInstallQueue(
- SharedPreferences sharedPrefs, PendingInstallShortcutInfo info) {
- synchronized(sLock) {
- String encoded = info.encodeToString();
- if (encoded != null) {
- Set<String> strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null);
- strings = (strings != null) ? new HashSet<>(strings) : new HashSet<String>(1);
- strings.add(encoded);
- sharedPrefs.edit().putStringSet(APPS_PENDING_INSTALL, strings).apply();
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_ADD_TO_QUEUE: {
+ Pair<Context, PendingInstallShortcutInfo> pair =
+ (Pair<Context, PendingInstallShortcutInfo>) msg.obj;
+ String encoded = pair.second.encodeToString();
+ SharedPreferences prefs = Utilities.getPrefs(pair.first);
+ Set<String> strings = prefs.getStringSet(APPS_PENDING_INSTALL, null);
+ strings = (strings != null) ? new HashSet<>(strings) : new HashSet<String>(1);
+ strings.add(encoded);
+ prefs.edit().putStringSet(APPS_PENDING_INSTALL, strings).apply();
+ return;
+ }
+ case MSG_FLUSH_QUEUE: {
+ Context context = (Context) msg.obj;
+ LauncherModel model = LauncherAppState.getInstance(context).getModel();
+ if (model.getCallback() == null) {
+ // Launcher not loaded
+ return;
+ }
+
+ ArrayList<Pair<ItemInfo, Object>> installQueue = new ArrayList<>();
+ SharedPreferences prefs = Utilities.getPrefs(context);
+ Set<String> strings = prefs.getStringSet(APPS_PENDING_INSTALL, null);
+ if (DBG) Log.d(TAG, "Getting and clearing APPS_PENDING_INSTALL: " + strings);
+ if (strings == null) {
+ return;
+ }
+
+ LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
+ for (String encoded : strings) {
+ PendingInstallShortcutInfo info = decode(encoded, context);
+ if (info == null) {
+ continue;
+ }
+
+ String pkg = getIntentPackage(info.launchIntent);
+ if (!TextUtils.isEmpty(pkg)
+ && !launcherApps.isPackageEnabledForProfile(pkg, info.user)) {
+ if (DBG) Log.d(TAG, "Ignoring shortcut for absent package: "
+ + info.launchIntent);
+ continue;
+ }
+
+ // Generate a shortcut info to add into the model
+ installQueue.add(info.getItemInfo());
+ }
+ prefs.edit().remove(APPS_PENDING_INSTALL).apply();
+ if (!installQueue.isEmpty()) {
+ model.addAndBindAddedWorkspaceItems(installQueue);
+ }
+ return;
+ }
}
}
- }
+ };
public static void removeFromInstallQueue(Context context, HashSet<String> packageNames,
UserHandle user) {
if (packageNames.isEmpty()) {
return;
}
+ Preconditions.assertWorkerThread();
+
SharedPreferences sp = Utilities.getPrefs(context);
- synchronized(sLock) {
- Set<String> strings = sp.getStringSet(APPS_PENDING_INSTALL, null);
- if (DBG) {
- Log.d(TAG, "APPS_PENDING_INSTALL: " + strings
- + ", removing packages: " + packageNames);
- }
- if (Utilities.isEmpty(strings)) {
- return;
- }
- Set<String> newStrings = new HashSet<>(strings);
- Iterator<String> newStringsIter = newStrings.iterator();
- while (newStringsIter.hasNext()) {
- String encoded = newStringsIter.next();
- try {
- Decoder decoder = new Decoder(encoded, context);
- if (packageNames.contains(getIntentPackage(decoder.launcherIntent)) &&
- user.equals(decoder.user)) {
- newStringsIter.remove();
- }
- } catch (JSONException | URISyntaxException e) {
- Log.d(TAG, "Exception reading shortcut to add: " + e);
+ Set<String> strings = sp.getStringSet(APPS_PENDING_INSTALL, null);
+ if (DBG) {
+ Log.d(TAG, "APPS_PENDING_INSTALL: " + strings
+ + ", removing packages: " + packageNames);
+ }
+ if (Utilities.isEmpty(strings)) {
+ return;
+ }
+ Set<String> newStrings = new HashSet<>(strings);
+ Iterator<String> newStringsIter = newStrings.iterator();
+ while (newStringsIter.hasNext()) {
+ String encoded = newStringsIter.next();
+ try {
+ Decoder decoder = new Decoder(encoded, context);
+ if (packageNames.contains(getIntentPackage(decoder.launcherIntent)) &&
+ user.equals(decoder.user)) {
newStringsIter.remove();
}
+ } catch (JSONException | URISyntaxException e) {
+ Log.d(TAG, "Exception reading shortcut to add: " + e);
+ newStringsIter.remove();
}
- sp.edit().putStringSet(APPS_PENDING_INSTALL, newStrings).apply();
}
- }
-
- private static ArrayList<PendingInstallShortcutInfo> getAndClearInstallQueue(Context context) {
- SharedPreferences sharedPrefs = Utilities.getPrefs(context);
- synchronized(sLock) {
- ArrayList<PendingInstallShortcutInfo> infos = new ArrayList<>();
- Set<String> strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null);
- if (DBG) Log.d(TAG, "Getting and clearing APPS_PENDING_INSTALL: " + strings);
- if (strings == null) {
- return infos;
- }
- for (String encoded : strings) {
- PendingInstallShortcutInfo info = decode(encoded, context);
- if (info != null) {
- infos.add(info);
- }
- }
- sharedPrefs.edit().putStringSet(APPS_PENDING_INSTALL, new HashSet<String>()).apply();
- return infos;
- }
+ sp.edit().putStringSet(APPS_PENDING_INSTALL, newStrings).apply();
}
public void onReceive(Context context, Intent data) {
@@ -256,7 +286,7 @@
private static void queuePendingShortcutInfo(PendingInstallShortcutInfo info, Context context) {
// Queue the item up for adding if launcher has not loaded properly yet
- addToInstallQueue(Utilities.getPrefs(context), info);
+ Message.obtain(sHandler, MSG_ADD_TO_QUEUE, Pair.create(context, info)).sendToTarget();
flushInstallQueue(context);
}
@@ -269,17 +299,10 @@
}
static void flushInstallQueue(Context context) {
- LauncherModel model = LauncherAppState.getInstance(context).getModel();
- boolean launcherNotLoaded = model.getCallback() == null;
- if (sInstallQueueDisabledFlags != 0 || launcherNotLoaded) {
+ if (sInstallQueueDisabledFlags != 0) {
return;
}
-
- ArrayList<PendingInstallShortcutInfo> items = getAndClearInstallQueue(context);
- if (!items.isEmpty()) {
- model.addAndBindAddedWorkspaceItems(
- new LazyShortcutsProvider(context.getApplicationContext(), items));
- }
+ Message.obtain(sHandler, MSG_FLUSH_QUEUE, context.getApplicationContext()).sendToTarget();
}
/**
@@ -601,42 +624,6 @@
return new PendingInstallShortcutInfo(info, original.mContext);
}
- private static class LazyShortcutsProvider extends Provider<List<Pair<ItemInfo, Object>>> {
-
- private final Context mContext;
- private final ArrayList<PendingInstallShortcutInfo> mPendingItems;
-
- public LazyShortcutsProvider(Context context, ArrayList<PendingInstallShortcutInfo> items) {
- mContext = context;
- mPendingItems = items;
- }
-
- /**
- * This must be called on the background thread as this requires multiple calls to
- * packageManager and icon cache.
- */
- @Override
- public ArrayList<Pair<ItemInfo, Object>> get() {
- Preconditions.assertNonUiThread();
- ArrayList<Pair<ItemInfo, Object>> installQueue = new ArrayList<>();
- LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mContext);
- for (PendingInstallShortcutInfo pendingInfo : mPendingItems) {
- // If the intent specifies a package, make sure the package exists
- String packageName = getIntentPackage(pendingInfo.launchIntent);
- if (!TextUtils.isEmpty(packageName) && !launcherApps.isPackageEnabledForProfile(
- packageName, pendingInfo.user)) {
- if (DBG) Log.d(TAG, "Ignoring shortcut for absent package: "
- + pendingInfo.launchIntent);
- continue;
- }
-
- // Generate a shortcut info to add into the model
- installQueue.add(pendingInfo.getItemInfo());
- }
- return installQueue;
- }
- }
-
private static ShortcutInfo createShortcutInfo(Intent data, LauncherAppState app) {
Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
diff --git a/src/com/android/launcher3/ItemInfoWithIcon.java b/src/com/android/launcher3/ItemInfoWithIcon.java
index 1e020e2..1c4e88b 100644
--- a/src/com/android/launcher3/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/ItemInfoWithIcon.java
@@ -33,11 +33,70 @@
*/
public boolean usingLowResIcon;
+ /**
+ * Indicates that the icon is disabled due to safe mode restrictions.
+ */
+ public static final int FLAG_DISABLED_SAFEMODE = 1 << 0;
+
+ /**
+ * Indicates that the icon is disabled as the app is not available.
+ */
+ public static final int FLAG_DISABLED_NOT_AVAILABLE = 1 << 1;
+
+ /**
+ * Indicates that the icon is disabled as the app is suspended
+ */
+ public static final int FLAG_DISABLED_SUSPENDED = 1 << 2;
+
+ /**
+ * Indicates that the icon is disabled as the user is in quiet mode.
+ */
+ public static final int FLAG_DISABLED_QUIET_USER = 1 << 3;
+
+ /**
+ * Indicates that the icon is disabled as the publisher has disabled the actual shortcut.
+ */
+ public static final int FLAG_DISABLED_BY_PUBLISHER = 1 << 4;
+
+ /**
+ * Indicates that the icon is disabled as the user partition is currently locked.
+ */
+ public static final int FLAG_DISABLED_LOCKED_USER = 1 << 5;
+
+ public static final int FLAG_DISABLED_MASK = FLAG_DISABLED_SAFEMODE |
+ FLAG_DISABLED_NOT_AVAILABLE | FLAG_DISABLED_SUSPENDED |
+ FLAG_DISABLED_QUIET_USER | FLAG_DISABLED_BY_PUBLISHER | FLAG_DISABLED_LOCKED_USER;
+
+
+ /**
+ * The item points to a system app.
+ */
+ public static final int FLAG_SYSTEM_YES = 1 << 6;
+
+ /**
+ * The item points to a non system app.
+ */
+ public static final int FLAG_SYSTEM_NO = 1 << 7;
+
+ public static final int FLAG_SYSTEM_MASK = FLAG_SYSTEM_YES | FLAG_SYSTEM_NO;
+
+ /**
+ * Status associated with the system state of the underlying item. This is calculated every
+ * time a new info is created and not persisted on the disk.
+ */
+ public int runtimeStatusFlags = 0;
+
protected ItemInfoWithIcon() { }
protected ItemInfoWithIcon(ItemInfoWithIcon info) {
super(info);
iconBitmap = info.iconBitmap;
usingLowResIcon = info.usingLowResIcon;
+ runtimeStatusFlags = info.runtimeStatusFlags;
+ }
+
+ @Override
+ public boolean isDisabled() {
+ return (runtimeStatusFlags & FLAG_DISABLED_MASK) != 0;
}
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 32af059..1542703 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -16,6 +16,22 @@
package com.android.launcher3;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_BY_PUBLISHER;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_QUIET_USER;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
+import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_APPS;
+import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_WIDGETS;
+
import android.Manifest;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -41,7 +57,6 @@
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
-import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Point;
@@ -50,11 +65,9 @@
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
-import android.os.Handler;
+import android.os.Parcelable;
import android.os.Process;
import android.os.StrictMode;
-import android.os.SystemClock;
-import android.os.Trace;
import android.os.UserHandle;
import android.support.annotation.Nullable;
import android.text.Selection;
@@ -62,6 +75,7 @@
import android.text.TextUtils;
import android.text.method.TextKeyListener;
import android.util.Log;
+import android.util.SparseArray;
import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
@@ -73,8 +87,6 @@
import android.view.View;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.OvershootInterpolator;
@@ -87,7 +99,6 @@
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.anim.AnimationLayerSet;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.LauncherAppsCompatVO;
@@ -98,7 +109,6 @@
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.dragndrop.PinItemDragListener;
-import com.android.launcher3.dynamicui.ExtractedColors;
import com.android.launcher3.dynamicui.WallpaperColorInfo;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
@@ -107,51 +117,50 @@
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.model.ModelWriter;
-import com.android.launcher3.model.PackageItemInfo;
-import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.pageindicators.PageIndicator;
+import com.android.launcher3.popup.BaseActionPopup;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.states.AllAppsState;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.ActivityResultInfo;
-import com.android.launcher3.util.RunnableWithId;
import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.ComponentKeyMapper;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.PendingRequestArgs;
+import com.android.launcher3.util.RunnableWithId;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.TestingUtils;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.util.TraceHelper;
+import com.android.launcher3.util.UiThreadHelper;
import com.android.launcher3.util.ViewOnDrawExecutor;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.WidgetAddFlowHandler;
import com.android.launcher3.widget.WidgetHostViewLoader;
-import com.android.launcher3.widget.WidgetsContainerView;
-
+import com.android.launcher3.widget.WidgetListRowEntry;
+import com.android.launcher3.widget.WidgetsFullSheet;
+import com.android.launcher3.widget.custom.CustomWidgetParser;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;
-import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_APPS;
-import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_WIDGETS;
-
/**
* Default launcher application.
*/
@@ -163,9 +172,7 @@
public static final String TAG = "Launcher";
static final boolean LOGD = false;
- static final boolean DEBUG_WIDGETS = false;
static final boolean DEBUG_STRICT_MODE = false;
- static final boolean DEBUG_RESUME_TIME = false;
private static final int REQUEST_CREATE_SHORTCUT = 1;
private static final int REQUEST_CREATE_APPWIDGET = 5;
@@ -187,11 +194,6 @@
*/
protected static final int REQUEST_LAST = 100;
- private static final int SOFT_INPUT_MODE_DEFAULT =
- WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
- private static final int SOFT_INPUT_MODE_ALL_APPS =
- WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
-
// The Intent extra that defines whether to ignore the launch animation
static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
"com.android.launcher3.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION";
@@ -204,19 +206,13 @@
private static final String RUNTIME_STATE_PENDING_REQUEST_ARGS = "launcher.request_args";
// Type: ActivityResultInfo
private static final String RUNTIME_STATE_PENDING_ACTIVITY_RESULT = "launcher.activity_result";
+ // Type: SparseArray<Parcelable>
+ private static final String RUNTIME_STATE_WIDGET_PANEL = "launcher.widget_panel";
- static final String APPS_VIEW_SHOWN = "launcher.apps_view_shown";
-
- /** The different states that Launcher can be in. */
- enum State { NONE, WORKSPACE, WORKSPACE_SPRING_LOADED, APPS, APPS_SPRING_LOADED,
- WIDGETS, WIDGETS_SPRING_LOADED }
-
- @Thunk State mState = State.WORKSPACE;
- @Thunk LauncherStateTransitionAnimation mStateTransitionAnimation;
+ private LauncherStateManager mStateManager;
private boolean mIsSafeModeEnabled;
- public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 500;
private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
// How long to wait before the new-shortcut animation automatically pans the workspace
@@ -224,8 +220,6 @@
private static final int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
@Thunk static final int NEW_APPS_ANIMATION_DELAY = 500;
- private final ExtractedColors mExtractedColors = new ExtractedColors();
-
@Thunk Workspace mWorkspace;
private View mLauncherView;
@Thunk DragLayer mDragLayer;
@@ -242,7 +236,6 @@
private ViewGroup mOverviewPanel;
private View mAllAppsButton;
- private View mWidgetsButton;
private DropTargetBar mDropTargetBar;
@@ -250,18 +243,10 @@
@Thunk AllAppsContainerView mAppsView;
AllAppsTransitionController mAllAppsController;
- // Main container view and the model for the widget tray screen.
- @Thunk WidgetsContainerView mWidgetsView;
-
// We need to store the orientation Launcher was created with, due to a bug (b/64916689)
// that results in widgets being inflated in the wrong orientation.
private int mOrientation;
- // We set the state in both onCreate and then onNewIntent in some cases, which causes both
- // scroll issues (because the workspace may not have been measured yet) and extra work.
- // Instead, just save the state that we need to restore Launcher to, and commit it in onResume.
- private State mOnResumeState = State.NONE;
-
private SpannableStringBuilder mDefaultKeySsb = null;
@Thunk boolean mWorkspaceLoading = true;
@@ -270,14 +255,14 @@
private boolean mOnResumeNeedsLoad;
private final ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<>();
- private final ArrayList<Runnable> mOnResumeCallbacks = new ArrayList<>();
+ private OnResumeCallback mOnResumeCallback;
+
private ViewOnDrawExecutor mPendingExecutor;
private LauncherModel mModel;
private ModelWriter mModelWriter;
private IconCache mIconCache;
private LauncherAccessibilityDelegate mAccessibilityDelegate;
- private final Handler mHandler = new Handler();
private boolean mHasFocus = false;
private ObjectAnimator mScrimAnimator;
@@ -285,45 +270,12 @@
private PopupDataProvider mPopupDataProvider;
- // Determines how long to wait after a rotation before restoring the screen orientation to
- // match the sensor state.
- private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500;
-
private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<>();
// We only want to get the SharedPreferences once since it does an FS stat each time we get
// it from the context.
private SharedPreferences mSharedPrefs;
- private boolean mMoveToDefaultScreenFromNewIntent;
-
- // This is set to the view that launched the activity that navigated the user away from
- // launcher. Since there is no callback for when the activity has finished launching, enable
- // the press state and keep this reference to reset the press state when we return to launcher.
- private BubbleTextView mWaitingForResume;
-
- protected static final HashMap<String, CustomAppWidget> sCustomAppWidgets =
- new HashMap<>();
-
- static {
- if (TestingUtils.ENABLE_CUSTOM_WIDGET_TEST) {
- TestingUtils.addDummyWidget(sCustomAppWidgets);
- }
- }
-
- // Exiting spring loaded mode happens with a delay. This runnable object triggers the
- // state transition. If another state transition happened during this delay,
- // simply unregister this runnable.
- private Runnable mExitSpringLoadedModeRunnable;
-
- @Thunk final Runnable mBuildLayersRunnable = new Runnable() {
- public void run() {
- if (mWorkspace != null) {
- mWorkspace.buildPageHardwareLayers();
- }
- }
- };
-
// Activity result which needs to be processed after workspace has loaded.
private ActivityResultInfo mPendingActivityResult;
/**
@@ -336,15 +288,7 @@
public ViewGroupFocusHelper mFocusHandler;
private boolean mRotationEnabled = false;
-
- @Thunk void setOrientation() {
- if (mRotationEnabled) {
- unlockScreenOrientation(true);
- } else {
- setRequestedOrientation(
- ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
- }
- }
+ private boolean mAppLaunchSuccess;
private RotationPrefChangeHandler mRotationPrefChangeHandler;
@@ -364,9 +308,7 @@
.penaltyDeath()
.build());
}
- if (LauncherAppState.PROFILE_STARTUP) {
- Trace.beginSection("Launcher-onCreate");
- }
+ TraceHelper.beginSection("Launcher-onCreate");
if (mLauncherCallbacks != null) {
mLauncherCallbacks.preOnCreate();
@@ -377,6 +319,7 @@
overrideTheme(wallpaperColorInfo.isDark(), wallpaperColorInfo.supportsDarkText());
super.onCreate(savedInstanceState);
+ TraceHelper.partitionSection("Launcher-onCreate", "super call");
LauncherAppState app = LauncherAppState.getInstance(this);
@@ -399,14 +342,11 @@
mDragController = new DragController(this);
mAllAppsController = new AllAppsTransitionController(this);
- mStateTransitionAnimation = new LauncherStateTransitionAnimation(this, mAllAppsController);
+ mStateManager = new LauncherStateManager(this, mAllAppsController);
mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
mAppWidgetHost = new LauncherAppWidgetHost(this);
- if (Utilities.ATLEAST_MARSHMALLOW) {
- mAppWidgetHost.addProviderChangeListener(this);
- }
mAppWidgetHost.startListening();
// If we are getting an onCreate, we can actually preempt onResume and unset mPaused here,
@@ -418,21 +358,14 @@
setupViews();
mDeviceProfile.layout(this, false /* notifyListeners */);
- loadExtractedColorsAndColorItems();
mPopupDataProvider = new PopupDataProvider(this);
((AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE))
.addAccessibilityStateChangeListener(this);
- lockAllApps();
-
restoreState(savedInstanceState);
- if (LauncherAppState.PROFILE_STARTUP) {
- Trace.endSection();
- }
-
// We only load the page synchronously if the user rotates (or triggers a
// configuration change) while launcher is in the foreground
int currentScreen = PagedView.INVALID_RESTORE_PAGE;
@@ -470,7 +403,8 @@
// On large interfaces, or on devices that a user has specifically enabled screen rotation,
// we want the screen to auto-rotate based on the current orientation
- setOrientation();
+ setRequestedOrientation(mRotationEnabled
+ ? SCREEN_ORIENTATION_UNSPECIFIED : SCREEN_ORIENTATION_NOSENSOR);
setContentView(mLauncherView);
@@ -487,6 +421,8 @@
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onCreate(savedInstanceState);
}
+
+ TraceHelper.endSection("Launcher-onCreate");
}
@Override
@@ -494,6 +430,10 @@
recreate();
}
+ public LauncherStateManager getStateManager() {
+ return mStateManager;
+ }
+
protected void overrideTheme(boolean isDark, boolean supportsDarkText) {
if (isDark) {
setTheme(R.style.LauncherThemeDark);
@@ -508,40 +448,14 @@
}
@Override
- public void onExtractedColorsChanged() {
- loadExtractedColorsAndColorItems();
- mExtractedColors.notifyChange();
- }
-
- public ExtractedColors getExtractedColors() {
- return mExtractedColors;
- }
-
- @Override
public void onAppWidgetHostReset() {
if (mAppWidgetHost != null) {
mAppWidgetHost.startListening();
}
}
- private void loadExtractedColorsAndColorItems() {
- // TODO: do this in pre-N as well, once the extraction part is complete.
- if (Utilities.ATLEAST_NOUGAT) {
- mExtractedColors.load(this);
- mHotseat.updateColor(mExtractedColors, !mPaused);
- mWorkspace.getPageIndicator().updateColor(mExtractedColors);
- }
- }
-
private LauncherCallbacks mLauncherCallbacks;
- public void onPostCreate(Bundle savedInstanceState) {
- super.onPostCreate(savedInstanceState);
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onPostCreate(savedInstanceState);
- }
- }
-
public void onInsetsChanged(Rect insets) {
mDeviceProfile.updateInsets(insets);
mDeviceProfile.layout(this, true /* notifyListeners */);
@@ -569,44 +483,6 @@
}
}
- /** To be overridden by subclasses to hint to Launcher that we have custom content */
- protected boolean hasCustomContentToLeft() {
- if (mLauncherCallbacks != null) {
- return mLauncherCallbacks.hasCustomContentToLeft();
- }
- return false;
- }
-
- /**
- * To be overridden by subclasses to populate the custom content container and call
- * {@link #addToCustomContentPage}. This will only be invoked if
- * {@link #hasCustomContentToLeft()} is {@code true}.
- */
- protected void populateCustomContentContainer() {
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.populateCustomContentContainer();
- }
- }
-
- /**
- * Invoked by subclasses to signal a change to the {@link #addToCustomContentPage} value to
- * ensure the custom content page is added or removed if necessary.
- */
- protected void invalidateHasCustomContentToLeft() {
- if (mWorkspace == null || mWorkspace.getScreenOrder().isEmpty()) {
- // Not bound yet, wait for bindScreens to be called.
- return;
- }
-
- if (!mWorkspace.hasCustomContent() && hasCustomContentToLeft()) {
- // Create the custom content page and call the subclass to populate it.
- mWorkspace.createCustomContentContainer();
- populateCustomContentContainer();
- } else if (mWorkspace.hasCustomContent() && !hasCustomContentToLeft()) {
- mWorkspace.removeCustomContentPage();
- }
- }
-
public boolean isDraggingEnabled() {
// We prevent dragging when we are loading the workspace as it is possible to pick up a view
// that is subsequently removed from the workspace in startBinding().
@@ -689,8 +565,7 @@
Runnable exitSpringLoaded = new Runnable() {
@Override
public void run() {
- exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED),
- EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
+ mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
}
};
@@ -710,11 +585,11 @@
}
return;
} else if (requestCode == REQUEST_PICK_WALLPAPER) {
- if (resultCode == RESULT_OK && mWorkspace.isInOverviewMode()) {
+ if (resultCode == RESULT_OK && isInState(OVERVIEW)) {
// User could have free-scrolled between pages before picking a wallpaper; make sure
// we move to the closest one now.
mWorkspace.setCurrentPage(mWorkspace.getPageNearestToCenterOfScreen());
- showWorkspace(false);
+ mStateManager.goToState(NORMAL, false);
}
return;
}
@@ -742,7 +617,7 @@
final Runnable onComplete = new Runnable() {
@Override
public void run() {
- exitSpringLoadedDragModeDelayed(false, 0, null);
+ getStateManager().goToState(NORMAL);
}
};
@@ -806,7 +681,7 @@
}
}
- /** @Override for MNC */
+ @Override
public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
PendingRequestArgs pendingArgs = mPendingRequestArgs;
@@ -870,8 +745,7 @@
@Override
public void run() {
completeAddAppWidget(appWidgetId, requestArgs, layout, null);
- exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED),
- EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
+ mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
}
};
} else if (resultCode == RESULT_CANCELED) {
@@ -901,6 +775,10 @@
mAppWidgetHost.stopListening();
}
+ if (!mAppLaunchSuccess) {
+ getUserEventDispatcher().logActionCommand(Action.Command.STOP,
+ mStateManager.getState().containerType);
+ }
NotificationListener.removeNotificationsChangedListener();
}
@@ -943,36 +821,12 @@
@Override
protected void onResume() {
- long startTime = 0;
- if (DEBUG_RESUME_TIME) {
- startTime = System.currentTimeMillis();
- Log.v(TAG, "Launcher.onResume()");
- }
-
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.preOnResume();
- }
-
+ TraceHelper.beginSection("ON_RESUME");
super.onResume();
+ TraceHelper.partitionSection("ON_RESUME", "superCall");
+
+ mAppLaunchSuccess = false;
getUserEventDispatcher().resetElapsedSessionMillis();
-
- // Restore the previous launcher state
- if (mOnResumeState == State.WORKSPACE) {
- showWorkspace(false);
- } else if (mOnResumeState == State.APPS) {
- boolean launchedFromApp = (mWaitingForResume != null);
- // Don't update the predicted apps if the user is returning to launcher in the apps
- // view after launching an app, as they may be depending on the UI to be static to
- // switch to another app, otherwise, if it was
- showAppsView(false /* animated */, !launchedFromApp /* updatePredictedApps */);
- } else if (mOnResumeState == State.WIDGETS) {
- showWidgetsView(false, false);
- }
- if (mOnResumeState != State.APPS) {
- tryAndUpdatePredictedApps();
- }
- mOnResumeState = State.NONE;
-
mPaused = false;
if (mOnResumeNeedsLoad) {
setWorkspaceLoading(true);
@@ -982,64 +836,13 @@
if (mBindOnResumeCallbacks.size() > 0) {
// We might have postponed some bind calls until onResume (see waitUntilResume) --
// execute them here
- long startTimeCallbacks = 0;
- if (DEBUG_RESUME_TIME) {
- startTimeCallbacks = System.currentTimeMillis();
- }
-
for (int i = 0; i < mBindOnResumeCallbacks.size(); i++) {
mBindOnResumeCallbacks.get(i).run();
}
mBindOnResumeCallbacks.clear();
- if (DEBUG_RESUME_TIME) {
- Log.d(TAG, "Time spent processing callbacks in onResume: " +
- (System.currentTimeMillis() - startTimeCallbacks));
- }
- }
- if (mOnResumeCallbacks.size() > 0) {
- for (int i = 0; i < mOnResumeCallbacks.size(); i++) {
- mOnResumeCallbacks.get(i).run();
- }
- mOnResumeCallbacks.clear();
}
- // Reset the pressed state of icons that were locked in the press state while activities
- // were launching
- if (mWaitingForResume != null) {
- // Resets the previous workspace icon press state
- mWaitingForResume.setStayPressed(false);
- }
-
- // It is possible that widgets can receive updates while launcher is not in the foreground.
- // Consequently, the widgets will be inflated in the orientation of the foreground activity
- // (framework issue). On resuming, we ensure that any widgets are inflated for the current
- // orientation.
- if (!isWorkspaceLoading()) {
- getWorkspace().reinflateWidgetsIfNecessary();
- }
-
- if (DEBUG_RESUME_TIME) {
- Log.d(TAG, "Time spent in onResume: " + (System.currentTimeMillis() - startTime));
- }
-
- // We want to suppress callbacks about CustomContent being shown if we have just received
- // onNewIntent while the user was present within launcher. In that case, we post a call
- // to move the user to the main screen (which will occur after onResume). We don't want to
- // have onHide (from onPause), then onShow, then onHide again, which we get if we don't
- // suppress here.
- if (mWorkspace.getCustomContentCallbacks() != null
- && !mMoveToDefaultScreenFromNewIntent) {
- // If we are resuming and the custom content is the current page, we call onShow().
- // It is also possible that onShow will instead be called slightly after first layout
- // if PagedView#setRestorePage was set to the custom content page in onCreate().
- if (mWorkspace.isOnOrMovingToCustomContent()) {
- mWorkspace.getCustomContentCallbacks().onShow(true);
- }
- }
- mMoveToDefaultScreenFromNewIntent = false;
- updateInteraction(Workspace.State.NORMAL, mWorkspace.getState());
- mWorkspace.onResume();
-
+ setOnResumeCallback(null);
// Process any items that were added while Launcher was away.
InstallShortcutReceiver.disableAndFlushInstallQueue(
InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED, this);
@@ -1054,6 +857,9 @@
mLauncherCallbacks.onResume();
}
+ clearTypedText();
+
+ TraceHelper.endSection("ON_RESUME");
}
@Override
@@ -1066,32 +872,11 @@
mDragController.cancelDrag();
mDragController.resetLastGestureUpTime();
- // We call onHide() aggressively. The custom content callbacks should be able to
- // debounce excess onHide calls.
- if (mWorkspace.getCustomContentCallbacks() != null) {
- mWorkspace.getCustomContentCallbacks().onHide();
- }
-
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onPause();
}
}
- public interface CustomContentCallbacks {
- // Custom content is completely shown. {@code fromResume} indicates whether this was caused
- // by a onResume or by scrolling otherwise.
- void onShow(boolean fromResume);
-
- // Custom content is completely hidden
- void onHide();
-
- // Custom content scroll progress changed. From 0 (not showing) to 1 (fully showing).
- void onScrollProgressChanged(float progress);
-
- // Indicates whether the user is allowed to scroll away from the custom content.
- boolean isScrollingAllowed();
- }
-
public interface LauncherOverlay {
/**
@@ -1141,16 +926,6 @@
}
}
- public void addToCustomContentPage(View customContent,
- CustomContentCallbacks callbacks, String description) {
- mWorkspace.addToCustomContentPage(customContent, callbacks, description);
- }
-
- // The custom content needs to offset its content to account for the QSB
- public int getTopOffsetForCustomContent() {
- return mWorkspace.getPaddingTop();
- }
-
@Override
public Object onRetainNonConfigurationInstance() {
// Flag the loader to stop early before switching
@@ -1210,18 +985,21 @@
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_MENU) {
// Ignore the menu key if we are currently dragging or are on the custom content screen
- if (!isOnCustomContent() && !mDragController.isDragging()) {
+ if (!mDragController.isDragging()) {
// Close any open floating view
AbstractFloatingView.closeAllOpenViews(this);
- // Stop resizing any widgets
- mWorkspace.exitWidgetResizeMode();
-
// Show the overview mode if we are on the workspace
- if (mState == State.WORKSPACE && !mWorkspace.isInOverviewMode() &&
- !mWorkspace.isSwitchingState()) {
- mOverviewPanel.requestFocus();
- showOverviewMode(true, true /* requestButtonFocus */);
+ if (isInState(NORMAL) && !mWorkspace.isSwitchingState()) {
+ mStateManager.goToState(OVERVIEW, true /* animate */, new Runnable() {
+ @Override
+ public void run() {
+ // Hitting the menu button when in touch mode does not trigger touch
+ // mode to be disabled, so if requested, force focus on one of the
+ // overview panel buttons.
+ mOverviewPanel.requestFocusFromTouch();
+ }
+ });
}
}
return true;
@@ -1240,6 +1018,10 @@
Selection.setSelection(mDefaultKeySsb, 0);
}
+ public boolean isInState(LauncherState state) {
+ return mStateManager.getState() == state;
+ }
+
/**
* Restores the previous state, if it exists.
*
@@ -1250,12 +1032,11 @@
return;
}
- int stateOrdinal = savedState.getInt(RUNTIME_STATE, State.WORKSPACE.ordinal());
- State[] stateValues = State.values();
- State state = (stateOrdinal >= 0 && stateOrdinal < stateValues.length)
- ? stateValues[stateOrdinal] : State.WORKSPACE;
- if (state == State.APPS || state == State.WIDGETS) {
- mOnResumeState = state;
+ int stateOrdinal = savedState.getInt(RUNTIME_STATE, NORMAL.ordinal);
+ LauncherState[] stateValues = LauncherState.values();
+ LauncherState state = stateValues[stateOrdinal];
+ if (!state.doNotRestore) {
+ mStateManager.goToState(state, false /* animated */);
}
PendingRequestArgs requestArgs = savedState.getParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS);
@@ -1264,6 +1045,12 @@
}
mPendingActivityResult = savedState.getParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT);
+
+ SparseArray<Parcelable> widgetsState =
+ savedState.getSparseParcelableArray(RUNTIME_STATE_WIDGET_PANEL);
+ if (widgetsState != null) {
+ WidgetsFullSheet.show(this, false).restoreHierarchyState(widgetsState);
+ }
}
/**
@@ -1304,9 +1091,8 @@
// Get the search/delete/uninstall bar
mDropTargetBar = mDragLayer.findViewById(R.id.drop_target_bar);
- // Setup Apps and Widgets
- mAppsView = (AllAppsContainerView) findViewById(R.id.apps_view);
- mWidgetsView = (WidgetsContainerView) findViewById(R.id.widgets_view);
+ // Setup Apps
+ mAppsView = findViewById(R.id.apps_view);
// Setup the drag controller (drop targets have to be added in reverse order in priority)
mDragController.setMoveTarget(mWorkspace);
@@ -1321,7 +1107,7 @@
}
private void setupOverviewPanel() {
- mOverviewPanel = (ViewGroup) findViewById(R.id.overview_panel);
+ mOverviewPanel = findViewById(R.id.overview_panel);
// Bind wallpaper button actions
View wallpaperButton = findViewById(R.id.wallpaper_button);
@@ -1333,13 +1119,12 @@
}.attachTo(wallpaperButton);
// Bind widget button actions
- mWidgetsButton = findViewById(R.id.widget_button);
new OverviewButtonClickListener(ControlType.WIDGETS_BUTTON) {
@Override
public void handleViewClick(View view) {
onClickAddWidgetButton(view);
}
- }.attachTo(mWidgetsButton);
+ }.attachTo(findViewById(R.id.widget_button));
// Bind settings actions
View settingsButton = findViewById(R.id.settings_button);
@@ -1366,14 +1151,6 @@
mAllAppsButton = allAppsButton;
}
- public View getStartViewForAllAppsRevealAnimation() {
- return FeatureFlags.NO_ALL_APPS_ICON ? mWorkspace.getPageIndicator() : mAllAppsButton;
- }
-
- public View getWidgetsButton() {
- return mWidgetsButton;
- }
-
/**
* Creates a view representing a shortcut.
*
@@ -1449,7 +1226,7 @@
// If appropriate, either create a folder or add to an existing folder
if (mWorkspace.createUserFolderIfNecessary(view, container, layout, cellXY, 0,
- true, null, null)) {
+ true, null)) {
return;
}
DragObject dragObject = new DragObject();
@@ -1502,17 +1279,13 @@
appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(appWidgetId);
}
- if (appWidgetInfo.isCustomWidget) {
- appWidgetId = LauncherAppWidgetInfo.CUSTOM_WIDGET_ID;
- }
-
LauncherAppWidgetInfo launcherInfo;
launcherInfo = new LauncherAppWidgetInfo(appWidgetId, appWidgetInfo.provider);
launcherInfo.spanX = itemInfo.spanX;
launcherInfo.spanY = itemInfo.spanY;
launcherInfo.minSpanX = itemInfo.minSpanX;
launcherInfo.minSpanY = itemInfo.minSpanY;
- launcherInfo.user = appWidgetInfo.getUser();
+ launcherInfo.user = appWidgetInfo.getProfile();
getModelWriter().addItemToDatabase(launcherInfo,
itemInfo.container, itemInfo.screenId, itemInfo.cellX, itemInfo.cellY);
@@ -1538,15 +1311,10 @@
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (Intent.ACTION_SCREEN_OFF.equals(action)) {
- mDragLayer.clearResizeFrame();
-
// Reset AllApps to its initial state only if we are not in the middle of
// processing a multi-step drop
- if (mAppsView != null && mWidgetsView != null && mPendingRequestArgs == null) {
- if (!showWorkspace(false)) {
- // If we are already on the workspace, then manually reset all apps
- mAppsView.reset();
- }
+ if (mAppsView != null && mPendingRequestArgs == null) {
+ mStateManager.goToState(NORMAL);
}
mShouldFadeInScrim = true;
} else if (Intent.ACTION_USER_PRESENT.equals(action)) {
@@ -1564,9 +1332,9 @@
mWorkspace.updateIconBadges(updatedBadges);
mAppsView.updateIconBadges(updatedBadges);
- PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(Launcher.this);
- if (popup != null) {
- popup.updateNotificationHeader(updatedBadges);
+ BaseActionPopup popup = BaseActionPopup.getOpen(Launcher.this);
+ if (popup instanceof PopupContainerWithArrow) {
+ ((PopupContainerWithArrow) popup).updateNotificationHeader(updatedBadges);
}
}
};
@@ -1594,44 +1362,6 @@
}
}
- public void onWindowVisibilityChanged(int visibility) {
- // The following code used to be in onResume, but it turns out onResume is called when
- // you're in All Apps and click home to go to the workspace. onWindowVisibilityChanged
- // is a more appropriate event to handle
- if (visibility == View.VISIBLE) {
- if (!mWorkspaceLoading) {
- final ViewTreeObserver observer = mWorkspace.getViewTreeObserver();
- // We want to let Launcher draw itself at least once before we force it to build
- // layers on all the workspace pages, so that transitioning to Launcher from other
- // apps is nice and speedy.
- observer.addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
- private boolean mStarted = false;
- public void onDraw() {
- if (mStarted) return;
- mStarted = true;
- // We delay the layer building a bit in order to give
- // other message processing a time to run. In particular
- // this avoids a delay in hiding the IME if it was
- // currently shown, because doing that may involve
- // some communication back with the app.
- mWorkspace.postDelayed(mBuildLayersRunnable, 500);
- final ViewTreeObserver.OnDrawListener listener = this;
- mWorkspace.post(new Runnable() {
- public void run() {
- if (mWorkspace != null &&
- mWorkspace.getViewTreeObserver() != null) {
- mWorkspace.getViewTreeObserver().
- removeOnDrawListener(listener);
- }
- }
- });
- }
- });
- }
- clearTypedText();
- }
- }
-
public DragLayer getDragLayer() {
return mDragLayer;
}
@@ -1640,10 +1370,6 @@
return mAppsView;
}
- public WidgetsContainerView getWidgetsView() {
- return mWidgetsView;
- }
-
public Workspace getWorkspace() {
return mWorkspace;
}
@@ -1680,10 +1406,7 @@
@Override
protected void onNewIntent(Intent intent) {
- long startTime = 0;
- if (DEBUG_RESUME_TIME) {
- startTime = System.currentTimeMillis();
- }
+ TraceHelper.beginSection("NEW_INTENT");
super.onNewIntent(intent);
boolean alreadyOnHome = mHasFocus && ((intent.getFlags() &
@@ -1691,8 +1414,8 @@
!= Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
// Check this condition before handling isActionMain, as this will get reset.
- boolean shouldMoveToDefaultScreen = alreadyOnHome &&
- mState == State.WORKSPACE && AbstractFloatingView.getTopOpenView(this) == null;
+ boolean shouldMoveToDefaultScreen = alreadyOnHome && isInState(NORMAL)
+ && AbstractFloatingView.getTopOpenView(this) == null;
boolean isActionMain = Intent.ACTION_MAIN.equals(intent.getAction());
if (isActionMain) {
@@ -1704,39 +1427,22 @@
// Note: There should be at most one log per method call. This is enforced implicitly
// by using if-else statements.
UserEventDispatcher ued = getUserEventDispatcher();
-
- // TODO: Log this case.
- mWorkspace.exitWidgetResizeMode();
-
AbstractFloatingView topOpenView = AbstractFloatingView.getTopOpenView(this);
- if (topOpenView instanceof PopupContainerWithArrow) {
- ued.logActionCommand(Action.Command.HOME_INTENT,
- topOpenView.getExtendedTouchView(), ContainerType.DEEPSHORTCUTS);
- } else if (topOpenView instanceof Folder) {
- ued.logActionCommand(Action.Command.HOME_INTENT,
- ((Folder) topOpenView).getFolderIcon(), ContainerType.FOLDER);
+ if (topOpenView != null) {
+ topOpenView.logActionCommand(Action.Command.HOME_INTENT);
} else if (alreadyOnHome) {
- ued.logActionCommand(Action.Command.HOME_INTENT,
- mWorkspace.getState().containerType, mWorkspace.getCurrentPage());
+ Target target = newContainerTarget(mStateManager.getState().containerType);
+ target.pageIndex = mWorkspace.getCurrentPage();
+ ued.logActionCommand(Action.Command.HOME_INTENT, target);
}
// In all these cases, only animate if we're already on home
AbstractFloatingView.closeAllOpenViews(this, alreadyOnHome);
- exitSpringLoadedDragMode();
-
- // If we are already on home, then just animate back to the workspace,
- // otherwise, just wait until onResume to set the state back to Workspace
- if (alreadyOnHome) {
- showWorkspace(true);
- } else {
- mOnResumeState = State.WORKSPACE;
- }
+ mStateManager.goToState(NORMAL, alreadyOnHome /* animated */);
final View v = getWindow().peekDecorView();
if (v != null && v.getWindowToken() != null) {
- InputMethodManager imm = (InputMethodManager) getSystemService(
- INPUT_METHOD_SERVICE);
- imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
+ UiThreadHelper.hideKeyboardAsync(this, v.getWindowToken());
}
// Reset the apps view
@@ -1744,11 +1450,6 @@
mAppsView.reset();
}
- // Reset the widgets view
- if (!alreadyOnHome && mWidgetsView != null) {
- mWidgetsView.scrollToTop();
- }
-
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onHomeIntent();
}
@@ -1763,29 +1464,20 @@
// as slow logic in the callbacks eat into the time the scroller expects for the snapToPage
// animation.
if (isActionMain) {
- boolean callbackAllowsMoveToDefaultScreen =
- mLauncherCallbacks == null || mLauncherCallbacks
- .shouldMoveToDefaultScreenOnHomeIntent();
- if (shouldMoveToDefaultScreen && !mWorkspace.isTouchActive()
- && callbackAllowsMoveToDefaultScreen) {
+ if (shouldMoveToDefaultScreen && !mWorkspace.isTouchActive()) {
- // We use this flag to suppress noisy callbacks above custom content state
- // from onResume.
- mMoveToDefaultScreenFromNewIntent = true;
mWorkspace.post(new Runnable() {
@Override
public void run() {
if (mWorkspace != null) {
- mWorkspace.moveToDefaultScreen(true);
+ mWorkspace.moveToDefaultScreen();
}
}
});
}
}
- if (DEBUG_RESUME_TIME) {
- Log.d(TAG, "Time spent in onNewIntent: " + (System.currentTimeMillis() - startTime));
- }
+ TraceHelper.endSection("NEW_INTENT");
}
@Override
@@ -1799,13 +1491,22 @@
@Override
protected void onSaveInstanceState(Bundle outState) {
if (mWorkspace.getChildCount() > 0) {
- outState.putInt(RUNTIME_STATE_CURRENT_SCREEN,
- mWorkspace.getCurrentPageOffsetFromCustomContent());
+ outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getNextPage());
}
- super.onSaveInstanceState(outState);
+ outState.putInt(RUNTIME_STATE, mStateManager.getState().ordinal);
- outState.putInt(RUNTIME_STATE, mState.ordinal());
+
+ AbstractFloatingView widgets = AbstractFloatingView
+ .getOpenView(this, AbstractFloatingView.TYPE_WIDGETS_FULL_SHEET);
+ if (widgets != null) {
+ SparseArray<Parcelable> widgetsState = new SparseArray<>();
+ widgets.saveHierarchyState(widgetsState);
+ outState.putSparseParcelableArray(RUNTIME_STATE_WIDGET_PANEL, widgetsState);
+ } else {
+ outState.remove(RUNTIME_STATE_WIDGET_PANEL);
+ }
+
// We close any open folders and shortcut containers since they will not be re-opened,
// and we need to make sure this state is reflected.
AbstractFloatingView.closeAllOpenViews(this, false);
@@ -1817,6 +1518,8 @@
outState.putParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT, mPendingActivityResult);
}
+ super.onSaveInstanceState(outState);
+
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onSaveInstanceState(outState);
}
@@ -1827,7 +1530,6 @@
super.onDestroy();
unregisterReceiver(mReceiver);
- mWorkspace.removeCallbacks(mBuildLayersRunnable);
mWorkspace.removeFolderListeners();
// Stop callbacks from LauncherModel
@@ -1913,7 +1615,7 @@
}
// We need to show the workspace after starting the search
- showWorkspace(true);
+ mStateManager.goToState(NORMAL);
}
/**
@@ -1956,10 +1658,6 @@
}
}
- public boolean isOnCustomContent() {
- return mWorkspace.isOnOrMovingToCustomContent();
- }
-
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
@@ -1985,25 +1683,11 @@
}
private void setWorkspaceLoading(boolean value) {
- boolean isLocked = isWorkspaceLocked();
mWorkspaceLoading = value;
- if (isLocked != isWorkspaceLocked()) {
- onWorkspaceLockedChanged();
- }
}
public void setWaitingForResult(PendingRequestArgs args) {
- boolean isLocked = isWorkspaceLocked();
mPendingRequestArgs = args;
- if (isLocked != isWorkspaceLocked()) {
- onWorkspaceLockedChanged();
- }
- }
-
- protected void onWorkspaceLockedChanged() {
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onWorkspaceLockedChanged();
- }
}
void addAppWidgetFromDropImpl(int appWidgetId, ItemInfo info, AppWidgetHostView boundWidget,
@@ -2023,8 +1707,7 @@
@Override
public void run() {
// Exit spring loaded mode if necessary after adding the widget
- exitSpringLoadedDragModeDelayed(true, EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT,
- null);
+ mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
}
};
completeAddAppWidget(appWidgetId, info, boundWidget, addFlowHandler.getProviderInfo(this));
@@ -2032,12 +1715,6 @@
}
}
- protected void moveToCustomContentScreen(boolean animate) {
- // Close any folders that may be open.
- AbstractFloatingView.closeAllOpenViews(this, animate);
- mWorkspace.moveToCustomContentScreen(animate);
- }
-
public void addPendingItem(PendingAddItemInfo info, long container, long screenId,
int[] cell, int spanX, int spanY) {
info.container = container;
@@ -2078,7 +1755,7 @@
*/
private void addAppWidgetFromDrop(PendingAddWidgetInfo info) {
AppWidgetHostView hostView = info.boundWidget;
- int appWidgetId;
+ final int appWidgetId;
WidgetAddFlowHandler addFlowHandler = info.getHandler();
if (hostView != null) {
// In the case where we've prebound the widget, we remove it from the DragLayer
@@ -2095,7 +1772,13 @@
} else {
// In this case, we either need to start an activity to get permission to bind
// the widget, or we need to start an activity to configure the widget, or both.
- appWidgetId = getAppWidgetHost().allocateAppWidgetId();
+ if (FeatureFlags.ENABLE_CUSTOM_WIDGETS &&
+ info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET) {
+ appWidgetId = CustomWidgetParser.getWidgetIdForCustomProvider(
+ this, info.componentName);
+ } else {
+ appWidgetId = getAppWidgetHost().allocateAppWidgetId();
+ }
Bundle options = info.bindOptions;
boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
@@ -2204,31 +1887,11 @@
UserEventDispatcher ued = getUserEventDispatcher();
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
if (topView != null) {
- if (topView.getActiveTextView() != null) {
- topView.getActiveTextView().dispatchBackKey();
- } else {
- if (topView instanceof PopupContainerWithArrow) {
- ued.logActionCommand(Action.Command.BACK,
- topView.getExtendedTouchView(), ContainerType.DEEPSHORTCUTS);
- } else if (topView instanceof Folder) {
- ued.logActionCommand(Action.Command.BACK,
- ((Folder) topView).getFolderIcon(), ContainerType.FOLDER);
- }
- topView.close(true);
- }
- } else if (isAppsViewVisible()) {
- ued.logActionCommand(Action.Command.BACK, ContainerType.ALLAPPS);
- showWorkspace(true);
- } else if (isWidgetsViewVisible()) {
- ued.logActionCommand(Action.Command.BACK, ContainerType.WIDGETS);
- showOverviewMode(true);
- } else if (mWorkspace.isInOverviewMode()) {
- ued.logActionCommand(Action.Command.BACK, ContainerType.OVERVIEW);
- showWorkspace(true);
+ topView.onBackPressed();
+ } else if (!isInState(NORMAL)) {
+ ued.logActionCommand(Action.Command.BACK, mStateManager.getState().containerType);
+ mStateManager.goToState(NORMAL);
} else {
- // TODO: Log this case.
- mWorkspace.exitWidgetResizeMode();
-
// Back button is a no-op here, but give at least some feedback for the button press
mWorkspace.showOutlinesTemporarily();
}
@@ -2251,23 +1914,23 @@
}
if (v instanceof Workspace) {
- if (mWorkspace.isInOverviewMode()) {
+ if (isInState(OVERVIEW)) {
getUserEventDispatcher().logActionOnContainer(LauncherLogProto.Action.Type.TOUCH,
LauncherLogProto.Action.Direction.NONE,
LauncherLogProto.ContainerType.OVERVIEW, mWorkspace.getCurrentPage());
- showWorkspace(true);
+ mStateManager.goToState(NORMAL);
}
return;
}
if (v instanceof CellLayout) {
- if (mWorkspace.isInOverviewMode()) {
+ if (isInState(OVERVIEW)) {
int page = mWorkspace.indexOfChild(v);
getUserEventDispatcher().logActionOnContainer(LauncherLogProto.Action.Type.TOUCH,
LauncherLogProto.Action.Direction.NONE,
LauncherLogProto.ContainerType.OVERVIEW, page);
mWorkspace.snapToPageFromOverView(page);
- showWorkspace(true);
+ mStateManager.goToState(NORMAL);
}
return;
}
@@ -2339,12 +2002,12 @@
*/
protected void onClickAllAppsButton(View v) {
if (LOGD) Log.d(TAG, "onClickAllAppsButton");
- if (!isAppsViewVisible()) {
+ if (!isInState(ALL_APPS)) {
getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
ControlType.ALL_APPS_BUTTON);
- showAppsView(true /* animated */, true /* updatePredictedApps */);
+ mStateManager.goToState(ALL_APPS);
} else {
- showWorkspace(true);
+ mStateManager.goToState(NORMAL);
}
}
@@ -2377,11 +2040,7 @@
private void startMarketIntentForPackage(View v, String packageName) {
ItemInfo item = (ItemInfo) v.getTag();
Intent intent = PackageManagerHelper.getMarketIntent(packageName);
- boolean success = startActivitySafely(v, intent, item);
- if (success && v instanceof BubbleTextView) {
- mWaitingForResume = (BubbleTextView) v;
- mWaitingForResume.setStayPressed(true);
- }
+ startActivitySafely(v, intent, item);
}
/**
@@ -2399,10 +2058,10 @@
// Open shortcut
final ShortcutInfo shortcut = (ShortcutInfo) tag;
- if (shortcut.isDisabled != 0) {
- if ((shortcut.isDisabled &
- ~ShortcutInfo.FLAG_DISABLED_SUSPENDED &
- ~ShortcutInfo.FLAG_DISABLED_QUIET_USER) == 0) {
+ if (shortcut.isDisabled()) {
+ if ((shortcut.runtimeStatusFlags &
+ ~FLAG_DISABLED_SUSPENDED &
+ ~FLAG_DISABLED_QUIET_USER) == 0) {
// If the app is only disabled because of the above flags, launch activity anyway.
// Framework will tell the user why the app is suspended.
} else {
@@ -2413,10 +2072,10 @@
}
// Otherwise just use a generic error message.
int error = R.string.activity_not_available;
- if ((shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_SAFEMODE) != 0) {
+ if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_SAFEMODE) != 0) {
error = R.string.safemode_shortcut_error;
- } else if ((shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_BY_PUBLISHER) != 0 ||
- (shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_LOCKED_USER) != 0) {
+ } else if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_BY_PUBLISHER) != 0 ||
+ (shortcut.runtimeStatusFlags & FLAG_DISABLED_LOCKED_USER) != 0) {
error = R.string.shortcut_not_available;
}
Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
@@ -2451,13 +2110,7 @@
if (intent == null) {
throw new IllegalArgumentException("Input must have a valid intent");
}
- boolean success = startActivitySafely(v, intent, item);
- getUserEventDispatcher().logAppLaunch(v, intent); // TODO for discovered apps b/35802115
-
- if (success && v instanceof BubbleTextView) {
- mWaitingForResume = (BubbleTextView) v;
- mWaitingForResume.setStayPressed(true);
- }
+ startActivitySafely(v, intent, item);
}
/**
@@ -2487,7 +2140,7 @@
if (mIsSafeModeEnabled) {
Toast.makeText(this, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
} else {
- showWidgetsView(true /* animated */, true /* resetPageToZero */);
+ WidgetsFullSheet.show(this, true /* animated */);
}
}
@@ -2542,52 +2195,6 @@
mDragLayer.onAccessibilityStateChanged(enabled);
}
- public void onDragStarted() {
- if (isOnCustomContent()) {
- // Custom content screen doesn't participate in drag and drop. If on custom
- // content screen, move to default.
- moveWorkspaceToDefaultScreen();
- }
- }
-
- /**
- * Called when the user stops interacting with the launcher.
- * This implies that the user is now on the homescreen and is not doing housekeeping.
- */
- protected void onInteractionEnd() {
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onInteractionEnd();
- }
- }
-
- /**
- * Called when the user starts interacting with the launcher.
- * The possible interactions are:
- * - open all apps
- * - reorder an app shortcut, or a widget
- * - open the overview mode.
- * This is a good time to stop doing things that only make sense
- * when the user is on the homescreen and not doing housekeeping.
- */
- protected void onInteractionBegin() {
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onInteractionBegin();
- }
- }
-
- /** Updates the interaction state. */
- public void updateInteraction(Workspace.State fromState, Workspace.State toState) {
- // Only update the interacting state if we are transitioning to/from a view with an
- // overlay
- boolean fromStateWithOverlay = fromState != Workspace.State.NORMAL;
- boolean toStateWithOverlay = toState != Workspace.State.NORMAL;
- if (toStateWithOverlay) {
- onInteractionBegin();
- } else if (fromStateWithOverlay) {
- onInteractionEnd();
- }
- }
-
private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
try {
StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
@@ -2664,9 +2271,10 @@
}
public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
+ mAppLaunchSuccess = false;
if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
- return false;
+ return mAppLaunchSuccess;
}
// Only launch using the new animation if the shortcut has not opted out (this is a
// private contract between launcher and may be ignored in the future).
@@ -2696,12 +2304,23 @@
LauncherAppsCompat.getInstance(this).startActivityForProfile(
intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
}
- return true;
+
+ if (v instanceof BubbleTextView) {
+ // This is set to the view that launched the activity that navigated the user away
+ // from launcher. Since there is no callback for when the activity has finished
+ // launching, enable the press state and keep this reference to reset the press
+ // state when we return to launcher.
+ BubbleTextView btv = (BubbleTextView) v;
+ btv.setStayPressed(true);
+ setOnResumeCallback(btv);
+ }
+ mAppLaunchSuccess = true;
+ getUserEventDispatcher().logAppLaunch(v, intent); // TODO for discovered apps b/35802115
} catch (ActivityNotFoundException|SecurityException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
}
- return false;
+ return mAppLaunchSuccess;
}
@Override
@@ -2714,18 +2333,18 @@
public boolean onLongClick(View v) {
if (!isDraggingEnabled()) return false;
if (isWorkspaceLocked()) return false;
- if (mState != State.WORKSPACE) return false;
+ if (!isInState(NORMAL) && !isInState(OVERVIEW)) return false;
boolean ignoreLongPressToOverview =
mDeviceProfile.shouldIgnoreLongPressToOverview(mLastDispatchTouchEventX);
if (v instanceof Workspace) {
- if (!mWorkspace.isInOverviewMode()) {
+ if (!isInState(OVERVIEW)) {
if (!mWorkspace.isTouchActive() && !ignoreLongPressToOverview) {
getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
Action.Direction.NONE, ContainerType.WORKSPACE,
mWorkspace.getCurrentPage());
- showOverviewMode(true);
+ getStateManager().goToState(OVERVIEW);
mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
return true;
@@ -2751,7 +2370,7 @@
if (!mDragController.isDragging()) {
if (itemUnderLongClick == null) {
// User long pressed on empty space
- if (mWorkspace.isInOverviewMode()) {
+ if (mWorkspace.isPageRearrangeEnabled()) {
mWorkspace.startReordering(v);
getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
Action.Direction.NONE, ContainerType.OVERVIEW);
@@ -2762,7 +2381,7 @@
getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
Action.Direction.NONE, ContainerType.WORKSPACE,
mWorkspace.getCurrentPage());
- showOverviewMode(true);
+ getStateManager().goToState(OVERVIEW);
}
mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
@@ -2801,21 +2420,6 @@
}
}
- /**
- * For overridden classes.
- */
- public boolean isAllAppsVisible() {
- return isAppsViewVisible();
- }
-
- public boolean isAppsViewVisible() {
- return (mState == State.APPS) || (mOnResumeState == State.APPS);
- }
-
- public boolean isWidgetsViewVisible() {
- return (mState == State.WIDGETS) || (mOnResumeState == State.WIDGETS);
- }
-
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
@@ -2832,257 +2436,16 @@
}
}
- public boolean showWorkspace(boolean animated) {
- return showWorkspace(animated, null);
- }
-
- public boolean showWorkspace(boolean animated, Runnable onCompleteRunnable) {
- boolean changed = mState != State.WORKSPACE ||
- mWorkspace.getState() != Workspace.State.NORMAL;
- if (changed || mAllAppsController.isTransitioning()) {
- mWorkspace.setVisibility(View.VISIBLE);
- mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
- Workspace.State.NORMAL, animated, onCompleteRunnable);
-
- // Set focus to the AppsCustomize button
- if (mAllAppsButton != null) {
- mAllAppsButton.requestFocus();
- }
- }
-
- // Change the state *after* we've called all the transition code
- setState(State.WORKSPACE);
-
- if (changed) {
- // Send an accessibility event to announce the context change
- getWindow().getDecorView()
- .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
- }
- return changed;
- }
-
- /**
- * Shows the overview button.
- */
- public void showOverviewMode(boolean animated) {
- showOverviewMode(animated, false);
- }
-
- /**
- * Shows the overview button, and if {@param requestButtonFocus} is set, will force the focus
- * onto one of the overview panel buttons.
- */
- void showOverviewMode(boolean animated, boolean requestButtonFocus) {
- Runnable postAnimRunnable = null;
- if (requestButtonFocus) {
- postAnimRunnable = new Runnable() {
- @Override
- public void run() {
- // Hitting the menu button when in touch mode does not trigger touch mode to
- // be disabled, so if requested, force focus on one of the overview panel
- // buttons.
- mOverviewPanel.requestFocusFromTouch();
- }
- };
- }
- mWorkspace.setVisibility(View.VISIBLE);
- mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
- Workspace.State.OVERVIEW, animated, postAnimRunnable);
- setState(State.WORKSPACE);
-
- // If animated from long press, then don't allow any of the controller in the drag
- // layer to intercept any remaining touch.
- mWorkspace.requestDisallowInterceptTouchEvent(animated);
- }
-
- private void setState(State state) {
- this.mState = state;
- updateSoftInputMode();
- }
-
- private void updateSoftInputMode() {
- if (FeatureFlags.LAUNCHER3_UPDATE_SOFT_INPUT_MODE) {
- final int mode;
- if (isAppsViewVisible()) {
- mode = SOFT_INPUT_MODE_ALL_APPS;
- } else {
- mode = SOFT_INPUT_MODE_DEFAULT;
- }
- getWindow().setSoftInputMode(mode);
- }
- }
-
- /**
- * Shows the apps view.
- */
- public void showAppsView(boolean animated, boolean updatePredictedApps) {
- markAppsViewShown();
- if (updatePredictedApps) {
- tryAndUpdatePredictedApps();
- }
- showAppsOrWidgets(State.APPS, animated);
- }
-
- /**
- * Shows the widgets view.
- */
- void showWidgetsView(boolean animated, boolean resetPageToZero) {
- if (LOGD) Log.d(TAG, "showWidgetsView:" + animated + " resetPageToZero:" + resetPageToZero);
- if (resetPageToZero) {
- mWidgetsView.scrollToTop();
- }
- showAppsOrWidgets(State.WIDGETS, animated);
-
- mWidgetsView.post(new Runnable() {
- @Override
- public void run() {
- mWidgetsView.requestFocus();
- }
- });
- }
-
- /**
- * Sets up the transition to show the apps/widgets view.
- *
- * @return whether the current from and to state allowed this operation
- */
- // TODO: calling method should use the return value so that when {@code false} is returned
- // the workspace transition doesn't fall into invalid state.
- private boolean showAppsOrWidgets(State toState, boolean animated) {
- if (!(mState == State.WORKSPACE ||
- mState == State.APPS_SPRING_LOADED ||
- mState == State.WIDGETS_SPRING_LOADED ||
- (mState == State.APPS && mAllAppsController.isTransitioning()))) {
- return false;
- }
- if (toState != State.APPS && toState != State.WIDGETS) {
- return false;
- }
-
- // This is a safe and supported transition to bypass spring_loaded mode.
- if (mExitSpringLoadedModeRunnable != null) {
- mHandler.removeCallbacks(mExitSpringLoadedModeRunnable);
- mExitSpringLoadedModeRunnable = null;
- }
-
- if (toState == State.APPS) {
- mStateTransitionAnimation.startAnimationToAllApps(animated);
- } else {
- mStateTransitionAnimation.startAnimationToWidgets(animated);
- }
-
- // Change the state *after* we've called all the transition code
- setState(toState);
- AbstractFloatingView.closeAllOpenViews(this);
-
- // Send an accessibility event to announce the context change
- getWindow().getDecorView()
- .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
- return true;
- }
-
- /**
- * Updates the workspace and interaction state on state change, and return the animation to this
- * new state.
- */
- public Animator startWorkspaceStateChangeAnimation(Workspace.State toState,
- boolean animated, AnimationLayerSet layerViews) {
- Workspace.State fromState = mWorkspace.getState();
- Animator anim = mWorkspace.setStateWithAnimation(toState, animated, layerViews);
- updateInteraction(fromState, toState);
- return anim;
- }
-
- public void enterSpringLoadedDragMode() {
- if (LOGD) Log.d(TAG, String.format("enterSpringLoadedDragMode [mState=%s", mState.name()));
- if (isStateSpringLoaded()) {
- return;
- }
-
- mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
- Workspace.State.SPRING_LOADED, true /* animated */,
- null /* onCompleteRunnable */);
- setState(State.WORKSPACE_SPRING_LOADED);
- }
-
- public void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, int delay,
- final Runnable onCompleteRunnable) {
- if (!isStateSpringLoaded()) return;
-
- if (mExitSpringLoadedModeRunnable != null) {
- mHandler.removeCallbacks(mExitSpringLoadedModeRunnable);
- }
- mExitSpringLoadedModeRunnable = new Runnable() {
- @Override
- public void run() {
- if (successfulDrop) {
- // TODO(hyunyoungs): verify if this hack is still needed, if not, delete.
- //
- // Before we show workspace, hide all apps again because
- // exitSpringLoadedDragMode made it visible. This is a bit hacky; we should
- // clean up our state transition functions
- mWidgetsView.setVisibility(View.GONE);
- showWorkspace(true, onCompleteRunnable);
- } else {
- exitSpringLoadedDragMode();
- }
- mExitSpringLoadedModeRunnable = null;
- }
- };
- mHandler.postDelayed(mExitSpringLoadedModeRunnable, delay);
- }
-
- boolean isStateSpringLoaded() {
- return mState == State.WORKSPACE_SPRING_LOADED || mState == State.APPS_SPRING_LOADED
- || mState == State.WIDGETS_SPRING_LOADED;
- }
-
- public void exitSpringLoadedDragMode() {
- if (mState == State.APPS_SPRING_LOADED) {
- showAppsView(true /* animated */, false /* updatePredictedApps */);
- } else if (mState == State.WIDGETS_SPRING_LOADED) {
- showWidgetsView(true, false);
- } else if (mState == State.WORKSPACE_SPRING_LOADED) {
- showWorkspace(true);
- }
- }
-
- /**
- * Updates the set of predicted apps if it hasn't been updated since the last time Launcher was
- * resumed.
- */
- public void tryAndUpdatePredictedApps() {
- if (mLauncherCallbacks != null) {
- List<ComponentKeyMapper<AppInfo>> apps = mLauncherCallbacks.getPredictedApps();
- if (apps != null) {
- mAppsView.setPredictedApps(apps);
- }
- }
- }
-
- void lockAllApps() {
- // TODO
- }
-
- void unlockAllApps() {
- // TODO
- }
-
@Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
final boolean result = super.dispatchPopulateAccessibilityEvent(event);
final List<CharSequence> text = event.getText();
text.clear();
// Populate event with a fake title based on the current state.
- if (mState == State.APPS) {
- text.add(getString(R.string.all_apps_button_label));
- } else if (mState == State.WIDGETS) {
- text.add(getString(R.string.widget_button_text));
- } else if (mWorkspace != null) {
- text.add(mWorkspace.getCurrentPageDescription());
- } else {
- text.add(getString(R.string.all_apps_home_button_label));
- }
+ // TODO: When can workspace be null?
+ text.add(mWorkspace == null
+ ? getString(R.string.all_apps_home_button_label)
+ : mStateManager.getState().getDescription(this));
return result;
}
@@ -3113,8 +2476,11 @@
}
}
- public void addOnResumeCallback(Runnable run) {
- mOnResumeCallbacks.add(run);
+ public void setOnResumeCallback(OnResumeCallback callback) {
+ if (mOnResumeCallback != null) {
+ mOnResumeCallback.onLauncherResume();
+ }
+ mOnResumeCallback = callback;
}
/**
@@ -3173,11 +2539,12 @@
* Implementation of the method from LauncherModel.Callbacks.
*/
public void startBinding() {
- if (LauncherAppState.PROFILE_STARTUP) {
- Trace.beginSection("Starting page bind");
- }
-
- AbstractFloatingView.closeAllOpenViews(this);
+ TraceHelper.beginSection("startBinding");
+ // Floating panels (except the full widget sheet) are associated with individual icons. If
+ // we are starting a fresh bind, close all such panels as all the icons are about
+ // to go away.
+ AbstractFloatingView.closeOpenViews(this, true,
+ AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_WIDGETS_FULL_SHEET);
setWorkspaceLoading(true);
@@ -3188,9 +2555,7 @@
if (mHotseat != null) {
mHotseat.resetLayout();
}
- if (LauncherAppState.PROFILE_STARTUP) {
- Trace.endSection();
- }
+ TraceHelper.endSection("startBinding");
}
@Override
@@ -3207,13 +2572,6 @@
}
bindAddScreens(orderedScreenIds);
- // Create the custom content page (this call updates mDefaultScreen which calls
- // setCurrentPage() so ensure that all pages are added before calling this).
- if (hasCustomContentToLeft()) {
- mWorkspace.createCustomContentContainer();
- populateCustomContentContainer();
- }
-
// After we have added all the screens, if the wallpaper was locked to the default state,
// then notify to indicate that it can be released and a proper wallpaper offset can be
// computed before the next layout
@@ -3309,7 +2667,8 @@
(FolderInfo) item);
break;
}
- case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: {
+ case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+ case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: {
view = inflateAppWidget((LauncherAppWidgetInfo) item);
if (view == null) {
continue;
@@ -3400,10 +2759,7 @@
return view;
}
- final long start = DEBUG_WIDGETS ? SystemClock.uptimeMillis() : 0;
- if (DEBUG_WIDGETS) {
- Log.d(TAG, "bindAppWidget: " + item);
- }
+ TraceHelper.beginSection("BIND_WIDGET");
final LauncherAppWidgetProviderInfo appWidgetInfo;
@@ -3421,11 +2777,9 @@
if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) &&
(item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) {
if (appWidgetInfo == null) {
- if (DEBUG_WIDGETS) {
- Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
- + " belongs to component " + item.providerName
- + ", as the provider is null");
- }
+ Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
+ + " belongs to component " + item.providerName
+ + ", as the provider is null");
getModelWriter().deleteItemFromDatabase(item);
return null;
}
@@ -3486,11 +2840,6 @@
final AppWidgetHostView view;
if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
- if (DEBUG_WIDGETS) {
- Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component "
- + appWidgetInfo.provider);
- }
-
// Verify that we own the widget
if (appWidgetInfo == null) {
FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId);
@@ -3506,10 +2855,7 @@
}
prepareAppWidget(view, item);
- if (DEBUG_WIDGETS) {
- Log.d(TAG, "bound widget id="+item.appWidgetId+" in "
- + (SystemClock.uptimeMillis()-start) + "ms");
- }
+ TraceHelper.endSection("BIND_WIDGET", "id=" + item.appWidgetId);
return view;
}
@@ -3531,7 +2877,10 @@
info.pendingItemInfo = null;
}
- mWorkspace.reinflateWidgetsIfNecessary();
+ if (((PendingAppWidgetHostView) view).isReinflateIfNeeded()) {
+ view.reinflate();
+ }
+
getModelWriter().updateItemInDatabase(info);
return info;
}
@@ -3595,9 +2944,7 @@
if (waitUntilResume(r)) {
return;
}
- if (LauncherAppState.PROFILE_STARTUP) {
- Trace.beginSection("Page bind completed");
- }
+ TraceHelper.beginSection("finishBindingItems");
mWorkspace.restoreInstanceStateForRemainingPages();
setWorkspaceLoading(false);
@@ -3612,13 +2959,7 @@
InstallShortcutReceiver.FLAG_LOADER_RUNNING, this);
NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
-
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.finishBindingItems(false);
- }
- if (LauncherAppState.PROFILE_STARTUP) {
- Trace.endSection();
- }
+ TraceHelper.endSection("finishBindingItems");
}
private boolean canRunNewAppsAnimation() {
@@ -3638,13 +2979,6 @@
return mDeviceProfile.isVerticalBarLayout();
}
- public int getSearchBarHeight() {
- if (mLauncherCallbacks != null) {
- return mLauncherCallbacks.getSearchBarHeight();
- }
- return LauncherCallbacks.SEARCH_BAR_HEIGHT_NORMAL;
- }
-
/**
* Add the icons for all apps.
*
@@ -3662,7 +2996,7 @@
if (mAppsView != null) {
Executor pendingExecutor = getPendingExecutor();
- if (pendingExecutor != null && mState != State.APPS) {
+ if (pendingExecutor != null && !isInState(ALL_APPS)) {
// Wait until the fade in animation has finished before setting all apps list.
pendingExecutor.execute(r);
return;
@@ -3817,12 +3151,12 @@
// Update AllApps
if (mAppsView != null) {
mAppsView.removeApps(appInfos);
- tryAndUpdatePredictedApps();
}
}
@Override
- public void bindAllWidgets(final MultiHashMap<PackageItemInfo, WidgetItem> allWidgets) {
+ public void bindAllWidgets(final ArrayList<WidgetListRowEntry> allWidgets) {
+ mPopupDataProvider.setAllWidgets(allWidgets);
Runnable r = new RunnableWithId(RUNNABLE_ID_BIND_WIDGETS) {
@Override
public void run() {
@@ -3833,32 +3167,12 @@
return;
}
- if (mWidgetsView != null && allWidgets != null) {
- Executor pendingExecutor = getPendingExecutor();
- if (pendingExecutor != null && mState != State.WIDGETS) {
- pendingExecutor.execute(r);
- return;
- }
- mWidgetsView.setWidgets(allWidgets);
- }
-
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
if (topView != null) {
topView.onWidgetsBound();
}
}
- public List<WidgetItem> getWidgetsForPackageUser(PackageUserKey packageUserKey) {
- return mWidgetsView.getWidgetsForPackageUser(packageUserKey);
- }
-
- @Override
- public void notifyWidgetProvidersChanged() {
- if (mWorkspace.getState().shouldUpdateWidget) {
- refreshAndBindWidgetsForPackageUser(null);
- }
- }
-
/**
* @param packageUser if null, refreshes all widgets and shortcuts, otherwise only
* refreshes the widgets and shortcuts associated with the given package/user
@@ -3867,40 +3181,14 @@
mModel.refreshAndBindWidgetsAndShortcuts(packageUser);
}
- public void lockScreenOrientation() {
- if (mRotationEnabled) {
- setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
- }
- }
-
- public void unlockScreenOrientation(boolean immediate) {
- if (mRotationEnabled) {
- if (immediate) {
- setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
- } else {
- mHandler.postDelayed(new Runnable() {
- public void run() {
- setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
- }
- }, RESTORE_SCREEN_ORIENTATION_DELAY);
- }
- }
- }
-
- private void markAppsViewShown() {
- if (mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false)) {
- return;
- }
- mSharedPrefs.edit().putBoolean(APPS_VIEW_SHOWN, true).apply();
+ public boolean isRotationEnabled () {
+ return mRotationEnabled;
}
private boolean shouldShowDiscoveryBounce() {
- UserManagerCompat um = UserManagerCompat.getInstance(this);
- return mState == State.WORKSPACE && !mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false) && !um.isDemoUser();
- }
-
- protected void moveWorkspaceToDefaultScreen() {
- mWorkspace.moveToDefaultScreen(false);
+ return isInState(NORMAL)
+ && !mSharedPrefs.getBoolean(AllAppsState.APPS_VIEW_SHOWN, false)
+ && !UserManagerCompat.getInstance(this).isDemoUser();
}
/**
@@ -3912,7 +3200,7 @@
if (args.length > 0 && TextUtils.equals(args[0], "--all")) {
writer.println(prefix + "Workspace Items");
- for (int i = mWorkspace.numCustomPages(); i < mWorkspace.getPageCount(); i++) {
+ for (int i = 0; i < mWorkspace.getPageCount(); i++) {
writer.println(prefix + " Homescreen " + i);
ViewGroup layout = ((CellLayout) mWorkspace.getPageAt(i)).getShortcutsAndWidgets();
@@ -3958,7 +3246,7 @@
List<KeyboardShortcutGroup> data, Menu menu, int deviceId) {
ArrayList<KeyboardShortcutInfo> shortcutInfos = new ArrayList<>();
- if (mState == State.WORKSPACE) {
+ if (isInState(NORMAL)) {
shortcutInfos.add(new KeyboardShortcutInfo(getString(R.string.all_apps_button_label),
KeyEvent.KEYCODE_A, KeyEvent.META_CTRL_ON));
}
@@ -3984,8 +3272,8 @@
if (event.hasModifiers(KeyEvent.META_CTRL_ON)) {
switch (keyCode) {
case KeyEvent.KEYCODE_A:
- if (mState == State.WORKSPACE) {
- showAppsView(true, true);
+ if (isInState(NORMAL)) {
+ getStateManager().goToState(ALL_APPS);
return true;
}
break;
@@ -3996,7 +3284,7 @@
&& mAccessibilityDelegate.performAction(focusedView,
(ItemInfo) focusedView.getTag(),
LauncherAccessibilityDelegate.DEEP_SHORTCUTS)) {
- PopupContainerWithArrow.getOpen(this).requestFocus();
+ BaseActionPopup.getOpen(this).requestFocus();
return true;
}
break;
@@ -4011,14 +3299,6 @@
return super.onKeyShortcut(keyCode, event);
}
- public static CustomAppWidget getCustomAppWidget(String name) {
- return sCustomAppWidgets.get(name);
- }
-
- public static HashMap<String, CustomAppWidget> getCustomAppWidgets() {
- return sCustomAppWidgets;
- }
-
public static Launcher getLauncher(Context context) {
if (context instanceof Launcher) {
return (Launcher) context;
@@ -4037,4 +3317,12 @@
}
}
}
+
+ /**
+ * Callback for listening for onResume
+ */
+ public interface OnResumeCallback {
+
+ void onLauncherResume();
+ }
}
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index cfb9b57..dfe51af 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -30,6 +30,15 @@
import java.util.WeakHashMap;
public class LauncherAnimUtils {
+ /**
+ * Durations for various state animations. These are not defined in resources to allow
+ * easier access from static classes and enums
+ */
+ public static final int ALL_APPS_TRANSITION_MS = 320;
+ public static final int OVERVIEW_TRANSITION_MS = 250;
+ public static final int SPRING_LOADED_TRANSITION_MS = 150;
+ public static final int SPRING_LOADED_EXIT_DELAY = 500;
+
static WeakHashMap<Animator, Object> sAnimators = new WeakHashMap<Animator, Object>();
static Animator.AnimatorListener sEndAnimListener = new Animator.AnimatorListener() {
public void onAnimationStart(Animator animation) {
@@ -141,4 +150,18 @@
drawable.setAlpha(alpha);
}
};
+
+ public static final Property<View, Float> SCALE_PROPERTY =
+ new Property<View, Float>(Float.class, "scale") {
+ @Override
+ public Float get(View view) {
+ return view.getScaleX();
+ }
+
+ @Override
+ public void set(View view, Float scale) {
+ view.setScaleX(scale);
+ view.setScaleY(scale);
+ }
+ };
}
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 1ffe41b..dfb30fd 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -28,7 +28,6 @@
import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.dynamicui.ExtractionUtils;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.util.ConfigMonitor;
import com.android.launcher3.util.Preconditions;
@@ -42,8 +41,6 @@
public class LauncherAppState {
- public static final boolean PROFILE_STARTUP = FeatureFlags.IS_DOGFOOD_BUILD;
-
// We do not need any synchronization for this variable as its only written on UI thread.
private static LauncherAppState INSTANCE;
@@ -111,18 +108,11 @@
filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
- // For extracting colors from the wallpaper
- if (Utilities.ATLEAST_NOUGAT) {
- // TODO: add a broadcast entry to the manifest for pre-N.
- filter.addAction(Intent.ACTION_WALLPAPER_CHANGED);
- }
mContext.registerReceiver(mModel, filter);
UserManagerCompat.getInstance(mContext).enableAndResetCache();
new ConfigMonitor(mContext).register();
- ExtractionUtils.startColorExtractionServiceIfNecessary(mContext);
-
if (!mContext.getResources().getBoolean(R.bool.notification_badging_enabled)) {
mNotificationBadgingObserver = null;
} else {
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index 5573c5c..70440fa 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -116,13 +116,12 @@
public AppWidgetHostView createView(Context context, int appWidgetId,
LauncherAppWidgetProviderInfo appWidget) {
- if (appWidget.isCustomWidget) {
+ if (appWidget.isCustomWidget()) {
LauncherAppWidgetHostView lahv = new LauncherAppWidgetHostView(context);
LayoutInflater inflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(appWidget.initialLayout, lahv);
lahv.setAppWidget(0, appWidget);
- lahv.updateLastInflationOrientation();
return lahv;
} else {
try {
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index 7fe1308..6f953e5 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -19,6 +19,7 @@
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Handler;
@@ -58,10 +59,14 @@
private final CheckLongPressHelper mLongPressHelper;
private final StylusEventHelper mStylusEventHelper;
- private final Context mContext;
+ private final Launcher mLauncher;
+
+ private static final int DONT_REINFLATE = 0;
+ private static final int REINFLATE_ON_RESUME = 1;
+ private static final int REINFLATE_ON_CONFIG_CHANGE = 2;
@ViewDebug.ExportedProperty(category = "launcher")
- private int mPreviousOrientation;
+ private int mReinflateStatus;
private float mSlop;
@@ -85,11 +90,11 @@
public LauncherAppWidgetHostView(Context context) {
super(context);
- mContext = context;
+ mLauncher = Launcher.getLauncher(context);
mLongPressHelper = new CheckLongPressHelper(this, this);
mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
mInflater = LayoutInflater.from(context);
- setAccessibilityDelegate(Launcher.getLauncher(context).getAccessibilityDelegate());
+ setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
setBackgroundResource(R.drawable.widget_internal_focus_bg);
if (Utilities.ATLEAST_OREO) {
@@ -112,18 +117,28 @@
return mInflater.inflate(R.layout.appwidget_error, this, false);
}
- public void updateLastInflationOrientation() {
- mPreviousOrientation = mContext.getResources().getConfiguration().orientation;
- }
-
@Override
public void updateAppWidget(RemoteViews remoteViews) {
- // Store the orientation in which the widget was inflated
- updateLastInflationOrientation();
super.updateAppWidget(remoteViews);
// The provider info or the views might have changed.
checkIfAutoAdvance();
+
+ // It is possible that widgets can receive updates while launcher is not in the foreground.
+ // Consequently, the widgets will be inflated for the orientation of the foreground activity
+ // (framework issue). On resuming, we ensure that any widgets are inflated for the current
+ // orientation.
+ if (mReinflateStatus == DONT_REINFLATE && !isSameOrientation()) {
+ mReinflateStatus = REINFLATE_ON_RESUME;
+ if (!mLauncher.waitUntilResume(new ReInflateRunnable())) {
+ mReinflateStatus = REINFLATE_ON_CONFIG_CHANGE;
+ }
+ }
+ }
+
+ private boolean isSameOrientation() {
+ return mLauncher.getResources().getConfiguration().orientation ==
+ mLauncher.getOrientation();
}
private boolean checkScrollableRecursively(ViewGroup viewGroup) {
@@ -142,14 +157,6 @@
return false;
}
- public boolean isReinflateRequired(int orientation) {
- // Re-inflate is required if the orientation has changed since last inflated.
- if (mPreviousOrientation != orientation) {
- return true;
- }
- return false;
- }
-
public boolean onInterceptTouchEvent(MotionEvent ev) {
// Just in case the previous long press hasn't been cleared, we make sure to start fresh
// on touch down.
@@ -473,4 +480,45 @@
public PointF getTranslationForCentering() {
return mTranslationForCentering;
}
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ if (mReinflateStatus == REINFLATE_ON_CONFIG_CHANGE) {
+ // We are finally in the same orientation
+ reinflateIfNecessary();
+ }
+ }
+
+ private void reinflateIfNecessary() {
+ if (!isSameOrientation()) {
+ // We cannot reinflate yet, wait until next config change
+ mReinflateStatus = REINFLATE_ON_CONFIG_CHANGE;
+ return;
+ }
+
+ mReinflateStatus = DONT_REINFLATE;
+ if (isAttachedToWindow()) {
+ LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag();
+ reinflate();
+ }
+ }
+
+ public void reinflate() {
+ LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag();
+ // Remove and rebind the current widget (which was inflated in the wrong
+ // orientation), but don't delete it from the database
+ mLauncher.removeItem(this, info, false /* deleteFromDb */);
+ mLauncher.bindAppWidget(info);
+ }
+
+ private class ReInflateRunnable implements Runnable {
+ @Override
+ public void run() {
+ if (mReinflateStatus == REINFLATE_ON_RESUME) {
+ reinflateIfNecessary();
+ }
+ }
+ }
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index 6f23e56..051846c 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -71,7 +71,7 @@
/**
* Indicates that this is a locally defined widget and hence has no system allocated id.
*/
- static final int CUSTOM_WIDGET_ID = -100;
+ public static final int CUSTOM_WIDGET_ID = -100;
/**
* Identifier for this widget when talking with
@@ -104,15 +104,15 @@
private boolean mHasNotifiedInitialWidgetSizeChanged;
public LauncherAppWidgetInfo(int appWidgetId, ComponentName providerName) {
- if (appWidgetId == CUSTOM_WIDGET_ID) {
+ this.appWidgetId = appWidgetId;
+ this.providerName = providerName;
+
+ if (isCustomWidget()) {
itemType = LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
} else {
itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
}
- this.appWidgetId = appWidgetId;
- this.providerName = providerName;
-
// Since the widget isn't instantiated yet, we don't know these values. Set them to -1
// to indicate that they should be calculated based on the layout and minWidth/minHeight
spanX = -1;
@@ -128,7 +128,7 @@
}
public boolean isCustomWidget() {
- return appWidgetId == CUSTOM_WIDGET_ID;
+ return appWidgetId <= CUSTOM_WIDGET_ID;
}
@Override
diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
index 6cb703b..c713992 100644
--- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
@@ -2,15 +2,11 @@
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
-import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Point;
import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
import android.os.Parcel;
-import android.os.Process;
-import android.os.UserHandle;
/**
* This class is a thin wrapper around the framework AppWidgetProviderInfo class. This class affords
@@ -20,7 +16,7 @@
*/
public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo {
- public boolean isCustomWidget = false;
+ public static final String CLS_CUSTOM_WIDGET_PREFIX = "#custom-widget-";
public int spanX;
public int spanY;
@@ -48,22 +44,12 @@
return launcherInfo;
}
- private LauncherAppWidgetProviderInfo(Parcel in) {
+ protected LauncherAppWidgetProviderInfo() {}
+
+ protected LauncherAppWidgetProviderInfo(Parcel in) {
super(in);
}
- public LauncherAppWidgetProviderInfo(Context context, CustomAppWidget widget) {
- isCustomWidget = true;
-
- provider = new ComponentName(context, widget.getClass().getName());
- icon = widget.getIcon();
- label = widget.getLabel();
- previewImage = widget.getPreviewImage();
- initialLayout = widget.getWidgetLayout();
- resizeMode = widget.getResizeMode();
- initSpans(context);
- }
-
public void initSpans(Context context) {
InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
@@ -97,34 +83,15 @@
}
public String getLabel(PackageManager packageManager) {
- if (isCustomWidget) {
- return Utilities.trim(label);
- }
return super.loadLabel(packageManager);
}
- public Drawable getIcon(Context context, IconCache cache) {
- if (isCustomWidget) {
- return cache.getFullResIcon(provider.getPackageName(), icon);
- }
- return super.loadIcon(context, LauncherAppState.getIDP(context).fillResIconDpi);
+ public Point getMinSpans() {
+ return new Point((resizeMode & RESIZE_HORIZONTAL) != 0 ? minSpanX : -1,
+ (resizeMode & RESIZE_VERTICAL) != 0 ? minSpanY : -1);
}
- public String toString(PackageManager pm) {
- if (isCustomWidget) {
- return "WidgetProviderInfo(" + provider + ")";
- }
- return String.format("WidgetProviderInfo provider:%s package:%s short:%s label:%s",
- provider.toString(), provider.getPackageName(), provider.getShortClassName(), getLabel(pm));
- }
-
- public Point getMinSpans(InvariantDeviceProfile idp, Context context) {
- return new Point(
- (resizeMode & RESIZE_HORIZONTAL) != 0 ? minSpanX : -1,
- (resizeMode & RESIZE_VERTICAL) != 0 ? minSpanY : -1);
- }
-
- public UserHandle getUser() {
- return isCustomWidget ? Process.myUserHandle() : getProfile();
+ public boolean isCustomWidget() {
+ return provider.getClassName().startsWith(CLS_CUSTOM_WIDGET_PREFIX);
}
}
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index 66da046..78d753a 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -19,14 +19,10 @@
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
-import android.view.View;
-
-import com.android.launcher3.util.ComponentKeyMapper;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.List;
/**
* LauncherCallbacks is an interface used to extend the Launcher activity. It includes many hooks
@@ -43,14 +39,12 @@
*/
void preOnCreate();
void onCreate(Bundle savedInstanceState);
- void preOnResume();
void onResume();
void onStart();
void onStop();
void onPause();
void onDestroy();
void onSaveInstanceState(Bundle outState);
- void onPostCreate(Bundle savedInstanceState);
void onNewIntent(Intent intent);
void onActivityResult(int requestCode, int resultCode, Intent data);
void onRequestPermissionsResult(int requestCode, String[] permissions,
@@ -68,31 +62,16 @@
* Extension points for providing custom behavior on certain user interactions.
*/
void onLauncherProviderChange();
- void finishBindingItems(final boolean upgradePath);
void bindAllApplications(ArrayList<AppInfo> apps);
- void onInteractionBegin();
- void onInteractionEnd();
-
- @Deprecated
- void onWorkspaceLockedChanged();
/**
* Starts a search with {@param initialQuery}. Return false if search was not started.
*/
boolean startSearch(
String initialQuery, boolean selectInitialQuery, Bundle appSearchData);
- boolean hasCustomContentToLeft();
- void populateCustomContentContainer();
- View getQsbBar();
- Bundle getAdditionalSearchWidgetOptions();
/*
* Extensions points for adding / replacing some other aspects of the Launcher experience.
*/
- boolean shouldMoveToDefaultScreenOnHomeIntent();
boolean hasSettings();
- List<ComponentKeyMapper<AppInfo>> getPredictedApps();
- int SEARCH_BAR_HEIGHT_NORMAL = 0, SEARCH_BAR_HEIGHT_TALL = 1;
- /** Must return one of {@link #SEARCH_BAR_HEIGHT_NORMAL} or {@link #SEARCH_BAR_HEIGHT_TALL} */
- int getSearchBarHeight();
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index a906b00..469c5f1 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -37,7 +37,6 @@
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.dynamicui.ExtractionUtils;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.model.AddWorkspaceItemsTask;
import com.android.launcher3.model.BgDataModel;
@@ -63,6 +62,7 @@
import com.android.launcher3.util.Provider;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.ViewOnDrawExecutor;
+import com.android.launcher3.widget.WidgetListRowEntry;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -134,7 +134,7 @@
}
};
- public interface Callbacks extends LauncherAppWidgetHost.ProviderChangedListener {
+ public interface Callbacks {
public boolean setLoadOnResume();
public int getCurrentWorkspaceScreen();
public void clearPendingBinds();
@@ -154,7 +154,7 @@
public void bindRestoreItemsChange(HashSet<ItemInfo> updates);
public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher);
public void bindAppInfosRemoved(ArrayList<AppInfo> appInfos);
- public void bindAllWidgets(MultiHashMap<PackageItemInfo, WidgetItem> widgets);
+ public void bindAllWidgets(ArrayList<WidgetListRowEntry> widgets);
public void onPageBoundSynchronously(int page);
public void executeOnNextDraw(ViewOnDrawExecutor executor);
public void bindDeepShortcutMap(MultiHashMap<ComponentKey, String> deepShortcutMap);
@@ -193,9 +193,8 @@
/**
* Adds the provided items to the workspace.
*/
- public void addAndBindAddedWorkspaceItems(
- Provider<List<Pair<ItemInfo, Object>>> appsProvider) {
- enqueueModelUpdateTask(new AddWorkspaceItemsTask(appsProvider));
+ public void addAndBindAddedWorkspaceItems(List<Pair<ItemInfo, Object>> itemList) {
+ enqueueModelUpdateTask(new AddWorkspaceItemsTask(itemList));
}
public ModelWriter getWriter(boolean hasVerticalHotseat) {
@@ -406,8 +405,6 @@
enqueueModelUpdateTask(new UserLockStateChangedTask(user));
}
}
- } else if (Intent.ACTION_WALLPAPER_CHANGED.equals(action)) {
- ExtractionUtils.startColorExtractionServiceIfNecessary(context);
}
}
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index b31df98..25a698b 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -55,7 +55,6 @@
import com.android.launcher3.LauncherSettings.WorkspaceScreens;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.dynamicui.ExtractionUtils;
import com.android.launcher3.graphics.IconShapeOverride;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.DbDowngradeHelper;
@@ -149,9 +148,6 @@
*/
protected synchronized void createDbIfNotExists() {
if (mOpenHelper == null) {
- if (LauncherAppState.PROFILE_STARTUP) {
- Trace.beginSection("Opening workspace DB");
- }
mOpenHelper = new DatabaseHelper(getContext(), mListenerHandler);
if (RestoreDbTask.isPending(getContext())) {
@@ -162,10 +158,6 @@
// executed again.
RestoreDbTask.setPending(getContext(), false);
}
-
- if (LauncherAppState.PROFILE_STARTUP) {
- Trace.endSection();
- }
}
}
@@ -372,19 +364,6 @@
createDbIfNotExists();
switch (method) {
- case LauncherSettings.Settings.METHOD_SET_EXTRACTED_COLORS_AND_WALLPAPER_ID: {
- String extractedColors = extras.getString(
- LauncherSettings.Settings.EXTRA_EXTRACTED_COLORS);
- int wallpaperId = extras.getInt(LauncherSettings.Settings.EXTRA_WALLPAPER_ID);
- Utilities.getPrefs(getContext()).edit()
- .putString(ExtractionUtils.EXTRACTED_COLORS_PREFERENCE_KEY, extractedColors)
- .putInt(ExtractionUtils.WALLPAPER_ID_PREFERENCE_KEY, wallpaperId)
- .apply();
- mListenerHandler.sendEmptyMessage(ChangeListenerWrapper.MSG_EXTRACTED_COLORS_CHANGED);
- Bundle result = new Bundle();
- result.putString(LauncherSettings.Settings.EXTRA_VALUE, extractedColors);
- return result;
- }
case LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG: {
clearFlagEmptyDbCreated();
return null;
@@ -1160,8 +1139,7 @@
private static class ChangeListenerWrapper implements Handler.Callback {
private static final int MSG_LAUNCHER_PROVIDER_CHANGED = 1;
- private static final int MSG_EXTRACTED_COLORS_CHANGED = 2;
- private static final int MSG_APP_WIDGET_HOST_RESET = 3;
+ private static final int MSG_APP_WIDGET_HOST_RESET = 2;
private LauncherProviderChangeListener mListener;
@@ -1172,9 +1150,6 @@
case MSG_LAUNCHER_PROVIDER_CHANGED:
mListener.onLauncherProviderChanged();
break;
- case MSG_EXTRACTED_COLORS_CHANGED:
- mListener.onExtractedColorsChanged();
- break;
case MSG_APP_WIDGET_HOST_RESET:
mListener.onAppWidgetHostReset();
break;
diff --git a/src/com/android/launcher3/LauncherProviderChangeListener.java b/src/com/android/launcher3/LauncherProviderChangeListener.java
index 7044812..0243088 100644
--- a/src/com/android/launcher3/LauncherProviderChangeListener.java
+++ b/src/com/android/launcher3/LauncherProviderChangeListener.java
@@ -9,7 +9,5 @@
void onLauncherProviderChanged();
- void onExtractedColorsChanged();
-
void onAppWidgetHostReset();
}
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 87f62eb..3b337ef 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -304,11 +304,6 @@
public static final String METHOD_LOAD_DEFAULT_FAVORITES = "load_default_favorites";
- public static final String METHOD_SET_EXTRACTED_COLORS_AND_WALLPAPER_ID =
- "set_extracted_colors_and_wallpaper_id_setting";
- public static final String EXTRA_EXTRACTED_COLORS = "extra_extractedColors";
- public static final String EXTRA_WALLPAPER_ID = "extra_wallpaperId";
-
public static final String METHOD_REMOVE_GHOST_WIDGETS = "remove_ghost_widgets";
public static final String EXTRA_VALUE = "value";
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
new file mode 100644
index 0000000..63c232d
--- /dev/null
+++ b/src/com/android/launcher3/LauncherState.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2017 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 android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
+import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
+import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
+
+import android.view.View;
+
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.states.AllAppsState;
+import com.android.launcher3.states.SpringLoadedState;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+
+import java.util.Arrays;
+
+
+/**
+ * Base state for various states used for the Launcher
+ */
+public class LauncherState {
+
+ protected static final int FLAG_SHOW_SCRIM = 1 << 0;
+ protected static final int FLAG_MULTI_PAGE = 1 << 1;
+ protected static final int FLAG_HIDE_HOTSEAT = 1 << 2;
+ protected static final int FLAG_DISABLE_ACCESSIBILITY = 1 << 3;
+ protected static final int FLAG_DO_NOT_RESTORE = 1 << 4;
+
+ private static final LauncherState[] sAllStates = new LauncherState[4];
+
+ public static final LauncherState NORMAL = new LauncherState(0, ContainerType.WORKSPACE,
+ 0, 1f, FLAG_DO_NOT_RESTORE);
+
+ public static final LauncherState ALL_APPS = new AllAppsState(1);
+
+ public static final LauncherState SPRING_LOADED = new SpringLoadedState(2);
+
+ public static final LauncherState OVERVIEW = FeatureFlags.createOverviewState(3);
+
+ public final int ordinal;
+
+ /**
+ * Used for containerType in {@link com.android.launcher3.logging.UserEventDispatcher}
+ */
+ public final int containerType;
+
+ /**
+ * True if the state can be persisted across activity restarts.
+ */
+ public final boolean doNotRestore;
+
+ /**
+ * True if workspace has multiple pages visible.
+ */
+ public final boolean hasMultipleVisiblePages;
+
+ /**
+ * Accessibility flag for workspace and its pages.
+ * @see android.view.View#setImportantForAccessibility(int)
+ */
+ public final int workspaceAccessibilityFlag;
+
+ /**
+ * Properties related to state transition animation
+ *
+ * @see WorkspaceStateTransitionAnimation
+ */
+ public final boolean hasScrim;
+ public final boolean hideHotseat;
+ public final int transitionDuration;
+
+ /**
+ * Fraction shift in the vertical translation UI and related properties
+ *
+ * @see com.android.launcher3.allapps.AllAppsTransitionController
+ */
+ public final float verticalProgress;
+
+ public LauncherState(int id, int containerType, int transitionDuration, float verticalProgress,
+ int flags) {
+ this.containerType = containerType;
+ this.transitionDuration = transitionDuration;
+
+ this.hasScrim = (flags & FLAG_SHOW_SCRIM) != 0;
+ this.hasMultipleVisiblePages = (flags & FLAG_MULTI_PAGE) != 0;
+ this.hideHotseat = (flags & FLAG_HIDE_HOTSEAT) != 0;
+ this.workspaceAccessibilityFlag = (flags & FLAG_DISABLE_ACCESSIBILITY) != 0
+ ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+ : IMPORTANT_FOR_ACCESSIBILITY_AUTO;
+ this.doNotRestore = (flags & FLAG_DO_NOT_RESTORE) != 0;
+
+ this.verticalProgress = verticalProgress;
+
+ this.ordinal = id;
+ sAllStates[id] = this;
+ }
+
+ public static LauncherState[] values() {
+ return Arrays.copyOf(sAllStates, sAllStates.length);
+ }
+
+ public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
+ return new float[] {1, 0};
+ }
+
+ public void onStateEnabled(Launcher launcher) {
+ dispatchWindowStateChanged(launcher);
+ }
+
+ public void onStateDisabled(Launcher launcher) { }
+
+ public View getFinalFocus(Launcher launcher) {
+ return launcher.getWorkspace();
+ }
+
+ public String getDescription(Launcher launcher) {
+ return launcher.getWorkspace().getCurrentPageDescription();
+ }
+
+ protected static void dispatchWindowStateChanged(Launcher launcher) {
+ launcher.getWindow().getDecorView().sendAccessibilityEvent(TYPE_WINDOW_STATE_CHANGED);
+ }
+}
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
new file mode 100644
index 0000000..fd94067
--- /dev/null
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2015 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 android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.View;
+
+import com.android.launcher3.allapps.AllAppsTransitionController;
+import com.android.launcher3.anim.AnimationLayerSet;
+import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+
+/**
+ * TODO: figure out what kind of tests we can write for this
+ *
+ * Things to test when changing the following class.
+ * - Home from workspace
+ * - from center screen
+ * - from other screens
+ * - Home from all apps
+ * - from center screen
+ * - from other screens
+ * - Back from all apps
+ * - from center screen
+ * - from other screens
+ * - Launch app from workspace and quit
+ * - with back
+ * - with home
+ * - Launch app from all apps and quit
+ * - with back
+ * - with home
+ * - Go to a screen that's not the default, then all
+ * apps, and launch and app, and go back
+ * - with back
+ * -with home
+ * - On workspace, long press power and go back
+ * - with back
+ * - with home
+ * - On all apps, long press power and go back
+ * - with back
+ * - with home
+ * - On workspace, power off
+ * - On all apps, power off
+ * - Launch an app and turn off the screen while in that app
+ * - Go back with home key
+ * - Go back with back key TODO: make this not go to workspace
+ * - From all apps
+ * - From workspace
+ * - Enter and exit car mode (becuase it causes an extra configuration changed)
+ * - From all apps
+ * - From the center workspace
+ * - From another workspace
+ */
+public class LauncherStateManager {
+
+ public static final String TAG = "StateManager";
+
+ private final AnimationConfig mConfig = new AnimationConfig();
+ private final Handler mUiHandler;
+ private final Launcher mLauncher;
+ private final AllAppsTransitionController mAllAppsController;
+
+ private LauncherState mState = NORMAL;
+
+ public LauncherStateManager(
+ Launcher l, AllAppsTransitionController allAppsController) {
+ mUiHandler = new Handler(Looper.getMainLooper());
+ mLauncher = l;
+ mAllAppsController = allAppsController;
+ }
+
+ public LauncherState getState() {
+ return mState;
+ }
+
+ /**
+ * @see #goToState(LauncherState, boolean, Runnable)
+ */
+ public void goToState(LauncherState state) {
+ goToState(state, true, 0, null);
+ }
+
+ /**
+ * @see #goToState(LauncherState, boolean, Runnable)
+ */
+ public void goToState(LauncherState state, boolean animated) {
+ goToState(state, animated, 0, null);
+ }
+
+ /**
+ * Changes the Launcher state to the provided state.
+ *
+ * @param animated false if the state should change immediately without any animation,
+ * true otherwise
+ * @paras onCompleteRunnable any action to perform at the end of the transition, of null.
+ */
+ public void goToState(LauncherState state, boolean animated, Runnable onCompleteRunnable) {
+ goToState(state, animated, 0, onCompleteRunnable);
+ }
+
+ /**
+ * Changes the Launcher state to the provided state after the given delay.
+ */
+ public void goToState(LauncherState state, long delay, Runnable onCompleteRunnable) {
+ goToState(state, true, delay, onCompleteRunnable);
+ }
+
+ /**
+ * Changes the Launcher state to the provided state after the given delay.
+ */
+ public void goToState(LauncherState state, long delay) {
+ goToState(state, true, delay, null);
+ }
+
+ private void goToState(LauncherState state, boolean animated, long delay,
+ Runnable onCompleteRunnable) {
+ if (mLauncher.isInState(state) && mConfig.mCurrentAnimation == null) {
+
+ // Run any queued runnable
+ if (onCompleteRunnable != null) {
+ onCompleteRunnable.run();
+ }
+ return;
+ }
+
+ // Cancel the current animation
+ mConfig.reset();
+
+ if (!animated) {
+ setState(state);
+ mAllAppsController.setFinalProgress(state.verticalProgress);
+ mLauncher.getWorkspace().setState(state);
+ mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
+
+ // Run any queued runnable
+ if (onCompleteRunnable != null) {
+ onCompleteRunnable.run();
+ }
+ return;
+ }
+
+ // Since state NORMAL can be reached from multiple states, just assume that the
+ // transition plays in reverse and use the same duration as previous state.
+ mConfig.duration = state == NORMAL ? mState.transitionDuration : state.transitionDuration;
+
+ AnimatorSet animation = createAnimationToNewWorkspaceInternal(state, onCompleteRunnable);
+ Runnable runnable = new StartAnimRunnable(animation, state.getFinalFocus(mLauncher));
+ if (delay > 0) {
+ mUiHandler.postDelayed(runnable, delay);
+ } else {
+ mUiHandler.post(runnable);
+ }
+ }
+
+ /**
+ * Creates a {@link AnimatorPlaybackController} that can be used for a controlled
+ * state transition.
+ * @param state the final state for the transition.
+ * @param duration intended duration for normal playback. Use higher duration for better
+ * accuracy.
+ */
+ protected AnimatorPlaybackController createAnimationToNewWorkspace(
+ LauncherState state, long duration) {
+ mConfig.reset();
+ mConfig.userControlled = true;
+ mConfig.duration = duration;
+ return AnimatorPlaybackController.wrap(
+ createAnimationToNewWorkspaceInternal(state, null), duration);
+ }
+
+ protected AnimatorSet createAnimationToNewWorkspaceInternal(final LauncherState state,
+ final Runnable onCompleteRunnable) {
+
+ final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
+ final AnimationLayerSet layerViews = new AnimationLayerSet();
+
+ mAllAppsController.animateToFinalProgress(state.verticalProgress, animation, mConfig);
+ mLauncher.getWorkspace().setStateWithAnimation(state,
+ layerViews, animation, mConfig);
+
+ animation.addListener(layerViews);
+ animation.addListener(new AnimationSuccessListener() {
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ // Change the internal state only when the transition actually starts
+ setState(state);
+ }
+
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ // Run any queued runnables
+ if (onCompleteRunnable != null) {
+ onCompleteRunnable.run();
+ }
+
+ mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
+ }
+ });
+ mConfig.setAnimation(animation);
+ return mConfig.mCurrentAnimation;
+ }
+
+ private void setState(LauncherState state) {
+ mState.onStateDisabled(mLauncher);
+ mState = state;
+ mState.onStateEnabled(mLauncher);
+ }
+
+ /**
+ * Cancels the current animation.
+ */
+ public void cancelAnimation() {
+ mConfig.reset();
+ }
+
+ private class StartAnimRunnable implements Runnable {
+
+ private final AnimatorSet mAnim;
+ private final View mViewToFocus;
+
+ public StartAnimRunnable(AnimatorSet anim, View viewToFocus) {
+ mAnim = anim;
+ mViewToFocus = viewToFocus;
+ }
+
+ @Override
+ public void run() {
+ if (mConfig.mCurrentAnimation != mAnim) {
+ return;
+ }
+ if (mViewToFocus != null) {
+ mViewToFocus.requestFocus();
+ }
+ mAnim.start();
+ }
+ }
+
+ public static class AnimationConfig extends AnimatorListenerAdapter {
+ public long duration;
+ public boolean userControlled;
+
+ private AnimatorSet mCurrentAnimation;
+
+ public void reset() {
+ duration = 0;
+ userControlled = false;
+
+ if (mCurrentAnimation != null) {
+ mCurrentAnimation.setDuration(0);
+ mCurrentAnimation.cancel();
+ mCurrentAnimation = null;
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mCurrentAnimation == animation) {
+ mCurrentAnimation = null;
+ }
+ }
+
+ public void setAnimation(AnimatorSet animation) {
+ mCurrentAnimation = animation;
+ mCurrentAnimation.addListener(this);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
deleted file mode 100644
index e247490..0000000
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ /dev/null
@@ -1,719 +0,0 @@
-/*
- * Copyright (C) 2015 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 android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.content.res.Resources;
-import android.util.Log;
-import android.view.View;
-import android.view.animation.AccelerateInterpolator;
-
-import com.android.launcher3.allapps.AllAppsContainerView;
-import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.anim.AnimationLayerSet;
-import com.android.launcher3.anim.CircleRevealOutlineProvider;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.Thunk;
-import com.android.launcher3.widget.WidgetsContainerView;
-
-/**
- * TODO: figure out what kind of tests we can write for this
- *
- * Things to test when changing the following class.
- * - Home from workspace
- * - from center screen
- * - from other screens
- * - Home from all apps
- * - from center screen
- * - from other screens
- * - Back from all apps
- * - from center screen
- * - from other screens
- * - Launch app from workspace and quit
- * - with back
- * - with home
- * - Launch app from all apps and quit
- * - with back
- * - with home
- * - Go to a screen that's not the default, then all
- * apps, and launch and app, and go back
- * - with back
- * -with home
- * - On workspace, long press power and go back
- * - with back
- * - with home
- * - On all apps, long press power and go back
- * - with back
- * - with home
- * - On workspace, power off
- * - On all apps, power off
- * - Launch an app and turn off the screen while in that app
- * - Go back with home key
- * - Go back with back key TODO: make this not go to workspace
- * - From all apps
- * - From workspace
- * - Enter and exit car mode (becuase it causes an extra configuration changed)
- * - From all apps
- * - From the center workspace
- * - From another workspace
- */
-public class LauncherStateTransitionAnimation {
-
- /**
- * animation used for the widget tray
- */
- public static final int CIRCULAR_REVEAL = 0;
- /**
- * animation used for all apps tray
- */
- public static final int PULLUP = 1;
-
- private static final float FINAL_REVEAL_ALPHA_FOR_WIDGETS = 0.3f;
-
- /**
- * Private callbacks made during transition setup.
- */
- private static class PrivateTransitionCallbacks {
- private final float materialRevealViewFinalAlpha;
-
- PrivateTransitionCallbacks(float revealAlpha) {
- materialRevealViewFinalAlpha = revealAlpha;
- }
-
- float getMaterialRevealViewStartFinalRadius() {
- return 0;
- }
- AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(View revealView,
- View buttonView) {
- return null;
- }
- void onTransitionComplete() {}
- }
-
- public static final String TAG = "LSTAnimation";
-
- public static final int SINGLE_FRAME_DELAY = 16;
-
- @Thunk Launcher mLauncher;
- @Thunk AnimatorSet mCurrentAnimation;
- AllAppsTransitionController mAllAppsController;
-
- public LauncherStateTransitionAnimation(Launcher l, AllAppsTransitionController allAppsController) {
- mLauncher = l;
- mAllAppsController = allAppsController;
- }
-
- /**
- * Starts an animation to the apps view.
- */
- public void startAnimationToAllApps(final boolean animated) {
- final AllAppsContainerView toView = mLauncher.getAppsView();
- final View buttonView = mLauncher.getStartViewForAllAppsRevealAnimation();
- PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks(1f) {
- @Override
- public float getMaterialRevealViewStartFinalRadius() {
- int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
- return allAppsButtonSize / 2;
- }
- @Override
- public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
- final View revealView, final View allAppsButtonView) {
- return new AnimatorListenerAdapter() {
- public void onAnimationStart(Animator animation) {
- allAppsButtonView.setVisibility(View.INVISIBLE);
- }
- public void onAnimationEnd(Animator animation) {
- allAppsButtonView.setVisibility(View.VISIBLE);
- }
- };
- }
- @Override
- void onTransitionComplete() {
- mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
- }
- };
- // Only animate the search bar if animating from spring loaded mode back to all apps
- startAnimationToOverlay(
- Workspace.State.NORMAL_HIDDEN, buttonView, toView, animated, PULLUP, cb);
- }
-
- /**
- * Starts an animation to the widgets view.
- */
- public void startAnimationToWidgets(final boolean animated) {
- final WidgetsContainerView toView = mLauncher.getWidgetsView();
- final View buttonView = mLauncher.getWidgetsButton();
- startAnimationToOverlay(
- Workspace.State.OVERVIEW_HIDDEN, buttonView, toView, animated, CIRCULAR_REVEAL,
- new PrivateTransitionCallbacks(FINAL_REVEAL_ALPHA_FOR_WIDGETS){
- @Override
- void onTransitionComplete() {
- mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
- }
- });
- }
-
- /**
- * Starts an animation to the workspace from the current overlay view.
- */
- public void startAnimationToWorkspace(final Launcher.State fromState,
- final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState,
- final boolean animated, final Runnable onCompleteRunnable) {
- if (toWorkspaceState != Workspace.State.NORMAL &&
- toWorkspaceState != Workspace.State.SPRING_LOADED &&
- toWorkspaceState != Workspace.State.OVERVIEW) {
- Log.e(TAG, "Unexpected call to startAnimationToWorkspace");
- }
-
- if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED
- || mAllAppsController.isTransitioning()) {
- startAnimationToWorkspaceFromAllApps(fromWorkspaceState, toWorkspaceState,
- animated, PULLUP, onCompleteRunnable);
- } else if (fromState == Launcher.State.WIDGETS ||
- fromState == Launcher.State.WIDGETS_SPRING_LOADED) {
- startAnimationToWorkspaceFromWidgets(fromWorkspaceState, toWorkspaceState,
- animated, onCompleteRunnable);
- } else {
- startAnimationToNewWorkspaceState(fromWorkspaceState, toWorkspaceState,
- animated, onCompleteRunnable);
- }
- }
-
- /**
- * Creates and starts a new animation to a particular overlay view.
- */
- private void startAnimationToOverlay(
- final Workspace.State toWorkspaceState,
- final View buttonView, final BaseContainerView toView,
- final boolean animated, int animType, final PrivateTransitionCallbacks pCb) {
- final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
- final Resources res = mLauncher.getResources();
- final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
- final int revealDurationSlide = res.getInteger(R.integer.config_overlaySlideRevealTime);
-
- final int itemsAlphaStagger = res.getInteger(R.integer.config_overlayItemsAlphaStagger);
-
- final AnimationLayerSet layerViews = new AnimationLayerSet();
-
- // If for some reason our views aren't initialized, don't animate
- boolean initialized = buttonView != null;
-
- // Cancel the current animation
- cancelAnimation();
-
- final View contentView = toView.getContentView();
- playCommonTransitionAnimations(toWorkspaceState,
- animated, initialized, animation, layerViews);
- if (!animated || !initialized) {
- if (toWorkspaceState == Workspace.State.NORMAL_HIDDEN) {
- mAllAppsController.finishPullUp();
- }
- toView.setTranslationX(0.0f);
- toView.setTranslationY(0.0f);
- toView.setScaleX(1.0f);
- toView.setScaleY(1.0f);
- toView.setAlpha(1.0f);
- toView.setVisibility(View.VISIBLE);
-
- // Show the content view
- contentView.setVisibility(View.VISIBLE);
- pCb.onTransitionComplete();
- return;
- }
- if (animType == CIRCULAR_REVEAL) {
- // Setup the reveal view animation
- final View revealView = toView.getRevealView();
-
- int width = revealView.getMeasuredWidth();
- int height = revealView.getMeasuredHeight();
- float revealRadius = (float) Math.hypot(width / 2, height / 2);
- revealView.setVisibility(View.VISIBLE);
- revealView.setAlpha(0f);
- revealView.setTranslationY(0f);
- revealView.setTranslationX(0f);
-
- // Calculate the final animation values
- int[] buttonViewToPanelDelta =
- Utilities.getCenterDeltaInScreenSpace(revealView, buttonView);
- final float revealViewToAlpha = pCb.materialRevealViewFinalAlpha;
- final float revealViewToXDrift = buttonViewToPanelDelta[0];
- final float revealViewToYDrift = buttonViewToPanelDelta[1];
-
- // Create the animators
- PropertyValuesHolder panelAlpha =
- PropertyValuesHolder.ofFloat(View.ALPHA, revealViewToAlpha, 1f);
- PropertyValuesHolder panelDriftY =
- PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, revealViewToYDrift, 0);
- PropertyValuesHolder panelDriftX =
- PropertyValuesHolder.ofFloat(View.TRANSLATION_X, revealViewToXDrift, 0);
- ObjectAnimator panelAlphaAndDrift = ObjectAnimator.ofPropertyValuesHolder(revealView,
- panelAlpha, panelDriftY, panelDriftX);
- panelAlphaAndDrift.setDuration(revealDuration);
- panelAlphaAndDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
-
- // Play the animation
- layerViews.addView(revealView);
- animation.play(panelAlphaAndDrift);
-
- // Setup the animation for the content view
- contentView.setVisibility(View.VISIBLE);
- contentView.setAlpha(0f);
- contentView.setTranslationY(revealViewToYDrift);
- layerViews.addView(contentView);
-
- // Create the individual animators
- ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY",
- revealViewToYDrift, 0);
- pageDrift.setDuration(revealDuration);
- pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
- pageDrift.setStartDelay(itemsAlphaStagger);
- animation.play(pageDrift);
-
- ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 0f, 1f);
- itemsAlpha.setDuration(revealDuration);
- itemsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
- itemsAlpha.setStartDelay(itemsAlphaStagger);
- animation.play(itemsAlpha);
-
- float startRadius = pCb.getMaterialRevealViewStartFinalRadius();
- AnimatorListenerAdapter listener = pCb.getMaterialRevealViewAnimatorListener(
- revealView, buttonView);
- Animator reveal = new CircleRevealOutlineProvider(width / 2, height / 2,
- startRadius, revealRadius).createRevealAnimator(revealView);
- reveal.setDuration(revealDuration);
- reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
- if (listener != null) {
- reveal.addListener(listener);
- }
- animation.play(reveal);
-
- animation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- // Hide the reveal view
- revealView.setVisibility(View.INVISIBLE);
-
- // This can hold unnecessary references to views.
- cleanupAnimation();
- pCb.onTransitionComplete();
- }
-
- });
-
- toView.bringToFront();
- toView.setVisibility(View.VISIBLE);
-
- animation.addListener(layerViews);
- toView.post(new StartAnimRunnable(animation, toView));
- mCurrentAnimation = animation;
- } else if (animType == PULLUP) {
- if (!FeatureFlags.LAUNCHER3_PHYSICS) {
- // We are animating the content view alpha, so ensure we have a layer for it.
- layerViews.addView(contentView);
- }
-
- animation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- cleanupAnimation();
- pCb.onTransitionComplete();
- }
- });
- boolean shouldPost = mAllAppsController.animateToAllApps(animation, revealDurationSlide);
-
- Runnable startAnimRunnable = new StartAnimRunnable(animation, toView);
- mCurrentAnimation = animation;
- mCurrentAnimation.addListener(layerViews);
- if (shouldPost) {
- toView.post(startAnimRunnable);
- } else {
- startAnimRunnable.run();
- }
- }
- }
-
- /**
- * Plays animations used by various transitions.
- */
- private void playCommonTransitionAnimations(
- Workspace.State toWorkspaceState,
- boolean animated, boolean initialized, AnimatorSet animation,
- AnimationLayerSet layerViews) {
- // Create the workspace animation.
- // NOTE: this call apparently also sets the state for the workspace if !animated
- Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState,
- animated, layerViews);
-
- if (animated && initialized) {
- // Play the workspace animation
- if (workspaceAnim != null) {
- animation.play(workspaceAnim);
- }
- }
- }
-
- /**
- * Starts an animation to the workspace from the apps view.
- */
- private void startAnimationToWorkspaceFromAllApps(final Workspace.State fromWorkspaceState,
- final Workspace.State toWorkspaceState, final boolean animated, int type,
- final Runnable onCompleteRunnable) {
- // No alpha anim from all apps
- PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks(1f) {
- @Override
- float getMaterialRevealViewStartFinalRadius() {
- int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
- return allAppsButtonSize / 2;
- }
- @Override
- public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
- final View revealView, final View allAppsButtonView) {
- return new AnimatorListenerAdapter() {
- public void onAnimationStart(Animator animation) {
- // We set the alpha instead of visibility to ensure that the focus does not
- // get taken from the all apps view
- allAppsButtonView.setVisibility(View.VISIBLE);
- allAppsButtonView.setAlpha(0f);
- }
- public void onAnimationEnd(Animator animation) {
- // Hide the reveal view
- revealView.setVisibility(View.INVISIBLE);
-
- // Show the all apps button, and focus it
- allAppsButtonView.setAlpha(1f);
- }
- };
- }
- @Override
- void onTransitionComplete() {
- mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
- }
- };
- // Only animate the search bar if animating to spring loaded mode from all apps
- startAnimationToWorkspaceFromOverlay(fromWorkspaceState, toWorkspaceState,
- mLauncher.getStartViewForAllAppsRevealAnimation(), mLauncher.getAppsView(),
- animated, type, onCompleteRunnable, cb);
- }
-
- /**
- * Starts an animation to the workspace from the widgets view.
- */
- private void startAnimationToWorkspaceFromWidgets(final Workspace.State fromWorkspaceState,
- final Workspace.State toWorkspaceState, final boolean animated,
- final Runnable onCompleteRunnable) {
- final WidgetsContainerView widgetsView = mLauncher.getWidgetsView();
- PrivateTransitionCallbacks cb =
- new PrivateTransitionCallbacks(FINAL_REVEAL_ALPHA_FOR_WIDGETS) {
- @Override
- public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
- final View revealView, final View widgetsButtonView) {
- return new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animation) {
- // Hide the reveal view
- revealView.setVisibility(View.INVISIBLE);
- }
- };
- }
- @Override
- void onTransitionComplete() {
- mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
- }
- };
- startAnimationToWorkspaceFromOverlay(
- fromWorkspaceState, toWorkspaceState,
- mLauncher.getWidgetsButton(), widgetsView,
- animated, CIRCULAR_REVEAL, onCompleteRunnable, cb);
- }
-
- /**
- * Starts an animation to the workspace from another workspace state, e.g. normal to overview.
- */
- private void startAnimationToNewWorkspaceState(final Workspace.State fromWorkspaceState,
- final Workspace.State toWorkspaceState, final boolean animated,
- final Runnable onCompleteRunnable) {
- final View fromWorkspace = mLauncher.getWorkspace();
- final AnimationLayerSet layerViews = new AnimationLayerSet();
- final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
-
- // Cancel the current animation
- cancelAnimation();
-
- playCommonTransitionAnimations(toWorkspaceState, animated, animated, animation, layerViews);
- mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
-
- if (animated) {
- animation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- // Run any queued runnables
- if (onCompleteRunnable != null) {
- onCompleteRunnable.run();
- }
-
- // This can hold unnecessary references to views.
- cleanupAnimation();
- }
- });
- animation.addListener(layerViews);
- fromWorkspace.post(new StartAnimRunnable(animation, null));
- mCurrentAnimation = animation;
- } else /* if (!animated) */ {
- // Run any queued runnables
- if (onCompleteRunnable != null) {
- onCompleteRunnable.run();
- }
-
- mCurrentAnimation = null;
- }
- }
-
- /**
- * Creates and starts a new animation to the workspace.
- */
- private void startAnimationToWorkspaceFromOverlay(
- final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState,
- final View buttonView, final BaseContainerView fromView,
- final boolean animated, int animType, final Runnable onCompleteRunnable,
- final PrivateTransitionCallbacks pCb) {
- final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
- final Resources res = mLauncher.getResources();
- final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
- final int revealDurationSlide = res.getInteger(R.integer.config_overlaySlideRevealTime);
- final int itemsAlphaStagger = res.getInteger(R.integer.config_overlayItemsAlphaStagger);
-
- final View toView = mLauncher.getWorkspace();
- final View revealView = fromView.getRevealView();
- final View contentView = fromView.getContentView();
-
- final AnimationLayerSet layerViews = new AnimationLayerSet();
-
- // If for some reason our views aren't initialized, don't animate
- boolean initialized = buttonView != null;
-
- // Cancel the current animation
- cancelAnimation();
-
- playCommonTransitionAnimations(toWorkspaceState,
- animated, initialized, animation, layerViews);
- if (!animated || !initialized) {
- if (fromWorkspaceState == Workspace.State.NORMAL_HIDDEN) {
- mAllAppsController.finishPullDown();
- }
- fromView.setVisibility(View.GONE);
- pCb.onTransitionComplete();
-
- // Run any queued runnables
- if (onCompleteRunnable != null) {
- onCompleteRunnable.run();
- }
- return;
- }
- if (animType == CIRCULAR_REVEAL) {
- // hideAppsCustomizeHelper is called in some cases when it is already hidden
- // don't perform all these no-op animations. In particularly, this was causing
- // the all-apps button to pop in and out.
- if (fromView.getVisibility() == View.VISIBLE) {
- int width = revealView.getMeasuredWidth();
- int height = revealView.getMeasuredHeight();
- float revealRadius = (float) Math.hypot(width / 2, height / 2);
- revealView.setVisibility(View.VISIBLE);
- revealView.setAlpha(1f);
- revealView.setTranslationY(0);
- layerViews.addView(revealView);
-
- // Calculate the final animation values
- int[] buttonViewToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView, buttonView);
- final float revealViewToXDrift = buttonViewToPanelDelta[0];
- final float revealViewToYDrift = buttonViewToPanelDelta[1];
-
- // The vertical motion of the apps panel should be delayed by one frame
- // from the conceal animation in order to give the right feel. We correspondingly
- // shorten the duration so that the slide and conceal end at the same time.
- TimeInterpolator decelerateInterpolator = new LogDecelerateInterpolator(100, 0);
- ObjectAnimator panelDriftY = ObjectAnimator.ofFloat(revealView, "translationY",
- 0, revealViewToYDrift);
- panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY);
- panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
- panelDriftY.setInterpolator(decelerateInterpolator);
- animation.play(panelDriftY);
-
- ObjectAnimator panelDriftX = ObjectAnimator.ofFloat(revealView, "translationX",
- 0, revealViewToXDrift);
- panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY);
- panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
- panelDriftX.setInterpolator(decelerateInterpolator);
- animation.play(panelDriftX);
-
- // Setup animation for the reveal panel alpha
- if (pCb.materialRevealViewFinalAlpha != 1f) {
- ObjectAnimator panelAlpha = ObjectAnimator.ofFloat(revealView, "alpha",
- 1f, pCb.materialRevealViewFinalAlpha);
- panelAlpha.setDuration(revealDuration);
- panelAlpha.setInterpolator(decelerateInterpolator);
- animation.play(panelAlpha);
- }
-
- // Setup the animation for the content view
- layerViews.addView(contentView);
-
- // Create the individual animators
- ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY",
- 0, revealViewToYDrift);
- contentView.setTranslationY(0);
- pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY);
- pageDrift.setInterpolator(decelerateInterpolator);
- pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
- animation.play(pageDrift);
-
- contentView.setAlpha(1f);
- ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 1f, 0f);
- itemsAlpha.setDuration(100);
- itemsAlpha.setInterpolator(decelerateInterpolator);
- animation.play(itemsAlpha);
-
- // Invalidate the scrim throughout the animation to ensure the highlight
- // cutout is correct throughout.
- ValueAnimator invalidateScrim = ValueAnimator.ofFloat(0f, 1f);
- invalidateScrim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mLauncher.getDragLayer().invalidateScrim();
- }
- });
- animation.play(invalidateScrim);
-
- // Animate the all apps button
- float finalRadius = pCb.getMaterialRevealViewStartFinalRadius();
- AnimatorListenerAdapter listener =
- pCb.getMaterialRevealViewAnimatorListener(revealView, buttonView);
- Animator reveal = new CircleRevealOutlineProvider(width / 2, height / 2,
- revealRadius, finalRadius).createRevealAnimator(revealView);
- reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
- reveal.setDuration(revealDuration);
- reveal.setStartDelay(itemsAlphaStagger);
- if (listener != null) {
- reveal.addListener(listener);
- }
- animation.play(reveal);
- }
-
- animation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- fromView.setVisibility(View.GONE);
- // Run any queued runnables
- if (onCompleteRunnable != null) {
- onCompleteRunnable.run();
- }
-
- // Reset page transforms
- if (contentView != null) {
- contentView.setTranslationX(0);
- contentView.setTranslationY(0);
- contentView.setAlpha(1);
- }
-
- // This can hold unnecessary references to views.
- cleanupAnimation();
- pCb.onTransitionComplete();
- }
- });
-
- mCurrentAnimation = animation;
- mCurrentAnimation.addListener(layerViews);
- fromView.post(new StartAnimRunnable(animation, null));
- } else if (animType == PULLUP) {
- // We are animating the content view alpha, so ensure we have a layer for it
- layerViews.addView(contentView);
-
- animation.addListener(new AnimatorListenerAdapter() {
- boolean canceled = false;
- @Override
- public void onAnimationCancel(Animator animation) {
- canceled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (canceled) return;
- // Run any queued runnables
- if (onCompleteRunnable != null) {
- onCompleteRunnable.run();
- }
-
- cleanupAnimation();
- pCb.onTransitionComplete();
- }
-
- });
- boolean shouldPost = mAllAppsController.animateToWorkspace(animation, revealDurationSlide);
-
- Runnable startAnimRunnable = new StartAnimRunnable(animation, toView);
- mCurrentAnimation = animation;
- mCurrentAnimation.addListener(layerViews);
- if (shouldPost) {
- fromView.post(startAnimRunnable);
- } else {
- startAnimRunnable.run();
- }
- }
- return;
- }
-
- /**
- * Cancels the current animation.
- */
- private void cancelAnimation() {
- if (mCurrentAnimation != null) {
- mCurrentAnimation.setDuration(0);
- mCurrentAnimation.cancel();
- mCurrentAnimation = null;
- }
- }
-
- @Thunk void cleanupAnimation() {
- mCurrentAnimation = null;
- }
-
- private class StartAnimRunnable implements Runnable {
-
- private final AnimatorSet mAnim;
- private final View mViewToFocus;
-
- public StartAnimRunnable(AnimatorSet anim, View viewToFocus) {
- mAnim = anim;
- mViewToFocus = viewToFocus;
- }
-
- @Override
- public void run() {
- if (mCurrentAnimation != mAnim) {
- return;
- }
- if (mViewToFocus != null) {
- mViewToFocus.requestFocus();
- }
- mAnim.start();
- }
- }
-}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 87f3dda..4240a30 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -46,10 +46,10 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.Interpolator;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.pageindicators.PageIndicator;
import com.android.launcher3.touch.OverScroll;
-import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
import java.util.ArrayList;
@@ -87,8 +87,6 @@
public static final int INVALID_RESTORE_PAGE = -1001;
private boolean mFreeScroll = false;
- private int mFreeScrollMinScrollX = -1;
- private int mFreeScrollMaxScrollX = -1;
protected int mFlingThresholdVelocity;
protected int mMinFlingVelocity;
@@ -137,7 +135,6 @@
protected int mTouchSlop;
private int mMaximumVelocity;
protected boolean mAllowOverScroll = true;
- protected int[] mTempVisiblePagesRange = new int[2];
protected static final int INVALID_POINTER = -1;
@@ -218,7 +215,7 @@
*/
protected void init() {
mScroller = new LauncherScroller(getContext());
- setDefaultInterpolator(new ScrollInterpolator());
+ setDefaultInterpolator(Interpolators.SCROLL);
mCurrentPage = 0;
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
@@ -380,16 +377,8 @@
}
private int validateNewPage(int newPage) {
- int validatedPage = newPage;
- // When in free scroll mode, we need to clamp to the free scroll page range.
- if (mFreeScroll) {
- getFreeScrollPageRange(mTempVisiblePagesRange);
- validatedPage = Math.max(mTempVisiblePagesRange[0],
- Math.min(newPage, mTempVisiblePagesRange[1]));
- }
// Ensure that it is clamped by the actual set of children in all cases
- validatedPage = Utilities.boundToRange(validatedPage, 0, getPageCount() - 1);
- return validatedPage;
+ return Utilities.boundToRange(newPage, 0, getPageCount() - 1);
}
/**
@@ -491,13 +480,11 @@
if (mFreeScroll) {
// If the scroller is trying to move to a location beyond the maximum allowed
// in the free scroll mode, we make sure to end the scroll operation.
- if (!mScroller.isFinished() &&
- (x > mFreeScrollMaxScrollX || x < mFreeScrollMinScrollX)) {
+ if (!mScroller.isFinished() && (x > mMaxScrollX || x < 0)) {
forceFinishScroller(false);
}
- x = Math.min(x, mFreeScrollMaxScrollX);
- x = Math.max(x, mFreeScrollMinScrollX);
+ x = Utilities.boundToRange(x, 0, mMaxScrollX);
}
mUnboundedScrollX = x;
@@ -886,7 +873,7 @@
return 0;
}
- @Thunk void updateMaxScrollX() {
+ private void updateMaxScrollX() {
mMaxScrollX = computeMaxScrollX();
}
@@ -915,13 +902,11 @@
// This ensures that when children are added, they get the correct transforms / alphas
// in accordance with any scroll effects.
- updateFreescrollBounds();
invalidate();
}
@Override
public void onChildViewRemoved(View parent, View child) {
- updateFreescrollBounds();
mCurrentPage = validateNewPage(mCurrentPage);
invalidate();
}
@@ -974,11 +959,6 @@
return offset;
}
- protected void getFreeScrollPageRange(int[] range) {
- range[0] = 0;
- range[1] = Math.max(0, getChildCount() - 1);
- }
-
@Override
public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
int page = indexToPage(indexOfChild(child));
@@ -1341,38 +1321,20 @@
/**
* return true if freescroll has been enabled, false otherwise
*/
- public boolean enableFreeScroll() {
+ protected void enableFreeScroll() {
setEnableFreeScroll(true);
- return true;
}
- public void disableFreeScroll() {
+ protected void disableFreeScroll() {
setEnableFreeScroll(false);
}
- void updateFreescrollBounds() {
- getFreeScrollPageRange(mTempVisiblePagesRange);
- if (mIsRtl) {
- mFreeScrollMinScrollX = getScrollForPage(mTempVisiblePagesRange[1]);
- mFreeScrollMaxScrollX = getScrollForPage(mTempVisiblePagesRange[0]);
- } else {
- mFreeScrollMinScrollX = getScrollForPage(mTempVisiblePagesRange[0]);
- mFreeScrollMaxScrollX = getScrollForPage(mTempVisiblePagesRange[1]);
- }
- }
-
private void setEnableFreeScroll(boolean freeScroll) {
boolean wasFreeScroll = mFreeScroll;
mFreeScroll = freeScroll;
if (mFreeScroll) {
- updateFreescrollBounds();
- getFreeScrollPageRange(mTempVisiblePagesRange);
- if (getCurrentPage() < mTempVisiblePagesRange[0]) {
- setCurrentPage(mTempVisiblePagesRange[0]);
- } else if (getCurrentPage() > mTempVisiblePagesRange[1]) {
- setCurrentPage(mTempVisiblePagesRange[1]);
- }
+ setCurrentPage(getNextPage());
} else if (wasFreeScroll) {
snapToPage(getNextPage());
}
@@ -1388,12 +1350,12 @@
if (mDragView != null) {
int dragX = (int) (mDragView.getLeft() + (mDragView.getMeasuredWidth() / 2)
+ mDragView.getTranslationX());
- getFreeScrollPageRange(mTempVisiblePagesRange);
int minDistance = Integer.MAX_VALUE;
int minIndex = indexOfChild(mDragView);
- for (int i = mTempVisiblePagesRange[0]; i <= mTempVisiblePagesRange[1]; i++) {
+ int maxPageNo = getChildCount() - 1;
+ for (int i = 0; i <= maxPageNo; i++) {
View page = getPageAt(i);
- int pageX = (int) (page.getLeft() + page.getMeasuredWidth() / 2);
+ int pageX = (page.getLeft() + page.getMeasuredWidth() / 2);
int d = Math.abs(dragX - pageX);
if (d < minDistance) {
minIndex = i;
@@ -1488,11 +1450,7 @@
final int pageUnderPointIndex = getNearestHoverOverPageIndex();
// Do not allow any page to be moved to 0th position.
if (pageUnderPointIndex > 0 && pageUnderPointIndex != indexOfChild(mDragView)) {
- mTempVisiblePagesRange[0] = 0;
- mTempVisiblePagesRange[1] = getPageCount() - 1;
- getFreeScrollPageRange(mTempVisiblePagesRange);
- if (mTempVisiblePagesRange[0] <= pageUnderPointIndex &&
- pageUnderPointIndex <= mTempVisiblePagesRange[1] &&
+ if (0 <= pageUnderPointIndex && pageUnderPointIndex <= getPageCount() - 1 &&
pageUnderPointIndex != mSidePageHoverIndex && mScroller.isFinished()) {
mSidePageHoverIndex = pageUnderPointIndex;
mSidePageHoverRunnable = new Runnable() {
@@ -1809,16 +1767,6 @@
return PAGE_SNAP_ANIMATION_DURATION;
}
- public static class ScrollInterpolator implements Interpolator {
- public ScrollInterpolator() {
- }
-
- public float getInterpolation(float t) {
- t -= 1.0f;
- return t*t*t*t*t + 1;
- }
- }
-
// We want the duration of the page snap animation to be influenced by the distance that
// the screen has to travel, however, we don't want this duration to be effected in a
// purely linear fashion. Instead, we use this method to moderate the effect that the distance
@@ -2026,18 +1974,14 @@
// Do not allow the first page to be moved around
if (mTouchState != TOUCH_STATE_REST || dragViewIndex <= 0) return false;
- mTempVisiblePagesRange[0] = 0;
- mTempVisiblePagesRange[1] = getPageCount() - 1;
- getFreeScrollPageRange(mTempVisiblePagesRange);
- mReorderingStarted = true;
-
// Check if we are within the reordering range
- if (mTempVisiblePagesRange[0] <= dragViewIndex &&
- dragViewIndex <= mTempVisiblePagesRange[1]) {
+ if (0 <= dragViewIndex && dragViewIndex <= getPageCount() - 1) {
// Find the drag view under the pointer
mDragView = getChildAt(dragViewIndex);
mDragView.animate().scaleX(1.15f).scaleY(1.15f).setDuration(100).start();
mDragViewBaselineLeft = mDragView.getLeft();
+ mReorderingStarted = true;
+
snapToPage(getPageNearestToCenterOfScreen());
disableFreeScroll();
onStartReordering();
diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java
index c2d5501..b86d413 100644
--- a/src/com/android/launcher3/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/PendingAppWidgetHostView.java
@@ -110,9 +110,7 @@
mClickListener = l;
}
- @Override
- public boolean isReinflateRequired(int orientation) {
- // Re inflate is required any time the widget restore status changes
+ public boolean isReinflateIfNeeded() {
return mStartState != mInfo.restoreStatus;
}
diff --git a/src/com/android/launcher3/PinchAnimationManager.java b/src/com/android/launcher3/PinchAnimationManager.java
deleted file mode 100644
index c3d3bb3..0000000
--- a/src/com/android/launcher3/PinchAnimationManager.java
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * Copyright (C) 2016 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 android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.util.Log;
-import android.view.View;
-import android.view.animation.LinearInterpolator;
-
-import com.android.launcher3.anim.AnimationLayerSet;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-
-import static com.android.launcher3.Workspace.State.NORMAL;
-import static com.android.launcher3.Workspace.State.OVERVIEW;
-
-/**
- * Manages the animations that play as the user pinches to/from overview mode.
- *
- * It will look like this pinching in:
- * - Workspace scales down
- * - At some threshold 1, hotseat and QSB fade out (full animation)
- * - At a later threshold 2, panel buttons fade in and scrim fades in
- * - At a final threshold 3, snap to overview
- *
- * Pinching out:
- * - Workspace scales up
- * - At threshold 1, panel buttons fade out
- * - At threshold 2, hotseat and QSB fade in and scrim fades out
- * - At threshold 3, snap to workspace
- *
- * @see PinchToOverviewListener
- * @see PinchThresholdManager
- */
-public class PinchAnimationManager {
- private static final String TAG = "PinchAnimationManager";
-
- private static final int THRESHOLD_ANIM_DURATION = 150;
- private static final LinearInterpolator INTERPOLATOR = new LinearInterpolator();
-
- private static final int INDEX_HOTSEAT = 0;
- private static final int INDEX_OVERVIEW_PANEL_BUTTONS = 1;
- private static final int INDEX_SCRIM = 2;
-
- private final Animator[] mAnimators = new Animator[3];
-
- private Launcher mLauncher;
- private Workspace mWorkspace;
-
- private float mOverviewScale;
- private float mOverviewTranslationY;
- private int mNormalOverviewTransitionDuration;
- private boolean mIsAnimating;
-
- public PinchAnimationManager(Launcher launcher) {
- mLauncher = launcher;
- mWorkspace = launcher.mWorkspace;
-
- mOverviewScale = mWorkspace.getOverviewModeShrinkFactor();
- mOverviewTranslationY = mWorkspace.getOverviewModeTranslationY();
- mNormalOverviewTransitionDuration = mWorkspace.getStateTransitionAnimation()
- .mOverviewTransitionTime;
- }
-
- public int getNormalOverviewTransitionDuration() {
- return mNormalOverviewTransitionDuration;
- }
-
- /**
- * Interpolate from {@param currentProgress} to {@param toProgress}, calling
- * {@link #setAnimationProgress(float)} throughout the duration. If duration is -1,
- * the default overview transition duration is used.
- */
- public void animateToProgress(float currentProgress, float toProgress, int duration,
- final PinchThresholdManager thresholdManager) {
- if (duration == -1) {
- duration = mNormalOverviewTransitionDuration;
- }
- ValueAnimator animator = ValueAnimator.ofFloat(currentProgress, toProgress);
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float pinchProgress = (Float) animation.getAnimatedValue();
- setAnimationProgress(pinchProgress);
- thresholdManager.updateAndAnimatePassedThreshold(pinchProgress,
- PinchAnimationManager.this);
- }
- }
- );
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mIsAnimating = false;
- thresholdManager.reset();
- mWorkspace.onEndStateTransition();
- }
- });
- animator.setDuration(duration).start();
- mIsAnimating = true;
- }
-
- public boolean isAnimating() {
- return mIsAnimating;
- }
-
- /**
- * Animates to the specified progress. This should be called repeatedly throughout the pinch
- * gesture to run animations that interpolate throughout the gesture.
- * @param interpolatedProgress The progress from 0 to 1, where 0 is overview and 1 is workspace.
- */
- public void setAnimationProgress(float interpolatedProgress) {
- float interpolatedScale = interpolatedProgress * (1f - mOverviewScale) + mOverviewScale;
- float interpolatedTranslationY = (1f - interpolatedProgress) * mOverviewTranslationY;
- mWorkspace.setScaleX(interpolatedScale);
- mWorkspace.setScaleY(interpolatedScale);
- mWorkspace.setTranslationY(interpolatedTranslationY);
- setOverviewPanelsAlpha(1f - interpolatedProgress, 0);
- }
-
- /**
- * Animates certain properties based on which threshold was passed, and in what direction. The
- * starting state must also be taken into account because the thresholds mean different things
- * when going from workspace to overview and vice versa.
- * @param threshold One of {@link PinchThresholdManager#THRESHOLD_ONE},
- * {@link PinchThresholdManager#THRESHOLD_TWO}, or
- * {@link PinchThresholdManager#THRESHOLD_THREE}
- * @param startState {@link Workspace.State#NORMAL} or {@link Workspace.State#OVERVIEW}.
- * @param goingTowards {@link Workspace.State#NORMAL} or {@link Workspace.State#OVERVIEW}.
- * Note that this doesn't have to be the opposite of startState;
- */
- public void animateThreshold(float threshold, Workspace.State startState,
- Workspace.State goingTowards) {
- if (threshold == PinchThresholdManager.THRESHOLD_ONE) {
- if (startState == OVERVIEW) {
- animateOverviewPanelButtons(goingTowards == OVERVIEW);
- } else if (startState == NORMAL) {
- animateHotseatAndQsb(goingTowards == NORMAL);
- }
- } else if (threshold == PinchThresholdManager.THRESHOLD_TWO) {
- if (startState == OVERVIEW) {
- animateHotseatAndQsb(goingTowards == NORMAL);
- animateScrim(goingTowards == OVERVIEW);
- } else if (startState == NORMAL) {
- animateOverviewPanelButtons(goingTowards == OVERVIEW);
- animateScrim(goingTowards == OVERVIEW);
- }
- } else if (threshold == PinchThresholdManager.THRESHOLD_THREE) {
- // Passing threshold 3 ends the pinch and snaps to the new state.
- if (startState == OVERVIEW && goingTowards == NORMAL) {
- mLauncher.getUserEventDispatcher().logActionOnContainer(
- Action.Touch.PINCH, Action.Direction.NONE,
- ContainerType.OVERVIEW, mWorkspace.getCurrentPage());
- mLauncher.showWorkspace(true);
- mWorkspace.snapToPage(mWorkspace.getCurrentPage());
- } else if (startState == NORMAL && goingTowards == OVERVIEW) {
- mLauncher.getUserEventDispatcher().logActionOnContainer(
- Action.Touch.PINCH, Action.Direction.NONE,
- ContainerType.WORKSPACE, mWorkspace.getCurrentPage());
- mLauncher.showOverviewMode(true);
- }
- } else {
- Log.e(TAG, "Received unknown threshold to animate: " + threshold);
- }
- }
-
- private void setOverviewPanelsAlpha(float alpha, int duration) {
- int childCount = mWorkspace.getChildCount();
- for (int i = 0; i < childCount; i++) {
- final CellLayout cl = (CellLayout) mWorkspace.getChildAt(i);
- if (duration == 0) {
- cl.setBackgroundAlpha(alpha);
- } else {
- ObjectAnimator.ofFloat(cl, "backgroundAlpha", alpha).setDuration(duration).start();
- }
- }
- }
-
- private void animateHotseatAndQsb(boolean show) {
- startAnimator(INDEX_HOTSEAT,
- mWorkspace.createHotseatAlphaAnimator(show ? 1 : 0), THRESHOLD_ANIM_DURATION);
- }
-
- private void animateOverviewPanelButtons(boolean show) {
- animateShowHideView(INDEX_OVERVIEW_PANEL_BUTTONS, mLauncher.getOverviewPanel(), show);
- }
-
- private void animateScrim(boolean show) {
- float endValue = show ? mWorkspace.getStateTransitionAnimation().mWorkspaceScrimAlpha : 0;
- startAnimator(INDEX_SCRIM,
- ObjectAnimator.ofFloat(mLauncher.getDragLayer(), "backgroundAlpha", endValue),
- mNormalOverviewTransitionDuration);
- }
-
- private void animateShowHideView(int index, final View view, boolean show) {
- Animator animator = ObjectAnimator.ofFloat(view, View.ALPHA, show ? 1 : 0);
- animator.addListener(new AnimationLayerSet(view));
- if (show) {
- view.setVisibility(View.VISIBLE);
- } else {
- animator.addListener(new AnimatorListenerAdapter() {
- private boolean mCancelled = false;
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mCancelled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (!mCancelled) {
- view.setVisibility(View.INVISIBLE);
- }
- }
- });
- }
- startAnimator(index, animator, THRESHOLD_ANIM_DURATION);
- }
-
- private void startAnimator(int index, Animator animator, long duration) {
- if (mAnimators[index] != null) {
- mAnimators[index].cancel();
- }
- mAnimators[index] = animator;
- mAnimators[index].setInterpolator(INTERPOLATOR);
- mAnimators[index].setDuration(duration).start();
- }
-}
diff --git a/src/com/android/launcher3/PinchThresholdManager.java b/src/com/android/launcher3/PinchThresholdManager.java
deleted file mode 100644
index 52aac17..0000000
--- a/src/com/android/launcher3/PinchThresholdManager.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2016 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;
-
-/**
- * Keeps track of when thresholds are passed during a pinch gesture,
- * used to inform {@link PinchAnimationManager} throughout.
- *
- * @see PinchToOverviewListener
- * @see PinchAnimationManager
- */
-public class PinchThresholdManager {
- public static final float THRESHOLD_ZERO = 0.0f;
- public static final float THRESHOLD_ONE = 0.40f;
- public static final float THRESHOLD_TWO = 0.70f;
- public static final float THRESHOLD_THREE = 0.95f;
-
- private Workspace mWorkspace;
-
- private float mPassedThreshold = THRESHOLD_ZERO;
-
- public PinchThresholdManager(Workspace workspace) {
- mWorkspace = workspace;
- }
-
- /**
- * Uses the pinch progress to determine whether a threshold has been passed,
- * and asks the {@param animationManager} to animate if so.
- * @param progress From 0 to 1, where 0 is overview and 1 is workspace.
- * @param animationManager Animates the threshold change if one is passed.
- * @return The last passed threshold, one of
- * {@link PinchThresholdManager#THRESHOLD_ZERO},
- * {@link PinchThresholdManager#THRESHOLD_ONE},
- * {@link PinchThresholdManager#THRESHOLD_TWO}, or
- * {@link PinchThresholdManager#THRESHOLD_THREE}
- */
- public float updateAndAnimatePassedThreshold(float progress,
- PinchAnimationManager animationManager) {
- if (!mWorkspace.isInOverviewMode()) {
- // Invert the progress, because going from workspace to overview is 1 to 0.
- progress = 1f - progress;
- }
-
- float previousPassedThreshold = mPassedThreshold;
-
- if (progress < THRESHOLD_ONE) {
- mPassedThreshold = THRESHOLD_ZERO;
- } else if (progress < THRESHOLD_TWO) {
- mPassedThreshold = THRESHOLD_ONE;
- } else if (progress < THRESHOLD_THREE) {
- mPassedThreshold = THRESHOLD_TWO;
- } else {
- mPassedThreshold = THRESHOLD_THREE;
- }
-
- if (mPassedThreshold != previousPassedThreshold) {
- Workspace.State fromState = mWorkspace.isInOverviewMode() ? Workspace.State.OVERVIEW
- : Workspace.State.NORMAL;
- Workspace.State toState = mWorkspace.isInOverviewMode() ? Workspace.State.NORMAL
- : Workspace.State.OVERVIEW;
- float thresholdToAnimate = mPassedThreshold;
- if (mPassedThreshold < previousPassedThreshold) {
- // User reversed pinch, so heading back to the state that they started from.
- toState = fromState;
- thresholdToAnimate = previousPassedThreshold;
- }
- animationManager.animateThreshold(thresholdToAnimate, fromState, toState);
- }
- return mPassedThreshold;
- }
-
- public float getPassedThreshold() {
- return mPassedThreshold;
- }
-
- public void reset() {
- mPassedThreshold = THRESHOLD_ZERO;
- }
-}
diff --git a/src/com/android/launcher3/PinchToOverviewListener.java b/src/com/android/launcher3/PinchToOverviewListener.java
index 42515d1..27edaf6 100644
--- a/src/com/android/launcher3/PinchToOverviewListener.java
+++ b/src/com/android/launcher3/PinchToOverviewListener.java
@@ -16,49 +16,47 @@
package com.android.launcher3;
-import android.animation.TimeInterpolator;
-import android.content.Context;
+import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
+import android.view.ScaleGestureDetector.OnScaleGestureListener;
+import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.util.TouchController;
/**
* Detects pinches and animates the Workspace to/from overview mode.
- *
- * Usage: Pass MotionEvents to onInterceptTouchEvent() and onTouchEvent(). This class will handle
- * the pinch detection, and use {@link PinchAnimationManager} to handle the animations.
- *
- * @see PinchThresholdManager
- * @see PinchAnimationManager
*/
-public class PinchToOverviewListener extends ScaleGestureDetector.SimpleOnScaleGestureListener
- implements TouchController {
- private static final float OVERVIEW_PROGRESS = 0f;
- private static final float WORKSPACE_PROGRESS = 1f;
+public class PinchToOverviewListener extends AnimatorListenerAdapter
+ implements TouchController, OnScaleGestureListener {
+
+ private static final float ACCEPT_THRESHOLD = 0.65f;
/**
* The velocity threshold at which a pinch will be completed instead of canceled,
- * even if the first threshold has not been passed. Measured in progress / millisecond
+ * even if the first threshold has not been passed. Measured in scale / millisecond
*/
- private static final float FLING_VELOCITY = 0.003f;
+ private static final float FLING_VELOCITY = 0.001f;
- private ScaleGestureDetector mPinchDetector;
+ private final ScaleGestureDetector mPinchDetector;
private Launcher mLauncher;
private Workspace mWorkspace = null;
private boolean mPinchStarted = false;
- private float mPreviousProgress;
- private float mProgressDelta;
- private long mPreviousTimeMillis;
- private long mTimeDelta;
- private boolean mPinchCanceled = false;
- private TimeInterpolator mInterpolator;
- private PinchThresholdManager mThresholdManager;
- private PinchAnimationManager mAnimationManager;
+ private AnimatorPlaybackController mCurrentAnimation;
+ private float mCurrentScale;
+ private boolean mShouldGoToFinalState;
+
+ private LauncherState mToState;
public PinchToOverviewListener(Launcher launcher) {
mLauncher = launcher;
- mPinchDetector = new ScaleGestureDetector((Context) mLauncher, this);
+ mPinchDetector = new ScaleGestureDetector(mLauncher, this);
}
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
@@ -67,24 +65,17 @@
}
public boolean onControllerTouchEvent(MotionEvent ev) {
- if (mPinchStarted) {
- if (ev.getPointerCount() > 2) {
- // Using more than two fingers causes weird behavior, so just cancel the pinch.
- cancelPinch(mPreviousProgress, -1);
- } else {
- return mPinchDetector.onTouchEvent(ev);
- }
- }
- return false;
+ return mPinchDetector.onTouchEvent(ev);
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
- if (mLauncher.mState != Launcher.State.WORKSPACE || mLauncher.isOnCustomContent()) {
+ if (!mLauncher.isInState(NORMAL)
+ && !mLauncher.isInState(OVERVIEW)) {
// Don't listen for the pinch gesture if on all apps, widget picker, -1, etc.
return false;
}
- if (mAnimationManager != null && mAnimationManager.isAnimating()) {
+ if (mCurrentAnimation != null) {
// Don't listen for the pinch gesture if we are already animating from a previous one.
return false;
}
@@ -94,8 +85,6 @@
}
if (mWorkspace == null) {
mWorkspace = mLauncher.getWorkspace();
- mThresholdManager = new PinchThresholdManager(mWorkspace);
- mAnimationManager = new PinchAnimationManager(mLauncher);
}
if (mWorkspace.isSwitchingState() || mWorkspace.mScrollInteractionBegan) {
// Don't listen for the pinch gesture while switching state, as it will cause a jump
@@ -107,109 +96,63 @@
return false;
}
- mPreviousProgress = mWorkspace.isInOverviewMode() ? OVERVIEW_PROGRESS : WORKSPACE_PROGRESS;
- mPreviousTimeMillis = System.currentTimeMillis();
- mInterpolator = mWorkspace.isInOverviewMode() ? new LogDecelerateInterpolator(100, 0)
- : new LogAccelerateInterpolator(100, 0);
- mPinchStarted = true;
- mWorkspace.onPrepareStateTransition(true);
- return true;
- }
-
- @Override
- public void onScaleEnd(ScaleGestureDetector detector) {
- super.onScaleEnd(detector);
-
- float progressVelocity = mProgressDelta / mTimeDelta;
- float passedThreshold = mThresholdManager.getPassedThreshold();
- boolean isFling = mWorkspace.isInOverviewMode() && progressVelocity >= FLING_VELOCITY
- || !mWorkspace.isInOverviewMode() && progressVelocity <= -FLING_VELOCITY;
- boolean shouldCancelPinch = !isFling && passedThreshold < PinchThresholdManager.THRESHOLD_ONE;
- // If we are going towards overview, mPreviousProgress is how much further we need to
- // go, since it is going from 1 to 0. If we are going to workspace, we want
- // 1 - mPreviousProgress.
- float remainingProgress = mPreviousProgress;
- if (mWorkspace.isInOverviewMode() || shouldCancelPinch) {
- remainingProgress = 1f - mPreviousProgress;
- }
- int duration = computeDuration(remainingProgress, progressVelocity);
- if (shouldCancelPinch) {
- cancelPinch(mPreviousProgress, duration);
- } else if (passedThreshold < PinchThresholdManager.THRESHOLD_THREE) {
- float toProgress = mWorkspace.isInOverviewMode() ?
- WORKSPACE_PROGRESS : OVERVIEW_PROGRESS;
- mAnimationManager.animateToProgress(mPreviousProgress, toProgress, duration,
- mThresholdManager);
- } else {
- mThresholdManager.reset();
- mWorkspace.onEndStateTransition();
- }
- mPinchStarted = false;
- mPinchCanceled = false;
- }
-
- /**
- * Compute the amount of time required to complete the transition based on the current pinch
- * speed. If this time is too long, instead return the normal duration, ignoring the speed.
- */
- private int computeDuration(float remainingProgress, float progressVelocity) {
- float progressSpeed = Math.abs(progressVelocity);
- int remainingMillis = (int) (remainingProgress / progressSpeed);
- return Math.min(remainingMillis, mAnimationManager.getNormalOverviewTransitionDuration());
- }
-
- /**
- * Cancels the current pinch, returning back to where the pinch started (either workspace or
- * overview). If duration is -1, the default overview transition duration is used.
- */
- private void cancelPinch(float currentProgress, int duration) {
- if (mPinchCanceled) return;
- mPinchCanceled = true;
- float toProgress = mWorkspace.isInOverviewMode() ? OVERVIEW_PROGRESS : WORKSPACE_PROGRESS;
- mAnimationManager.animateToProgress(currentProgress, toProgress, duration,
- mThresholdManager);
- mPinchStarted = false;
- }
-
- @Override
- public boolean onScale(ScaleGestureDetector detector) {
- if (mThresholdManager.getPassedThreshold() == PinchThresholdManager.THRESHOLD_THREE) {
- // We completed the pinch, so stop listening to further movement until user lets go.
- return true;
- }
if (mLauncher.getDragController().isDragging()) {
mLauncher.getDragController().cancelDrag();
}
- float pinchDist = detector.getCurrentSpan() - detector.getPreviousSpan();
- if (pinchDist < 0 && mWorkspace.isInOverviewMode() ||
- pinchDist > 0 && !mWorkspace.isInOverviewMode()) {
- // Pinching the wrong way, so ignore.
- return false;
- }
- // Pinch distance must equal the workspace width before switching states.
- int pinchDistanceToCompleteTransition = mWorkspace.getWidth();
- float overviewScale = mWorkspace.getOverviewModeShrinkFactor();
- float initialWorkspaceScale = mWorkspace.isInOverviewMode() ? overviewScale : 1f;
- float pinchScale = initialWorkspaceScale + pinchDist / pinchDistanceToCompleteTransition;
- // Bound the scale between the overview scale and the normal workspace scale (1f).
- pinchScale = Math.max(overviewScale, Math.min(pinchScale, 1f));
- // Progress ranges from 0 to 1, where 0 corresponds to the overview scale and 1
- // corresponds to the normal workspace scale (1f).
- float progress = (pinchScale - overviewScale) / (1f - overviewScale);
- float interpolatedProgress = mInterpolator.getInterpolation(progress);
+ mToState = mLauncher.isInState(OVERVIEW) ? NORMAL : OVERVIEW;
+ mCurrentAnimation = mLauncher.getStateManager()
+ .createAnimationToNewWorkspace(mToState, OVERVIEW_TRANSITION_MS);
+ mCurrentAnimation.getTarget().addListener(this);
+ mPinchStarted = true;
+ mCurrentScale = 1;
+ mShouldGoToFinalState = false;
- mAnimationManager.setAnimationProgress(interpolatedProgress);
- float passedThreshold = mThresholdManager.updateAndAnimatePassedThreshold(
- interpolatedProgress, mAnimationManager);
- if (passedThreshold == PinchThresholdManager.THRESHOLD_THREE) {
- return true;
+ mCurrentAnimation.dispatchOnStart();
+ return true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mCurrentAnimation = null;
+ mPinchStarted = false;
+ }
+
+ @Override
+ public void onScaleEnd(ScaleGestureDetector detector) {
+ if (mShouldGoToFinalState) {
+ mCurrentAnimation.start();
+ } else {
+ mCurrentAnimation.setEndAction(new Runnable() {
+ @Override
+ public void run() {
+ mLauncher.getStateManager().goToState(
+ mToState == OVERVIEW ? NORMAL : OVERVIEW, false);
+ }
+ });
+ mCurrentAnimation.reverse();
+ }
+ }
+
+ @Override
+ public boolean onScale(ScaleGestureDetector detector) {
+ mCurrentScale = detector.getScaleFactor() * mCurrentScale;
+
+ // If we are zooming out, inverse the mCurrentScale so that animationFraction = [0, 1]
+ // 0 => Animation complete
+ // 1=> Animation started
+ float animationFraction = mToState == OVERVIEW ? mCurrentScale : (1 / mCurrentScale);
+
+ float velocity = (1 - detector.getScaleFactor()) / detector.getTimeDelta();
+ if (Math.abs(velocity) >= FLING_VELOCITY) {
+ LauncherState toState = velocity > 0 ? OVERVIEW : NORMAL;
+ mShouldGoToFinalState = toState == mToState;
+ } else {
+ mShouldGoToFinalState = animationFraction <= ACCEPT_THRESHOLD;
}
- mProgressDelta = interpolatedProgress - mPreviousProgress;
- mPreviousProgress = interpolatedProgress;
- mTimeDelta = System.currentTimeMillis() - mPreviousTimeMillis;
- mPreviousTimeMillis = System.currentTimeMillis();
- return false;
+ // Move the transition animation to that duration.
+ mCurrentAnimation.setPlayFraction(1 - animationFraction);
+ return true;
}
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index a7e68ff..841c0cd 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -108,28 +108,21 @@
public void measureChild(View child) {
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
- if (!lp.isFullscreen) {
- final DeviceProfile profile = mLauncher.getDeviceProfile();
+ final DeviceProfile profile = mLauncher.getDeviceProfile();
- if (child instanceof LauncherAppWidgetHostView) {
- lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX,
- profile.appWidgetScale.x, profile.appWidgetScale.y);
- // Widgets have their own padding
- } else {
- lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX);
- // Center the icon/folder
- int cHeight = getCellContentHeight();
- int cellPaddingY = (int) Math.max(0, ((lp.height - cHeight) / 2f));
- int cellPaddingX = mContainerType == CellLayout.WORKSPACE
- ? profile.workspaceCellPaddingXPx
- : (int) (profile.edgeMarginPx / 2f);
- child.setPadding(cellPaddingX, cellPaddingY, cellPaddingX, 0);
- }
+ if (child instanceof LauncherAppWidgetHostView) {
+ lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX,
+ profile.appWidgetScale.x, profile.appWidgetScale.y);
+ // Widgets have their own padding
} else {
- lp.x = 0;
- lp.y = 0;
- lp.width = getMeasuredWidth();
- lp.height = getMeasuredHeight();
+ lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX);
+ // Center the icon/folder
+ int cHeight = getCellContentHeight();
+ int cellPaddingY = (int) Math.max(0, ((lp.height - cHeight) / 2f));
+ int cellPaddingX = mContainerType == CellLayout.WORKSPACE
+ ? profile.workspaceCellPaddingXPx
+ : (int) (profile.edgeMarginPx / 2f);
+ child.setPadding(cellPaddingX, cellPaddingY, cellPaddingX, 0);
}
int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index adf008b..ec608ca 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -65,13 +65,6 @@
public static final int FLAG_SUPPORTS_WEB_UI = 16; //0B10000;
/**
- * Indicates if it represents a common type mentioned in {@link CommonAppTypeParser}.
- * Upto 15 different types supported.
- */
- @Deprecated
- public static final int FLAG_RESTORED_APP_TYPE = 0B0011110000;
-
- /**
* The intent used to start the application.
*/
public Intent intent;
@@ -83,42 +76,6 @@
public Intent.ShortcutIconResource iconResource;
/**
- * Indicates that the icon is disabled due to safe mode restrictions.
- */
- public static final int FLAG_DISABLED_SAFEMODE = 1 << 0;
-
- /**
- * Indicates that the icon is disabled as the app is not available.
- */
- public static final int FLAG_DISABLED_NOT_AVAILABLE = 1 << 1;
-
- /**
- * Indicates that the icon is disabled as the app is suspended
- */
- public static final int FLAG_DISABLED_SUSPENDED = 1 << 2;
-
- /**
- * Indicates that the icon is disabled as the user is in quiet mode.
- */
- public static final int FLAG_DISABLED_QUIET_USER = 1 << 3;
-
- /**
- * Indicates that the icon is disabled as the publisher has disabled the actual shortcut.
- */
- public static final int FLAG_DISABLED_BY_PUBLISHER = 1 << 4;
-
- /**
- * Indicates that the icon is disabled as the user partition is currently locked.
- */
- public static final int FLAG_DISABLED_LOCKED_USER = 1 << 5;
-
- /**
- * Could be disabled, if the the app is installed but unavailable (eg. in safe mode or when
- * sd-card is not available).
- */
- public int isDisabled = DEFAULT;
-
- /**
* A message to display when the user tries to start a disabled shortcut.
* This is currently only used for deep shortcuts.
*/
@@ -142,7 +99,6 @@
iconResource = info.iconResource;
status = info.status;
mInstallProgress = info.mInstallProgress;
- isDisabled = info.isDisabled;
}
/** TODO: Remove this. It's only called by ApplicationInfo.makeShortcut. */
@@ -150,7 +106,6 @@
super(info);
title = Utilities.trim(info.title);
intent = new Intent(info.intent);
- isDisabled = info.isDisabled;
}
/**
@@ -219,9 +174,9 @@
contentDescription = UserManagerCompat.getInstance(context)
.getBadgedLabelForUser(label, user);
if (shortcutInfo.isEnabled()) {
- isDisabled &= ~FLAG_DISABLED_BY_PUBLISHER;
+ runtimeStatusFlags &= ~FLAG_DISABLED_BY_PUBLISHER;
} else {
- isDisabled |= FLAG_DISABLED_BY_PUBLISHER;
+ runtimeStatusFlags |= FLAG_DISABLED_BY_PUBLISHER;
}
disabledMessage = shortcutInfo.getDisabledMessage();
}
@@ -233,11 +188,6 @@
}
@Override
- public boolean isDisabled() {
- return isDisabled != 0;
- }
-
- @Override
public ComponentName getTargetComponent() {
ComponentName cn = super.getTargetComponent();
if (cn == null && (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java
index 902fd34..68a441a 100644
--- a/src/com/android/launcher3/UninstallDropTarget.java
+++ b/src/com/android/launcher3/UninstallDropTarget.java
@@ -1,5 +1,8 @@
package com.android.launcher3;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_SYSTEM_MASK;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_SYSTEM_NO;
+
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -10,18 +13,28 @@
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.View;
import android.widget.Toast;
+import com.android.launcher3.Launcher.OnResumeCallback;
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import java.net.URISyntaxException;
-public class UninstallDropTarget extends ButtonDropTarget {
+public class UninstallDropTarget extends ButtonDropTarget implements OnAlarmListener {
private static final String TAG = "UninstallDropTarget";
- private static Boolean sUninstallDisabled;
+
+ private static final long CACHE_EXPIRE_TIMEOUT = 5000;
+ private final ArrayMap<UserHandle, Boolean> mUninstallDisabledCache = new ArrayMap<>(1);
+
+ private final Alarm mCacheExpireAlarm;
public UninstallDropTarget(Context context, AttributeSet attrs) {
this(context, attrs, 0);
@@ -29,6 +42,9 @@
public UninstallDropTarget(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+
+ mCacheExpireAlarm = new Alarm();
+ mCacheExpireAlarm.setOnAlarmListener(this);
}
@Override
@@ -44,34 +60,45 @@
}
@Override
- protected boolean supportsDrop(DragSource source, ItemInfo info) {
- return supportsDrop(getContext(), info);
+ public void onAlarm(Alarm alarm) {
+ mUninstallDisabledCache.clear();
}
- public static boolean supportsDrop(Context context, ItemInfo info) {
- if (sUninstallDisabled == null) {
- UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
- Bundle restrictions = userManager.getUserRestrictions();
- sUninstallDisabled = restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false)
+ @Override
+ public int getAccessibilityAction() {
+ return LauncherAccessibilityDelegate.UNINSTALL;
+ }
+
+ @Override
+ protected boolean supportsDrop(ItemInfo info) {
+ Boolean uninstallDisabled = mUninstallDisabledCache.get(info.user);
+ if (uninstallDisabled == null) {
+ UserManager userManager =
+ (UserManager) getContext().getSystemService(Context.USER_SERVICE);
+ Bundle restrictions = userManager.getUserRestrictions(info.user);
+ uninstallDisabled = restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false)
|| restrictions.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS, false);
+ mUninstallDisabledCache.put(info.user, uninstallDisabled);
}
- if (sUninstallDisabled) {
+ // Cancel any pending alarm and set cache expiry after some time
+ mCacheExpireAlarm.setAlarm(CACHE_EXPIRE_TIMEOUT);
+ if (uninstallDisabled) {
return false;
}
- if (info instanceof AppInfo) {
- AppInfo appInfo = (AppInfo) info;
- if (appInfo.isSystemApp != AppInfo.FLAG_SYSTEM_UNKNOWN) {
- return (appInfo.isSystemApp & AppInfo.FLAG_SYSTEM_NO) != 0;
+ if (info instanceof ItemInfoWithIcon) {
+ ItemInfoWithIcon iconInfo = (ItemInfoWithIcon) info;
+ if ((iconInfo.runtimeStatusFlags & FLAG_SYSTEM_MASK) != 0) {
+ return (iconInfo.runtimeStatusFlags & FLAG_SYSTEM_NO) != 0;
}
}
- return getUninstallTarget(context, info) != null;
+ return getUninstallTarget(info) != null;
}
/**
* @return the component name that should be uninstalled or null.
*/
- private static ComponentName getUninstallTarget(Context context, ItemInfo item) {
+ private ComponentName getUninstallTarget(ItemInfo item) {
Intent intent = null;
UserHandle user = null;
if (item != null &&
@@ -80,7 +107,7 @@
user = item.user;
}
if (intent != null) {
- LauncherActivityInfo info = LauncherAppsCompat.getInstance(context)
+ LauncherActivityInfo info = LauncherAppsCompat.getInstance(mLauncher)
.resolveActivity(intent, user);
if (info != null
&& (info.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
@@ -91,98 +118,101 @@
}
@Override
- public void onDrop(DragObject d) {
- // Differ item deletion
- if (d.dragSource instanceof DropTargetSource) {
- ((DropTargetSource) d.dragSource).deferCompleteDropAfterUninstallActivity();
- }
- super.onDrop(d);
+ public void onDrop(DragObject d, DragOptions options) {
+ // Defer onComplete
+ d.dragSource = new DeferredOnComplete(d.dragSource, getContext());
+ super.onDrop(d, options);
}
@Override
public void completeDrop(final DragObject d) {
- DropTargetResultCallback callback = d.dragSource instanceof DropTargetResultCallback
- ? (DropTargetResultCallback) d.dragSource : null;
- startUninstallActivity(mLauncher, d.dragInfo, callback);
+ ComponentName target = performDropAction(d.dragInfo);
+ if (d.dragSource instanceof DeferredOnComplete) {
+ DeferredOnComplete deferred = (DeferredOnComplete) d.dragSource;
+ if (target != null) {
+ deferred.mPackageName = target.getPackageName();
+ mLauncher.setOnResumeCallback(deferred);
+ } else {
+ deferred.sendFailure();
+ }
+ }
}
- public static boolean startUninstallActivity(Launcher launcher, ItemInfo info) {
- return startUninstallActivity(launcher, info, null);
- }
-
- public static boolean startUninstallActivity(
- final Launcher launcher, ItemInfo info, DropTargetResultCallback callback) {
- final ComponentName cn = getUninstallTarget(launcher, info);
-
- boolean canUninstall;
+ /**
+ * Performs the drop action and returns the target component for the dragObject or null if
+ * the action was not performed.
+ */
+ protected ComponentName performDropAction(ItemInfo info) {
+ ComponentName cn = getUninstallTarget(info);
if (cn == null) {
// System applications cannot be installed. For now, show a toast explaining that.
// We may give them the option of disabling apps this way.
- Toast.makeText(launcher, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show();
- canUninstall = false;
- } else {
- try {
- Intent i = Intent.parseUri(launcher.getString(R.string.delete_package_intent), 0)
- .setData(Uri.fromParts("package", cn.getPackageName(), cn.getClassName()))
- .putExtra(Intent.EXTRA_USER, info.user);
- launcher.startActivity(i);
- canUninstall = true;
- } catch (URISyntaxException e) {
- Log.e(TAG, "Failed to parse intent to start uninstall activity for item=" + info);
- canUninstall = false;
+ Toast.makeText(mLauncher, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show();
+ return null;
+ }
+ try {
+ Intent i = Intent.parseUri(mLauncher.getString(R.string.delete_package_intent), 0)
+ .setData(Uri.fromParts("package", cn.getPackageName(), cn.getClassName()))
+ .putExtra(Intent.EXTRA_USER, info.user);
+ mLauncher.startActivity(i);
+ return cn;
+ } catch (URISyntaxException e) {
+ Log.e(TAG, "Failed to parse intent to start uninstall activity for item=" + info);
+ return null;
+ }
+ }
+
+ @Override
+ public void onAccessibilityDrop(View view, ItemInfo item) {
+ performDropAction(item);
+ }
+
+ /**
+ * A wrapper around {@link DragSource} which delays the {@link #onDropCompleted} action until
+ * {@link #onLauncherResume}
+ */
+ private class DeferredOnComplete implements DragSource, OnResumeCallback {
+
+ private final DragSource mOriginal;
+ private final Context mContext;
+
+ private String mPackageName;
+ private DragObject mDragObject;
+
+ public DeferredOnComplete(DragSource original, Context context) {
+ mOriginal = original;
+ mContext = context;
+ }
+
+ @Override
+ public void onDropCompleted(View target, DragObject d,
+ boolean success) {
+ mDragObject = d;
+ }
+
+ @Override
+ public void fillInLogContainerData(View v, ItemInfo info, Target target,
+ Target targetParent) {
+ mOriginal.fillInLogContainerData(v, info, target, targetParent);
+ }
+
+ @Override
+ public void onLauncherResume() {
+ // We use MATCH_UNINSTALLED_PACKAGES as the app can be on SD card as well.
+ if (LauncherAppsCompat.getInstance(mContext)
+ .getApplicationInfo(mPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
+ mDragObject.dragInfo.user) == null) {
+ mDragObject.dragSource = mOriginal;
+ mOriginal.onDropCompleted(UninstallDropTarget.this, mDragObject, true);
+ } else {
+ sendFailure();
}
}
- if (callback != null) {
- sendUninstallResult(launcher, canUninstall, cn, info.user, callback);
+
+ public void sendFailure() {
+ mDragObject.dragSource = mOriginal;
+ mDragObject.cancelled = true;
+ mOriginal.onDropCompleted(UninstallDropTarget.this, mDragObject, false);
}
- return canUninstall;
- }
-
- /**
- * Notifies the {@param callback} whether the uninstall was successful or not.
- *
- * Since there is no direct callback for an uninstall request, we check the package existence
- * when the launch resumes next time. This assumes that the uninstall activity will finish only
- * after the task is completed
- */
- protected static void sendUninstallResult(
- final Launcher launcher, boolean activityStarted,
- final ComponentName cn, final UserHandle user,
- final DropTargetResultCallback callback) {
- if (activityStarted) {
- final Runnable checkIfUninstallWasSuccess = new Runnable() {
- @Override
- public void run() {
- // We use MATCH_UNINSTALLED_PACKAGES as the app can be on SD card as well.
- boolean uninstallSuccessful = LauncherAppsCompat.getInstance(launcher)
- .getApplicationInfo(cn.getPackageName(),
- PackageManager.MATCH_UNINSTALLED_PACKAGES, user) == null;
- callback.onDragObjectRemoved(uninstallSuccessful);
- }
- };
- launcher.addOnResumeCallback(checkIfUninstallWasSuccess);
- } else {
- callback.onDragObjectRemoved(false);
- }
- }
-
- public interface DropTargetResultCallback {
- /**
- * A drag operation was complete.
- * @param isRemoved true if the drag object should be removed, false otherwise.
- */
- void onDragObjectRemoved(boolean isRemoved);
- }
-
- /**
- * Interface defining an object that can provide uninstallable drag objects.
- */
- public interface DropTargetSource extends DropTargetResultCallback {
-
- /**
- * Indicates that an uninstall request are made and the actual result may come
- * after some time.
- */
- void deferCompleteDropAfterUninstallActivity();
}
}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index b6876f6..e6bc770 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -309,6 +309,9 @@
float highScore = -1;
int bestHue = -1;
+ int[] pixels = new int[samples];
+ int pixelCount = 0;
+
for (int y = 0; y < height; y += sampleStride) {
for (int x = 0; x < width; x += sampleStride) {
int argb = bitmap.getPixel(x, y);
@@ -326,6 +329,9 @@
// Defensively avoid array bounds violations.
continue;
}
+ if (pixelCount < samples) {
+ pixels[pixelCount++] = rgb;
+ }
float score = hsv[1] * hsv[2];
hueScoreHistogram[hue] += score;
if (hueScoreHistogram[hue] > highScore) {
@@ -335,31 +341,29 @@
}
}
- SparseArray<Float> rgbScores = new SparseArray<Float>();
+ SparseArray<Float> rgbScores = new SparseArray<>();
int bestColor = 0xff000000;
highScore = -1;
// Go back over the RGB colors that match the winning hue,
// creating a histogram of weighted s*v scores, for up to 100*100 [s,v] buckets.
// The highest-scoring RGB color wins.
- for (int y = 0; y < height; y += sampleStride) {
- for (int x = 0; x < width; x += sampleStride) {
- int rgb = bitmap.getPixel(x, y) | 0xff000000;
- Color.colorToHSV(rgb, hsv);
- int hue = (int) hsv[0];
- if (hue == bestHue) {
- float s = hsv[1];
- float v = hsv[2];
- int bucket = (int) (s * 100) + (int) (v * 10000);
- // Score by cumulative saturation * value.
- float score = s * v;
- Float oldTotal = rgbScores.get(bucket);
- float newTotal = oldTotal == null ? score : oldTotal + score;
- rgbScores.put(bucket, newTotal);
- if (newTotal > highScore) {
- highScore = newTotal;
- // All the colors in the winning bucket are very similar. Last in wins.
- bestColor = rgb;
- }
+ for (int i = 0; i < pixelCount; i++) {
+ int rgb = pixels[i];
+ Color.colorToHSV(rgb, hsv);
+ int hue = (int) hsv[0];
+ if (hue == bestHue) {
+ float s = hsv[1];
+ float v = hsv[2];
+ int bucket = (int) (s * 100) + (int) (v * 10000);
+ // Score by cumulative saturation * value.
+ float score = s * v;
+ Float oldTotal = rgbScores.get(bucket);
+ float newTotal = oldTotal == null ? score : oldTotal + score;
+ rgbScores.put(bucket, newTotal);
+ if (newTotal > highScore) {
+ highScore = newTotal;
+ // All the colors in the winning bucket are very similar. Last in wins.
+ bestColor = rgb;
}
}
}
@@ -610,6 +614,12 @@
return c == null || c.isEmpty();
}
+ public static boolean isAccessibilityEnabled(Context context) {
+ AccessibilityManager accessibilityManager = (AccessibilityManager)
+ context.getSystemService(Context.ACCESSIBILITY_SERVICE);
+ return accessibilityManager.isEnabled();
+ }
+
public static void sendCustomAccessibilityEvent(View target, int type, String text) {
AccessibilityManager accessibilityManager = (AccessibilityManager)
target.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
diff --git a/src/com/android/launcher3/VerticalSwipeController.java b/src/com/android/launcher3/VerticalSwipeController.java
new file mode 100644
index 0000000..12c6916
--- /dev/null
+++ b/src/com/android/launcher3/VerticalSwipeController.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2017 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.ALL_APPS;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
+import static com.android.launcher3.anim.SpringAnimationHandler.Y_DIRECTION;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.support.animation.SpringAnimation;
+import android.util.Log;
+import android.view.MotionEvent;
+
+import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.SpringAnimationHandler;
+import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.util.TouchController;
+
+import java.util.ArrayList;
+
+/**
+ * Handles vertical touch gesture on the DragLayer
+ */
+public class VerticalSwipeController extends AnimatorListenerAdapter
+ implements TouchController, SwipeDetector.Listener {
+
+ private static final String TAG = "VerticalSwipeController";
+
+ private static final float RECATCH_REJECTION_FRACTION = .0875f;
+ private static final int SINGLE_FRAME_MS = 16;
+
+ // Progress after which the transition is assumed to be a success in case user does not fling
+ private static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
+
+ private final Launcher mLauncher;
+ private final SwipeDetector mDetector;
+
+ private boolean mNoIntercept;
+ private int mStartContainerType;
+
+ private AnimatorPlaybackController mCurrentAnimation;
+ private LauncherState mToState;
+
+ private float mStartProgress;
+ // Ratio of transition process [0, 1] to drag displacement (px)
+ private float mProgressMultiplier;
+
+ private SpringAnimationHandler[] mSpringHandlers;
+
+ public VerticalSwipeController(Launcher l) {
+ mLauncher = l;
+ mDetector = new SwipeDetector(l, this, SwipeDetector.VERTICAL);
+ }
+
+ private boolean canInterceptTouch(MotionEvent ev) {
+ if (!mLauncher.isInState(NORMAL) && !mLauncher.isInState(ALL_APPS)) {
+ // Don't listen for the pinch gesture if on all apps, widget picker, -1, etc.
+ return false;
+ }
+ if (mCurrentAnimation != null) {
+ // If we are already animating from a previous state, we can intercept.
+ return true;
+ }
+ if (mLauncher.isInState(ALL_APPS) && !mLauncher.getAppsView().shouldContainerScroll(ev)) {
+ return false;
+ }
+ if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ if (mCurrentAnimation != null && animation == mCurrentAnimation.getTarget()) {
+ Log.e(TAG, "Who dare cancel the animation when I am in control", new Exception());
+ mDetector.finishedScrolling();
+ mCurrentAnimation = null;
+ }
+ }
+
+ private void initSprings() {
+ AllAppsContainerView appsView = mLauncher.getAppsView();
+
+ SpringAnimationHandler handler = appsView.getSpringAnimationHandler();
+ if (handler == null) {
+ mSpringHandlers = new SpringAnimationHandler[0];
+ return;
+ }
+
+ ArrayList<SpringAnimationHandler> handlers = new ArrayList<>();
+ handlers.add(handler);
+
+ SpringAnimation searchSpring = appsView.getSearchUiManager().getSpringForFling();
+ if (searchSpring != null) {
+ SpringAnimationHandler searchHandler =
+ new SpringAnimationHandler(Y_DIRECTION, handler.getFactory());
+ searchHandler.add(searchSpring, true /* setDefaultValues */);
+ handlers.add(searchHandler);
+ }
+
+ mSpringHandlers = handlers.toArray(new SpringAnimationHandler[handlers.size()]);
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mNoIntercept = !canInterceptTouch(ev);
+ if (mNoIntercept) {
+ return false;
+ }
+
+ // Now figure out which direction scroll events the controller will start
+ // calling the callbacks.
+ final int directionsToDetectScroll;
+ boolean ignoreSlopWhenSettling = false;
+
+ if (mCurrentAnimation != null) {
+ if (mCurrentAnimation.getProgressFraction() > 1 - RECATCH_REJECTION_FRACTION) {
+ directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE;
+ } else if (mCurrentAnimation.getProgressFraction() < RECATCH_REJECTION_FRACTION ) {
+ directionsToDetectScroll = SwipeDetector.DIRECTION_NEGATIVE;
+ } else {
+ directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
+ ignoreSlopWhenSettling = true;
+ }
+ } else {
+ if (mLauncher.isInState(ALL_APPS)) {
+ directionsToDetectScroll = SwipeDetector.DIRECTION_NEGATIVE;
+ mStartContainerType = ContainerType.ALLAPPS;
+ } else {
+ directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE;
+ mStartContainerType = mLauncher.getDragLayer().isEventOverHotseat(ev) ?
+ ContainerType.HOTSEAT : ContainerType.WORKSPACE;
+ }
+ }
+
+ mDetector.setDetectableScrollConditions(
+ directionsToDetectScroll, ignoreSlopWhenSettling);
+
+ if (mSpringHandlers == null) {
+ initSprings();
+ }
+ }
+
+ if (mNoIntercept) {
+ return false;
+ }
+
+ onControllerTouchEvent(ev);
+ return mDetector.isDraggingOrSettling();
+ }
+
+ @Override
+ public boolean onControllerTouchEvent(MotionEvent ev) {
+ for (SpringAnimationHandler h : mSpringHandlers) {
+ h.addMovement(ev);
+ }
+ return mDetector.onTouchEvent(ev);
+ }
+
+ @Override
+ public void onDragStart(boolean start) {
+ if (mCurrentAnimation == null) {
+ float range = getShiftRange();
+ long maxAccuracy = (long) (2 * range);
+
+ // Build current animation
+ mToState = mLauncher.isInState(ALL_APPS) ? NORMAL : ALL_APPS;
+ mCurrentAnimation = mLauncher.getStateManager()
+ .createAnimationToNewWorkspace(mToState, maxAccuracy);
+ mCurrentAnimation.getTarget().addListener(this);
+ mStartProgress = 0;
+ mProgressMultiplier = (mLauncher.isInState(ALL_APPS) ? 1 : -1) / range;
+ mCurrentAnimation.dispatchOnStart();
+ } else {
+ mCurrentAnimation.pause();
+ mStartProgress = mCurrentAnimation.getProgressFraction();
+ }
+
+ for (SpringAnimationHandler h : mSpringHandlers) {
+ h.skipToEnd();
+ }
+ }
+
+ private float getShiftRange() {
+ return mLauncher.mAllAppsController.getShiftRange();
+ }
+
+ @Override
+ public boolean onDrag(float displacement, float velocity) {
+ float deltaProgress = mProgressMultiplier * displacement;
+ mCurrentAnimation.setPlayFraction(deltaProgress + mStartProgress);
+ return true;
+ }
+
+ @Override
+ public void onDragEnd(float velocity, boolean fling) {
+ final long animationDuration;
+ final int logAction;
+ final LauncherState targetState;
+ final float progress = mCurrentAnimation.getProgressFraction();
+
+ if (fling) {
+ logAction = Touch.FLING;
+ if (velocity < 0) {
+ targetState = ALL_APPS;
+ animationDuration = SwipeDetector.calculateDuration(velocity,
+ mToState == ALL_APPS ? (1 - progress) : progress);
+ } else {
+ targetState = NORMAL;
+ animationDuration = SwipeDetector.calculateDuration(velocity,
+ mToState == ALL_APPS ? progress : (1 - progress));
+ }
+ // snap to top or bottom using the release velocity
+ } else {
+ logAction = Touch.SWIPE;
+ if (progress > SUCCESS_TRANSITION_PROGRESS) {
+ targetState = mToState;
+ animationDuration = SwipeDetector.calculateDuration(velocity, 1 - progress);
+ } else {
+ targetState = mToState == ALL_APPS ? NORMAL : ALL_APPS;
+ animationDuration = SwipeDetector.calculateDuration(velocity, progress);
+ }
+ }
+
+ if (fling && targetState == ALL_APPS) {
+ for (SpringAnimationHandler h : mSpringHandlers) {
+ // The icons are moving upwards, so we go to 0 from 1. (y-axis 1 is below 0.)
+ h.animateToFinalPosition(0 /* pos */, 1 /* startValue */);
+ }
+ }
+
+ mCurrentAnimation.setEndAction(new Runnable() {
+ @Override
+ public void run() {
+ if (targetState == mToState) {
+ // Transition complete. log the action
+ mLauncher.getUserEventDispatcher().logActionOnContainer(logAction,
+ mToState == ALL_APPS ? Direction.UP : Direction.DOWN,
+ mStartContainerType, mLauncher.getWorkspace().getCurrentPage());
+ } else {
+ mLauncher.getStateManager().goToState(
+ mToState == ALL_APPS ? NORMAL : ALL_APPS, false);
+ }
+ mDetector.finishedScrolling();
+ mCurrentAnimation = null;
+ }
+ });
+
+ float nextFrameProgress = Utilities.boundToRange(
+ progress + velocity * SINGLE_FRAME_MS / getShiftRange(), 0f, 1f);
+
+ ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
+ anim.setFloatValues(nextFrameProgress, targetState == mToState ? 1f : 0f);
+ anim.setDuration(animationDuration);
+ anim.setInterpolator(scrollInterpolatorForVelocity(velocity));
+ anim.start();
+ }
+}
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index a65ea9b..bdfeae1 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -22,7 +22,6 @@
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
-import android.os.Build;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.UserHandle;
@@ -93,12 +92,11 @@
* @return a request id which can be used to cancel the request.
*/
public CancellationSignal getPreview(WidgetItem item, int previewWidth,
- int previewHeight, WidgetCell caller, boolean animate) {
+ int previewHeight, WidgetCell caller) {
String size = previewWidth + "x" + previewHeight;
WidgetCacheKey key = new WidgetCacheKey(item.componentName, item.user, size);
- PreviewLoadTask task = new PreviewLoadTask(key, item, previewWidth, previewHeight, caller,
- animate);
+ PreviewLoadTask task = new PreviewLoadTask(key, item, previewWidth, previewHeight, caller);
task.executeOnExecutor(Utilities.THREAD_POOL_EXECUTOR);
CancellationSignal signal = new CancellationSignal();
@@ -412,7 +410,8 @@
// Draw icon in the center.
try {
- Drawable icon = info.getIcon(launcher, mIconCache);
+ Drawable icon =
+ mIconCache.getFullResIcon(info.provider.getPackageName(), info.icon);
if (icon != null) {
int appIconSize = launcher.getDeviceProfile().iconSizePx;
int iconSize = (int) Math.min(appIconSize * scale,
@@ -527,19 +526,17 @@
private final int mPreviewHeight;
private final int mPreviewWidth;
private final WidgetCell mCaller;
- private final boolean mAnimatePreviewIn;
private final BaseActivity mActivity;
@Thunk long[] mVersions;
@Thunk Bitmap mBitmapToRecycle;
PreviewLoadTask(WidgetCacheKey key, WidgetItem info, int previewWidth,
- int previewHeight, WidgetCell caller, boolean animate) {
+ int previewHeight, WidgetCell caller) {
mKey = key;
mInfo = info;
mPreviewHeight = previewHeight;
mPreviewWidth = previewWidth;
mCaller = caller;
- mAnimatePreviewIn = animate;
mActivity = BaseActivity.fromContext(mCaller.getContext());
if (DEBUG) {
Log.d(TAG, String.format("%s, %s, %d, %d",
@@ -595,7 +592,7 @@
@Override
protected void onPostExecute(final Bitmap preview) {
- mCaller.applyPreview(preview, mAnimatePreviewIn);
+ mCaller.applyPreview(preview);
// Write the generated preview to the DB in the worker thread
if (mVersions != null) {
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 37ee6d7..24c4704 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -16,6 +16,14 @@
package com.android.launcher3;
+import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
+import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
+import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS;
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.SPRING_LOADED;
+import static com.android.launcher3.Utilities.isAccessibilityEnabled;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -48,20 +56,17 @@
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityManager;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
import android.widget.Toast;
-import com.android.launcher3.Launcher.CustomContentCallbacks;
import com.android.launcher3.Launcher.LauncherOverlay;
import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
-import com.android.launcher3.UninstallDropTarget.DropTargetSource;
+import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
import com.android.launcher3.accessibility.OverviewAccessibilityDelegate;
import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate;
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
import com.android.launcher3.anim.AnimationLayerSet;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.badge.FolderBadgeInfo;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.config.FeatureFlags;
@@ -77,6 +82,7 @@
import com.android.launcher3.graphics.PreloadIconDrawable;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
+import com.android.launcher3.states.OverviewState;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
@@ -84,7 +90,6 @@
import com.android.launcher3.util.LongArrayMap;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Thunk;
-import com.android.launcher3.util.VerticalFlingDetector;
import com.android.launcher3.util.WallpaperOffsetInterpolator;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
@@ -101,7 +106,7 @@
public class Workspace extends PagedView
implements DropTarget, DragSource, View.OnTouchListener,
DragController.DragListener, ViewGroup.OnHierarchyChangeListener,
- Insettable, DropTargetSource {
+ Insettable {
private static final String TAG = "Launcher.Workspace";
/** The value that {@link #mTransitionProgress} must be greater than for
@@ -119,6 +124,8 @@
private static final int ADJACENT_SCREEN_DROP_DURATION = 300;
+ private static final int DEFAULT_PAGE = 0;
+
private static final boolean MAP_NO_RECURSE = false;
private static final boolean MAP_RECURSE = true;
@@ -127,12 +134,6 @@
// The is the first screen. It is always present, even if its empty.
public static final long FIRST_SCREEN_ID = 0;
- private final static long CUSTOM_CONTENT_SCREEN_ID = -301;
-
- private static final long CUSTOM_CONTENT_GESTURE_DELAY = 200;
- private long mTouchDownTime = -1;
- private long mCustomContentShowTime = -1;
-
private LayoutTransition mLayoutTransition;
@Thunk final WallpaperManager mWallpaperManager;
@@ -156,11 +157,6 @@
private int mDragOverX = -1;
private int mDragOverY = -1;
- CustomContentCallbacks mCustomContentCallbacks;
- boolean mCustomContentShowing;
- private float mLastCustomContentScrollProgress = -1f;
- private String mCustomContentDescription = "";
-
/**
* The CellLayout that is currently being dragged over
*/
@@ -178,37 +174,11 @@
@Thunk final Launcher mLauncher;
@Thunk DragController mDragController;
- // These are temporary variables to prevent having to allocate a new object just to
- // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
- private static final Rect sTempRect = new Rect();
-
private final int[] mTempXY = new int[2];
@Thunk float[] mDragViewVisualCenter = new float[2];
private final float[] mTempTouchCoordinates = new float[2];
private SpringLoadedDragController mSpringLoadedDragController;
- private final float mOverviewModeShrinkFactor;
-
- // State variable that indicates whether the pages are small (ie when you're
- // in all apps or customize mode)
-
- public enum State {
- NORMAL (false, false, ContainerType.WORKSPACE),
- NORMAL_HIDDEN (false, false, ContainerType.ALLAPPS),
- SPRING_LOADED (false, true, ContainerType.WORKSPACE),
- OVERVIEW (true, true, ContainerType.OVERVIEW),
- OVERVIEW_HIDDEN (true, false, ContainerType.WIDGETS);
-
- public final boolean shouldUpdateWidget;
- public final boolean hasMultipleVisiblePages;
- public final int containerType;
-
- State(boolean shouldUpdateWidget, boolean hasMultipleVisiblePages, int containerType) {
- this.shouldUpdateWidget = shouldUpdateWidget;
- this.hasMultipleVisiblePages = hasMultipleVisiblePages;
- this.containerType = containerType;
- }
- }
// Direction used for moving the workspace and hotseat UI
public enum Direction {
@@ -235,11 +205,8 @@
*/
private final float[] mHotseatAlpha = new float[] {1, 1, 1};
- @ViewDebug.ExportedProperty(category = "launcher")
- private State mState = State.NORMAL;
private boolean mIsSwitchingState = false;
- boolean mAnimatingViewIntoPlace = false;
boolean mChildrenLayersEnabled = true;
private boolean mStripScreensOnPageStopMoving = false;
@@ -250,9 +217,6 @@
final WallpaperOffsetInterpolator mWallpaperOffset;
private boolean mUnlockWallpaperFromDefaultPageOnLayout;
- @Thunk Runnable mDelayedResizeRunnable;
- private Runnable mDelayedSnapToPageRunnable;
-
// Variables relating to the creation of user folders by hovering shortcuts over shortcuts
private static final int FOLDER_CREATION_TIMEOUT = 0;
public static final int REORDER_TIMEOUT = 350;
@@ -264,8 +228,6 @@
private boolean mAddToExistingFolderOnDrop = false;
private float mMaxDistanceForFolderCreation;
- private final Canvas mCanvas = new Canvas();
-
// Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget)
private float mXDown;
private float mYDown;
@@ -295,10 +257,6 @@
private float mCurrentScale;
private float mTransitionProgress;
- @Thunk Runnable mDeferredAction;
- private boolean mDeferDropAfterUninstall;
- private boolean mUninstallSuccessful;
-
// State related to Launcher Overlay
LauncherOverlay mLauncherOverlay;
boolean mScrollInteractionBegan;
@@ -307,6 +265,8 @@
boolean mOverlayShown = false;
private boolean mForceDrawAdjacentPages = false;
+ private boolean mPageRearrangeEnabled = false;
+
// Total over scrollX in the overlay direction.
private float mOverlayTranslation;
@@ -343,8 +303,6 @@
mWallpaperManager = WallpaperManager.getInstance(context);
mWallpaperOffset = new WallpaperOffsetInterpolator(this);
- mOverviewModeShrinkFactor =
- res.getInteger(R.integer.config_workspaceOverviewShrinkPercentage) / 100f;
setOnHierarchyChangeListener(this);
setHapticFeedbackEnabled(false);
@@ -358,29 +316,18 @@
@Override
public void setInsets(Rect insets) {
mInsets.set(insets);
-
- CellLayout customScreen = getScreenWithId(CUSTOM_CONTENT_SCREEN_ID);
- if (customScreen != null) {
- View customContent = customScreen.getShortcutsAndWidgets().getChildAt(0);
- if (customContent instanceof Insettable) {
- ((Insettable) customContent).setInsets(mInsets);
- }
- }
}
/**
* Estimates the size of an item using spans: hSpan, vSpan.
*
- * @param springLoaded True if we are in spring loaded mode.
- * @param unscaledSize True if caller wants to return the unscaled size
* @return MAX_VALUE for each dimension if unsuccessful.
*/
- public int[] estimateItemSize(ItemInfo itemInfo, boolean springLoaded, boolean unscaledSize) {
- float shrinkFactor = mLauncher.getDeviceProfile().workspaceSpringLoadShrinkFactor;
+ public int[] estimateItemSize(ItemInfo itemInfo) {
int[] size = new int[2];
if (getChildCount() > 0) {
- // Use the first non-custom page to estimate the child position
- CellLayout cl = (CellLayout) getChildAt(numCustomPages());
+ // Use the first page to estimate the child position
+ CellLayout cl = (CellLayout) getChildAt(0);
boolean isWidget = itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
Rect r = estimateItemPosition(cl, 0, 0, itemInfo.spanX, itemInfo.spanY);
@@ -393,15 +340,10 @@
size[0] = r.width();
size[1] = r.height();
- if (isWidget && unscaledSize) {
+ if (isWidget) {
size[0] /= scale;
size[1] /= scale;
}
-
- if (springLoaded) {
- size[0] *= shrinkFactor;
- size[1] *= shrinkFactor;
- }
return size;
} else {
size[0] = Integer.MAX_VALUE;
@@ -428,16 +370,15 @@
}
if (mOutlineProvider != null) {
- // The outline is used to visualize where the item will land if dropped
- mOutlineProvider.generateDragOutline(mCanvas);
+ if (dragObject.dragView != null) {
+ Bitmap preview = dragObject.dragView.getPreviewBitmap();
+
+ // The outline is used to visualize where the item will land if dropped
+ mOutlineProvider.generateDragOutline(preview);
+ }
}
- updateChildrenLayersEnabled(false);
- mLauncher.onDragStarted();
- mLauncher.lockScreenOrientation();
- mLauncher.onInteractionBegin();
- // Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging
- InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_DRAG_AND_DROP);
+ updateChildrenLayersEnabled();
// Do not add a new page if it is a accessible drag which was not started by the workspace.
// We do not support accessibility drag from other sources and instead provide a direct
@@ -469,7 +410,7 @@
}
// Always enter the spring loaded mode
- mLauncher.enterSpringLoadedDragMode();
+ mLauncher.getStateManager().goToState(SPRING_LOADED);
}
public void deferRemoveExtraEmptyScreen() {
@@ -486,30 +427,23 @@
removeExtraEmptyScreen(true, mDragSourceInternal != null);
}
- updateChildrenLayersEnabled(false);
- mLauncher.unlockScreenOrientation(false);
-
- // Re-enable any Un/InstallShortcutReceiver and now process any queued items
- InstallShortcutReceiver.disableAndFlushInstallQueue(
- InstallShortcutReceiver.FLAG_DRAG_AND_DROP, getContext());
-
+ updateChildrenLayersEnabled();
mOutlineProvider = null;
- mDragInfo = null;
mDragSourceInternal = null;
- mLauncher.onInteractionEnd();
}
/**
* Initializes various states for this workspace.
*/
protected void initWorkspace() {
- mCurrentPage = getDefaultPage();
+ mCurrentPage = DEFAULT_PAGE;
DeviceProfile grid = mLauncher.getDeviceProfile();
setWillNotDraw(false);
setClipChildren(false);
setClipToPadding(false);
- setMinScale(mOverviewModeShrinkFactor);
+ // TODO: Remove this
+ setMinScale(OverviewState.SCALE_FACTOR);
setupLayoutTransition();
mMaxDistanceForFolderCreation = (0.55f * grid.iconSizePx);
@@ -524,10 +458,6 @@
mPageIndicator.setAccessibilityDelegate(new OverviewAccessibilityDelegate());
}
- private int getDefaultPage() {
- return numCustomPages();
- }
-
private void setupLayoutTransition() {
// We want to show layout transitions when pages are deleted, to close the gap.
mLayoutTransition = new LayoutTransition();
@@ -571,33 +501,6 @@
}
// Add the first page
CellLayout firstPage = insertNewWorkspaceScreen(Workspace.FIRST_SCREEN_ID, 0);
- if (FeatureFlags.PULLDOWN_SEARCH) {
- firstPage.setOnTouchListener(new VerticalFlingDetector(mLauncher) {
- // detect fling when touch started from empty space
- @Override
- public boolean onTouch(View v, MotionEvent ev) {
- if (workspaceInModalState()) return false;
- if (shouldConsumeTouch(v)) return true;
- if (super.onTouch(v, ev)) {
- mLauncher.startSearch("", false, null, false);
- return true;
- }
- return false;
- }
- });
- firstPage.setOnInterceptTouchListener(new VerticalFlingDetector(mLauncher) {
- // detect fling when touch started from on top of the icons
- @Override
- public boolean onTouch(View v, MotionEvent ev) {
- if (shouldConsumeTouch(v)) return true;
- if (super.onTouch(v, ev)) {
- mLauncher.startSearch("", false, null, false);
- return true;
- }
- return false;
- }
- });
- }
// Always add a QSB on the first screen.
if (qsb == null) {
// In transposed layout, we add the QSB in the Grid. As workspace does not touch the
@@ -615,16 +518,9 @@
public void removeAllWorkspaceScreens() {
// Disable all layout transitions before removing all pages to ensure that we don't get the
- // transition animations competing with us changing the scroll when we add pages or the
- // custom content screen
+ // transition animations competing with us changing the scroll when we add pages
disableLayoutTransitions();
- // Since we increment the current page when we call addCustomContentPage via bindScreens
- // (and other places), we need to adjust the current page back when we clear the pages
- if (hasCustomContent()) {
- removeCustomContentPage();
- }
-
// Recycle the QSB widget
View qsb = findViewById(R.id.search_container_workspace);
if (qsb != null) {
@@ -677,6 +573,8 @@
mWorkspaceScreens.put(screenId, newScreen);
mScreenOrder.add(insertIndex, screenId);
addView(newScreen, insertIndex);
+ mStateTransitionAnimation.applyChildState(
+ mLauncher.getStateManager().getState(), newScreen, insertIndex);
if (mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) {
newScreen.enableAccessibleDrag(true, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
@@ -685,78 +583,6 @@
return newScreen;
}
- public void createCustomContentContainer() {
- CellLayout customScreen = (CellLayout)
- LayoutInflater.from(getContext()).inflate(R.layout.workspace_screen, this, false);
- customScreen.disableDragTarget();
- customScreen.disableJailContent();
-
- mWorkspaceScreens.put(CUSTOM_CONTENT_SCREEN_ID, customScreen);
- mScreenOrder.add(0, CUSTOM_CONTENT_SCREEN_ID);
-
- // We want no padding on the custom content
- customScreen.setPadding(0, 0, 0, 0);
-
- addFullScreenPage(customScreen);
-
- // Update the custom content hint
- setCurrentPage(getCurrentPage() + 1);
- }
-
- public void removeCustomContentPage() {
- CellLayout customScreen = getScreenWithId(CUSTOM_CONTENT_SCREEN_ID);
- if (customScreen == null) {
- throw new RuntimeException("Expected custom content screen to exist");
- }
-
- mWorkspaceScreens.remove(CUSTOM_CONTENT_SCREEN_ID);
- mScreenOrder.remove(CUSTOM_CONTENT_SCREEN_ID);
- removeView(customScreen);
-
- if (mCustomContentCallbacks != null) {
- mCustomContentCallbacks.onScrollProgressChanged(0);
- mCustomContentCallbacks.onHide();
- }
-
- mCustomContentCallbacks = null;
-
- // Update the custom content hint
- setCurrentPage(getCurrentPage() - 1);
- }
-
- public void addToCustomContentPage(View customContent, CustomContentCallbacks callbacks,
- String description) {
- if (getPageIndexForScreenId(CUSTOM_CONTENT_SCREEN_ID) < 0) {
- throw new RuntimeException("Expected custom content screen to exist");
- }
-
- // Add the custom content to the full screen custom page
- CellLayout customScreen = getScreenWithId(CUSTOM_CONTENT_SCREEN_ID);
- int spanX = customScreen.getCountX();
- int spanY = customScreen.getCountY();
- CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, spanX, spanY);
- lp.canReorder = false;
- lp.isFullscreen = true;
- if (customContent instanceof Insettable) {
- ((Insettable)customContent).setInsets(mInsets);
- }
-
- // Verify that the child is removed from any existing parent.
- if (customContent.getParent() instanceof ViewGroup) {
- ViewGroup parent = (ViewGroup) customContent.getParent();
- parent.removeView(customContent);
- }
- customScreen.removeAllViews();
- customContent.setFocusable(true);
- customContent.setOnKeyListener(new FullscreenKeyEventListener());
- customContent.setOnFocusChangeListener(mLauncher.mFocusHandler
- .getHideIndicatorOnFocusListener());
- customScreen.addViewToCellLayout(customContent, 0, 0, lp, true);
- mCustomContentDescription = description;
-
- mCustomContentCallbacks = callbacks;
- }
-
public void addExtraEmptyScreenOnDrag() {
boolean lastChildOnScreen = false;
boolean childOnFinalScreen = false;
@@ -800,7 +626,6 @@
if (hasExtraEmptyScreen() || mScreenOrder.size() == 0) return;
long finalScreenId = mScreenOrder.get(mScreenOrder.size() - 1);
- if (finalScreenId == CUSTOM_CONTENT_SCREEN_ID) return;
CellLayout finalScreen = mWorkspaceScreens.get(finalScreenId);
// If the final screen is empty, convert it to the extra empty screen
@@ -809,7 +634,7 @@
mWorkspaceScreens.remove(finalScreenId);
mScreenOrder.remove(finalScreenId);
- // if this is the last non-custom content screen, convert it to the empty screen
+ // if this is the last screen, convert it to the empty screen
mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, finalScreen);
mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
@@ -905,9 +730,7 @@
}
public boolean hasExtraEmptyScreen() {
- int nScreens = getChildCount();
- nScreens = nScreens - numCustomPages();
- return mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID) && nScreens > 1;
+ return mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID) && getChildCount() > 1;
}
public long commitExtraEmptyScreen() {
@@ -988,7 +811,7 @@
// We enforce at least one page to add new items to. In the case that we remove the last
// such screen, we convert the last screen to the empty screen
- int minScreens = 1 + numCustomPages();
+ int minScreens = 1;
int pageShift = 0;
for (Long id: removeScreens) {
@@ -1007,7 +830,7 @@
removeView(cl);
} else {
- // if this is the last non-custom content screen, convert it to the empty screen
+ // if this is the last screen, convert it to the empty screen
mRemoveEmptyScreenRunnable = null;
mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, cl);
mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
@@ -1156,10 +979,6 @@
|| (mTransitionProgress > FINISHED_SWITCHING_STATE_TRANSITION_PROGRESS);
}
- protected void onWindowVisibilityChanged (int visibility) {
- mLauncher.onWindowVisibilityChanged(visibility);
- }
-
@Override
public boolean dispatchUnhandledMove(View focused, int direction) {
if (workspaceInModalState() || !isFinishedSwitchingState()) {
@@ -1175,7 +994,6 @@
case MotionEvent.ACTION_DOWN:
mXDown = ev.getX();
mYDown = ev.getY();
- mTouchDownTime = System.currentTimeMillis();
break;
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_UP:
@@ -1190,41 +1008,6 @@
}
@Override
- public boolean onGenericMotionEvent(MotionEvent event) {
- // Ignore pointer scroll events if the custom content doesn't allow scrolling.
- if ((getScreenIdForPageIndex(getCurrentPage()) == CUSTOM_CONTENT_SCREEN_ID)
- && (mCustomContentCallbacks != null)
- && !mCustomContentCallbacks.isScrollingAllowed()) {
- return false;
- }
- return super.onGenericMotionEvent(event);
- }
-
- protected void reinflateWidgetsIfNecessary() {
- final int clCount = getChildCount();
- for (int i = 0; i < clCount; i++) {
- CellLayout cl = (CellLayout) getChildAt(i);
- ShortcutAndWidgetContainer swc = cl.getShortcutsAndWidgets();
- final int itemCount = swc.getChildCount();
- for (int j = 0; j < itemCount; j++) {
- View v = swc.getChildAt(j);
-
- if (v instanceof LauncherAppWidgetHostView
- && v.getTag() instanceof LauncherAppWidgetInfo) {
- LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
- LauncherAppWidgetHostView lahv = (LauncherAppWidgetHostView) v;
- if (lahv.isReinflateRequired(mLauncher.getOrientation())) {
- // Remove and rebind the current widget (which was inflated in the wrong
- // orientation), but don't delete it from the database
- mLauncher.removeItem(lahv, info, false /* deleteFromDb */);
- mLauncher.bindAppWidget(info);
- }
- }
- }
- }
- }
-
- @Override
protected void determineScrollingStart(MotionEvent ev) {
if (!isFinishedSwitchingState()) return;
@@ -1241,24 +1024,6 @@
cancelCurrentPageLongPress();
}
- boolean passRightSwipesToCustomContent =
- (mTouchDownTime - mCustomContentShowTime) > CUSTOM_CONTENT_GESTURE_DELAY;
-
- boolean swipeInIgnoreDirection = mIsRtl ? deltaX < 0 : deltaX > 0;
- boolean onCustomContentScreen =
- getScreenIdForPageIndex(getCurrentPage()) == CUSTOM_CONTENT_SCREEN_ID;
- if (swipeInIgnoreDirection && onCustomContentScreen && passRightSwipesToCustomContent) {
- // Pass swipes to the right to the custom content page.
- return;
- }
-
- if (onCustomContentScreen && (mCustomContentCallbacks != null)
- && !mCustomContentCallbacks.isScrollingAllowed()) {
- // Don't allow workspace scrolling if the current custom content screen doesn't allow
- // scrolling.
- return;
- }
-
if (theta > MAX_SWIPE_ANGLE) {
// Above MAX_SWIPE_ANGLE, we don't want to ever start scrolling the workspace
return;
@@ -1279,12 +1044,12 @@
protected void onPageBeginTransition() {
super.onPageBeginTransition();
- updateChildrenLayersEnabled(false);
+ updateChildrenLayersEnabled();
}
protected void onPageEndTransition() {
super.onPageEndTransition();
- updateChildrenLayersEnabled(false);
+ updateChildrenLayersEnabled();
if (mDragController.isDragging()) {
if (workspaceInModalState()) {
@@ -1294,15 +1059,6 @@
}
}
- if (mDelayedResizeRunnable != null && !mIsSwitchingState) {
- mDelayedResizeRunnable.run();
- mDelayedResizeRunnable = null;
- }
-
- if (mDelayedSnapToPageRunnable != null) {
- mDelayedSnapToPageRunnable.run();
- mDelayedSnapToPageRunnable = null;
- }
if (mStripScreensOnPageStopMoving) {
stripEmptyScreens();
mStripScreensOnPageStopMoving = false;
@@ -1364,11 +1120,10 @@
}
updatePageAlphaValues();
- updateStateForCustomContent();
enableHwLayersOnVisiblePages();
}
- private void showPageIndicatorAtCurrentScroll() {
+ public void showPageIndicatorAtCurrentScroll() {
if (mPageIndicator != null) {
mPageIndicator.setScroll(getScrollX(), computeMaxScrollX());
}
@@ -1376,9 +1131,6 @@
@Override
protected void overScroll(float amount) {
- boolean shouldOverScroll = (amount <= 0 && (!hasCustomContent() || mIsRtl)) ||
- (amount >= 0 && (!hasCustomContent() || !mIsRtl));
-
boolean shouldScrollOverlay = mLauncherOverlay != null &&
((amount <= 0 && !mIsRtl) || (amount >= 0 && mIsRtl));
@@ -1393,7 +1145,7 @@
mLastOverlayScroll = Math.abs(amount / getViewportWidth());
mLauncherOverlay.onScrollChange(mLastOverlayScroll, mIsRtl);
- } else if (shouldOverScroll) {
+ } else {
dampedOverScroll(amount);
}
@@ -1409,8 +1161,6 @@
super.shouldFlingForVelocity(velocityX);
}
- private final Interpolator mAlphaInterpolator = new DecelerateInterpolator(3f);
-
/**
* The overlay scroll is being controlled locally, just update our overlay effect
*/
@@ -1435,7 +1185,7 @@
scroll = Math.max(scroll - offset, 0);
scroll = Math.min(1, scroll / (1 - offset));
- float alpha = 1 - mAlphaInterpolator.getInterpolation(scroll);
+ float alpha = 1 - Interpolators.DEACCEL_3.getInterpolation(scroll);
float transX = mLauncher.getDragLayer().getMeasuredWidth() * scroll;
transX *= 1 - slip;
@@ -1527,9 +1277,7 @@
}
});
- AccessibilityManager am = (AccessibilityManager)
- mLauncher.getSystemService(Context.ACCESSIBILITY_SERVICE);
- final boolean accessibilityEnabled = am.isEnabled();
+ final boolean accessibilityEnabled = isAccessibilityEnabled(mLauncher);
animator.addUpdateListener(
new AlphaUpdateListener(mLauncher.getHotseat(), accessibilityEnabled));
animator.addUpdateListener(
@@ -1546,22 +1294,6 @@
mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
swipeDirection, ContainerType.WORKSPACE, prevPage);
}
- if (hasCustomContent() && getNextPage() == 0 && !mCustomContentShowing) {
- mCustomContentShowing = true;
- if (mCustomContentCallbacks != null) {
- mCustomContentCallbacks.onShow(false);
- mCustomContentShowTime = System.currentTimeMillis();
- }
- } else if (hasCustomContent() && getNextPage() != 0 && mCustomContentShowing) {
- mCustomContentShowing = false;
- if (mCustomContentCallbacks != null) {
- mCustomContentCallbacks.onHide();
- }
- }
- }
-
- protected CustomContentCallbacks getCustomContentCallbacks() {
- return mCustomContentCallbacks;
}
protected void setWallpaperDimension() {
@@ -1588,26 +1320,6 @@
}
}
- protected void snapToPage(int whichPage, Runnable r) {
- snapToPage(whichPage, SLOW_PAGE_SNAP_ANIMATION_DURATION, r);
- }
-
- protected void snapToPage(int whichPage, int duration, Runnable r) {
- if (mDelayedSnapToPageRunnable != null) {
- mDelayedSnapToPageRunnable.run();
- }
- mDelayedSnapToPageRunnable = r;
- snapToPage(whichPage, duration);
- }
-
- public void snapToScreenId(long screenId) {
- snapToScreenId(screenId, null);
- }
-
- protected void snapToScreenId(long screenId, Runnable r) {
- snapToPage(getPageIndexForScreenId(screenId), r);
- }
-
@Override
public void computeScroll() {
super.computeScroll();
@@ -1628,7 +1340,7 @@
@Override
public void announceForAccessibility(CharSequence text) {
// Don't announce if apps is on top of us.
- if (!mLauncher.isAppsViewVisible()) {
+ if (!mLauncher.isInState(ALL_APPS)) {
super.announceForAccessibility(text);
}
}
@@ -1642,7 +1354,7 @@
private void updatePageAlphaValues() {
if (!workspaceInModalState() && !mIsSwitchingState) {
int screenCenter = getScrollX() + getViewportWidth() / 2;
- for (int i = numCustomPages(); i < getChildCount(); i++) {
+ for (int i = 0; i < getChildCount(); i++) {
CellLayout child = (CellLayout) getChildAt(i);
if (child != null) {
float scrollProgress = getScrollProgress(screenCenter, child, i);
@@ -1660,66 +1372,6 @@
}
}
- public boolean hasCustomContent() {
- return (mScreenOrder.size() > 0 && mScreenOrder.get(0) == CUSTOM_CONTENT_SCREEN_ID);
- }
-
- public int numCustomPages() {
- return hasCustomContent() ? 1 : 0;
- }
-
- public boolean isOnOrMovingToCustomContent() {
- return hasCustomContent() && getNextPage() == 0;
- }
-
- private void updateStateForCustomContent() {
- float translationX = 0;
- float progress = 0;
- if (hasCustomContent()) {
- int index = mScreenOrder.indexOf(CUSTOM_CONTENT_SCREEN_ID);
-
- int scrollDelta = getScrollX() - getScrollForPage(index) -
- getLayoutTransitionOffsetForPage(index);
- float scrollRange = getScrollForPage(index + 1) - getScrollForPage(index);
- translationX = scrollRange - scrollDelta;
- progress = (scrollRange - scrollDelta) / scrollRange;
-
- if (mIsRtl) {
- translationX = Math.min(0, translationX);
- } else {
- translationX = Math.max(0, translationX);
- }
- progress = Math.max(0, progress);
- }
-
- if (Float.compare(progress, mLastCustomContentScrollProgress) == 0) return;
-
- CellLayout cc = mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID);
- if (progress > 0 && cc.getVisibility() != VISIBLE && !workspaceInModalState()) {
- cc.setVisibility(VISIBLE);
- }
-
- mLastCustomContentScrollProgress = progress;
-
- // We should only update the drag layer background alpha if we are not in all apps or the
- // widgets tray
- if (mState == State.NORMAL) {
- mLauncher.getDragLayer().setBackgroundAlpha(progress == 1 ? 0 : progress * 0.8f);
- }
-
- if (mLauncher.getHotseat() != null) {
- mLauncher.getHotseat().setTranslationX(translationX);
- }
-
- if (mPageIndicator != null) {
- mPageIndicator.setTranslationX(translationX);
- }
-
- if (mCustomContentCallbacks != null) {
- mCustomContentCallbacks.onScrollProgressChanged(progress);
- }
- }
-
protected void onAttachedToWindow() {
super.onAttachedToWindow();
IBinder windowToken = getWindowToken();
@@ -1733,10 +1385,6 @@
mWallpaperOffset.setWindowToken(null);
}
- protected void onResume() {
- mWallpaperOffset.onResume();
- }
-
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (mUnlockWallpaperFromDefaultPageOnLayout) {
@@ -1759,18 +1407,18 @@
return super.getDescendantFocusability();
}
- public boolean workspaceInModalState() {
- return mState != State.NORMAL;
+ private boolean workspaceInModalState() {
+ return !mLauncher.isInState(NORMAL);
}
/** Returns whether a drag should be allowed to be started from the current workspace state. */
public boolean workspaceIconsCanBeDragged() {
- return mState == State.NORMAL || mState == State.SPRING_LOADED;
+ return mLauncher.isInState(NORMAL) || mLauncher.isInState(SPRING_LOADED);
}
- @Thunk void updateChildrenLayersEnabled(boolean force) {
- boolean small = mState == State.OVERVIEW || mIsSwitchingState;
- boolean enableChildrenLayers = force || small || mAnimatingViewIntoPlace || isPageInTransition();
+ private void updateChildrenLayersEnabled() {
+ boolean enableChildrenLayers =
+ isPageRearrangeEnabled() || mIsSwitchingState || isPageInTransition();
if (enableChildrenLayers != mChildrenLayersEnabled) {
mChildrenLayersEnabled = enableChildrenLayers;
@@ -1800,7 +1448,7 @@
int leftScreen = -1;
int rightScreen = -1;
- for (int i = numCustomPages(); i < screenCount; i++) {
+ for (int i = 0; i < screenCount; i++) {
final View child = getPageAt(i);
float left = child.getLeft() + child.getTranslationX() - getScrollX();
@@ -1813,8 +1461,7 @@
}
if (mForceDrawAdjacentPages) {
// In overview mode, make sure that the two side pages are visible.
- leftScreen = Utilities.boundToRange(getCurrentPage() - 1,
- numCustomPages(), rightScreen);
+ leftScreen = Utilities.boundToRange(getCurrentPage() - 1, 0, rightScreen);
rightScreen = Utilities.boundToRange(getCurrentPage() + 1,
leftScreen, getPageCount() - 1);
}
@@ -1828,7 +1475,7 @@
}
}
- for (int i = numCustomPages(); i < screenCount; i++) {
+ for (int i = 0; i < screenCount; i++) {
final CellLayout layout = (CellLayout) getPageAt(i);
// enable layers between left and right screen inclusive.
boolean enableLayer = leftScreen <= i && i <= rightScreen;
@@ -1837,19 +1484,6 @@
}
}
- public void buildPageHardwareLayers() {
- // force layers to be enabled just for the call to buildLayer
- updateChildrenLayersEnabled(true);
- if (getWindowToken() != null) {
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- CellLayout cl = (CellLayout) getChildAt(i);
- cl.buildHardwareLayer();
- }
- }
- updateChildrenLayersEnabled(false);
- }
-
protected void onWallpaperTap(MotionEvent ev) {
final int[] position = mTempXY;
getLocationOnScreen(position);
@@ -1868,24 +1502,6 @@
mOutlineProvider = outlineProvider;
}
- public void exitWidgetResizeMode() {
- DragLayer dragLayer = mLauncher.getDragLayer();
- dragLayer.clearResizeFrame();
- }
-
- @Override
- protected void getFreeScrollPageRange(int[] range) {
- getOverviewModePages(range);
- }
-
- private void getOverviewModePages(int[] range) {
- int start = numCustomPages();
- int end = getChildCount() - 1;
-
- range[0] = Math.max(0, Math.min(start, getChildCount() - 1));
- range[1] = Math.max(0, end);
- }
-
public void onStartReordering() {
super.onStartReordering();
// Reordering handles its own animations, disable the automatic ones.
@@ -1920,115 +1536,71 @@
enableLayoutTransitions();
}
- public boolean isInOverviewMode() {
- return mState == State.OVERVIEW;
- }
-
public void snapToPageFromOverView(int whichPage) {
- mStateTransitionAnimation.snapToPageFromOverView(whichPage);
+ snapToPage(whichPage, OVERVIEW_TRANSITION_MS, Interpolators.ZOOM_IN);
}
- int getOverviewModeTranslationY() {
- DeviceProfile grid = mLauncher.getDeviceProfile();
- int overviewButtonBarHeight = grid.getOverviewModeButtonBarHeight();
+ private void onStartStateTransition(LauncherState state) {
+ mIsSwitchingState = true;
+ mTransitionProgress = 0;
- int scaledHeight = (int) (mOverviewModeShrinkFactor * getNormalChildHeight());
- Rect workspacePadding = grid.getWorkspacePadding(sTempRect);
- int workspaceTop = mInsets.top + workspacePadding.top;
- int workspaceBottom = getViewportHeight() - mInsets.bottom - workspacePadding.bottom;
- int overviewTop = mInsets.top;
- int overviewBottom = getViewportHeight() - mInsets.bottom - overviewButtonBarHeight;
- int workspaceOffsetTopEdge = workspaceTop + ((workspaceBottom - workspaceTop) - scaledHeight) / 2;
- int overviewOffsetTopEdge = overviewTop + (overviewBottom - overviewTop - scaledHeight) / 2;
- return -workspaceOffsetTopEdge + overviewOffsetTopEdge;
+ updateChildrenLayersEnabled();
}
- float getSpringLoadedTranslationY() {
- DeviceProfile grid = mLauncher.getDeviceProfile();
- if (grid.isVerticalBarLayout() || getChildCount() == 0) {
- return 0;
- }
+ private void onEndStateTransition() {
+ mIsSwitchingState = false;
+ mForceDrawAdjacentPages = false;
+ mTransitionProgress = 1;
- float scaledHeight = grid.workspaceSpringLoadShrinkFactor * getNormalChildHeight();
- float shrunkTop = mInsets.top + grid.dropTargetBarSizePx;
- float shrunkBottom = getViewportHeight() - mInsets.bottom
- - grid.getWorkspacePadding(sTempRect).bottom
- - grid.workspaceSpringLoadedBottomSpace;
- float totalShrunkSpace = shrunkBottom - shrunkTop;
-
- float desiredCellTop = shrunkTop + (totalShrunkSpace - scaledHeight) / 2;
-
- float halfHeight = getHeight() / 2;
- float myCenter = getTop() + halfHeight;
- float cellTopFromCenter = halfHeight - getChildAt(0).getTop();
- float actualCellTop = myCenter - cellTopFromCenter * grid.workspaceSpringLoadShrinkFactor;
- return (desiredCellTop - actualCellTop) / grid.workspaceSpringLoadShrinkFactor;
- }
-
- float getOverviewModeShrinkFactor() {
- return mOverviewModeShrinkFactor;
+ updateChildrenLayersEnabled();
+ updateAccessibilityFlags();
}
/**
- * Sets the current workspace {@link State}, returning an animation transitioning the workspace
- * to that new state.
+ * Sets the current workspace {@link LauncherState} and updates the UI without any animations
*/
- public Animator setStateWithAnimation(State toState, boolean animated,
- AnimationLayerSet layerViews) {
- final State fromState = mState;
-
- // Update the current state
- mState = toState;
-
- // Create the animation to the new state
- AnimatorSet workspaceAnim = mStateTransitionAnimation.getAnimationToState(fromState,
- toState, animated, layerViews);
-
- boolean shouldNotifyWidgetChange = !fromState.shouldUpdateWidget
- && toState.shouldUpdateWidget;
-
- updateAccessibilityFlags();
-
- if (shouldNotifyWidgetChange) {
- mLauncher.notifyWidgetProvidersChanged();
- }
-
- onPrepareStateTransition(mState.hasMultipleVisiblePages);
-
- StateTransitionListener listener = new StateTransitionListener();
- if (animated) {
- ValueAnimator stepAnimator = ValueAnimator.ofFloat(0, 1);
- stepAnimator.addUpdateListener(listener);
-
- workspaceAnim.play(stepAnimator);
- workspaceAnim.addListener(listener);
- } else {
- listener.onAnimationStart(null);
- listener.onAnimationEnd(null);
- }
-
- return workspaceAnim;
+ public void setState(LauncherState toState) {
+ onStartStateTransition(toState);
+ mStateTransitionAnimation.setState(toState);
+ onEndStateTransition();
}
- public State getState() {
- return mState;
+ /**
+ * Sets the current workspace {@link LauncherState}, then animates the UI
+ */
+ public void setStateWithAnimation(LauncherState toState, AnimationLayerSet layerViews,
+ AnimatorSet anim, AnimationConfig config) {
+ StateTransitionListener listener = new StateTransitionListener(toState);
+ mStateTransitionAnimation.setStateWithAnimation(toState, anim, layerViews, config);
+
+ // Invalidate the pages now, so that we have the visible pages before the
+ // animation is started
+ if (toState.hasMultipleVisiblePages) {
+ mForceDrawAdjacentPages = true;
+ }
+ invalidate(); // This will call dispatchDraw(), which calls getVisiblePages().
+
+ ValueAnimator stepAnimator = ValueAnimator.ofFloat(0, 1);
+ stepAnimator.addUpdateListener(listener);
+ stepAnimator.setDuration(config.duration);
+ anim.play(stepAnimator);
+ anim.addListener(listener);
}
public void updateAccessibilityFlags() {
// TODO: Update the accessibility flags appropriately when dragging.
+ int accessibilityFlag = mLauncher.getStateManager().getState().workspaceAccessibilityFlag;
if (!mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) {
int total = getPageCount();
- for (int i = numCustomPages(); i < total; i++) {
- updateAccessibilityFlags((CellLayout) getPageAt(i), i);
+ for (int i = 0; i < total; i++) {
+ updateAccessibilityFlags(accessibilityFlag, (CellLayout) getPageAt(i), i);
}
- setImportantForAccessibility((mState == State.NORMAL || mState == State.OVERVIEW)
- ? IMPORTANT_FOR_ACCESSIBILITY_AUTO
- : IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+ setImportantForAccessibility(accessibilityFlag);
}
}
- private void updateAccessibilityFlags(CellLayout page, int pageNo) {
- if (mState == State.OVERVIEW) {
+ private void updateAccessibilityFlags(int accessibilityFlag, CellLayout page, int pageNo) {
+ if (isPageRearrangeEnabled()) {
page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
page.getShortcutsAndWidgets().setImportantForAccessibility(
IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
@@ -2042,62 +1614,26 @@
page.setAccessibilityDelegate(mPagesAccessibilityDelegate);
}
} else {
- int accessible = mState == State.NORMAL ?
- IMPORTANT_FOR_ACCESSIBILITY_AUTO :
- IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
- page.getShortcutsAndWidgets().setImportantForAccessibility(accessible);
+ page.getShortcutsAndWidgets().setImportantForAccessibility(accessibilityFlag);
page.setContentDescription(null);
page.setAccessibilityDelegate(null);
}
}
- public void onPrepareStateTransition(boolean multiplePagesVisible) {
- mIsSwitchingState = true;
- mTransitionProgress = 0;
-
- if (multiplePagesVisible) {
- mForceDrawAdjacentPages = true;
- }
- invalidate(); // This will call dispatchDraw(), which calls getVisiblePages().
-
- updateChildrenLayersEnabled(false);
- hideCustomContentIfNecessary();
- }
-
- public void onEndStateTransition() {
- mIsSwitchingState = false;
- updateChildrenLayersEnabled(false);
- showCustomContentIfNecessary();
- mForceDrawAdjacentPages = false;
- mTransitionProgress = 1;
- }
-
- void updateCustomContentVisibility() {
- int visibility = mState == Workspace.State.NORMAL ? VISIBLE : INVISIBLE;
- setCustomContentVisibility(visibility);
- }
-
- void setCustomContentVisibility(int visibility) {
- if (hasCustomContent()) {
- mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID).setVisibility(visibility);
+ public void setPageRearrangeEnabled(boolean isEnabled) {
+ if (mPageRearrangeEnabled != isEnabled) {
+ mPageRearrangeEnabled = isEnabled;
+ if (isEnabled) {
+ enableFreeScroll();
+ } else {
+ disableFreeScroll();
+ }
}
}
- void showCustomContentIfNecessary() {
- boolean show = mState == Workspace.State.NORMAL;
- if (show && hasCustomContent()) {
- mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID).setVisibility(VISIBLE);
- }
- }
-
- void hideCustomContentIfNecessary() {
- boolean hide = mState != Workspace.State.NORMAL;
- if (hide && hasCustomContent()) {
- disableLayoutTransitions();
- mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID).setVisibility(INVISIBLE);
- enableLayoutTransitions();
- }
+ public boolean isPageRearrangeEnabled() {
+ return mPageRearrangeEnabled;
}
public void startDrag(CellLayout.CellInfo cellInfo, DragOptions options) {
@@ -2144,7 +1680,7 @@
mOutlineProvider = previewProvider;
// The drag bitmap follows the touch point around on the screen
- final Bitmap b = previewProvider.createDragBitmap(mCanvas);
+ final Bitmap b = previewProvider.createDragBitmap();
int halfPadding = previewProvider.previewPadding / 2;
float scale = previewProvider.getScaleAndPosition(b, mTempXY);
@@ -2191,19 +1727,19 @@
DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source,
dragObject, dragVisualizeOffset, dragRect, scale, dragOptions);
- dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor());
- b.recycle();
+ dv.setIntrinsicIconScaleFactor(dragOptions.intrinsicIconScaleFactor);
return dv;
}
private boolean transitionStateShouldAllowDrop() {
- return ((!isSwitchingState() || mTransitionProgress > ALLOW_DROP_TRANSITION_PROGRESS) &&
- (mState == State.NORMAL || mState == State.SPRING_LOADED));
+ return (!isSwitchingState() || mTransitionProgress > ALLOW_DROP_TRANSITION_PROGRESS) &&
+ workspaceIconsCanBeDragged();
}
/**
* {@inheritDoc}
*/
+ @Override
public boolean acceptDrop(DragObject d) {
// If it's an external drop (e.g. from All Apps), check if it should be accepted
CellLayout dropTargetLayout = mDropToLayout;
@@ -2335,8 +1871,7 @@
}
boolean createUserFolderIfNecessary(View newView, long container, CellLayout target,
- int[] targetCell, float distance, boolean external, DragView dragView,
- Runnable postAnimationRunnable) {
+ int[] targetCell, float distance, boolean external, DragView dragView) {
if (distance > mMaxDistanceForFolderCreation) return false;
View v = target.getChildAt(targetCell[0], targetCell[1]);
@@ -2380,8 +1915,8 @@
// folder background to the newly created icon. This preserves animation state.
fi.setFolderBackground(mFolderCreateBg);
mFolderCreateBg = new PreviewBackground();
- fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale,
- postAnimationRunnable);
+ fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale
+ );
} else {
fi.prepareCreateAnimation(v);
fi.addItem(destInfo);
@@ -2403,7 +1938,7 @@
if (dropOverView instanceof FolderIcon) {
FolderIcon fi = (FolderIcon) dropOverView;
if (fi.acceptDrop(d.dragInfo)) {
- fi.onDrop(d);
+ fi.onDrop(d, false /* itemReturnedOnFailedDrop */);
// if the drag started here, we need to remove it from the workspace
if (!external) {
@@ -2418,7 +1953,7 @@
@Override
public void prepareAccessibilityDrop() { }
- public void onDrop(final DragObject d) {
+ public void onDrop(final DragObject d, DragOptions options) {
mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter);
CellLayout dropTargetLayout = mDropToLayout;
@@ -2435,13 +1970,14 @@
int snapScreen = -1;
boolean resizeOnDrop = false;
- if (d.dragSource != this) {
+ if (d.dragSource != this || mDragInfo == null) {
final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0],
(int) mDragViewVisualCenter[1] };
onDropExternal(touchXY, dropTargetLayout, d);
- } else if (mDragInfo != null) {
+ } else {
final View cell = mDragInfo.cell;
boolean droppedOnOriginalCellDuringTransition = false;
+ Runnable onCompleteRunnable = null;
if (dropTargetLayout != null && !d.cancelled) {
// Move internally
@@ -2465,12 +2001,10 @@
// If the item being dropped is a shortcut and the nearest drop
// cell also contains a shortcut, then create a folder with the two shortcuts.
if (createUserFolderIfNecessary(cell, container,
- dropTargetLayout, mTargetCell, distance, false, d.dragView, null)) {
- return;
- }
-
- if (addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell,
- distance, d, false)) {
+ dropTargetLayout, mTargetCell, distance, false, d.dragView) ||
+ addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell,
+ distance, d, false)) {
+ mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
return;
}
@@ -2552,11 +2086,10 @@
AppWidgetProviderInfo pInfo = hostView.getAppWidgetInfo();
if (pInfo != null && pInfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE
&& !d.accessibleDrag) {
- mDelayedResizeRunnable = new Runnable() {
+ onCompleteRunnable = new Runnable() {
public void run() {
if (!isPageInTransition()) {
- DragLayer dragLayer = mLauncher.getDragLayer();
- dragLayer.addResizeFrame(hostView, cellLayout);
+ AppWidgetResizeFrame.showForWidget(hostView, cellLayout);
}
}
};
@@ -2580,24 +2113,13 @@
}
final CellLayout parent = (CellLayout) cell.getParent().getParent();
- // Prepare it to be animated into its new position
- // This must be called after the view has been re-parented
- final Runnable onCompleteRunnable = new Runnable() {
- @Override
- public void run() {
- mAnimatingViewIntoPlace = false;
- updateChildrenLayersEnabled(false);
- }
- };
- mAnimatingViewIntoPlace = true;
if (d.dragView.hasDrawn()) {
if (droppedOnOriginalCellDuringTransition) {
// Animate the item to its original position, while simultaneously exiting
// spring-loaded mode so the page meets the icon where it was picked up.
mLauncher.getDragController().animateDragViewToOriginalPosition(
- mDelayedResizeRunnable, cell,
- mStateTransitionAnimation.mSpringLoadedTransitionTime);
- mLauncher.exitSpringLoadedDragMode();
+ onCompleteRunnable, cell, SPRING_LOADED_TRANSITION_MS);
+ mLauncher.getStateManager().goToState(NORMAL);
mLauncher.getDropTargetBar().onDragEnd();
parent.onDropChild(cell);
return;
@@ -2608,19 +2130,22 @@
if (isWidget) {
int animationType = resizeOnDrop ? ANIMATE_INTO_POSITION_AND_RESIZE :
ANIMATE_INTO_POSITION_AND_DISAPPEAR;
- animateWidgetDrop(info, parent, d.dragView,
- onCompleteRunnable, animationType, cell, false);
+ animateWidgetDrop(info, parent, d.dragView, null, animationType, cell, false);
} else {
int duration = snapScreen < 0 ? -1 : ADJACENT_SCREEN_DROP_DURATION;
mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration,
- onCompleteRunnable, this);
+ this);
}
} else {
d.deferDragViewCleanupPostAnimation = false;
cell.setVisibility(VISIBLE);
}
parent.onDropChild(cell);
+
+ mLauncher.getStateManager().goToState(
+ NORMAL, SPRING_LOADED_EXIT_DELAY, onCompleteRunnable);
}
+
if (d.stateAnnouncer != null && !droppedOnOriginalCell) {
d.stateAnnouncer.completeAction(R.string.item_moved);
}
@@ -2838,17 +2363,6 @@
xy[1] = mTempXY[1];
}
- /*
- *
- * Convert the 2D coordinate xy from this CellLayout's coordinate space to
- * the parent View's coordinate space. The argument xy is modified with the return result.
- *
- */
- void mapPointFromChildToSelf(View v, float[] xy) {
- xy[0] += v.getLeft();
- xy[1] += v.getTop();
- }
-
private boolean isDragWidget(DragObject d) {
return (d.dragInfo instanceof LauncherAppWidgetInfo ||
d.dragInfo instanceof PendingAddWidgetInfo);
@@ -2977,7 +2491,7 @@
}
// Always pick the current page.
- if (layout == null && nextPage >= numCustomPages() && nextPage < getPageCount()) {
+ if (layout == null && nextPage >= 0 && nextPage < getPageCount()) {
layout = (CellLayout) getChildAt(nextPage);
}
if (layout != mDragTargetLayout) {
@@ -2992,7 +2506,7 @@
* Returns the child CellLayout if the point is inside the page coordinates, null otherwise.
*/
private CellLayout verifyInsidePage(int pageNo, float[] touchXy) {
- if (pageNo >= numCustomPages() && pageNo < getPageCount()) {
+ if (pageNo >= 0 && pageNo < getPageCount()) {
CellLayout cl = (CellLayout) getChildAt(pageNo);
mapPointFromSelfToChild(cl, touchXy);
if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() &&
@@ -3139,14 +2653,6 @@
* to add an item to one of the workspace screens.
*/
private void onDropExternal(final int[] touchXY, final CellLayout cellLayout, DragObject d) {
- final Runnable exitSpringLoadedRunnable = new Runnable() {
- @Override
- public void run() {
- mLauncher.exitSpringLoadedDragModeDelayed(true,
- Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
- }
- };
-
if (d.dragInfo instanceof PendingAddShortcutInfo) {
ShortcutInfo si = ((PendingAddShortcutInfo) d.dragInfo)
.activityInfo.createShortcutInfo();
@@ -3169,8 +2675,8 @@
final long screenId = getIdForScreen(cellLayout);
if (!mLauncher.isHotseatLayout(cellLayout)
&& screenId != getScreenIdForPageIndex(mCurrentPage)
- && mState != State.SPRING_LOADED) {
- snapToScreenId(screenId, null);
+ && !mLauncher.isInState(SPRING_LOADED)) {
+ snapToPage(getPageIndexForScreenId(screenId));
}
if (info instanceof PendingAddItemInfo) {
@@ -3244,6 +2750,8 @@
animationStyle, finalView, true);
} else {
// This is for other drag/drop cases, like dragging from All Apps
+ mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
+
View view;
switch (info.itemType) {
@@ -3272,9 +2780,8 @@
cellLayout, mTargetCell);
float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
mDragViewVisualCenter[1], mTargetCell);
- d.postAnimationRunnable = exitSpringLoadedRunnable;
if (createUserFolderIfNecessary(view, container, cellLayout, mTargetCell, distance,
- true, d.dragView, d.postAnimationRunnable)) {
+ true, d.dragView)) {
return;
}
if (addToExistingFolderIfNecessary(view, cellLayout, mTargetCell, distance, d,
@@ -3305,16 +2812,15 @@
// We wrap the animation call in the temporary set and reset of the current
// cellLayout to its final transform -- this means we animate the drag view to
// the correct final location.
- setFinalTransitionTransform(cellLayout);
- mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, view,
- exitSpringLoadedRunnable, this);
- resetTransitionTransform(cellLayout);
+ setFinalTransitionTransform();
+ mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, view, this);
+ resetTransitionTransform();
}
}
}
public Bitmap createWidgetBitmap(ItemInfo widgetInfo, View layout) {
- int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(widgetInfo, false, true);
+ int[] unScaledSize = estimateItemSize(widgetInfo);
int visibility = layout.getVisibility();
layout.setVisibility(VISIBLE);
@@ -3322,12 +2828,9 @@
int height = MeasureSpec.makeMeasureSpec(unScaledSize[1], MeasureSpec.EXACTLY);
Bitmap b = Bitmap.createBitmap(unScaledSize[0], unScaledSize[1],
Bitmap.Config.ARGB_8888);
- mCanvas.setBitmap(b);
-
layout.measure(width, height);
layout.layout(0, 0, unScaledSize[0], unScaledSize[1]);
- layout.draw(mCanvas);
- mCanvas.setBitmap(null);
+ layout.draw(new Canvas(b));
layout.setVisibility(visibility);
return b;
}
@@ -3347,10 +2850,10 @@
loc[0] = r.left;
loc[1] = r.top;
- setFinalTransitionTransform(layout);
+ setFinalTransitionTransform();
float cellLayoutScale =
mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(layout, loc, true);
- resetTransitionTransform(layout);
+ resetTransitionTransform();
if (scale) {
float dragViewScaleX = (1.0f * r.width()) / dragView.getMeasuredWidth();
@@ -3434,24 +2937,20 @@
}
}
- public void setFinalTransitionTransform(CellLayout layout) {
+ public void setFinalTransitionTransform() {
if (isSwitchingState()) {
mCurrentScale = getScaleX();
setScaleX(mStateTransitionAnimation.getFinalScale());
setScaleY(mStateTransitionAnimation.getFinalScale());
}
}
- public void resetTransitionTransform(CellLayout layout) {
+ public void resetTransitionTransform() {
if (isSwitchingState()) {
setScaleX(mCurrentScale);
setScaleY(mCurrentScale);
}
}
- public WorkspaceStateTransitionAnimation getStateTransitionAnimation() {
- return mStateTransitionAnimation;
- }
-
/**
* Return the current CellInfo describing our current drag; this method exists
* so that Launcher can sync this object with the correct info when the activity is created/
@@ -3462,10 +2961,6 @@
return mDragInfo;
}
- public int getCurrentPageOffsetFromCustomContent() {
- return getNextPage() - numCustomPages();
- }
-
/**
* Calculate the nearest cell where the given object would be dropped.
*
@@ -3483,29 +2978,16 @@
// hardware layers on children are enabled on startup, but should be disabled until
// needed
- updateChildrenLayersEnabled(false);
+ updateChildrenLayersEnabled();
}
/**
* Called at the end of a drag which originated on the workspace.
*/
public void onDropCompleted(final View target, final DragObject d,
- final boolean isFlingToDelete, final boolean success) {
- if (mDeferDropAfterUninstall) {
- final CellLayout.CellInfo dragInfo = mDragInfo;
- mDeferredAction = new Runnable() {
- public void run() {
- mDragInfo = dragInfo; // Restore the drag info that was cleared in onDragEnd()
- onDropCompleted(target, d, isFlingToDelete, success);
- mDeferredAction = null;
- }
- };
- return;
- }
+ final boolean success) {
- boolean beingCalledAfterUninstall = mDeferredAction != null;
-
- if (success && !(beingCalledAfterUninstall && !mUninstallSuccessful)) {
+ if (success) {
if (target != this && mDragInfo != null) {
removeWorkspaceItem(mDragInfo.cell);
}
@@ -3519,18 +3001,10 @@
+ "Workspace#onDropCompleted. Please file a bug. ");
}
}
- if ((d.cancelled || (beingCalledAfterUninstall && !mUninstallSuccessful))
- && mDragInfo != null && mDragInfo.cell != null) {
+ if (d.cancelled && mDragInfo != null && mDragInfo.cell != null) {
mDragInfo.cell.setVisibility(VISIBLE);
}
mDragInfo = null;
-
- if (!isFlingToDelete) {
- // Fling to delete already exits spring loaded mode after the animation finishes.
- mLauncher.exitSpringLoadedDragModeDelayed(success,
- Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, mDelayedResizeRunnable);
- mDelayedResizeRunnable = null;
- }
}
/**
@@ -3567,36 +3041,6 @@
});
}
- @Override
- public void deferCompleteDropAfterUninstallActivity() {
- mDeferDropAfterUninstall = true;
- }
-
- /// maybe move this into a smaller part
- @Override
- public void onDragObjectRemoved(boolean success) {
- mDeferDropAfterUninstall = false;
- mUninstallSuccessful = success;
- if (mDeferredAction != null) {
- mDeferredAction.run();
- }
- }
-
- @Override
- public float getIntrinsicIconScaleFactor() {
- return 1f;
- }
-
- @Override
- public boolean supportsAppInfoDropTarget() {
- return true;
- }
-
- @Override
- public boolean supportsDeleteDropTarget() {
- return true;
- }
-
public boolean isDropEnabled() {
return true;
}
@@ -3967,7 +3411,7 @@
.getInstance(mLauncher).findProvider(item.providerName, item.user);
} else {
widgetInfo = AppWidgetManagerCompat.getInstance(mLauncher)
- .getAppWidgetInfo(item.appWidgetId);
+ .getLauncherAppWidgetInfo(item.appWidgetId);
}
if (widgetInfo != null) {
@@ -3992,14 +3436,10 @@
}
}
- void moveToDefaultScreen(boolean animate) {
- int page = getDefaultPage();
+ void moveToDefaultScreen() {
+ int page = DEFAULT_PAGE;
if (!workspaceInModalState() && getNextPage() != page) {
- if (animate) {
- snapToPage(page);
- } else {
- setCurrentPage(page);
- }
+ snapToPage(page);
}
View child = getChildAt(page);
if (child != null) {
@@ -4007,22 +3447,6 @@
}
}
- void moveToCustomContentScreen(boolean animate) {
- if (hasCustomContent()) {
- int ccIndex = getPageIndexForScreenId(CUSTOM_CONTENT_SCREEN_ID);
- if (animate) {
- snapToPage(ccIndex);
- } else {
- setCurrentPage(ccIndex);
- }
- View child = getChildAt(ccIndex);
- if (child != null) {
- child.requestFocus();
- }
- }
- exitWidgetResizeMode();
- }
-
@Override
protected String getPageIndicatorDescription() {
return getResources().getString(R.string.all_apps_button_label);
@@ -4030,16 +3454,12 @@
@Override
protected String getCurrentPageDescription() {
- if (hasCustomContent() && getNextPage() == 0) {
- return mCustomContentDescription;
- }
int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
return getPageDescription(page);
}
private String getPageDescription(int page) {
- int delta = numCustomPages();
- int nScreens = getChildCount() - delta;
+ int nScreens = getChildCount();
int extraScreenId = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
if (extraScreenId >= 0 && nScreens > 1) {
if (page == extraScreenId) {
@@ -4051,8 +3471,7 @@
// When the workspace is not loaded, we do not know how many screen will be bound.
return getContext().getString(R.string.all_apps_home_button_label);
}
- return getContext().getString(R.string.workspace_scroll_format,
- page + 1 - delta, nScreens);
+ return getContext().getString(R.string.workspace_scroll_format, page + 1, nScreens);
}
@Override
@@ -4069,16 +3488,6 @@
}
}
- @Override
- public boolean enableFreeScroll() {
- if (getState() == State.OVERVIEW) {
- return super.enableFreeScroll();
- } else {
- Log.w(TAG, "enableFreeScroll called but not in overview: state=" + getState());
- return false;
- }
- }
-
/**
* Used as a workaround to ensure that the AppWidgetService receives the
* PACKAGE_ADDED broadcast before updating widgets.
@@ -4135,6 +3544,13 @@
private class StateTransitionListener extends AnimatorListenerAdapter
implements AnimatorUpdateListener {
+
+ private final LauncherState mToState;
+
+ StateTransitionListener(LauncherState toState) {
+ mToState = toState;
+ }
+
@Override
public void onAnimationUpdate(ValueAnimator anim) {
mTransitionProgress = anim.getAnimatedFraction();
@@ -4142,11 +3558,7 @@
@Override
public void onAnimationStart(Animator animation) {
- if (mState == State.SPRING_LOADED) {
- // Show the page indicator at the same time as the rest of the transition.
- showPageIndicatorAtCurrentScroll();
- }
- mTransitionProgress = 0;
+ onStartStateTransition(mToState);
}
@Override
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index a105a73..e14461e 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -16,6 +16,9 @@
package com.android.launcher3;
+import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA;
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -24,17 +27,13 @@
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
+import android.util.Property;
import android.view.View;
-import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.animation.DecelerateInterpolator;
+import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.anim.AnimationLayerSet;
-import com.android.launcher3.anim.PropertyListBuilder;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.util.Thunk;
+import com.android.launcher3.anim.Interpolators;
/**
* A convenience class to update a view's visibility state after an alpha animation.
@@ -87,122 +86,20 @@
}
/**
- * This interpolator emulates the rate at which the perceived scale of an object changes
- * as its distance from a camera increases. When this interpolator is applied to a scale
- * animation on a view, it evokes the sense that the object is shrinking due to moving away
- * from the camera.
- */
-class ZInterpolator implements TimeInterpolator {
- private float focalLength;
-
- public ZInterpolator(float foc) {
- focalLength = foc;
- }
-
- public float getInterpolation(float input) {
- return (1.0f - focalLength / (focalLength + input)) /
- (1.0f - focalLength / (focalLength + 1.0f));
- }
-}
-
-/**
- * The exact reverse of ZInterpolator.
- */
-class InverseZInterpolator implements TimeInterpolator {
- private ZInterpolator zInterpolator;
- public InverseZInterpolator(float foc) {
- zInterpolator = new ZInterpolator(foc);
- }
- public float getInterpolation(float input) {
- return 1 - zInterpolator.getInterpolation(1 - input);
- }
-}
-
-/**
- * InverseZInterpolator compounded with an ease-out.
- */
-class ZoomInInterpolator implements TimeInterpolator {
- private final InverseZInterpolator inverseZInterpolator = new InverseZInterpolator(0.35f);
- private final DecelerateInterpolator decelerate = new DecelerateInterpolator(3.0f);
-
- public float getInterpolation(float input) {
- return decelerate.getInterpolation(inverseZInterpolator.getInterpolation(input));
- }
-}
-
-/**
- * Stores the transition states for convenience.
- */
-class TransitionStates {
-
- // Raw states
- final boolean oldStateIsNormal;
- final boolean oldStateIsSpringLoaded;
- final boolean oldStateIsNormalHidden;
- final boolean oldStateIsOverviewHidden;
- final boolean oldStateIsOverview;
-
- final boolean stateIsNormal;
- final boolean stateIsSpringLoaded;
- final boolean stateIsNormalHidden;
- final boolean stateIsOverviewHidden;
- final boolean stateIsOverview;
-
- // Convenience members
- final boolean workspaceToAllApps;
- final boolean overviewToAllApps;
- final boolean allAppsToWorkspace;
- final boolean workspaceToOverview;
- final boolean overviewToWorkspace;
-
- public TransitionStates(final Workspace.State fromState, final Workspace.State toState) {
- oldStateIsNormal = (fromState == Workspace.State.NORMAL);
- oldStateIsSpringLoaded = (fromState == Workspace.State.SPRING_LOADED);
- oldStateIsNormalHidden = (fromState == Workspace.State.NORMAL_HIDDEN);
- oldStateIsOverviewHidden = (fromState == Workspace.State.OVERVIEW_HIDDEN);
- oldStateIsOverview = (fromState == Workspace.State.OVERVIEW);
-
- stateIsNormal = (toState == Workspace.State.NORMAL);
- stateIsSpringLoaded = (toState == Workspace.State.SPRING_LOADED);
- stateIsNormalHidden = (toState == Workspace.State.NORMAL_HIDDEN);
- stateIsOverviewHidden = (toState == Workspace.State.OVERVIEW_HIDDEN);
- stateIsOverview = (toState == Workspace.State.OVERVIEW);
-
- workspaceToOverview = (oldStateIsNormal && stateIsOverview);
- workspaceToAllApps = (oldStateIsNormal && stateIsNormalHidden);
- overviewToWorkspace = (oldStateIsOverview && stateIsNormal);
- overviewToAllApps = (oldStateIsOverview && stateIsOverviewHidden);
- allAppsToWorkspace = (oldStateIsNormalHidden && stateIsNormal);
- }
-}
-
-/**
* Manages the animations between each of the workspace states.
*/
public class WorkspaceStateTransitionAnimation {
- public static final String TAG = "WorkspaceStateTransitionAnimation";
+ private static final PropertySetter NO_ANIM_PROPERTY_SETTER = new PropertySetter();
- @Thunk static final int BACKGROUND_FADE_OUT_DURATION = 350;
+ public final int mWorkspaceScrimAlpha;
- final @Thunk Launcher mLauncher;
- final @Thunk Workspace mWorkspace;
+ private final Launcher mLauncher;
+ private final Workspace mWorkspace;
- @Thunk AnimatorSet mStateAnimator;
+ private final boolean mWorkspaceFadeInAdjacentScreens;
- @Thunk float mCurrentScale;
- @Thunk float mNewScale;
-
- @Thunk final ZoomInInterpolator mZoomInInterpolator = new ZoomInInterpolator();
-
- @Thunk float mSpringLoadedShrinkFactor;
- @Thunk float mOverviewModeShrinkFactor;
- @Thunk float mWorkspaceScrimAlpha;
- @Thunk int mAllAppsTransitionTime;
- @Thunk int mOverviewTransitionTime;
- @Thunk int mOverlayTransitionTime;
- @Thunk int mSpringLoadedTransitionTime;
- @Thunk boolean mWorkspaceFadeInAdjacentScreens;
+ private float mNewScale;
public WorkspaceStateTransitionAnimation(Launcher launcher, Workspace workspace) {
mLauncher = launcher;
@@ -210,32 +107,19 @@
DeviceProfile grid = mLauncher.getDeviceProfile();
Resources res = launcher.getResources();
- mAllAppsTransitionTime = res.getInteger(R.integer.config_allAppsTransitionTime);
- mOverviewTransitionTime = res.getInteger(R.integer.config_overviewTransitionTime);
- mOverlayTransitionTime = res.getInteger(R.integer.config_overlayTransitionTime);
- mSpringLoadedTransitionTime = mOverlayTransitionTime / 2;
- mSpringLoadedShrinkFactor = mLauncher.getDeviceProfile().workspaceSpringLoadShrinkFactor;
- mOverviewModeShrinkFactor =
- res.getInteger(R.integer.config_workspaceOverviewShrinkPercentage) / 100f;
- mWorkspaceScrimAlpha = res.getInteger(R.integer.config_workspaceScrimAlpha) / 100f;
+ mWorkspaceScrimAlpha = res.getInteger(R.integer.config_workspaceScrimAlpha);
mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens();
}
- public void snapToPageFromOverView(int whichPage) {
- mWorkspace.snapToPage(whichPage, mOverviewTransitionTime, mZoomInInterpolator);
+ public void setState(LauncherState toState) {
+ setWorkspaceProperty(toState, NO_ANIM_PROPERTY_SETTER);
}
- public AnimatorSet getAnimationToState(Workspace.State fromState, Workspace.State toState,
- boolean animated, AnimationLayerSet layerViews) {
- AccessibilityManager am = (AccessibilityManager)
- mLauncher.getSystemService(Context.ACCESSIBILITY_SERVICE);
- final boolean accessibilityEnabled = am.isEnabled();
- TransitionStates states = new TransitionStates(fromState, toState);
- int workspaceDuration = getAnimationDuration(states);
- animateWorkspace(states, animated, workspaceDuration, layerViews,
- accessibilityEnabled);
- animateBackgroundGradient(states, animated, BACKGROUND_FADE_OUT_DURATION);
- return mStateAnimator;
+ public void setStateWithAnimation(LauncherState toState, AnimatorSet anim,
+ AnimationLayerSet layerViews, AnimationConfig config) {
+ AnimatedPropertySetter propertySetter =
+ new AnimatedPropertySetter(config.duration, layerViews, anim);
+ setWorkspaceProperty(toState, propertySetter);
}
public float getFinalScale() {
@@ -243,245 +127,134 @@
}
/**
- * Returns the proper animation duration for a transition.
- */
- private int getAnimationDuration(TransitionStates states) {
- if (states.workspaceToAllApps || states.overviewToAllApps) {
- return mAllAppsTransitionTime;
- } else if (states.workspaceToOverview || states.overviewToWorkspace) {
- return mOverviewTransitionTime;
- } else if (mLauncher.mState == Launcher.State.WORKSPACE_SPRING_LOADED
- || states.oldStateIsNormal && states.stateIsSpringLoaded) {
- return mSpringLoadedTransitionTime;
- } else {
- return mOverlayTransitionTime;
- }
- }
-
- /**
* Starts a transition animation for the workspace.
*/
- private void animateWorkspace(final TransitionStates states, final boolean animated,
- final int duration, AnimationLayerSet layerViews, final boolean accessibilityEnabled) {
- // Cancel existing workspace animations and create a new animator set if requested
- cancelAnimation();
- if (animated) {
- mStateAnimator = LauncherAnimUtils.createAnimatorSet();
- }
-
- // Update the workspace state
- float finalBackgroundAlpha = (states.stateIsSpringLoaded || states.stateIsOverview) ?
- 1.0f : 0f;
- float finalHotseatAlpha = (states.stateIsNormal || states.stateIsSpringLoaded ||
- states.stateIsNormalHidden) ? 1f : 0f;
- float finalQsbAlpha = (states.stateIsNormal || states.stateIsNormalHidden) ? 1f : 0f;
-
- float finalWorkspaceTranslationY = 0;
- if (states.stateIsOverview || states.stateIsOverviewHidden) {
- finalWorkspaceTranslationY = mWorkspace.getOverviewModeTranslationY();
- } else if (states.stateIsSpringLoaded) {
- finalWorkspaceTranslationY = mWorkspace.getSpringLoadedTranslationY();
- }
-
- final int childCount = mWorkspace.getChildCount();
- final int customPageCount = mWorkspace.numCustomPages();
-
- mNewScale = 1.0f;
-
- if (states.oldStateIsOverview) {
- mWorkspace.disableFreeScroll();
- } else if (states.stateIsOverview) {
- mWorkspace.enableFreeScroll();
- }
-
- if (!states.stateIsNormal) {
- if (states.stateIsSpringLoaded) {
- mNewScale = mSpringLoadedShrinkFactor;
- } else if (states.stateIsOverview || states.stateIsOverviewHidden) {
- mNewScale = mOverviewModeShrinkFactor;
- }
- }
+ private void setWorkspaceProperty(LauncherState state, PropertySetter propertySetter) {
+ float[] scaleAndTranslationY = state.getWorkspaceScaleAndTranslation(mLauncher);
+ mNewScale = scaleAndTranslationY[0];
+ final float finalWorkspaceTranslationY = scaleAndTranslationY[1];
int toPage = mWorkspace.getPageNearestToCenterOfScreen();
- // TODO: Animate the celllayout alpha instead of the pages.
+ final int childCount = mWorkspace.getChildCount();
for (int i = 0; i < childCount; i++) {
- final CellLayout cl = (CellLayout) mWorkspace.getChildAt(i);
- float initialAlpha = cl.getShortcutsAndWidgets().getAlpha();
- float finalAlpha;
- if (states.stateIsOverviewHidden) {
- finalAlpha = 0f;
- } else if(states.stateIsNormalHidden) {
- finalAlpha = (i == mWorkspace.getNextPage()) ? 1 : 0;
- } else if (states.stateIsNormal && mWorkspaceFadeInAdjacentScreens) {
- finalAlpha = (i == toPage || i < customPageCount) ? 1f : 0f;
- } else {
- finalAlpha = 1f;
- }
-
- // If we are animating to/from the small state, then hide the side pages and fade the
- // current page in
- if (!FeatureFlags.NO_ALL_APPS_ICON && !mWorkspace.isSwitchingState()) {
- if (states.workspaceToAllApps || states.allAppsToWorkspace) {
- boolean isCurrentPage = (i == toPage);
- if (states.allAppsToWorkspace && isCurrentPage) {
- initialAlpha = 0f;
- } else if (!isCurrentPage) {
- initialAlpha = finalAlpha = 0f;
- }
- cl.setShortcutAndWidgetAlpha(initialAlpha);
- }
- }
-
- if (animated) {
- float oldBackgroundAlpha = cl.getBackgroundAlpha();
- if (initialAlpha != finalAlpha) {
- Animator alphaAnim = ObjectAnimator.ofFloat(
- cl.getShortcutsAndWidgets(), View.ALPHA, finalAlpha);
- alphaAnim.setDuration(duration)
- .setInterpolator(mZoomInInterpolator);
- mStateAnimator.play(alphaAnim);
- }
- if (oldBackgroundAlpha != 0 || finalBackgroundAlpha != 0) {
- ValueAnimator bgAnim = ObjectAnimator.ofFloat(cl, "backgroundAlpha",
- oldBackgroundAlpha, finalBackgroundAlpha);
- bgAnim.setInterpolator(mZoomInInterpolator);
- bgAnim.setDuration(duration);
- mStateAnimator.play(bgAnim);
- }
- } else {
- cl.setBackgroundAlpha(finalBackgroundAlpha);
- cl.setShortcutAndWidgetAlpha(finalAlpha);
- }
+ applyChildState(state, (CellLayout) mWorkspace.getChildAt(i), i, toPage,
+ propertySetter);
}
- final ViewGroup overviewPanel = mLauncher.getOverviewPanel();
+ float finalHotseatAlpha = state.hideHotseat ? 0f : 1f;
- float finalOverviewPanelAlpha = states.stateIsOverview ? 1f : 0f;
- if (animated) {
- // This is true when transitioning between:
- // - Overview <-> Workspace
- // - Overview <-> Widget Tray
- if (finalOverviewPanelAlpha != overviewPanel.getAlpha()) {
- Animator overviewPanelAlpha = ObjectAnimator.ofFloat(
- overviewPanel, View.ALPHA, finalOverviewPanelAlpha);
- overviewPanelAlpha.addListener(new AlphaUpdateListener(overviewPanel,
- accessibilityEnabled));
- layerViews.addView(overviewPanel);
+ // This is true when transitioning between:
+ // - Overview <-> Workspace
+ propertySetter.setViewAlpha(null, mLauncher.getOverviewPanel(), 1 - finalHotseatAlpha);
+ propertySetter.setViewAlpha(mWorkspace.createHotseatAlphaAnimator(finalHotseatAlpha),
+ mLauncher.getHotseat(), finalHotseatAlpha);
- if (states.overviewToWorkspace) {
- overviewPanelAlpha.setInterpolator(new DecelerateInterpolator(2));
- } else if (states.workspaceToOverview) {
- overviewPanelAlpha.setInterpolator(null);
- }
+ propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, Interpolators.ZOOM_IN);
+ propertySetter.setFloat(mWorkspace, View.TRANSLATION_Y,
+ finalWorkspaceTranslationY, Interpolators.ZOOM_IN);
- overviewPanelAlpha.setDuration(duration);
- mStateAnimator.play(overviewPanelAlpha);
- }
+ // Set scrim
+ propertySetter.setInt(mLauncher.getDragLayer().getScrim(), DRAWABLE_ALPHA,
+ state.hasScrim ? mWorkspaceScrimAlpha : 0, Interpolators.DEACCEL_1_5);
+ }
- Animator scale = LauncherAnimUtils.ofPropertyValuesHolder(mWorkspace,
- new PropertyListBuilder().scale(mNewScale)
- .translationY(finalWorkspaceTranslationY).build())
- .setDuration(duration);
- scale.setInterpolator(mZoomInInterpolator);
- mStateAnimator.play(scale);
+ public void applyChildState(LauncherState state, CellLayout cl, int childIndex) {
+ applyChildState(state, cl, childIndex, mWorkspace.getPageNearestToCenterOfScreen(),
+ NO_ANIM_PROPERTY_SETTER);
+ }
- // For animation optimization, we may need to provide the Launcher transition
- // with a set of views on which to force build and manage layers in certain scenarios.
- layerViews.addView(mLauncher.getHotseat());
- layerViews.addView(mWorkspace.getPageIndicator());
+ private void applyChildState(LauncherState state, CellLayout cl, int childIndex,
+ int centerPage, PropertySetter propertySetter) {
+ propertySetter.setInt(cl.getScrimBackground(),
+ DRAWABLE_ALPHA, state.hasScrim ? 255 : 0, Interpolators.ZOOM_IN);
- Animator hotseatAlpha = mWorkspace.createHotseatAlphaAnimator(finalHotseatAlpha);
- if (states.workspaceToOverview) {
- hotseatAlpha.setInterpolator(new DecelerateInterpolator(2));
- } else if (states.overviewToWorkspace) {
- hotseatAlpha.setInterpolator(null);
- }
- hotseatAlpha.setDuration(duration);
- mStateAnimator.play(hotseatAlpha);
- mStateAnimator.addListener(new AnimatorListenerAdapter() {
- boolean canceled = false;
- @Override
- public void onAnimationCancel(Animator animation) {
- canceled = true;
- }
-
- @Override
- public void onAnimationStart(Animator animation) {
- mWorkspace.getPageIndicator().setShouldAutoHide(!states.stateIsSpringLoaded);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- mStateAnimator = null;
- if (canceled) return;
- if (accessibilityEnabled && overviewPanel.getVisibility() == View.VISIBLE) {
- overviewPanel.getChildAt(0).performAccessibilityAction(
- AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
- }
- }
- });
- } else {
- overviewPanel.setAlpha(finalOverviewPanelAlpha);
- AlphaUpdateListener.updateVisibility(overviewPanel, accessibilityEnabled);
- mWorkspace.getPageIndicator().setShouldAutoHide(!states.stateIsSpringLoaded);
-
- mWorkspace.createHotseatAlphaAnimator(finalHotseatAlpha).end();
- mWorkspace.updateCustomContentVisibility();
- mWorkspace.setScaleX(mNewScale);
- mWorkspace.setScaleY(mNewScale);
- mWorkspace.setTranslationY(finalWorkspaceTranslationY);
-
- if (accessibilityEnabled && overviewPanel.getVisibility() == View.VISIBLE) {
- overviewPanel.getChildAt(0).performAccessibilityAction(
- AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
- }
+ // Only animate the page alpha when we actually fade pages
+ if (mWorkspaceFadeInAdjacentScreens) {
+ float finalAlpha = state == LauncherState.NORMAL && childIndex != centerPage ? 0 : 1f;
+ propertySetter.setFloat(cl.getShortcutsAndWidgets(), View.ALPHA,
+ finalAlpha, Interpolators.ZOOM_IN);
}
}
- /**
- * Animates the background scrim. Add to the state animator to prevent jankiness.
- *
- * @param states the current and final workspace states
- * @param animated whether or not to set the background alpha immediately
- * @duration duration of the animation
- */
- private void animateBackgroundGradient(TransitionStates states,
- boolean animated, int duration) {
+ private static class PropertySetter {
- final DragLayer dragLayer = mLauncher.getDragLayer();
- final float startAlpha = dragLayer.getBackgroundAlpha();
- float finalAlpha = states.stateIsNormal || states.stateIsNormalHidden ?
- 0 : mWorkspaceScrimAlpha;
-
- if (finalAlpha != startAlpha) {
- if (animated) {
- // These properties refer to the background protection gradient used for AllApps
- // and Widget tray.
- ValueAnimator bgFadeOutAnimation = ValueAnimator.ofFloat(startAlpha, finalAlpha);
- bgFadeOutAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- dragLayer.setBackgroundAlpha(
- ((Float)animation.getAnimatedValue()).floatValue());
- }
- });
- bgFadeOutAnimation.setInterpolator(new DecelerateInterpolator(1.5f));
- bgFadeOutAnimation.setDuration(duration);
- mStateAnimator.play(bgFadeOutAnimation);
- } else {
- dragLayer.setBackgroundAlpha(finalAlpha);
+ public void setViewAlpha(Animator anim, View view, float alpha) {
+ if (anim != null) {
+ anim.end();
+ return;
}
+ view.setAlpha(alpha);
+ AlphaUpdateListener.updateVisibility(view, isAccessibilityEnabled(view));
+ }
+
+ public <T> void setFloat(T target, Property<T, Float> property, float value,
+ TimeInterpolator interpolator) {
+ property.set(target, value);
+ }
+
+ public <T> void setInt(T target, Property<T, Integer> property, int value,
+ TimeInterpolator interpolator) {
+ property.set(target, value);
+ }
+
+ protected boolean isAccessibilityEnabled(View v) {
+ AccessibilityManager am = (AccessibilityManager)
+ v.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
+ return am.isEnabled();
}
}
- /**
- * Cancels the current animation.
- */
- private void cancelAnimation() {
- if (mStateAnimator != null) {
- mStateAnimator.setDuration(0);
- mStateAnimator.cancel();
+ private static class AnimatedPropertySetter extends PropertySetter {
+
+ private final long mDuration;
+ private final AnimationLayerSet mLayerViews;
+ private final AnimatorSet mStateAnimator;
+
+ AnimatedPropertySetter(long duration, AnimationLayerSet layerView, AnimatorSet anim) {
+ mDuration = duration;
+ mLayerViews = layerView;
+ mStateAnimator = anim;
}
- mStateAnimator = null;
+
+ @Override
+ public void setViewAlpha(Animator anim, View view, float alpha) {
+ if (anim == null) {
+ if (view.getAlpha() == alpha) {
+ return;
+ }
+ anim = ObjectAnimator.ofFloat(view, View.ALPHA, alpha);
+ anim.addListener(new AlphaUpdateListener(view, isAccessibilityEnabled(view)));
+ }
+
+ anim.setDuration(mDuration).setInterpolator(getFadeInterpolator(alpha));
+ mLayerViews.addView(view);
+ mStateAnimator.play(anim);
+ }
+
+ @Override
+ public <T> void setFloat(T target, Property<T, Float> property, float value,
+ TimeInterpolator interpolator) {
+ if (property.get(target) == value) {
+ return;
+ }
+ Animator anim = ObjectAnimator.ofFloat(target, property, value);
+ anim.setDuration(mDuration).setInterpolator(interpolator);
+ mStateAnimator.play(anim);
+ }
+
+ @Override
+ public <T> void setInt(T target, Property<T, Integer> property, int value,
+ TimeInterpolator interpolator) {
+ if (property.get(target) == value) {
+ return;
+ }
+ Animator anim = ObjectAnimator.ofInt(target, property, value);
+ anim.setDuration(mDuration).setInterpolator(interpolator);
+ mStateAnimator.play(anim);
+ }
+
+ private TimeInterpolator getFadeInterpolator(float finalAlpha) {
+ return finalAlpha == 0 ? Interpolators.DEACCEL_2 : null;
+ }
}
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index a0ad07a..512db72 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -1,5 +1,7 @@
package com.android.launcher3.accessibility;
+import static com.android.launcher3.LauncherState.NORMAL;
+
import android.app.AlertDialog;
import android.appwidget.AppWidgetProviderInfo;
import android.content.DialogInterface;
@@ -17,6 +19,7 @@
import com.android.launcher3.AppInfo;
import com.android.launcher3.AppWidgetResizeFrame;
import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.ButtonDropTarget;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeleteDropTarget;
import com.android.launcher3.DropTarget.DragObject;
@@ -27,6 +30,7 @@
import com.android.launcher3.LauncherAppWidgetHostView;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutInfo;
@@ -45,9 +49,9 @@
private static final String TAG = "LauncherAccessibilityDelegate";
- protected static final int REMOVE = R.id.action_remove;
- protected static final int INFO = R.id.action_info;
- protected static final int UNINSTALL = R.id.action_uninstall;
+ public static final int REMOVE = R.id.action_remove;
+ public static final int INFO = R.id.action_info;
+ public static final int UNINSTALL = R.id.action_uninstall;
protected static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace;
protected static final int MOVE = R.id.action_move;
protected static final int MOVE_TO_WORKSPACE = R.id.action_move_to_workspace;
@@ -108,14 +112,10 @@
info.addAction(mActions.get(DEEP_SHORTCUTS));
}
- if (DeleteDropTarget.supportsAccessibleDrop(item)) {
- info.addAction(mActions.get(REMOVE));
- }
- if (UninstallDropTarget.supportsDrop(host.getContext(), item)) {
- info.addAction(mActions.get(UNINSTALL));
- }
- if (InfoDropTarget.supportsDrop(host.getContext(), item)) {
- info.addAction(mActions.get(INFO));
+ for (ButtonDropTarget target : mLauncher.getDropTargetBar().getDropTargets()) {
+ if (target.supportsAccessibilityDrop(item)) {
+ info.addAction(mActions.get(target.getAccessibilityAction()));
+ }
}
// Do not add move actions for keyboard request as this uses virtual nodes.
@@ -148,27 +148,19 @@
}
public boolean performAction(final View host, final ItemInfo item, int action) {
- if (action == REMOVE) {
- DeleteDropTarget.removeWorkspaceOrFolderItem(mLauncher, item, host);
- return true;
- } else if (action == INFO) {
- InfoDropTarget.startDetailsActivityForInfo(item, mLauncher, null);
- return true;
- } else if (action == UNINSTALL) {
- return UninstallDropTarget.startUninstallActivity(mLauncher, item);
- } else if (action == MOVE) {
+ if (action == MOVE) {
beginAccessibleDrag(host, item);
} else if (action == ADD_TO_WORKSPACE) {
final int[] coordinates = new int[2];
final long screenId = findSpaceOnWorkspace(item, coordinates);
- mLauncher.showWorkspace(true, new Runnable() {
+ mLauncher.getStateManager().goToState(NORMAL, true, new Runnable() {
@Override
public void run() {
if (item instanceof AppInfo) {
ShortcutInfo info = ((AppInfo) item).makeShortcut();
mLauncher.getModelWriter().addItemToDatabase(info,
- LauncherSettings.Favorites.CONTAINER_DESKTOP,
+ Favorites.CONTAINER_DESKTOP,
screenId, coordinates[0], coordinates[1]);
ArrayList<ItemInfo> itemList = new ArrayList<>();
@@ -178,7 +170,7 @@
PendingAddItemInfo info = (PendingAddItemInfo) item;
Workspace workspace = mLauncher.getWorkspace();
workspace.snapToPage(workspace.getPageIndexForScreenId(screenId));
- mLauncher.addPendingItem(info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
+ mLauncher.addPendingItem(info, Favorites.CONTAINER_DESKTOP,
screenId, coordinates, info.spanX, info.spanY);
}
announceConfirmation(R.string.item_added_to_workspace);
@@ -231,6 +223,13 @@
return true;
} else if (action == DEEP_SHORTCUTS) {
return PopupContainerWithArrow.showForIcon((BubbleTextView) host) != null;
+ } else {
+ for (ButtonDropTarget dropTarget : mLauncher.getDropTargetBar().getDropTargets()) {
+ if (action == dropTarget.getAccessibilityAction()) {
+ dropTarget.onAccessibilityDrop(host, item);
+ return true;
+ }
+ }
}
return false;
}
@@ -411,7 +410,7 @@
CellLayout layout = (CellLayout) workspace.getPageAt(screenIndex);
boolean found = layout.findCellForSpan(outCoordinates, info.spanX, info.spanY);
- screenIndex = workspace.hasCustomContent() ? 1 : 0;
+ screenIndex = 0;
while (!found && screenIndex < workspaceScreens.size()) {
screenId = workspaceScreens.get(screenIndex);
layout = (CellLayout) workspace.getPageAt(screenIndex);
diff --git a/src/com/android/launcher3/accessibility/OverviewAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/OverviewAccessibilityDelegate.java
index 29dd95c..771353e 100644
--- a/src/com/android/launcher3/accessibility/OverviewAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/OverviewAccessibilityDelegate.java
@@ -24,6 +24,7 @@
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -55,7 +56,7 @@
public boolean performAccessibilityAction(View host, int action, Bundle args) {
Launcher launcher = Launcher.getLauncher(host.getContext());
if (action == OVERVIEW) {
- launcher.showOverviewMode(true);
+ launcher.getStateManager().goToState(LauncherState.OVERVIEW);
return true;
} else if (action == WALLPAPERS) {
launcher.onClickWallpaperPicker(host);
diff --git a/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java
index edb0b16..f9eb2ed 100644
--- a/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java
@@ -88,7 +88,7 @@
info.addAction(mActions.get(MOVE_FORWARD));
}
- int startIndex = mWorkspace.numCustomPages() + (FeatureFlags.QSB_ON_FIRST_SCREEN ? 1 : 0);
+ int startIndex = FeatureFlags.QSB_ON_FIRST_SCREEN ? 1 : 0;
if (index > startIndex) {
info.addAction(mActions.get(MOVE_BACKWARD));
}
diff --git a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
index 5b7353a..cfb0520 100644
--- a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
@@ -16,6 +16,8 @@
package com.android.launcher3.accessibility;
+import static com.android.launcher3.LauncherState.NORMAL;
+
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
@@ -79,9 +81,7 @@
}
};
- if (!mLauncher.showWorkspace(true, onComplete)) {
- onComplete.run();
- }
+ mLauncher.getStateManager().goToState(NORMAL, true, onComplete);
return true;
} else if (action == DISMISS_NOTIFICATION) {
if (!(host instanceof NotificationMainView)) {
diff --git a/src/com/android/launcher3/allapps/AllAppsCaretController.java b/src/com/android/launcher3/allapps/AllAppsCaretController.java
deleted file mode 100644
index 583b49f..0000000
--- a/src/com/android/launcher3/allapps/AllAppsCaretController.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2016 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.allapps;
-
-import android.animation.ObjectAnimator;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-import com.android.launcher3.pageindicators.CaretDrawable;
-import com.android.launcher3.touch.SwipeDetector;
-
-public class AllAppsCaretController {
- // Determines when the caret should flip. Should be accessed via getThreshold()
- private static final float CARET_THRESHOLD = 0.015f;
- private static final float CARET_THRESHOLD_LAND = 0.5f;
- // The velocity at which the caret will peak (i.e. exhibit a 90 degree bend)
- private static final float PEAK_VELOCITY = SwipeDetector.RELEASE_VELOCITY_PX_MS * .7f;
-
- private Launcher mLauncher;
-
- private ObjectAnimator mCaretAnimator;
- private CaretDrawable mCaretDrawable;
- private float mLastCaretProgress;
- private boolean mThresholdCrossed;
-
- public AllAppsCaretController(CaretDrawable caret, Launcher launcher) {
- mLauncher = launcher;
- mCaretDrawable = caret;
-
- final long caretAnimationDuration = launcher.getResources().getInteger(
- R.integer.config_caretAnimationDuration);
- final Interpolator caretInterpolator = AnimationUtils.loadInterpolator(launcher,
- android.R.interpolator.fast_out_slow_in);
-
- // We will set values later
- mCaretAnimator = ObjectAnimator.ofFloat(mCaretDrawable, "caretProgress", 0);
- mCaretAnimator.setDuration(caretAnimationDuration);
- mCaretAnimator.setInterpolator(caretInterpolator);
- }
-
- /**
- * Updates the state of the caret based on the progress of the {@link AllAppsContainerView}, as
- * defined by the {@link AllAppsTransitionController}. Uses the container's velocity to adjust
- * angle of caret.
- *
- * @param containerProgress The progress of the container in the range [0..1]
- * @param velocity The velocity of the container
- * @param dragging {@code true} if the container is being dragged
- */
- public void updateCaret(float containerProgress, float velocity, boolean dragging) {
- // If we're in portrait and the shift is not 0 or 1, adjust the caret based on velocity
- if (getThreshold() < containerProgress && containerProgress < 1 - getThreshold() &&
- !mLauncher.useVerticalBarLayout()) {
- mThresholdCrossed = true;
-
- // How fast are we moving as a percentage of the peak velocity?
- final float pctOfFlingVelocity = Math.max(-1, Math.min(velocity / PEAK_VELOCITY, 1));
-
- mCaretDrawable.setCaretProgress(pctOfFlingVelocity);
-
- // Set the last caret progress to this progress to prevent animator cancellation
- mLastCaretProgress = pctOfFlingVelocity;
- // Animate to neutral. This is necessary so the caret doesn't "freeze" when the
- // container stops moving (e.g., during a drag or when the threshold is reached).
- animateCaretToProgress(CaretDrawable.PROGRESS_CARET_NEUTRAL);
- } else if (!dragging) {
- // Otherwise, if we're not dragging, match the caret to the appropriate state
- if (containerProgress <= getThreshold()) { // All Apps is up
- animateCaretToProgress(CaretDrawable.PROGRESS_CARET_POINTING_DOWN);
- } else if (containerProgress >= 1 - getThreshold()) { // All Apps is down
- animateCaretToProgress(CaretDrawable.PROGRESS_CARET_POINTING_UP);
- }
- }
- }
-
- private void animateCaretToProgress(float progress) {
- // If the new progress is the same as the last progress we animated to, terminate early
- if (Float.compare(mLastCaretProgress, progress) == 0) {
- return;
- }
-
- if (mCaretAnimator.isRunning()) {
- mCaretAnimator.cancel(); // Stop the animator in its tracks
- }
-
- // Update the progress and start the animation
- mLastCaretProgress = progress;
- mCaretAnimator.setFloatValues(progress);
- mCaretAnimator.start();
- }
-
- private float getThreshold() {
- // In landscape, just return the landscape threshold.
- if (mLauncher.useVerticalBarLayout()) {
- return CARET_THRESHOLD_LAND;
- }
-
- // Before the threshold is crossed, it is reported as zero. This makes the caret immediately
- // responsive when a drag begins. After the threshold is crossed, we return the constant
- // value. This means the caret will start its state-based adjustment sooner. That is, we
- // won't have to wait until the panel is completely settled to begin animation.
- return mThresholdCrossed ? CARET_THRESHOLD : 0f;
- }
-
- public void onDragStart() {
- mThresholdCrossed = false;
- }
-}
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 4eba5c6..a1f37ba 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -16,10 +16,8 @@
package com.android.launcher3.allapps;
import android.content.Context;
-import android.graphics.Color;
+import android.graphics.Bitmap;
import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.InsetDrawable;
import android.support.v7.widget.LinearLayoutManager;
import android.text.Selection;
import android.text.SpannableStringBuilder;
@@ -28,30 +26,29 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.RelativeLayout;
import com.android.launcher3.AppInfo;
-import com.android.launcher3.BaseContainerView;
import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.DeleteDropTarget;
+import com.android.launcher3.ClickShadowView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
+import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.Insettable;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
import com.android.launcher3.PromiseAppInfo;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.folder.Folder;
import com.android.launcher3.keyboard.FocusedItemDecorator;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.ComponentKeyMapper;
import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.TransformingTouchDelegate;
import java.util.List;
import java.util.Set;
@@ -59,13 +56,17 @@
/**
* The all apps view container.
*/
-public class AllAppsContainerView extends BaseContainerView implements DragSource,
- View.OnLongClickListener, Insettable {
+public class AllAppsContainerView extends RelativeLayout implements DragSource,
+ View.OnLongClickListener, Insettable, DeviceProfile.LauncherLayoutChangeListener,
+ BubbleTextView.BubbleTextShadowHandler {
+
+ protected final Rect mBasePadding = new Rect();
private final Launcher mLauncher;
private final AlphabeticalAppsList mApps;
private final AllAppsGridAdapter mAdapter;
private final LinearLayoutManager mLayoutManager;
+ private final ClickShadowView mTouchFeedbackView;
private AllAppsRecyclerView mAppsRecyclerView;
private SearchUiManager mSearchUiManager;
@@ -78,6 +79,8 @@
private SpringAnimationHandler mSpringAnimationHandler;
+ private TransformingTouchDelegate mTouchDelegate;
+
public AllAppsContainerView(Context context) {
this(context, null);
}
@@ -98,27 +101,70 @@
mSearchQueryBuilder = new SpannableStringBuilder();
Selection.setSelection(mSearchQueryBuilder, 0);
+
+ mTouchFeedbackView = new ClickShadowView(context);
+ // Make the feedback view large enough to hold the blur bitmap.
+ int size = mLauncher.getDeviceProfile().allAppsIconSizePx
+ + mTouchFeedbackView.getExtraSize();
+ addView(mTouchFeedbackView, size, size);
}
@Override
- protected void updateBackground(
- int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) {
- if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
- getRevealView().setBackground(new InsetDrawable(mBaseDrawable,
- paddingLeft, paddingTop, paddingRight, paddingBottom));
- getContentView().setBackground(
- new InsetDrawable(new ColorDrawable(Color.TRANSPARENT),
- paddingLeft, paddingTop, paddingRight, paddingBottom));
- } else {
- getRevealView().setBackground(mBaseDrawable);
- }
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
+ grid.addLauncherLayoutChangedListener(this);
+
+ mTouchDelegate = new TransformingTouchDelegate(mAppsRecyclerView);
+ setTouchDelegate(mTouchDelegate);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
+ grid.removeLauncherLayoutChangedListener(this);
}
/**
- * Sets the current set of predicted apps.
+ * Calculate the background padding as it can change due to insets/content padding change.
*/
- public void setPredictedApps(List<ComponentKeyMapper<AppInfo>> apps) {
- mApps.setPredictedApps(apps);
+ @Override
+ public void onLauncherLayoutChanged() {
+ DeviceProfile grid = mLauncher.getDeviceProfile();
+ if (!grid.isVerticalBarLayout()) {
+ return;
+ }
+
+ int[] padding = grid.getContainerPadding();
+ int paddingLeft = padding[0];
+ int paddingRight = padding[1];
+ mBasePadding.set(paddingLeft, 0, paddingRight, 0);
+ setPadding(paddingLeft, 0, paddingRight, 0);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ mTouchDelegate.setBounds(
+ mAppsRecyclerView.getLeft() - mBasePadding.left,
+ mAppsRecyclerView.getTop() - mBasePadding.top,
+ mAppsRecyclerView.getRight() + mBasePadding.right,
+ mAppsRecyclerView.getBottom() + mBasePadding.bottom);
+ }
+
+ @Override
+ public void setPressedIcon(BubbleTextView icon, Bitmap background) {
+ if (icon == null || background == null) {
+ mTouchFeedbackView.setBitmap(null);
+ mTouchFeedbackView.animate().cancel();
+ } else if (mTouchFeedbackView.setBitmap(background)) {
+ View rv = findViewById(R.id.apps_list_view);
+ mTouchFeedbackView.alignWithIconView(icon, (ViewGroup) icon.getParent(), rv);
+ mTouchFeedbackView.animateShadow();
+ }
}
/**
@@ -164,23 +210,7 @@
if (mLauncher.getDragLayer().isEventOverView(mSearchContainer, ev)) {
return true;
}
-
- int[] point = new int[2];
- point[0] = (int) ev.getX();
- point[1] = (int) ev.getY();
- Utilities.mapCoordInSelfToDescendant(
- mAppsRecyclerView.getScrollBar(), mLauncher.getDragLayer(), point);
- // IF the MotionEvent is inside the thumb, container should not be pulled down.
- if (mAppsRecyclerView.getScrollBar().shouldBlockIntercept(point[0], point[1])) {
- return false;
- }
-
- // IF scroller is at the very top OR there is no scroll bar because there is probably not
- // enough items to scroll, THEN it's okay for the container to be pulled down.
- if (mAppsRecyclerView.getCurrentScrollY() == 0) {
- return true;
- }
- return false;
+ return mAppsRecyclerView.shouldContainerScroll(ev, mLauncher.getDragLayer());
}
/**
@@ -198,7 +228,7 @@
// This is a focus listener that proxies focus from a view into the list view. This is to
// work around the search box from getting first focus and showing the cursor.
- getContentView().setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
@@ -229,9 +259,7 @@
mAppsRecyclerView.preMeasureViews(mAdapter);
mAdapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
- getRevealView().setVisibility(View.VISIBLE);
- getContentView().setVisibility(View.VISIBLE);
- getContentView().setBackground(null);
+ onLauncherLayoutChanged();
}
public SearchUiManager getSearchUiManager() {
@@ -239,11 +267,6 @@
}
@Override
- public View getTouchDelegateTargetView() {
- return mAppsRecyclerView;
- }
-
- @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
DeviceProfile grid = mLauncher.getDeviceProfile();
// Update the number of items in the grid before we measure the view
@@ -270,7 +293,7 @@
@Override
public boolean onLongClick(final View v) {
// When we have exited all apps or are in transition, disregard long clicks
- if (!mLauncher.isAppsViewVisible() ||
+ if (!mLauncher.isInState(LauncherState.ALL_APPS) ||
mLauncher.getWorkspace().isSwitchingState()) return false;
// Return if global dragging is not enabled or we are already dragging
if (!mLauncher.isDraggingEnabled()) return false;
@@ -290,42 +313,16 @@
dragController.removeDragListener(this);
}
});
- mLauncher.getWorkspace().beginDragShared(v, this, new DragOptions());
- return false;
- }
- @Override
- public boolean supportsAppInfoDropTarget() {
- return true;
- }
-
- @Override
- public boolean supportsDeleteDropTarget() {
- return false;
- }
-
- @Override
- public float getIntrinsicIconScaleFactor() {
DeviceProfile grid = mLauncher.getDeviceProfile();
- return (float) grid.allAppsIconSizePx / grid.iconSizePx;
+ DragOptions options = new DragOptions();
+ options.intrinsicIconScaleFactor = (float) grid.allAppsIconSizePx / grid.iconSizePx;
+ mLauncher.getWorkspace().beginDragShared(v, this, options);
+ return false;
}
@Override
- public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete,
- boolean success) {
- if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
- !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
- // Exit spring loaded mode if we have not successfully dropped or have not handled the
- // drop in Workspace
- mLauncher.exitSpringLoadedDragModeDelayed(true,
- Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
- }
- mLauncher.unlockScreenOrientation(false);
-
- if (!success) {
- d.deferDragViewCleanupPostAnimation = false;
- }
- }
+ public void onDropCompleted(View target, DragObject d, boolean success) { }
@Override
public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 48e6caa..ed5bf9f 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -425,6 +425,9 @@
// The amount by which each adjacent rows' stiffness will differ.
private static final float ROW_STIFFNESS_COEFFICIENT = 50f;
+ // The percentage by which we multiply each row to create the row factor.
+ private static final float ROW_PERCENTAGE = 0.3f;
+
@Override
public SpringAnimation initialize(ViewHolder vh) {
return SpringAnimationHandler.forView(vh.itemView, DynamicAnimation.TRANSLATION_Y, 0);
@@ -463,7 +466,7 @@
* effect.
*/
private void calculateSpringValues(SpringAnimation spring, int row, int col) {
- float rowFactor = (1 + row) * 0.5f;
+ float rowFactor = (1 + row) * ROW_PERCENTAGE;
float colFactor = getColumnFactor(col, mAppsPerRow);
float minValue = DEFAULT_MIN_VALUE_PX * (rowFactor + colFactor);
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
deleted file mode 100644
index 517dc94..0000000
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2015 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.allapps;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.RelativeLayout;
-
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.BubbleTextView.BubbleTextShadowHandler;
-import com.android.launcher3.ClickShadowView;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-
-/**
- * A container for RecyclerView to allow for the click shadow view to be shown behind an icon that
- * is launching.
- */
-public class AllAppsRecyclerViewContainerView extends RelativeLayout
- implements BubbleTextShadowHandler {
-
- private final ClickShadowView mTouchFeedbackView;
-
- public AllAppsRecyclerViewContainerView(Context context) {
- this(context, null);
- }
-
- public AllAppsRecyclerViewContainerView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public AllAppsRecyclerViewContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
-
- Launcher launcher = Launcher.getLauncher(context);
- DeviceProfile grid = launcher.getDeviceProfile();
-
- mTouchFeedbackView = new ClickShadowView(context);
-
- // Make the feedback view large enough to hold the blur bitmap.
- int size = grid.allAppsIconSizePx + mTouchFeedbackView.getExtraSize();
- addView(mTouchFeedbackView, size, size);
- }
-
- @Override
- public void setPressedIcon(BubbleTextView icon, Bitmap background) {
- if (icon == null || background == null) {
- mTouchFeedbackView.setBitmap(null);
- mTouchFeedbackView.animate().cancel();
- } else if (mTouchFeedbackView.setBitmap(background)) {
- View rv = findViewById(R.id.apps_list_view);
- mTouchFeedbackView.alignWithIconView(icon, (ViewGroup) icon.getParent(), rv);
- mTouchFeedbackView.animateShadow();
- }
- }
-}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 743b16e..9b64043 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -1,37 +1,29 @@
package com.android.launcher3.allapps;
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
-import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
-import android.graphics.Color;
-import android.support.animation.SpringAnimation;
-import android.support.v4.graphics.ColorUtils;
-import android.support.v4.view.animation.FastOutSlowInInterpolator;
-import android.view.MotionEvent;
+import android.util.Property;
import android.view.View;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
-import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Hotseat;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
-import com.android.launcher3.anim.SpringAnimationHandler;
-import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.graphics.GradientView;
-import com.android.launcher3.touch.SwipeDetector;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
-import com.android.launcher3.util.TouchController;
/**
* Handles AllApps view transition.
@@ -43,35 +35,32 @@
* If release velocity < THRES1, snap according to either top or bottom depending on whether it's
* closer to top or closer to the page indicator.
*/
-public class AllAppsTransitionController implements TouchController, SwipeDetector.Listener,
- SearchUiManager.OnScrollRangeChangeListener {
+public class AllAppsTransitionController implements SearchUiManager.OnScrollRangeChangeListener {
- private static final String TAG = "AllAppsTrans";
- private static final boolean DBG = false;
+ private static final Property<AllAppsTransitionController, Float> PROGRESS =
+ new Property<AllAppsTransitionController, Float>(Float.class, "progress") {
- private final Interpolator mWorkspaceAccelnterpolator = new AccelerateInterpolator(2f);
- private final Interpolator mHotseatAccelInterpolator = new AccelerateInterpolator(1.5f);
- private final Interpolator mDecelInterpolator = new DecelerateInterpolator(3f);
- private final Interpolator mFastOutSlowInInterpolator = new FastOutSlowInInterpolator();
- private final SwipeDetector.ScrollInterpolator mScrollInterpolator
- = new SwipeDetector.ScrollInterpolator();
+ @Override
+ public Float get(AllAppsTransitionController controller) {
+ return controller.mProgress;
+ }
+
+ @Override
+ public void set(AllAppsTransitionController controller, Float progress) {
+ controller.setProgress(progress);
+ }
+ };
+
+ private final Interpolator mWorkspaceAccelnterpolator = Interpolators.ACCEL_2;
+ private final Interpolator mHotseatAccelInterpolator = Interpolators.ACCEL_1_5;
private static final float PARALLAX_COEFFICIENT = .125f;
- private static final int SINGLE_FRAME_MS = 16;
private AllAppsContainerView mAppsView;
- private int mAllAppsBackgroundColor;
private Workspace mWorkspace;
private Hotseat mHotseat;
- private int mHotseatBackgroundColor;
-
- private AllAppsCaretController mCaretController;
-
- private float mStatusBarHeight;
private final Launcher mLauncher;
- private final SwipeDetector mDetector;
- private final ArgbEvaluator mEvaluator;
private final boolean mIsDarkTheme;
// Animation in this class is controlled by a single variable {@link mProgress}.
@@ -80,215 +69,36 @@
// When {@link mProgress} is 0, all apps container is pulled up.
// When {@link mProgress} is 1, all apps container is pulled down.
- private float mShiftStart; // [0, mShiftRange]
private float mShiftRange; // changes depending on the orientation
private float mProgress; // [0, 1], mShiftRange * mProgress = shiftCurrent
- // Velocity of the container. Unit is in px/ms.
- private float mContainerVelocity;
-
private static final float DEFAULT_SHIFT_RANGE = 10;
- private static final float RECATCH_REJECTION_FRACTION = .0875f;
-
- private long mAnimationDuration;
-
- private AnimatorSet mCurrentAnimation;
- private boolean mNoIntercept;
- private boolean mTouchEventStartedOnHotseat;
-
- // Used in discovery bounce animation to provide the transition without workspace changing.
private boolean mIsTranslateWithoutWorkspace = false;
private Animator mDiscoBounceAnimation;
private GradientView mGradientView;
- private SpringAnimation mSearchSpring;
- private SpringAnimationHandler mSpringAnimationHandler;
-
public AllAppsTransitionController(Launcher l) {
mLauncher = l;
- mDetector = new SwipeDetector(l, this, SwipeDetector.VERTICAL);
mShiftRange = DEFAULT_SHIFT_RANGE;
mProgress = 1f;
- mEvaluator = new ArgbEvaluator();
- mAllAppsBackgroundColor = Themes.getAttrColor(l, android.R.attr.colorPrimary);
mIsDarkTheme = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark);
}
- @Override
- public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mNoIntercept = false;
- mTouchEventStartedOnHotseat = mLauncher.getDragLayer().isEventOverHotseat(ev);
- if (!mLauncher.isAllAppsVisible() && mLauncher.getWorkspace().workspaceInModalState()) {
- mNoIntercept = true;
- } else if (mLauncher.isAllAppsVisible() &&
- !mAppsView.shouldContainerScroll(ev)) {
- mNoIntercept = true;
- } else if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
- mNoIntercept = true;
- } else {
- // Now figure out which direction scroll events the controller will start
- // calling the callbacks.
- int directionsToDetectScroll = 0;
- boolean ignoreSlopWhenSettling = false;
-
- if (mDetector.isIdleState()) {
- if (mLauncher.isAllAppsVisible()) {
- directionsToDetectScroll |= SwipeDetector.DIRECTION_NEGATIVE;
- } else {
- directionsToDetectScroll |= SwipeDetector.DIRECTION_POSITIVE;
- }
- } else {
- if (isInDisallowRecatchBottomZone()) {
- directionsToDetectScroll |= SwipeDetector.DIRECTION_POSITIVE;
- } else if (isInDisallowRecatchTopZone()) {
- directionsToDetectScroll |= SwipeDetector.DIRECTION_NEGATIVE;
- } else {
- directionsToDetectScroll |= SwipeDetector.DIRECTION_BOTH;
- ignoreSlopWhenSettling = true;
- }
- }
- mDetector.setDetectableScrollConditions(directionsToDetectScroll,
- ignoreSlopWhenSettling);
- }
- }
-
- if (mNoIntercept) {
- return false;
- }
- mDetector.onTouchEvent(ev);
- if (mDetector.isSettlingState() && (isInDisallowRecatchBottomZone() || isInDisallowRecatchTopZone())) {
- return false;
- }
- return mDetector.isDraggingOrSettling();
+ public float getShiftRange() {
+ return mShiftRange;
}
- @Override
- public boolean onControllerTouchEvent(MotionEvent ev) {
- if (hasSpringAnimationHandler()) {
- mSpringAnimationHandler.addMovement(ev);
- }
- return mDetector.onTouchEvent(ev);
- }
-
- private boolean isInDisallowRecatchTopZone() {
- return mProgress < RECATCH_REJECTION_FRACTION;
- }
-
- private boolean isInDisallowRecatchBottomZone() {
- return mProgress > 1 - RECATCH_REJECTION_FRACTION;
- }
-
- @Override
- public void onDragStart(boolean start) {
- mCaretController.onDragStart();
- cancelAnimation();
- mCurrentAnimation = LauncherAnimUtils.createAnimatorSet();
- mShiftStart = mAppsView.getTranslationY();
- preparePull(start);
- if (hasSpringAnimationHandler()) {
- mSpringAnimationHandler.skipToEnd();
- }
- }
-
- @Override
- public boolean onDrag(float displacement, float velocity) {
- if (mAppsView == null) {
- return false; // early termination.
- }
-
- mContainerVelocity = velocity;
-
- float shift = Math.min(Math.max(0, mShiftStart + displacement), mShiftRange);
- setProgress(shift / mShiftRange);
-
- return true;
- }
-
- @Override
- public void onDragEnd(float velocity, boolean fling) {
- if (mAppsView == null) {
- return; // early termination.
- }
-
- final int containerType = mTouchEventStartedOnHotseat
- ? ContainerType.HOTSEAT : ContainerType.WORKSPACE;
-
- if (fling) {
- if (velocity < 0) {
- calculateDuration(velocity, mAppsView.getTranslationY());
-
- if (!mLauncher.isAllAppsVisible()) {
- mLauncher.getUserEventDispatcher().logActionOnContainer(
- Action.Touch.FLING,
- Action.Direction.UP,
- containerType);
- }
- mLauncher.showAppsView(true /* animated */, false /* updatePredictedApps */);
- if (hasSpringAnimationHandler()) {
- mSpringAnimationHandler.add(mSearchSpring, true /* setDefaultValues */);
- // The icons are moving upwards, so we go to 0 from 1. (y-axis 1 is below 0.)
- mSpringAnimationHandler.animateToFinalPosition(0 /* pos */, 1 /* startValue */);
- }
- } else {
- calculateDuration(velocity, Math.abs(mShiftRange - mAppsView.getTranslationY()));
- mLauncher.showWorkspace(true);
- }
- // snap to top or bottom using the release velocity
- } else {
- if (mAppsView.getTranslationY() > mShiftRange / 2) {
- calculateDuration(velocity, Math.abs(mShiftRange - mAppsView.getTranslationY()));
- mLauncher.showWorkspace(true);
- } else {
- calculateDuration(velocity, Math.abs(mAppsView.getTranslationY()));
- if (!mLauncher.isAllAppsVisible()) {
- mLauncher.getUserEventDispatcher().logActionOnContainer(
- Action.Touch.SWIPE,
- Action.Direction.UP,
- containerType);
- }
- mLauncher.showAppsView(true, /* animated */ false /* updatePredictedApps */);
- }
- }
- }
-
- public boolean isTransitioning() {
- return mDetector.isDraggingOrSettling();
- }
-
- /**
- * @param start {@code true} if start of new drag.
- */
- public void preparePull(boolean start) {
- if (start) {
- // Initialize values that should not change until #onDragEnd
- mStatusBarHeight = mLauncher.getDragLayer().getInsets().top;
- mHotseat.setVisibility(View.VISIBLE);
- mHotseatBackgroundColor = mHotseat.getBackgroundDrawableColor();
- mHotseat.setBackgroundTransparent(true /* transparent */);
- if (!mLauncher.isAllAppsVisible()) {
- mLauncher.tryAndUpdatePredictedApps();
- mAppsView.setVisibility(View.VISIBLE);
- if (!FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
- mAppsView.setRevealDrawableColor(mHotseatBackgroundColor);
- }
- }
- }
+ private void onProgressAnimationStart() {
+ // Initialize values that should not change until #onDragEnd
+ mHotseat.setVisibility(View.VISIBLE);
+ mAppsView.setVisibility(View.VISIBLE);
}
private void updateLightStatusBar(float shift) {
- // Do not modify status bar on landscape as all apps is not full bleed.
- if (!FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS
- && mLauncher.getDeviceProfile().isVerticalBarLayout()) {
- return;
- }
-
// Use a light system UI (dark icons) if all apps is behind at least half of the status bar.
- boolean forceChange = FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS ?
- shift <= mShiftRange / 4 :
- shift <= mStatusBarHeight / 2;
+ boolean forceChange = shift <= mShiftRange / 4;
if (forceChange) {
mLauncher.getSystemUiController().updateUiState(
SystemUiController.UI_STATE_ALL_APPS, !mIsDarkTheme);
@@ -301,17 +111,21 @@
private void updateAllAppsBg(float progress) {
// gradient
if (mGradientView == null) {
- mGradientView = (GradientView) mLauncher.findViewById(R.id.gradient_bg);
- mGradientView.setVisibility(View.VISIBLE);
+ mGradientView = mLauncher.findViewById(R.id.gradient_bg);
}
mGradientView.setProgress(progress);
}
/**
- * @param progress value between 0 and 1, 0 shows all apps and 1 shows workspace
+ * Note this method should not be called outside this class. This is public because it is used
+ * in xml-based animations which also handle updating the appropriate UI.
+ *
+ * @param progress value between 0 and 1, 0 shows all apps and 1 shows workspace
+ *
+ * @see #setFinalProgress(float)
+ * @see #animateToFinalProgress(float, AnimatorSet, AnimationConfig)
*/
public void setProgress(float progress) {
- float shiftPrevious = mProgress * mShiftRange;
mProgress = progress;
float shiftCurrent = progress * mShiftRange;
@@ -320,18 +134,8 @@
float workspaceAlpha = mWorkspaceAccelnterpolator.getInterpolation(workspaceHotseatAlpha);
float hotseatAlpha = mHotseatAccelInterpolator.getInterpolation(workspaceHotseatAlpha);
- int color = (Integer) mEvaluator.evaluate(mDecelInterpolator.getInterpolation(alpha),
- mHotseatBackgroundColor, mAllAppsBackgroundColor);
- int bgAlpha = Color.alpha((int) mEvaluator.evaluate(alpha,
- mHotseatBackgroundColor, mAllAppsBackgroundColor));
-
- if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
- updateAllAppsBg(alpha);
- } else {
- mAppsView.setRevealDrawableColor(ColorUtils.setAlphaComponent(color, bgAlpha));
- }
-
- mAppsView.getContentView().setAlpha(alpha);
+ updateAllAppsBg(alpha);
+ mAppsView.setAlpha(alpha);
mAppsView.setTranslationY(shiftCurrent);
if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) {
@@ -349,12 +153,6 @@
mWorkspace.setWorkspaceYTranslationAndAlpha(
PARALLAX_COEFFICIENT * (-mShiftRange + shiftCurrent), workspaceAlpha);
- if (!mDetector.isDraggingState()) {
- mContainerVelocity = mDetector.computeVelocity(shiftCurrent - shiftPrevious,
- System.currentTimeMillis());
- }
-
- mCaretController.updateCaret(progress, mContainerVelocity, mDetector.isDraggingState());
updateLightStatusBar(shiftCurrent);
}
@@ -362,58 +160,48 @@
return mProgress;
}
- private void calculateDuration(float velocity, float disp) {
- mAnimationDuration = SwipeDetector.calculateDuration(velocity, disp / mShiftRange);
+ /**
+ * Sets the vertical transition progress to {@param progress} and updates all the dependent UI
+ * accordingly.
+ */
+ public void setFinalProgress(float progress) {
+ setProgress(progress);
+ onProgressAnimationEnd();
}
- public boolean animateToAllApps(AnimatorSet animationOut, long duration) {
- boolean shouldPost = true;
- if (animationOut == null) {
- return shouldPost;
- }
- Interpolator interpolator;
- if (mDetector.isIdleState()) {
- preparePull(true);
- mAnimationDuration = duration;
- mShiftStart = mAppsView.getTranslationY();
- interpolator = mFastOutSlowInInterpolator;
- } else {
- mScrollInterpolator.setVelocityAtZero(Math.abs(mContainerVelocity));
- interpolator = mScrollInterpolator;
- float nextFrameProgress = mProgress + mContainerVelocity * SINGLE_FRAME_MS / mShiftRange;
- if (nextFrameProgress >= 0f) {
- mProgress = nextFrameProgress;
- }
- shouldPost = false;
+ /**
+ * Creates an animation which updates the vertical transition progress and updates all the
+ * dependent UI using various animation events
+ *
+ * @param progress the final vertical progress at the end of the animation
+ * @param animationOut the target AnimatorSet where this animation should be added
+ * @param outConfig an in/out configuration which can be shared with other animations
+ */
+ public void animateToFinalProgress(
+ float progress, AnimatorSet animationOut, AnimationConfig outConfig) {
+ if (Float.compare(mProgress, progress) == 0) {
+ // Fail fast
+ onProgressAnimationEnd();
+ return;
}
- ObjectAnimator driftAndAlpha = ObjectAnimator.ofFloat(this, "progress",
- mProgress, 0f);
- driftAndAlpha.setDuration(mAnimationDuration);
- driftAndAlpha.setInterpolator(interpolator);
- animationOut.play(driftAndAlpha);
-
- animationOut.addListener(new AnimatorListenerAdapter() {
- boolean canceled = false;
-
+ Interpolator interpolator = outConfig.userControlled ? LINEAR : FAST_OUT_SLOW_IN;
+ ObjectAnimator anim = ObjectAnimator.ofFloat(this, PROGRESS, mProgress, progress);
+ anim.setDuration(outConfig.duration);
+ anim.setInterpolator(interpolator);
+ anim.addListener(new AnimationSuccessListener() {
@Override
- public void onAnimationCancel(Animator animation) {
- canceled = true;
+ public void onAnimationSuccess(Animator animator) {
+ onProgressAnimationEnd();
}
@Override
- public void onAnimationEnd(Animator animation) {
- if (canceled) {
- return;
- } else {
- finishPullUp();
- cleanUpAnimation();
- mDetector.finishedScrolling();
- }
+ public void onAnimationStart(Animator animation) {
+ onProgressAnimationStart();
}
});
- mCurrentAnimation = animationOut;
- return shouldPost;
+
+ animationOut.play(anim);
}
public void showDiscoveryBounce() {
@@ -427,12 +215,12 @@
@Override
public void onAnimationStart(Animator animator) {
mIsTranslateWithoutWorkspace = true;
- preparePull(true);
+ onProgressAnimationStart();
}
@Override
public void onAnimationEnd(Animator animator) {
- finishPullDown();
+ onProgressAnimationEnd();
mDiscoBounceAnimation = null;
mIsTranslateWithoutWorkspace = false;
}
@@ -449,84 +237,6 @@
});
}
- public boolean animateToWorkspace(AnimatorSet animationOut, long duration) {
- boolean shouldPost = true;
- if (animationOut == null) {
- return shouldPost;
- }
- Interpolator interpolator;
- if (mDetector.isIdleState()) {
- preparePull(true);
- mAnimationDuration = duration;
- mShiftStart = mAppsView.getTranslationY();
- interpolator = mFastOutSlowInInterpolator;
- } else {
- mScrollInterpolator.setVelocityAtZero(Math.abs(mContainerVelocity));
- interpolator = mScrollInterpolator;
- float nextFrameProgress = mProgress + mContainerVelocity * SINGLE_FRAME_MS / mShiftRange;
- if (nextFrameProgress <= 1f) {
- mProgress = nextFrameProgress;
- }
- shouldPost = false;
- }
-
- ObjectAnimator driftAndAlpha = ObjectAnimator.ofFloat(this, "progress",
- mProgress, 1f);
- driftAndAlpha.setDuration(mAnimationDuration);
- driftAndAlpha.setInterpolator(interpolator);
- animationOut.play(driftAndAlpha);
-
- animationOut.addListener(new AnimatorListenerAdapter() {
- boolean canceled = false;
-
- @Override
- public void onAnimationCancel(Animator animation) {
- canceled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (canceled) {
- return;
- } else {
- finishPullDown();
- cleanUpAnimation();
- mDetector.finishedScrolling();
- }
- }
- });
- mCurrentAnimation = animationOut;
- return shouldPost;
- }
-
- public void finishPullUp() {
- mHotseat.setVisibility(View.INVISIBLE);
- if (hasSpringAnimationHandler()) {
- mSpringAnimationHandler.remove(mSearchSpring);
- mSpringAnimationHandler.reset();
- }
- setProgress(0f);
- }
-
- public void finishPullDown() {
- mAppsView.setVisibility(View.INVISIBLE);
- mHotseat.setBackgroundTransparent(false /* transparent */);
- mHotseat.setVisibility(View.VISIBLE);
- mAppsView.reset();
- if (hasSpringAnimationHandler()) {
- mSpringAnimationHandler.reset();
- }
- setProgress(1f);
- }
-
- private void cancelAnimation() {
- if (mCurrentAnimation != null) {
- mCurrentAnimation.cancel();
- mCurrentAnimation = null;
- }
- cancelDiscoveryAnimation();
- }
-
public void cancelDiscoveryAnimation() {
if (mDiscoBounceAnimation == null) {
return;
@@ -535,24 +245,12 @@
mDiscoBounceAnimation = null;
}
- private void cleanUpAnimation() {
- mCurrentAnimation = null;
- }
-
public void setupViews(AllAppsContainerView appsView, Hotseat hotseat, Workspace workspace) {
mAppsView = appsView;
mHotseat = hotseat;
mWorkspace = workspace;
mHotseat.bringToFront();
- mCaretController = new AllAppsCaretController(
- mWorkspace.getPageIndicator().getCaretDrawable(), mLauncher);
mAppsView.getSearchUiManager().addOnScrollRangeChangeListener(this);
- mSpringAnimationHandler = mAppsView.getSpringAnimationHandler();
- mSearchSpring = mAppsView.getSearchUiManager().getSpringForFling();
- }
-
- private boolean hasSpringAnimationHandler() {
- return FeatureFlags.LAUNCHER3_PHYSICS && mSpringAnimationHandler != null;
}
@Override
@@ -560,4 +258,19 @@
mShiftRange = scrollRange;
setProgress(mProgress);
}
+
+ /**
+ * Set the final view states based on the progress.
+ * TODO: This logic should go in {@link LauncherState}
+ */
+ private void onProgressAnimationEnd() {
+ if (Float.compare(mProgress, 1f) == 0) {
+ mAppsView.setVisibility(View.INVISIBLE);
+ mHotseat.setVisibility(View.VISIBLE);
+ mAppsView.reset();
+ } else if (Float.compare(mProgress, 0f) == 0) {
+ mHotseat.setVisibility(View.INVISIBLE);
+ mAppsView.setVisibility(View.VISIBLE);
+ }
+ }
}
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 6bbe3ea..f0b650b 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -362,9 +362,12 @@
int size = apps.size();
for (int i = 0; i < size; ++i) {
AppInfo info = apps.get(i);
- AdapterItem appItem = AdapterItem.asPredictedApp(i, "", info, i);
- appItem.rowAppIndex = i;
- mAdapterItems.set(i, appItem);
+ AdapterItem orgItem = mAdapterItems.get(i);
+ AdapterItem newItem = AdapterItem.asPredictedApp(orgItem.position, "", info,
+ orgItem.appIndex);
+ newItem.rowAppIndex = orgItem.rowAppIndex;
+
+ mAdapterItems.set(i, newItem);
mFilteredApps.set(i, info);
mAdapter.notifyItemChanged(i);
}
diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index 63aa7be..bf03a0e 100644
--- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -15,16 +15,13 @@
*/
package com.android.launcher3.allapps.search;
-import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.KeyEvent;
-import android.view.View;
import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodManager;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
@@ -50,7 +47,6 @@
protected String mQuery;
protected SearchAlgorithm mSearchAlgorithm;
- protected InputMethodManager mInputMethodManager;
public void setVisibility(int visibility) {
mInput.setVisibility(visibility);
@@ -68,10 +64,6 @@
mInput.addTextChangedListener(this);
mInput.setOnEditorActionListener(this);
mInput.setOnBackKeyListener(this);
-
- mInputMethodManager = (InputMethodManager)
- mInput.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
-
mSearchAlgorithm = searchAlgorithm;
}
@@ -137,22 +129,9 @@
* Resets the search bar state.
*/
public void reset() {
- unfocusSearchField();
mCb.clearSearchResult();
- mInput.setText("");
+ mInput.reset();
mQuery = null;
- hideKeyboard();
- }
-
- protected void hideKeyboard() {
- mInputMethodManager.hideSoftInputFromWindow(mInput.getWindowToken(), 0);
- }
-
- protected void unfocusSearchField() {
- View nextFocus = mInput.focusSearch(View.FOCUS_DOWN);
- if (nextFocus != null) {
- nextFocus.requestFocus();
- }
}
/**
diff --git a/src/com/android/launcher3/anim/AnimationSuccessListener.java b/src/com/android/launcher3/anim/AnimationSuccessListener.java
new file mode 100644
index 0000000..9448632
--- /dev/null
+++ b/src/com/android/launcher3/anim/AnimationSuccessListener.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 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.anim;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+
+/**
+ * Extension of {@link AnimatorListenerAdapter} for listening for non-cancelled animations
+ */
+public abstract class AnimationSuccessListener extends AnimatorListenerAdapter {
+
+ protected boolean mCancelled = false;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCancelled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!mCancelled) {
+ onAnimationSuccess(animation);
+ }
+ }
+
+ public abstract void onAnimationSuccess(Animator animator);
+}
diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
new file mode 100644
index 0000000..826a20e
--- /dev/null
+++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2017 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.anim;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Helper class to control the playback of an {@link AnimatorSet}, with custom interpolators
+ * and durations.
+ *
+ * Note: The implementation does not support start delays on child animations or
+ * sequential playbacks.
+ */
+public abstract class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateListener {
+
+ public static AnimatorPlaybackController wrap(AnimatorSet anim, long duration) {
+
+ /**
+ * TODO: use {@link AnimatorSet#setCurrentPlayTime(long)} once b/68382377 is fixed.
+ */
+ return new AnimatorPlaybackControllerVL(anim, duration);
+ }
+
+ private final ValueAnimator mAnimationPlayer;
+ private final long mDuration;
+
+ protected final AnimatorSet mAnim;
+
+ protected float mCurrentFraction;
+ private Runnable mEndAction;
+
+ protected AnimatorPlaybackController(AnimatorSet anim, long duration) {
+ mAnim = anim;
+ mDuration = duration;
+
+ mAnimationPlayer = ValueAnimator.ofFloat(0, 1);
+ mAnimationPlayer.setInterpolator(Interpolators.LINEAR);
+ mAnimationPlayer.addListener(new OnAnimationEndDispatcher());
+ mAnimationPlayer.addUpdateListener(this);
+ }
+
+ public AnimatorSet getTarget() {
+ return mAnim;
+ }
+
+ /**
+ * Starts playing the animation forward from current position.
+ */
+ public void start() {
+ mAnimationPlayer.setFloatValues(mCurrentFraction, 1);
+ mAnimationPlayer.setDuration(clampDuration(1 - mCurrentFraction));
+ mAnimationPlayer.start();
+ }
+
+ /**
+ * Starts playing the animation backwards from current position
+ */
+ public void reverse() {
+ mAnimationPlayer.setFloatValues(mCurrentFraction, 0);
+ mAnimationPlayer.setDuration(clampDuration(mCurrentFraction));
+ mAnimationPlayer.start();
+ }
+
+ /**
+ * Pauses the currently playing animation.
+ */
+ public void pause() {
+ mAnimationPlayer.cancel();
+ }
+
+ /**
+ * Returns the underlying animation used for controlling the set.
+ */
+ public ValueAnimator getAnimationPlayer() {
+ return mAnimationPlayer;
+ }
+
+ /**
+ * Sets the current animation position and updates all the child animators accordingly.
+ */
+ public abstract void setPlayFraction(float fraction);
+
+ public float getProgressFraction() {
+ return mCurrentFraction;
+ }
+
+ /**
+ * Sets the action to be called when the animation is completed. Also clears any
+ * previously set action.
+ */
+ public void setEndAction(Runnable runnable) {
+ mEndAction = runnable;
+ }
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ setPlayFraction((float) valueAnimator.getAnimatedValue());
+ }
+
+ protected long clampDuration(float fraction) {
+ float playPos = mDuration * fraction;
+ if (playPos <= 0) {
+ return 0;
+ } else {
+ return Math.min((long) playPos, mDuration);
+ }
+ }
+
+ public void dispatchOnStart() {
+ dispatchOnStartRecursively(mAnim);
+ }
+
+ private void dispatchOnStartRecursively(Animator animator) {
+ for (AnimatorListener l : nonNullList(animator.getListeners())) {
+ l.onAnimationStart(animator);
+ }
+
+ if (animator instanceof AnimatorSet) {
+ for (Animator anim : nonNullList(((AnimatorSet) animator).getChildAnimations())) {
+ dispatchOnStartRecursively(anim);
+ }
+ }
+ }
+
+ public static class AnimatorPlaybackControllerVL extends AnimatorPlaybackController {
+
+ private final ValueAnimator[] mChildAnimations;
+
+ private AnimatorPlaybackControllerVL(AnimatorSet anim, long duration) {
+ super(anim, duration);
+
+ // Build animation list
+ ArrayList<ValueAnimator> childAnims = new ArrayList<>();
+ getAnimationsRecur(mAnim, childAnims);
+ mChildAnimations = childAnims.toArray(new ValueAnimator[childAnims.size()]);
+ }
+
+ private void getAnimationsRecur(AnimatorSet anim, ArrayList<ValueAnimator> out) {
+ long forceDuration = anim.getDuration();
+ for (Animator child : anim.getChildAnimations()) {
+ if (forceDuration > 0) {
+ child.setDuration(forceDuration);
+ }
+ if (child instanceof ValueAnimator) {
+ out.add((ValueAnimator) child);
+ } else if (child instanceof AnimatorSet) {
+ getAnimationsRecur((AnimatorSet) child, out);
+ } else {
+ throw new RuntimeException("Unknown animation type " + child);
+ }
+ }
+ }
+
+ @Override
+ public void setPlayFraction(float fraction) {
+ mCurrentFraction = fraction;
+ long playPos = clampDuration(fraction);
+ for (ValueAnimator anim : mChildAnimations) {
+ anim.setCurrentPlayTime(Math.min(playPos, anim.getDuration()));
+ }
+ }
+
+ }
+
+ private class OnAnimationEndDispatcher extends AnimationSuccessListener {
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mCancelled = false;
+ }
+
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ dispatchOnEndRecursively(mAnim);
+ if (mEndAction != null) {
+ mEndAction.run();
+ }
+ }
+
+ private void dispatchOnEndRecursively(Animator animator) {
+ for (AnimatorListener l : nonNullList(animator.getListeners())) {
+ l.onAnimationEnd(animator);
+ }
+
+ if (animator instanceof AnimatorSet) {
+ for (Animator anim : nonNullList(((AnimatorSet) animator).getChildAnimations())) {
+ dispatchOnEndRecursively(anim);
+ }
+ }
+ }
+ }
+
+ private static <T> List<T> nonNullList(ArrayList<T> list) {
+ return list == null ? Collections.<T>emptyList() : list;
+ }
+}
diff --git a/src/com/android/launcher3/anim/CircleRevealOutlineProvider.java b/src/com/android/launcher3/anim/CircleRevealOutlineProvider.java
deleted file mode 100644
index 9fb6b49..0000000
--- a/src/com/android/launcher3/anim/CircleRevealOutlineProvider.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2016 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.anim;
-
-public class CircleRevealOutlineProvider extends RevealOutlineAnimation {
-
- private int mCenterX;
- private int mCenterY;
- private float mRadius0;
- private float mRadius1;
-
- /**
- * @param x reveal center x
- * @param y reveal center y
- * @param r0 initial radius
- * @param r1 final radius
- */
- public CircleRevealOutlineProvider(int x, int y, float r0, float r1) {
- mCenterX = x;
- mCenterY = y;
- mRadius0 = r0;
- mRadius1 = r1;
- }
-
- @Override
- public boolean shouldRemoveElevationDuringAnimation() {
- return true;
- }
-
- @Override
- public void setProgress(float progress) {
- mOutlineRadius = (1 - progress) * mRadius0 + progress * mRadius1;
-
- mOutline.left = (int) (mCenterX - mOutlineRadius);
- mOutline.top = (int) (mCenterY - mOutlineRadius);
- mOutline.right = (int) (mCenterX + mOutlineRadius);
- mOutline.bottom = (int) (mCenterY + mOutlineRadius);
- }
-}
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
new file mode 100644
index 0000000..8826e64
--- /dev/null
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 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.anim;
+
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.PathInterpolator;
+
+
+/**
+ * Common interpolators used in Launcher
+ */
+public class Interpolators {
+
+ public static final Interpolator LINEAR = new LinearInterpolator();
+
+ public static final Interpolator ACCEL = new AccelerateInterpolator();
+ public static final Interpolator ACCEL_1_5 = new AccelerateInterpolator(1.5f);
+ public static final Interpolator ACCEL_2 = new AccelerateInterpolator(2);
+
+ public static final Interpolator DEACCEL = new DecelerateInterpolator();
+ public static final Interpolator DEACCEL_1_5 = new DecelerateInterpolator(1.5f);
+ public static final Interpolator DEACCEL_2 = new DecelerateInterpolator(2);
+ public static final Interpolator DEACCEL_2_5 = new DecelerateInterpolator(2.5f);
+ public static final Interpolator DEACCEL_3 = new DecelerateInterpolator(3f);
+
+ public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+
+ /**
+ * Inversion of zInterpolate, compounded with an ease-out.
+ */
+ public static final Interpolator ZOOM_IN = new Interpolator() {
+
+ private static final float FOCAL_LENGTH = 0.35f;
+
+ @Override
+ public float getInterpolation(float v) {
+ return DEACCEL_3.getInterpolation(1 - zInterpolate(1 - v));
+ }
+
+ /**
+ * This interpolator emulates the rate at which the perceived scale of an object changes
+ * as its distance from a camera increases. When this interpolator is applied to a scale
+ * animation on a view, it evokes the sense that the object is shrinking due to moving away
+ * from the camera.
+ */
+ private float zInterpolate(float input) {
+ return (1.0f - FOCAL_LENGTH / (FOCAL_LENGTH + input)) /
+ (1.0f - FOCAL_LENGTH / (FOCAL_LENGTH + 1.0f));
+ }
+ };
+
+ public static final Interpolator SCROLL = new Interpolator() {
+ @Override
+ public float getInterpolation(float t) {
+ t -= 1.0f;
+ return t*t*t*t*t + 1;
+ }
+ };
+
+ public static final Interpolator SCROLL_CUBIC = new Interpolator() {
+ @Override
+ public float getInterpolation(float t) {
+ t -= 1.0f;
+ return t*t*t + 1;
+ }
+ };
+
+ private static final float FAST_FLING_PX_MS = 10;
+
+ public static Interpolator scrollInterpolatorForVelocity(float velocity) {
+ return Math.abs(velocity) > FAST_FLING_PX_MS ? SCROLL : SCROLL_CUBIC;
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/anim/SpringAnimationHandler.java b/src/com/android/launcher3/anim/SpringAnimationHandler.java
index eec3a48..29a2430 100644
--- a/src/com/android/launcher3/anim/SpringAnimationHandler.java
+++ b/src/com/android/launcher3/anim/SpringAnimationHandler.java
@@ -83,6 +83,10 @@
mAnimations.add(spring);
}
+ public AnimationFactory<T> getFactory() {
+ return mAnimationFactory;
+ }
+
/**
* Adds a new or recycled animation to the list of springs handled by this class.
*
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
index a77a87f..fd1f0cc 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
@@ -24,10 +24,13 @@
import android.os.UserHandle;
import android.support.annotation.Nullable;
+import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.widget.custom.CustomWidgetParser;
import java.util.HashMap;
import java.util.List;
@@ -58,12 +61,13 @@
mAppWidgetManager = AppWidgetManager.getInstance(context);
}
- public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
- return mAppWidgetManager.getAppWidgetInfo(appWidgetId);
- }
-
public LauncherAppWidgetProviderInfo getLauncherAppWidgetInfo(int appWidgetId) {
- AppWidgetProviderInfo info = getAppWidgetInfo(appWidgetId);
+ if (FeatureFlags.ENABLE_CUSTOM_WIDGETS
+ && appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) {
+ return CustomWidgetParser.getWidgetProvider(mContext, appWidgetId);
+ }
+
+ AppWidgetProviderInfo info = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
return info == null ? null : LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
}
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
index cb3bd6c..8430285 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
@@ -20,14 +20,17 @@
import android.content.ComponentName;
import android.content.Context;
import android.os.Bundle;
+import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.support.annotation.Nullable;
+import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.widget.custom.CustomWidgetParser;
import java.util.ArrayList;
import java.util.Collections;
@@ -54,6 +57,10 @@
for (UserHandle user : mUserManager.getUserProfiles()) {
providers.addAll(mAppWidgetManager.getInstalledProvidersForProfile(user));
}
+
+ if (FeatureFlags.ENABLE_CUSTOM_WIDGETS) {
+ providers.addAll(CustomWidgetParser.getCustomWidgets(mContext));
+ }
return providers;
}
// Only get providers for the given package/user.
@@ -65,6 +72,11 @@
iterator.remove();
}
}
+
+ if (FeatureFlags.ENABLE_CUSTOM_WIDGETS && Process.myUserHandle().equals(packageUser.mUser)
+ && mContext.getPackageName().equals(packageUser.mPackageName)) {
+ providers.addAll(CustomWidgetParser.getCustomWidgets(mContext));
+ }
return providers;
}
@@ -74,6 +86,11 @@
if (FeatureFlags.GO_DISABLE_WIDGETS) {
return false;
}
+
+ if (FeatureFlags.ENABLE_CUSTOM_WIDGETS
+ && appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) {
+ return true;
+ }
return mAppWidgetManager.bindAppWidgetIdIfAllowed(
appWidgetId, info.getProfile(), info.provider, options);
}
@@ -89,6 +106,15 @@
return LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
}
}
+
+ if (FeatureFlags.ENABLE_CUSTOM_WIDGETS && Process.myUserHandle().equals(user)) {
+ for (LauncherAppWidgetProviderInfo info :
+ CustomWidgetParser.getCustomWidgets(mContext)) {
+ if (info.provider.equals(provider)) {
+ return info;
+ }
+ }
+ }
return null;
}
@@ -104,6 +130,13 @@
result.put(new ComponentKey(info.provider, user), info);
}
}
+
+ if (FeatureFlags.ENABLE_CUSTOM_WIDGETS) {
+ for (LauncherAppWidgetProviderInfo info :
+ CustomWidgetParser.getCustomWidgets(mContext)) {
+ result.put(new ComponentKey(info.provider, info.getProfile()), info);
+ }
+ }
return result;
}
}
diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java b/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
index 8e572ee..4cc70d3 100644
--- a/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
@@ -15,6 +15,10 @@
*/
package com.android.launcher3.compat;
+import static android.app.WallpaperManager.FLAG_SYSTEM;
+
+import static com.android.launcher3.Utilities.getDevicePrefs;
+
import android.app.WallpaperInfo;
import android.app.WallpaperManager;
import android.app.job.JobInfo;
@@ -38,7 +42,6 @@
import android.os.HandlerThread;
import android.os.ParcelFileDescriptor;
import android.support.annotation.Nullable;
-import android.support.v7.graphics.Palette;
import android.util.Log;
import android.util.Pair;
@@ -46,12 +49,6 @@
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-import static android.app.WallpaperManager.FLAG_SYSTEM;
-import static com.android.launcher3.Utilities.getDevicePrefs;
public class WallpaperManagerCompatVL extends WallpaperManagerCompat {
@@ -260,27 +257,8 @@
String value = VERSION_PREFIX + wallpaperId;
if (bitmap != null) {
- Palette palette = Palette.from(bitmap).generate();
- bitmap.recycle();
-
- StringBuilder builder = new StringBuilder(value);
- List<Pair<Integer,Integer>> colorsToOccurrences = new ArrayList<>();
- for (Palette.Swatch swatch : palette.getSwatches()) {
- colorsToOccurrences.add(new Pair(swatch.getRgb(), swatch.getPopulation()));
- }
-
- Collections.sort(colorsToOccurrences, new Comparator<Pair<Integer, Integer>>() {
- @Override
- public int compare(Pair<Integer, Integer> a, Pair<Integer, Integer> b) {
- return b.second - a.second;
- }
- });
-
- for (int i=0; i < Math.min(3, colorsToOccurrences.size()); i++) {
- builder.append(',').append(colorsToOccurrences.get(i).first);
- }
-
- value = builder.toString();
+ int color = Utilities.findDominantColorByHue(bitmap, MAX_WALLPAPER_EXTRACTION_AREA);
+ value += "," + color;
}
// Send the result
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 6a4cbcb..4fbab39 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -16,6 +16,9 @@
package com.android.launcher3.config;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.states.OverviewState;
+
/**
* Defines a set of flags used to control various launcher behaviors.
*
@@ -32,17 +35,11 @@
// Custom flags go below this
public static boolean LAUNCHER3_DISABLE_ICON_NORMALIZATION = false;
- public static boolean LAUNCHER3_LEGACY_FOLDER_ICON = false;
public static boolean LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW = false;
- public static boolean LAUNCHER3_NEW_FOLDER_ANIMATION = true;
// When enabled allows to use any point on the fast scrollbar to start dragging.
public static final boolean LAUNCHER3_DIRECT_SCROLL = true;
- // When enabled while all-apps open, the soft input will be set to adjust resize .
- public static final boolean LAUNCHER3_UPDATE_SOFT_INPUT_MODE = false;
// When enabled the promise icon is visible in all apps while installation an app.
public static final boolean LAUNCHER3_PROMISE_APPS_IN_ALL_APPS = false;
- // When enabled uses the AllAppsRadialGradientAndScrimDrawable for all apps
- public static final boolean LAUNCHER3_GRADIENT_ALL_APPS = true;
// When enabled allows use of physics based motions in the Launcher.
public static final boolean LAUNCHER3_PHYSICS = true;
// When enabled allows use of spring motions on the icons.
@@ -52,19 +49,20 @@
public static final boolean QSB_ON_FIRST_SCREEN = true;
// When enabled the all-apps icon is not added to the hotseat.
public static final boolean NO_ALL_APPS_ICON = true;
- // When enabled fling down gesture on the first workspace triggers search.
- public static final boolean PULLDOWN_SEARCH = false;
- // When enabled the status bar may show dark icons based on the top of the wallpaper.
- public static final boolean LIGHT_STATUS_BAR = false;
// When enabled, icons not supporting {@link AdaptiveIconDrawable} will be wrapped in {@link FixedScaleDrawable}.
public static final boolean LEGACY_ICON_TREATMENT = true;
// When enabled, adaptive icons would have shadows baked when being stored to icon cache.
public static final boolean ADAPTIVE_ICON_SHADOW = true;
// When enabled, app discovery will be enabled if service is implemented
public static final boolean DISCOVERY_ENABLED = false;
- // When enabled, the qsb will be moved to the hotseat.
- public static final boolean QSB_IN_HOTSEAT = true;
+
+ // When true, custom widgets are loaded using CustomWidgetParser.
+ public static final boolean ENABLE_CUSTOM_WIDGETS = false;
// Features to control Launcher3Go behavior
public static final boolean GO_DISABLE_WIDGETS = false;
+
+ public static LauncherState createOverviewState(int id) {
+ return new OverviewState(id);
+ }
}
diff --git a/src/com/android/launcher3/discovery/AppDiscoveryAppInfo.java b/src/com/android/launcher3/discovery/AppDiscoveryAppInfo.java
index 06493b2..ed5cfeb 100644
--- a/src/com/android/launcher3/discovery/AppDiscoveryAppInfo.java
+++ b/src/com/android/launcher3/discovery/AppDiscoveryAppInfo.java
@@ -41,7 +41,6 @@
this.intent = item.isInstantApp ? item.launchIntent : item.installIntent;
this.title = item.title;
this.iconBitmap = item.bitmap;
- this.isDisabled = ShortcutInfo.DEFAULT;
this.usingLowResIcon = false;
this.isInstantApp = item.isInstantApp;
this.isRecent = item.isRecent;
diff --git a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
index d0f2629..96aaee0 100644
--- a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
@@ -28,12 +28,10 @@
import android.view.DragEvent;
import android.view.View;
-import com.android.launcher3.DeleteDropTarget;
import com.android.launcher3.DragSource;
-import com.android.launcher3.DropTarget;
+import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
-import com.android.launcher3.folder.Folder;
import com.android.launcher3.widget.PendingItemDragHelper;
import java.util.UUID;
@@ -141,7 +139,7 @@
}
@Override
- public void onPreDragStart(DropTarget.DragObject dragObject) {
+ public void onPreDragStart(DragObject dragObject) {
// The predrag starts when the workspace is not yet loaded. In some cases we set
// the dragLayer alpha to 0 to have a nice fade-in animation. But that will prevent the
// dragView from being visible. Instead just skip the fade-in animation here.
@@ -152,41 +150,14 @@
}
@Override
- public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
+ public void onPreDragEnd(DragObject dragObject, boolean dragStarted) {
if (dragStarted) {
dragObject.dragView.setColor(0);
}
}
@Override
- public boolean supportsAppInfoDropTarget() {
- return false;
- }
-
- @Override
- public boolean supportsDeleteDropTarget() {
- return false;
- }
-
- @Override
- public float getIntrinsicIconScaleFactor() {
- return 1f;
- }
-
- @Override
- public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete,
- boolean success) {
- if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
- !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
- // Exit spring loaded mode if we have not successfully dropped or have not handled the
- // drop in Workspace
- mLauncher.exitSpringLoadedDragModeDelayed(true,
- Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
- }
-
- if (!success) {
- d.deferDragViewCleanupPostAnimation = false;
- }
+ public void onDropCompleted(View target, DragObject d, boolean success) {
postCleanup();
}
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index b852714..9402383 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -16,6 +16,9 @@
package com.android.launcher3.dragndrop;
+import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
+import static com.android.launcher3.LauncherState.NORMAL;
+
import android.content.ComponentName;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -27,7 +30,6 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
-import android.view.inputmethod.InputMethodManager;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
@@ -39,6 +41,7 @@
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.TouchController;
+import com.android.launcher3.util.UiThreadHelper;
import java.util.ArrayList;
@@ -120,6 +123,9 @@
/**
* Starts a drag.
+ * When the drag is started, the UI automatically goes into spring loaded mode. On a successful
+ * drop, it is the responsibility of the {@link DropTarget} to exit out of the spring loaded
+ * mode. If the drop was cancelled for some reason, the UI will automatically exit out of this mode.
*
* @param b The bitmap to display as the drag image. It will be re-scaled to the
* enlarged size.
@@ -138,8 +144,7 @@
}
// Hide soft keyboard, if visible
- mLauncher.getSystemService(InputMethodManager.class)
- .hideSoftInputFromWindow(mWindowToken, 0);
+ UiThreadHelper.hideKeyboardAsync(mLauncher, mWindowToken);
mOptions = options;
if (mOptions.systemDndStartPoint != null) {
@@ -249,12 +254,23 @@
mDragObject.cancelled = true;
mDragObject.dragComplete = true;
if (!mIsInPreDrag) {
- mDragObject.dragSource.onDropCompleted(null, mDragObject, false, false);
+ dispatchDropComplete(null, false);
}
}
endDrag();
}
+ private void dispatchDropComplete(View dropTarget, boolean accepted) {
+ if (!accepted) {
+ // If it was not accepted, cleanup the state. If it was accepted, it is the
+ // responsibility of the drop target to cleanup the state.
+ mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
+ mDragObject.deferDragViewCleanupPostAnimation = false;
+ }
+
+ mDragObject.dragSource.onDropCompleted(dropTarget, mDragObject, accepted);
+ }
+
public void onAppsRemoved(ItemInfoMatcher matcher) {
// Cancel the current drag if we are removing an app that we are dragging
if (mDragObject != null) {
@@ -576,7 +592,7 @@
if (flingAnimation != null) {
flingAnimation.run();
} else if (!mIsInPreDrag) {
- dropTarget.onDrop(mDragObject);
+ dropTarget.onDrop(mDragObject, mOptions);
}
accepted = true;
}
@@ -584,8 +600,7 @@
final View dropTargetAsView = dropTarget instanceof View ? (View) dropTarget : null;
if (!mIsInPreDrag) {
mLauncher.getUserEventDispatcher().logDragNDrop(mDragObject, dropTargetAsView);
- mDragObject.dragSource.onDropCompleted(
- dropTargetAsView, mDragObject, flingAnimation != null, accepted);
+ dispatchDropComplete(dropTargetAsView, accepted);
}
}
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index fde7995..0f0b20d 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -24,46 +24,38 @@
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.Rect;
-import android.graphics.Region;
-import android.support.v4.graphics.ColorUtils;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.KeyEvent;
-import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
-import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.AppWidgetResizeFrame;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DropTargetBar;
-import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppWidgetHostView;
import com.android.launcher3.PinchToOverviewListener;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Utilities;
+import com.android.launcher3.VerticalSwipeController;
import com.android.launcher3.allapps.AllAppsTransitionController;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.dynamicui.WallpaperColorInfo;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
-import com.android.launcher3.logging.LoggerUtils;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.TouchController;
-import com.android.launcher3.widget.WidgetsBottomSheet;
import java.util.ArrayList;
@@ -81,39 +73,31 @@
private Launcher mLauncher;
- // Variables relating to resizing widgets
- private final boolean mIsRtl;
- private AppWidgetResizeFrame mCurrentResizeFrame;
-
// Variables relating to animation of views after drop
private ValueAnimator mDropAnim = null;
- private final TimeInterpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f);
+ private final TimeInterpolator mCubicEaseOutInterpolator = Interpolators.DEACCEL_1_5;
@Thunk DragView mDropView = null;
@Thunk int mAnchorViewInitialScrollX = 0;
@Thunk View mAnchorView = null;
private boolean mHoverPointClosesFolder = false;
private final Rect mHitRect = new Rect();
- private final Rect mHighlightRect = new Rect();
private TouchCompleteListener mTouchCompleteListener;
private int mTopViewIndex;
private int mChildCountOnLastUpdate = -1;
- // Darkening scrim
- private float mBackgroundAlpha = 0;
-
// Related to adjacent page hints
- private final Rect mScrollChildPosition = new Rect();
private final ViewGroupFocusHelper mFocusIndicatorHelper;
- private final WallpaperColorInfo mWallpaperColorInfo;
+ private final PageCutOutScrimDrawable mPageCutOutScrim;
// Related to pinch-to-go-to-overview gesture.
private PinchToOverviewListener mPinchListener = null;
// Handles all apps pull up interaction
private AllAppsTransitionController mAllAppsController;
+ private VerticalSwipeController mVerticalSwipeController;
private TouchController mActiveController;
/**
@@ -129,9 +113,9 @@
setMotionEventSplittingEnabled(false);
setChildrenDrawingOrderEnabled(true);
- mIsRtl = Utilities.isRtl(getResources());
mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
- mWallpaperColorInfo = WallpaperColorInfo.getInstance(getContext());
+ mPageCutOutScrim = new PageCutOutScrimDrawable(this);
+ mPageCutOutScrim.setCallback(this);
}
public void setup(Launcher launcher, DragController dragController,
@@ -139,6 +123,7 @@
mLauncher = launcher;
mDragController = dragController;
mAllAppsController = allAppsTransitionController;
+ mVerticalSwipeController = new VerticalSwipeController(mLauncher);
boolean isAccessibilityEnabled = ((AccessibilityManager) mLauncher.getSystemService(
Context.ACCESSIBILITY_SERVICE)).isEnabled();
@@ -154,15 +139,16 @@
return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);
}
+ @Override
+ protected boolean verifyDrawable(Drawable who) {
+ return super.verifyDrawable(who) || who == mPageCutOutScrim;
+ }
+
public void onAccessibilityStateChanged(boolean isAccessibilityEnabled) {
mPinchListener = FeatureFlags.LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW || isAccessibilityEnabled
? null : new PinchToOverviewListener(mLauncher);
}
- public boolean isEventOverPageIndicator(MotionEvent ev) {
- return isEventOverView(mLauncher.getWorkspace().getPageIndicator(), ev);
- }
-
public boolean isEventOverHotseat(MotionEvent ev) {
return isEventOverView(mLauncher.getHotseat(), ev);
}
@@ -180,36 +166,6 @@
return mHitRect.contains((int) ev.getX(), (int) ev.getY());
}
- private boolean handleTouchDown(MotionEvent ev, boolean intercept) {
- AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mLauncher);
- if (topView != null && intercept) {
- ExtendedEditText textView = topView.getActiveTextView();
- if (textView != null) {
- if (!isEventOverView(textView, ev)) {
- textView.dispatchBackKey();
- return true;
- }
- } else if (!isEventOverView(topView, ev)) {
- if (isInAccessibleDrag()) {
- // Do not close the container if in drag and drop.
- if (!isEventOverDropTargetBar(ev)) {
- return true;
- }
- } else {
- mLauncher.getUserEventDispatcher().logActionTapOutside(
- LoggerUtils.newContainerTarget(topView.getLogContainerType()));
- topView.close(true);
-
- // We let touches on the original icon go through so that users can launch
- // the app with one tap if they don't find a shortcut they want.
- View extendedTouch = topView.getExtendedTouchView();
- return extendedTouch == null || !isEventOverView(extendedTouch, ev);
- }
- }
- }
- return false;
- }
-
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
@@ -219,9 +175,6 @@
// dray layer even if mAllAppsController is NOT the active controller.
// TODO: handle other input other than touch
mAllAppsController.cancelDiscoveryAnimation();
- if (handleTouchDown(ev, true)) {
- return true;
- }
} else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
if (mTouchCompleteListener != null) {
mTouchCompleteListener.onTouchComplete();
@@ -230,12 +183,10 @@
}
mActiveController = null;
- if (mCurrentResizeFrame != null
- && mCurrentResizeFrame.onControllerInterceptTouchEvent(ev)) {
- mActiveController = mCurrentResizeFrame;
+ AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mLauncher);
+ if (topView != null && topView.onControllerInterceptTouchEvent(ev)) {
+ mActiveController = topView;
return true;
- } else {
- clearResizeFrame();
}
if (mDragController.onControllerInterceptTouchEvent(ev)) {
@@ -243,14 +194,8 @@
return true;
}
- if (mAllAppsController.onControllerInterceptTouchEvent(ev)) {
- mActiveController = mAllAppsController;
- return true;
- }
-
- WidgetsBottomSheet widgetsBottomSheet = WidgetsBottomSheet.getOpen(mLauncher);
- if (widgetsBottomSheet != null && widgetsBottomSheet.onControllerInterceptTouchEvent(ev)) {
- mActiveController = widgetsBottomSheet;
+ if (mVerticalSwipeController.onControllerInterceptTouchEvent(ev)) {
+ mActiveController = mVerticalSwipeController;
return true;
}
@@ -357,12 +302,7 @@
@Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
-
- if (action == MotionEvent.ACTION_DOWN) {
- if (handleTouchDown(ev, false)) {
- return true;
- }
- } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
if (mTouchCompleteListener != null) {
mTouchCompleteListener.onTouchComplete();
}
@@ -542,25 +482,6 @@
}
}
- public void clearResizeFrame() {
- if (mCurrentResizeFrame != null) {
- removeView(mCurrentResizeFrame);
- mCurrentResizeFrame = null;
- }
- }
-
- public void addResizeFrame(LauncherAppWidgetHostView widget, CellLayout cellLayout) {
- clearResizeFrame();
-
- mCurrentResizeFrame = (AppWidgetResizeFrame) LayoutInflater.from(mLauncher)
- .inflate(R.layout.app_widget_resize_frame, this, false);
- mCurrentResizeFrame.setupForWidget(widget, cellLayout, this);
- ((LayoutParams) mCurrentResizeFrame.getLayoutParams()).customPosition = true;
-
- addView(mCurrentResizeFrame);
- mCurrentResizeFrame.snapToWidget(false);
- }
-
public void animateViewIntoPosition(DragView dragView, final int[] pos, float alpha,
float scaleX, float scaleY, int animationEndStyle, Runnable onFinishRunnable,
int duration) {
@@ -573,13 +494,12 @@
onFinishRunnable, animationEndStyle, duration, null);
}
- public void animateViewIntoPosition(DragView dragView, final View child,
- final Runnable onFinishAnimationRunnable, View anchorView) {
- animateViewIntoPosition(dragView, child, -1, onFinishAnimationRunnable, anchorView);
+ public void animateViewIntoPosition(DragView dragView, final View child, View anchorView) {
+ animateViewIntoPosition(dragView, child, -1, anchorView);
}
public void animateViewIntoPosition(DragView dragView, final View child, int duration,
- final Runnable onFinishAnimationRunnable, View anchorView) {
+ View anchorView) {
ShortcutAndWidgetContainer parentChildren = (ShortcutAndWidgetContainer) child.getParent();
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
parentChildren.measureChild(child);
@@ -636,9 +556,6 @@
Runnable onCompleteRunnable = new Runnable() {
public void run() {
child.setVisibility(VISIBLE);
- if (onFinishAnimationRunnable != null) {
- onFinishAnimationRunnable.run();
- }
}
};
animateViewIntoPosition(dragView, fromX, fromY, toX, toY, 1, 1, 1, toScale, toScale,
@@ -858,48 +775,23 @@
}
public void invalidateScrim() {
- if (mBackgroundAlpha > 0.0f) {
+ if (mPageCutOutScrim.getAlpha() > 0) {
invalidate();
}
}
+ public Drawable getScrim() {
+ return mPageCutOutScrim;
+ }
+
@Override
protected void dispatchDraw(Canvas canvas) {
// Draw the background below children.
- if (mBackgroundAlpha > 0.0f) {
- // Update the scroll position first to ensure scrim cutout is in the right place.
- mLauncher.getWorkspace().computeScrollWithoutInvalidation();
-
- int alpha = (int) (mBackgroundAlpha * 255);
- CellLayout currCellLayout = mLauncher.getWorkspace().getCurrentDragOverlappingLayout();
- canvas.save();
- if (currCellLayout != null && currCellLayout != mLauncher.getHotseat().getLayout()) {
- // Cut a hole in the darkening scrim on the page that should be highlighted, if any.
- getDescendantRectRelativeToSelf(currCellLayout, mHighlightRect);
- canvas.clipRect(mHighlightRect, Region.Op.DIFFERENCE);
- }
- // for super light wallpaper it needs to be darken for contrast to workspace
- // for dark wallpapers the text is white so darkening works as well
- int color = ColorUtils.compositeColors(0x66000000, mWallpaperColorInfo.getMainColor());
- canvas.drawColor(ColorUtils.setAlphaComponent(color, alpha));
- canvas.restore();
- }
-
+ mPageCutOutScrim.draw(canvas);
mFocusIndicatorHelper.draw(canvas);
super.dispatchDraw(canvas);
}
- public void setBackgroundAlpha(float alpha) {
- if (alpha != mBackgroundAlpha) {
- mBackgroundAlpha = alpha;
- invalidate();
- }
- }
-
- public float getBackgroundAlpha() {
- return mBackgroundAlpha;
- }
-
@Override
protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
View topView = AbstractFloatingView.getTopOpenView(mLauncher);
diff --git a/src/com/android/launcher3/dragndrop/DragOptions.java b/src/com/android/launcher3/dragndrop/DragOptions.java
index 9433aad..f108f8b 100644
--- a/src/com/android/launcher3/dragndrop/DragOptions.java
+++ b/src/com/android/launcher3/dragndrop/DragOptions.java
@@ -34,6 +34,9 @@
/** Determines when a pre-drag should transition to a drag. By default, this is immediate. */
public PreDragCondition preDragCondition = null;
+ /** Scale of the icons over the workspace icon size. */
+ public float intrinsicIconScaleFactor = 1f;
+
/**
* Specifies a condition that must be met before DragListener#onDragStart() is called.
* By default, there is no condition and onDragStart() is called immediately following
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 33d4fa6..7c89df3 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -43,7 +43,6 @@
import android.support.animation.SpringAnimation;
import android.support.animation.SpringForce;
import android.view.View;
-import android.view.animation.DecelerateInterpolator;
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.ItemInfo;
@@ -54,6 +53,7 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.ShortcutConfigActivityInfo;
import com.android.launcher3.config.FeatureFlags;
@@ -169,7 +169,7 @@
}
});
- mBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight());
+ mBitmap = bitmap;
setDragRegion(new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()));
// The point in our scaled bitmap that the touch events are located
@@ -427,6 +427,10 @@
return mDragRegion;
}
+ public Bitmap getPreviewBitmap() {
+ return mBitmap;
+ }
+
@Override
protected void onDraw(Canvas canvas) {
mHasDrawn = true;
@@ -468,7 +472,7 @@
public void crossFade(int duration) {
ValueAnimator va = LauncherAnimUtils.ofFloat(0f, 1f);
va.setDuration(duration);
- va.setInterpolator(new DecelerateInterpolator(1.5f));
+ va.setInterpolator(Interpolators.DEACCEL_1_5);
va.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
diff --git a/src/com/android/launcher3/dragndrop/PageCutOutScrimDrawable.java b/src/com/android/launcher3/dragndrop/PageCutOutScrimDrawable.java
new file mode 100644
index 0000000..367124b
--- /dev/null
+++ b/src/com/android/launcher3/dragndrop/PageCutOutScrimDrawable.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 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.dragndrop;
+
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.drawable.Drawable;
+import android.support.v4.graphics.ColorUtils;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.dynamicui.WallpaperColorInfo;
+
+/**
+ * Scrim drawable which draws a hole for the current drop target page.
+ */
+public class PageCutOutScrimDrawable extends Drawable {
+
+ private final Rect mHighlightRect = new Rect();
+ private final DragLayer mDragLayer;
+ private final Launcher mLauncher;
+ private final WallpaperColorInfo mWallpaperColorInfo;
+
+ private int mAlpha = 0;
+
+ public PageCutOutScrimDrawable(DragLayer dragLayer) {
+ mDragLayer = dragLayer;
+ mLauncher = Launcher.getLauncher(mDragLayer.getContext());
+ mWallpaperColorInfo = WallpaperColorInfo.getInstance(mLauncher);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ // Draw the background below children.
+ if (mAlpha > 0) {
+ // Update the scroll position first to ensure scrim cutout is in the right place.
+ mLauncher.getWorkspace().computeScrollWithoutInvalidation();
+ CellLayout currCellLayout = mLauncher.getWorkspace().getCurrentDragOverlappingLayout();
+ canvas.save();
+ if (currCellLayout != null && currCellLayout != mLauncher.getHotseat().getLayout()) {
+ // Cut a hole in the darkening scrim on the page that should be highlighted, if any.
+ mDragLayer.getDescendantRectRelativeToSelf(currCellLayout, mHighlightRect);
+ canvas.clipRect(mHighlightRect, Region.Op.DIFFERENCE);
+ }
+ // for super light wallpaper it needs to be darken for contrast to workspace
+ // for dark wallpapers the text is white so darkening works as well
+ int color = ColorUtils.compositeColors(0x66000000, mWallpaperColorInfo.getMainColor());
+ canvas.drawColor(ColorUtils.setAlphaComponent(color, mAlpha));
+ canvas.restore();
+ }
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ if (mAlpha != alpha) {
+ mAlpha = alpha;
+ invalidateSelf();
+ }
+ }
+
+ @Override
+ public int getAlpha() {
+ return mAlpha;
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) { }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+}
diff --git a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
index a70a9bb..52167bb 100644
--- a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
+++ b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
@@ -29,7 +29,7 @@
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.IconCache;
-import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
@@ -83,8 +83,8 @@
public com.android.launcher3.ShortcutInfo createShortcutInfo() {
// Total duration for the drop animation to complete.
long duration = mContext.getResources().getInteger(R.integer.config_dropAnimMaxDuration) +
- Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT +
- mContext.getResources().getInteger(R.integer.config_overlayTransitionTime) / 2;
+ LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY +
+ LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS;
// Delay the actual accept() call until the drop animation is complete.
return LauncherAppsCompatVO.createShortcutInfoFromPinItemRequest(
mContext, mRequest, duration);
diff --git a/src/com/android/launcher3/dynamicui/ColorExtractionService.java b/src/com/android/launcher3/dynamicui/ColorExtractionService.java
deleted file mode 100644
index b9dd3b5..0000000
--- a/src/com/android/launcher3/dynamicui/ColorExtractionService.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Copyright (C) 2016 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.dynamicui;
-
-import android.annotation.TargetApi;
-import android.app.WallpaperManager;
-import android.app.job.JobParameters;
-import android.app.job.JobService;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.BitmapRegionDecoder;
-import android.graphics.Rect;
-import android.graphics.drawable.BitmapDrawable;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.ParcelFileDescriptor;
-import android.support.v7.graphics.Palette;
-import android.util.Log;
-
-import com.android.launcher3.LauncherProvider;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
-
-import java.io.IOException;
-
-/**
- * Extracts colors from the wallpaper, and saves results to {@link LauncherProvider}.
- */
-public class ColorExtractionService extends JobService {
-
- private static final String TAG = "ColorExtractionService";
- private static final boolean DEBUG = false;
-
- /** The fraction of the wallpaper to extract colors for use on the hotseat. */
- private static final float HOTSEAT_FRACTION = 1f / 4;
-
- private HandlerThread mWorkerThread;
- private Handler mWorkerHandler;
-
- @Override
- public void onCreate() {
- super.onCreate();
- mWorkerThread = new HandlerThread("ColorExtractionService");
- mWorkerThread.start();
- mWorkerHandler = new Handler(mWorkerThread.getLooper());
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- mWorkerThread.quit();
- }
-
- @Override
- public boolean onStartJob(final JobParameters jobParameters) {
- if (DEBUG) Log.d(TAG, "onStartJob");
- mWorkerHandler.post(new Runnable() {
- @Override
- public void run() {
- WallpaperManager wallpaperManager = WallpaperManager.getInstance(
- ColorExtractionService.this);
- int wallpaperId = ExtractionUtils.getWallpaperId(wallpaperManager);
-
- ExtractedColors extractedColors = new ExtractedColors();
- if (wallpaperManager.getWallpaperInfo() != null) {
- // We can't extract colors from live wallpapers; always use the default color.
- extractedColors.updateHotseatPalette(null);
-
- if (FeatureFlags.QSB_IN_HOTSEAT || FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
- extractedColors.updateWallpaperThemePalette(null);
- }
- } else {
- // We extract colors for the hotseat and status bar separately,
- // since they only consider part of the wallpaper.
- extractedColors.updateHotseatPalette(getHotseatPalette());
-
- if (FeatureFlags.LIGHT_STATUS_BAR) {
- extractedColors.updateStatusBarPalette(getStatusBarPalette());
- }
-
- if (FeatureFlags.QSB_IN_HOTSEAT || FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
- extractedColors.updateWallpaperThemePalette(getWallpaperPalette());
- }
- }
-
- // Save the extracted colors and wallpaper id to LauncherProvider.
- String colorsString = extractedColors.encodeAsString();
- Bundle extras = new Bundle();
- extras.putInt(LauncherSettings.Settings.EXTRA_WALLPAPER_ID, wallpaperId);
- extras.putString(LauncherSettings.Settings.EXTRA_EXTRACTED_COLORS, colorsString);
- getContentResolver().call(
- LauncherSettings.Settings.CONTENT_URI,
- LauncherSettings.Settings.METHOD_SET_EXTRACTED_COLORS_AND_WALLPAPER_ID,
- null, extras);
- jobFinished(jobParameters, false /* needsReschedule */);
- if (DEBUG) Log.d(TAG, "job finished!");
- }
- });
- return true;
- }
-
- @Override
- public boolean onStopJob(JobParameters jobParameters) {
- if (DEBUG) Log.d(TAG, "onStopJob");
- mWorkerHandler.removeCallbacksAndMessages(null);
- return true;
- }
-
- @TargetApi(Build.VERSION_CODES.N)
- private Palette getHotseatPalette() {
- WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
- if (Utilities.ATLEAST_NOUGAT) {
- try (ParcelFileDescriptor fd = wallpaperManager
- .getWallpaperFile(WallpaperManager.FLAG_SYSTEM)) {
- BitmapRegionDecoder decoder = BitmapRegionDecoder
- .newInstance(fd.getFileDescriptor(), false);
- int height = decoder.getHeight();
- Rect decodeRegion = new Rect(0, (int) (height * (1f - HOTSEAT_FRACTION)),
- decoder.getWidth(), height);
- Bitmap bitmap = decoder.decodeRegion(decodeRegion, null);
- decoder.recycle();
- if (bitmap != null) {
- return Palette.from(bitmap).clearFilters().generate();
- }
- } catch (IOException | NullPointerException e) {
- Log.e(TAG, "Fetching partial bitmap failed, trying old method", e);
- }
- }
-
- Bitmap wallpaper = ((BitmapDrawable) wallpaperManager.getDrawable()).getBitmap();
- return Palette.from(wallpaper)
- .setRegion(0, (int) (wallpaper.getHeight() * (1f - HOTSEAT_FRACTION)),
- wallpaper.getWidth(), wallpaper.getHeight())
- .clearFilters()
- .generate();
- }
-
- @TargetApi(Build.VERSION_CODES.N)
- private Palette getStatusBarPalette() {
- WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
- int statusBarHeight = getResources()
- .getDimensionPixelSize(R.dimen.status_bar_height);
-
- if (Utilities.ATLEAST_NOUGAT) {
- try (ParcelFileDescriptor fd = wallpaperManager
- .getWallpaperFile(WallpaperManager.FLAG_SYSTEM)) {
- BitmapRegionDecoder decoder = BitmapRegionDecoder
- .newInstance(fd.getFileDescriptor(), false);
- Rect decodeRegion = new Rect(0, 0,
- decoder.getWidth(), statusBarHeight);
- Bitmap bitmap = decoder.decodeRegion(decodeRegion, null);
- decoder.recycle();
- if (bitmap != null) {
- return Palette.from(bitmap).clearFilters().generate();
- }
- } catch (IOException | NullPointerException e) {
- Log.e(TAG, "Fetching partial bitmap failed, trying old method", e);
- }
- }
-
- Bitmap wallpaper = ((BitmapDrawable) wallpaperManager.getDrawable()).getBitmap();
- return Palette.from(wallpaper)
- .setRegion(0, 0, wallpaper.getWidth(), statusBarHeight)
- .clearFilters()
- .generate();
- }
-
- @TargetApi(Build.VERSION_CODES.N)
- private Palette getWallpaperPalette() {
- WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
- if (Utilities.ATLEAST_NOUGAT) {
- try (ParcelFileDescriptor fd = wallpaperManager
- .getWallpaperFile(WallpaperManager.FLAG_SYSTEM)) {
- Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fd.getFileDescriptor());
- if (bitmap != null) {
- return Palette.from(bitmap).clearFilters().generate();
- }
- } catch (IOException | NullPointerException e) {
- Log.e(TAG, "Fetching partial bitmap failed, trying old method", e);
- }
- }
-
- Bitmap wallpaper = ((BitmapDrawable) wallpaperManager.getDrawable()).getBitmap();
- return Palette.from(wallpaper).clearFilters().generate();
- }
-}
diff --git a/src/com/android/launcher3/dynamicui/ExtractedColors.java b/src/com/android/launcher3/dynamicui/ExtractedColors.java
deleted file mode 100644
index 2d8bb86..0000000
--- a/src/com/android/launcher3/dynamicui/ExtractedColors.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2016 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.dynamicui;
-
-import android.app.WallpaperManager;
-import android.content.Context;
-import android.graphics.Color;
-import android.support.annotation.Nullable;
-import android.support.v4.graphics.ColorUtils;
-import android.support.v7.graphics.Palette;
-import android.util.Log;
-
-import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-
-/**
- * Saves and loads colors extracted from the wallpaper, as well as the associated wallpaper id.
- */
-public class ExtractedColors {
- private static final String TAG = "ExtractedColors";
-
- public static final int DEFAULT_LIGHT = Color.WHITE;
- public static final int DEFAULT_DARK = Color.BLACK;
-
- // These color profile indices should NOT be changed, since they are used when saving and
- // loading extracted colors. New colors should always be added at the end.
- public static final int VERSION_INDEX = 0;
- public static final int HOTSEAT_INDEX = 1;
- public static final int STATUS_BAR_INDEX = 2;
- public static final int WALLPAPER_VIBRANT_INDEX = 3;
- public static final int ALLAPPS_GRADIENT_MAIN_INDEX = 4;
- public static final int ALLAPPS_GRADIENT_SECONDARY_INDEX = 5;
-
- private static final int VERSION;
- private static final int[] DEFAULT_VALUES;
-
- static {
- if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
- VERSION = 3;
- DEFAULT_VALUES = new int[] {
- VERSION, // VERSION_INDEX
- 0x40FFFFFF, // HOTSEAT_INDEX: White with 25% alpha
- DEFAULT_DARK, // STATUS_BAR_INDEX
- 0xFFCCCCCC, // WALLPAPER_VIBRANT_INDEX
- 0xFF000000, // ALLAPPS_GRADIENT_MAIN_INDEX
- 0xFF000000 // ALLAPPS_GRADIENT_SECONDARY_INDEX
- };
- } else if (FeatureFlags.QSB_IN_HOTSEAT) {
- VERSION = 2;
- DEFAULT_VALUES = new int[] {
- VERSION, // VERSION_INDEX
- 0x40FFFFFF, // HOTSEAT_INDEX: White with 25% alpha
- DEFAULT_DARK, // STATUS_BAR_INDEX
- 0xFFCCCCCC, // WALLPAPER_VIBRANT_INDEX
- };
- } else {
- VERSION = 1;
- DEFAULT_VALUES = new int[] {
- VERSION, // VERSION_INDEX
- 0x40FFFFFF, // HOTSEAT_INDEX: White with 25% alpha
- DEFAULT_DARK, // STATUS_BAR_INDEX
- };
- }
- }
-
- private static final String COLOR_SEPARATOR = ",";
-
- private final ArrayList<OnChangeListener> mListeners = new ArrayList<>();
- private final int[] mColors;
-
- public ExtractedColors() {
- // The first entry is reserved for the version number.
- mColors = Arrays.copyOf(DEFAULT_VALUES, DEFAULT_VALUES.length);
- }
-
- public void setColorAtIndex(int index, int color) {
- if (index > VERSION_INDEX && index < mColors.length) {
- mColors[index] = color;
- } else {
- Log.e(TAG, "Attempted to set a color at an invalid index " + index);
- }
- }
-
- /**
- * Encodes {@link #mColors} as a comma-separated String.
- */
- String encodeAsString() {
- StringBuilder colorsStringBuilder = new StringBuilder();
- for (int color : mColors) {
- colorsStringBuilder.append(color).append(COLOR_SEPARATOR);
- }
- return colorsStringBuilder.toString();
- }
-
- /**
- * Loads colors and wallpaper id from {@link Utilities#getPrefs(Context)}.
- * These were saved there in {@link ColorExtractionService}.
- */
- public void load(Context context) {
- String encodedString = Utilities.getPrefs(context).getString(
- ExtractionUtils.EXTRACTED_COLORS_PREFERENCE_KEY, VERSION + "");
-
- String[] splitColorsString = encodedString.split(COLOR_SEPARATOR);
- if (splitColorsString.length == DEFAULT_VALUES.length &&
- Integer.parseInt(splitColorsString[VERSION_INDEX]) == VERSION) {
- // Parse and apply the saved values.
- for (int i = 0; i < mColors.length; i++) {
- mColors[i] = Integer.parseInt(splitColorsString[i]);
- }
- } else {
- // Leave the values as default values as the saved values may not be compatible.
- ExtractionUtils.startColorExtractionService(context);
- }
- }
-
- /** @param index must be one of the index values defined at the top of this class. */
- public int getColor(int index) {
- return mColors[index];
- }
-
- /**
- * The hotseat's color is defined as follows:
- * - 12% black for super light wallpaper
- * - 18% white for super dark
- * - 25% white otherwise
- */
- public void updateHotseatPalette(Palette hotseatPalette) {
- int hotseatColor;
- if (hotseatPalette != null && ExtractionUtils.isSuperLight(hotseatPalette)) {
- hotseatColor = ColorUtils.setAlphaComponent(Color.BLACK, (int) (0.12f * 255));
- } else if (hotseatPalette != null && ExtractionUtils.isSuperDark(hotseatPalette)) {
- hotseatColor = ColorUtils.setAlphaComponent(Color.WHITE, (int) (0.18f * 255));
- } else {
- hotseatColor = DEFAULT_VALUES[HOTSEAT_INDEX];
- }
- setColorAtIndex(HOTSEAT_INDEX, hotseatColor);
- }
-
- public void updateStatusBarPalette(Palette statusBarPalette) {
- setColorAtIndex(STATUS_BAR_INDEX, ExtractionUtils.isSuperLight(statusBarPalette) ?
- DEFAULT_LIGHT : DEFAULT_DARK);
- }
-
- public void updateWallpaperThemePalette(@Nullable Palette wallpaperPalette) {
- int defaultColor = DEFAULT_VALUES[WALLPAPER_VIBRANT_INDEX];
- setColorAtIndex(WALLPAPER_VIBRANT_INDEX, wallpaperPalette == null
- ? defaultColor : wallpaperPalette.getVibrantColor(defaultColor));
- }
-
- public void addOnChangeListener(OnChangeListener listener) {
- mListeners.add(listener);
- }
-
- public void notifyChange() {
- for (OnChangeListener listener : mListeners) {
- listener.onExtractedColorsChanged();
- }
- }
-
- /**
- * Interface for listening for extracted color changes
- */
- public interface OnChangeListener {
-
- void onExtractedColorsChanged();
- }
-}
diff --git a/src/com/android/launcher3/dynamicui/ExtractionUtils.java b/src/com/android/launcher3/dynamicui/ExtractionUtils.java
deleted file mode 100644
index cc0e0be..0000000
--- a/src/com/android/launcher3/dynamicui/ExtractionUtils.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2016 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.dynamicui;
-
-import android.annotation.TargetApi;
-import android.app.WallpaperManager;
-import android.app.job.JobInfo;
-import android.app.job.JobScheduler;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.graphics.Color;
-import android.os.Build;
-import android.support.v4.graphics.ColorUtils;
-import android.support.v7.graphics.Palette;
-
-import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
-
-import java.util.List;
-
-/**
- * Contains helper fields and methods related to extracting colors from the wallpaper.
- */
-public class ExtractionUtils {
- public static final String EXTRACTED_COLORS_PREFERENCE_KEY = "pref_extractedColors";
- public static final String WALLPAPER_ID_PREFERENCE_KEY = "pref_wallpaperId";
-
- private static final float MIN_CONTRAST_RATIO = 2f;
-
- /**
- * Extract colors in the :wallpaper-chooser process, if the wallpaper id has changed.
- * When the new colors are saved in the LauncherProvider,
- * Launcher will be notified in Launcher#onSettingsChanged(String, String).
- */
- public static void startColorExtractionServiceIfNecessary(final Context context) {
- if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
- return;
- }
- // Run on a background thread, since the service is asynchronous anyway.
- Utilities.THREAD_POOL_EXECUTOR.execute(new Runnable() {
- @Override
- public void run() {
- if (hasWallpaperIdChanged(context)) {
- startColorExtractionService(context);
- }
- }
- });
- }
-
- /** Starts the {@link ColorExtractionService} without checking the wallpaper id */
- public static void startColorExtractionService(Context context) {
- if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
- return;
- }
- JobScheduler jobScheduler = (JobScheduler) context.getSystemService(
- Context.JOB_SCHEDULER_SERVICE);
- jobScheduler.schedule(new JobInfo.Builder(Utilities.COLOR_EXTRACTION_JOB_ID,
- new ComponentName(context, ColorExtractionService.class))
- .setMinimumLatency(0).build());
- }
-
- private static boolean hasWallpaperIdChanged(Context context) {
- if (!Utilities.ATLEAST_NOUGAT) {
- // TODO: update an id in sharedprefs in onWallpaperChanged broadcast, and read it here.
- return false;
- }
- final SharedPreferences sharedPrefs = Utilities.getPrefs(context);
- int wallpaperId = getWallpaperId(WallpaperManager.getInstance(context));
- int savedWallpaperId = sharedPrefs.getInt(ExtractionUtils.WALLPAPER_ID_PREFERENCE_KEY, -1);
- return wallpaperId != savedWallpaperId;
- }
-
- @TargetApi(Build.VERSION_CODES.N)
- public static int getWallpaperId(WallpaperManager wallpaperManager) {
- return Utilities.ATLEAST_NOUGAT ?
- wallpaperManager.getWallpaperId(WallpaperManager.FLAG_SYSTEM) : -1;
- }
-
- public static boolean isSuperLight(Palette p) {
- return !isLegibleOnWallpaper(Color.WHITE, p.getSwatches());
- }
-
- public static boolean isSuperDark(Palette p) {
- return !isLegibleOnWallpaper(Color.BLACK, p.getSwatches());
- }
-
- /**
- * Given a color, returns true if that color is legible on
- * the given wallpaper color swatches, else returns false.
- */
- private static boolean isLegibleOnWallpaper(int color, List<Palette.Swatch> wallpaperSwatches) {
- int legiblePopulation = 0;
- int illegiblePopulation = 0;
- for (Palette.Swatch swatch : wallpaperSwatches) {
- if (isLegible(color, swatch.getRgb())) {
- legiblePopulation += swatch.getPopulation();
- } else {
- illegiblePopulation += swatch.getPopulation();
- }
- }
- return legiblePopulation > illegiblePopulation;
- }
-
- /** @return Whether the foreground color is legible on the background color. */
- private static boolean isLegible(int foreground, int background) {
- background = ColorUtils.setAlphaComponent(background, 255);
- return ColorUtils.calculateContrast(foreground, background) >= MIN_CONTRAST_RATIO;
- }
-
-}
diff --git a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
index f25345e..5954efa 100644
--- a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
+++ b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
@@ -1,9 +1,8 @@
package com.android.launcher3.folder;
+public class ClippedFolderIconLayoutRule {
-public class ClippedFolderIconLayoutRule implements FolderIcon.PreviewLayoutRule {
-
- static final int MAX_NUM_ITEMS_IN_PREVIEW = 4;
+ public static final int MAX_NUM_ITEMS_IN_PREVIEW = 4;
private static final int MIN_NUM_ITEMS_IN_PREVIEW = 2;
private static final float MIN_SCALE = 0.48f;
@@ -11,8 +10,8 @@
private static final float MAX_RADIUS_DILATION = 0.15f;
private static final float ITEM_RADIUS_SCALE_FACTOR = 1.33f;
- private static final int EXIT_INDEX = -2;
- private static final int ENTER_INDEX = -3;
+ public static final int EXIT_INDEX = -2;
+ public static final int ENTER_INDEX = -3;
private float[] mTmpPoint = new float[2];
@@ -22,7 +21,6 @@
private boolean mIsRtl;
private float mBaselineIconScale;
- @Override
public void init(int availableSpace, float intrinsicIconSize, boolean rtl) {
mAvailableSpace = availableSpace;
mRadius = ITEM_RADIUS_SCALE_FACTOR * availableSpace / 2f;
@@ -31,19 +29,18 @@
mBaselineIconScale = availableSpace / (intrinsicIconSize * 1f);
}
- @Override
public PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
PreviewItemDrawingParams params) {
- float totalScale = scaleForItem(index, curNumItems);
+ float totalScale = scaleForItem(curNumItems);
float transX;
float transY;
float overlayAlpha = 0;
- if (index == getExitIndex()) {
+ if (index == EXIT_INDEX) {
// 0 1 * <-- Exit position (row 0, col 2)
// 2 3
getGridPosition(0, 2, mTmpPoint);
- } else if (index == getEnterIndex()) {
+ } else if (index == ENTER_INDEX) {
// 0 1
// 2 3 * <-- Enter position (row 1, col 2)
getGridPosition(1, 2, mTmpPoint);
@@ -120,7 +117,7 @@
MIN_NUM_ITEMS_IN_PREVIEW) / (MAX_NUM_ITEMS_IN_PREVIEW - MIN_NUM_ITEMS_IN_PREVIEW));
double theta = theta0 + index * (2 * Math.PI / curNumItems) * direction;
- float halfIconSize = (mIconSize * scaleForItem(index, curNumItems)) / 2;
+ float halfIconSize = (mIconSize * scaleForItem(curNumItems)) / 2;
// Map the location along the circle, and offset the coordinates to represent the center
// of the icon, and to be based from the top / left of the preview area. The y component
@@ -130,10 +127,9 @@
}
- @Override
- public float scaleForItem(int index, int numItems) {
+ public float scaleForItem(int numItems) {
// Scale is determined by the number of items in the preview.
- float scale = 1f;
+ final float scale;
if (numItems <= 2) {
scale = MAX_SCALE;
} else if (numItems == 3) {
@@ -141,37 +137,10 @@
} else {
scale = MIN_SCALE;
}
-
return scale * mBaselineIconScale;
}
- @Override
public float getIconSize() {
return mIconSize;
}
-
- @Override
- public int maxNumItems() {
- return MAX_NUM_ITEMS_IN_PREVIEW;
- }
-
- @Override
- public boolean clipToBackground() {
- return true;
- }
-
- @Override
- public boolean hasEnterExitIndices() {
- return true;
- }
-
- @Override
- public int getExitIndex() {
- return EXIT_INDEX;
- }
-
- @Override
- public int getEnterIndex() {
- return ENTER_INDEX;
- }
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 85792d4..2168001 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -16,11 +16,12 @@
package com.android.launcher3.folder;
+import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
+import static com.android.launcher3.LauncherState.NORMAL;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
@@ -34,10 +35,10 @@
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug;
import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.AccelerateInterpolator;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.EditorInfo;
import android.widget.TextView;
@@ -55,24 +56,20 @@
import com.android.launcher3.FolderInfo.FolderListener;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.LogDecelerateInterpolator;
import com.android.launcher3.OnAlarmListener;
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.UninstallDropTarget.DropTargetSource;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
-import com.android.launcher3.anim.AnimationLayerSet;
-import com.android.launcher3.anim.CircleRevealOutlineProvider;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragController.DragListener;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.logging.LoggerUtils;
import com.android.launcher3.pageindicators.PageIndicatorDots;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
@@ -89,8 +86,7 @@
*/
public class Folder extends AbstractFloatingView implements DragSource, View.OnClickListener,
View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener,
- View.OnFocusChangeListener, DragListener, DropTargetSource,
- ExtendedEditText.OnBackKeyListener {
+ View.OnFocusChangeListener, DragListener, ExtendedEditText.OnBackKeyListener {
private static final String TAG = "Launcher.Folder";
/**
@@ -137,10 +133,6 @@
private AnimatorSet mCurrentAnimator;
- private final int mExpandDuration;
- public final int mMaterialExpandDuration;
- private final int mMaterialExpandStagger;
-
protected final Launcher mLauncher;
protected DragController mDragController;
public FolderInfo mInfo;
@@ -181,10 +173,6 @@
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mDestroyed;
- @Thunk Runnable mDeferredAction;
- private boolean mDeferDropAfterUninstall;
- private boolean mUninstallSuccessful;
-
// Folder scrolling
private int mScrollAreaOffset;
@@ -201,9 +189,6 @@
super(context, attrs);
setAlwaysDrawnWithCacheEnabled(false);
Resources res = getResources();
- mExpandDuration = res.getInteger(R.integer.config_folderExpandDuration);
- mMaterialExpandDuration = res.getInteger(R.integer.config_materialFolderExpandDuration);
- mMaterialExpandStagger = res.getInteger(R.integer.config_materialFolderExpandStagger);
if (sDefaultFolderName == null) {
sDefaultFolderName = res.getString(R.string.folder_name);
@@ -382,11 +367,6 @@
return false;
}
- @Override
- public ExtendedEditText getActiveTextView() {
- return isEditingName() ? mFolderName : null;
- }
-
public FolderIcon getFolderIcon() {
return mFolderIcon;
}
@@ -487,25 +467,6 @@
? R.layout.user_folder : R.layout.user_folder_icon_normalized, null);
}
- /**
- * This method is intended to make the UserFolder to be visually identical in size and position
- * to its associated FolderIcon. This allows for a seamless transition into the expanded state.
- */
- private void positionAndSizeAsIcon() {
- if (!(getParent() instanceof DragLayer)) return;
- setScaleX(0.8f);
- setScaleY(0.8f);
- setAlpha(0f);
- mState = STATE_SMALL;
- }
-
- private void prepareReveal() {
- setScaleX(1f);
- setScaleY(1f);
- setAlpha(1f);
- mState = STATE_SMALL;
- }
-
private void startAnimation(final AnimatorSet a) {
if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
mCurrentAnimator.cancel();
@@ -525,61 +486,6 @@
a.start();
}
- private AnimatorSet getOpeningAnimator() {
- prepareReveal();
- mFolderIcon.growAndFadeOut();
-
- AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
-
- int width = getFolderWidth();
- int height = getFolderHeight();
-
- float transX = - 0.075f * (width / 2 - getPivotX());
- float transY = - 0.075f * (height / 2 - getPivotY());
- setTranslationX(transX);
- setTranslationY(transY);
- PropertyValuesHolder tx = PropertyValuesHolder.ofFloat(TRANSLATION_X, transX, 0);
- PropertyValuesHolder ty = PropertyValuesHolder.ofFloat(TRANSLATION_Y, transY, 0);
-
- Animator drift = ObjectAnimator.ofPropertyValuesHolder(this, tx, ty);
- drift.setDuration(mMaterialExpandDuration);
- drift.setStartDelay(mMaterialExpandStagger);
- drift.setInterpolator(new LogDecelerateInterpolator(100, 0));
-
- int rx = (int) Math.max(Math.max(width - getPivotX(), 0), getPivotX());
- int ry = (int) Math.max(Math.max(height - getPivotY(), 0), getPivotY());
- float radius = (float) Math.hypot(rx, ry);
-
- Animator reveal = new CircleRevealOutlineProvider((int) getPivotX(),
- (int) getPivotY(), 0, radius).createRevealAnimator(this);
- reveal.setDuration(mMaterialExpandDuration);
- reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
-
- mContent.setAlpha(0f);
- Animator iconsAlpha = ObjectAnimator.ofFloat(mContent, "alpha", 0f, 1f);
- iconsAlpha.setDuration(mMaterialExpandDuration);
- iconsAlpha.setStartDelay(mMaterialExpandStagger);
- iconsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
-
- mFooter.setAlpha(0f);
- Animator textAlpha = ObjectAnimator.ofFloat(mFooter, "alpha", 0f, 1f);
- textAlpha.setDuration(mMaterialExpandDuration);
- textAlpha.setStartDelay(mMaterialExpandStagger);
- textAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
-
- anim.play(drift);
- anim.play(iconsAlpha);
- anim.play(textAlpha);
- anim.play(reveal);
-
- AnimationLayerSet layerSet = new AnimationLayerSet();
- layerSet.addView(mContent);
- layerSet.addView(mFooter);
- anim.addListener(layerSet);
-
- return anim;
- }
-
/**
* Opens the user folder described by the specified tag. The opening of the folder
* is animated relative to the specified View. If the View is null, no animation
@@ -621,9 +527,7 @@
final Runnable onCompleteRunnable;
centerAboutIcon();
- AnimatorSet anim = FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION
- ? new FolderAnimationManager(this, true /* isOpening */).getAnimator()
- : getOpeningAnimator();
+ AnimatorSet anim = new FolderAnimationManager(this, true /* isOpening */).getAnimator();
onCompleteRunnable = new Runnable() {
@Override
public void run() {
@@ -633,12 +537,8 @@
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
- mFolderIcon.setBackgroundVisible(false);
- mFolderIcon.drawLeaveBehindIfExists();
- } else {
- mFolderIcon.setVisibility(INVISIBLE);
- }
+ mFolderIcon.setBackgroundVisible(false);
+ mFolderIcon.drawLeaveBehindIfExists();
Utilities.sendCustomAccessibilityEvent(
Folder.this,
@@ -728,11 +628,7 @@
}
if (mFolderIcon != null) {
- if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
- mFolderIcon.clearLeaveBehindIfExists();
- } else {
- mFolderIcon.shrinkAndFadeIn(animate);
- }
+ mFolderIcon.clearLeaveBehindIfExists();
}
if (!(getParent() instanceof DragLayer)) return;
@@ -749,21 +645,8 @@
parent.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
- private AnimatorSet getClosingAnimator() {
- AnimatorSet animatorSet = LauncherAnimUtils.createAnimatorSet();
- animatorSet.play(LauncherAnimUtils.ofViewAlphaAndScale(this, 0, 0.9f, 0.9f));
-
- AnimationLayerSet layerSet = new AnimationLayerSet();
- layerSet.addView(this);
- animatorSet.addListener(layerSet);
- animatorSet.setDuration(mExpandDuration);
- return animatorSet;
- }
-
private void animateClosed() {
- AnimatorSet a = FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION
- ? new FolderAnimationManager(this, false /* isOpening */).getAnimator()
- : getClosingAnimator();
+ AnimatorSet a = new FolderAnimationManager(this, false /* isOpening */).getAnimator();
a.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -790,16 +673,12 @@
clearFocus();
if (mFolderIcon != null) {
mFolderIcon.setVisibility(View.VISIBLE);
- if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
- mFolderIcon.setBackgroundVisible(true);
- mFolderIcon.mFolderName.setTextVisibility(true);
- }
+ mFolderIcon.setBackgroundVisible(true);
+ mFolderIcon.mFolderName.setTextVisibility(true);
if (wasAnimated) {
- if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
- mFolderIcon.mBackground.fadeInBackgroundShadow();
- mFolderIcon.mBackground.animateBackgroundStroke();
- mFolderIcon.onFolderClose(mContent.getCurrentPage());
- }
+ mFolderIcon.mBackground.fadeInBackgroundShadow();
+ mFolderIcon.mBackground.animateBackgroundStroke();
+ mFolderIcon.onFolderClose(mContent.getCurrentPage());
if (mFolderIcon.hasBadge()) {
mFolderIcon.createBadgeScaleAnimator(0f, 1f).start();
}
@@ -824,6 +703,7 @@
mContent.setCurrentPage(0);
}
+ @Override
public boolean acceptDrop(DragObject d) {
final ItemInfo item = d.dragInfo;
final int itemType = item.itemType;
@@ -852,18 +732,14 @@
return (getLayoutDirection() == LAYOUT_DIRECTION_RTL);
}
- @Override
- public void onDragOver(DragObject d) {
- onDragOver(d, REORDER_DELAY);
- }
-
private int getTargetRank(DragObject d, float[] recycle) {
recycle = d.getVisualCenter(recycle);
return mContent.findNearestArea(
(int) recycle[0] - getPaddingLeft(), (int) recycle[1] - getPaddingTop());
}
- @Thunk void onDragOver(DragObject d, int reorderDelay) {
+ @Override
+ public void onDragOver(DragObject d) {
if (mScrollPauseAlarm.alarmPending()) {
return;
}
@@ -976,23 +852,9 @@
}
public void onDropCompleted(final View target, final DragObject d,
- final boolean isFlingToDelete, final boolean success) {
- if (mDeferDropAfterUninstall) {
- Log.d(TAG, "Deferred handling drop because waiting for uninstall.");
- mDeferredAction = new Runnable() {
- public void run() {
- onDropCompleted(target, d, isFlingToDelete, success);
- mDeferredAction = null;
- }
- };
- return;
- }
+ final boolean success) {
- boolean beingCalledAfterUninstall = mDeferredAction != null;
- boolean successfulDrop =
- success && (!beingCalledAfterUninstall || mUninstallSuccessful);
-
- if (successfulDrop) {
+ if (success) {
if (mDeleteFolderOnDropCompleted && !mItemAddedBackToSelfViaIcon && target != this) {
replaceFolderWithFinalItem();
}
@@ -1007,14 +869,14 @@
mItemsInvalidated = true;
try (SuppressInfoChanges s = new SuppressInfoChanges()) {
- mFolderIcon.onDrop(d);
+ mFolderIcon.onDrop(d, true /* itemReturnedOnFailedDrop */);
}
}
if (target != this) {
if (mOnExitAlarm.alarmPending()) {
mOnExitAlarm.cancelAlarm();
- if (!successfulDrop) {
+ if (!success) {
mSuppressFolderDeletion = true;
}
mScrollPauseAlarm.cancelAlarm();
@@ -1038,41 +900,6 @@
mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, false,
mLauncher.getModelWriter());
}
-
- if (!isFlingToDelete) {
- // Fling to delete already exits spring loaded mode after the animation finishes.
- mLauncher.exitSpringLoadedDragModeDelayed(successfulDrop,
- Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
- }
- }
-
- @Override
- public void deferCompleteDropAfterUninstallActivity() {
- mDeferDropAfterUninstall = true;
- }
-
- @Override
- public void onDragObjectRemoved(boolean success) {
- mDeferDropAfterUninstall = false;
- mUninstallSuccessful = success;
- if (mDeferredAction != null) {
- mDeferredAction.run();
- }
- }
-
- @Override
- public float getIntrinsicIconScaleFactor() {
- return 1f;
- }
-
- @Override
- public boolean supportsAppInfoDropTarget() {
- return true;
- }
-
- @Override
- public boolean supportsDeleteDropTarget() {
- return true;
}
private void updateItemLocationsInDatabaseBatch() {
@@ -1095,10 +922,7 @@
}
public boolean isDropEnabled() {
- if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
- return mState != STATE_ANIMATING;
- }
- return true;
+ return mState != STATE_ANIMATING;
}
public boolean isFull() {
@@ -1113,7 +937,7 @@
int width = getFolderWidth();
int height = getFolderHeight();
- float scale = parent.getDescendantRectRelativeToSelf(mFolderIcon, sTempRect);
+ parent.getDescendantRectRelativeToSelf(mFolderIcon, sTempRect);
int centerX = sTempRect.centerX();
int centerY = sTempRect.centerY();
int centeredLeft = centerX - width / 2;
@@ -1329,22 +1153,7 @@
}
}
- public void onDrop(DragObject d) {
- Runnable cleanUpRunnable = null;
-
- // If we are coming from All Apps space, we defer removing the extra empty screen
- // until the folder closes
- if (d.dragSource != mLauncher.getWorkspace() && !(d.dragSource instanceof Folder)) {
- cleanUpRunnable = new Runnable() {
- @Override
- public void run() {
- mLauncher.exitSpringLoadedDragModeDelayed(true,
- Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT,
- null);
- }
- };
- }
-
+ public void onDrop(DragObject d, DragOptions options) {
// If the icon was dropped while the page was being scrolled, we need to compute
// the target location again such that the icon is placed of the final page.
if (!mContent.rankOnCurrentPage(mEmptyCellRank)) {
@@ -1410,8 +1219,7 @@
float scaleY = getScaleY();
setScaleX(1.0f);
setScaleY(1.0f);
- mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, currentDragView,
- cleanUpRunnable, null);
+ mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, currentDragView, null);
setScaleX(scaleX);
setScaleY(scaleY);
} else {
@@ -1436,6 +1244,7 @@
mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true, mLauncher.getModelWriter());
}
+ mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
if (d.stateAnnouncer != null) {
d.stateAnnouncer.completeAction(R.string.item_moved);
}
@@ -1611,7 +1420,7 @@
@Override
public void onAlarm(Alarm alarm) {
// Reorder immediately on page change.
- onDragOver(mDragObject, 1);
+ onDragOver(mDragObject);
}
}
@@ -1654,7 +1463,45 @@
}
@Override
- public int getLogContainerType() {
- return ContainerType.FOLDER;
+ public void logActionCommand(int command) {
+ mLauncher.getUserEventDispatcher().logActionCommand(
+ command, getFolderIcon(), ContainerType.FOLDER);
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (isEditingName()) {
+ mFolderName.dispatchBackKey();
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ DragLayer dl = mLauncher.getDragLayer();
+
+ if (isEditingName()) {
+ if (!dl.isEventOverView(mFolderName, ev)) {
+ mFolderName.dispatchBackKey();
+ return true;
+ }
+ return false;
+ } else if (!dl.isEventOverView(this, ev)) {
+ if (mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) {
+ // Do not close the container if in drag and drop.
+ if (!dl.isEventOverView(mLauncher.getDropTargetBar(), ev)) {
+ return true;
+ }
+ } else {
+ mLauncher.getUserEventDispatcher().logActionTapOutside(
+ LoggerUtils.newContainerTarget(ContainerType.FOLDER));
+ close(true);
+ return true;
+ }
+ }
+ }
+ return false;
}
}
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 69705d5..ec448e9 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -16,12 +16,16 @@
package com.android.launcher3.folder;
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.GradientDrawable;
@@ -74,19 +78,6 @@
private final PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
- private static final Property<View, Float> SCALE_PROPERTY =
- new Property<View, Float>(Float.class, "scale") {
- @Override
- public Float get(View view) {
- return view.getScaleX();
- }
-
- @Override
- public void set(View view, Float scale) {
- view.setScaleX(scale);
- view.setScaleY(scale);
- }
- };
public FolderAnimationManager(Folder folder, boolean isOpening) {
mFolder = folder;
@@ -101,8 +92,9 @@
mIsOpening = isOpening;
- mDuration = mFolder.mMaterialExpandDuration;
- mDelay = mContext.getResources().getInteger(R.integer.config_folderDelay);
+ Resources res = mContent.getResources();
+ mDuration = res.getInteger(R.integer.config_materialFolderExpandDuration);
+ mDelay = res.getInteger(R.integer.config_folderDelay);
mFolderInterpolator = AnimationUtils.loadInterpolator(mContext,
R.interpolator.folder_interpolator);
@@ -118,7 +110,7 @@
*/
public AnimatorSet getAnimator() {
final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) mFolder.getLayoutParams();
- FolderIcon.PreviewLayoutRule rule = mFolderIcon.getLayoutRule();
+ ClippedFolderIconLayoutRule rule = mFolderIcon.getLayoutRule();
final List<BubbleTextView> itemsInPreview = mFolderIcon.getPreviewItems();
// Match position of the FolderIcon
@@ -129,7 +121,7 @@
float initialSize = (scaledRadius * 2) * scaleRelativeToDragLayer;
// Match size/scale of icons in the preview
- float previewScale = rule.scaleForItem(0, itemsInPreview.size());
+ float previewScale = rule.scaleForItem(itemsInPreview.size());
float previewSize = rule.getIconSize() * previewScale;
float initialScale = previewSize / itemsInPreview.get(0).getIconSize()
* scaleRelativeToDragLayer;
@@ -242,15 +234,14 @@
*/
private void addPreviewItemAnimators(AnimatorSet animatorSet, final float folderScale,
int previewItemOffsetX, int previewItemOffsetY) {
- FolderIcon.PreviewLayoutRule rule = mFolderIcon.getLayoutRule();
+ ClippedFolderIconLayoutRule rule = mFolderIcon.getLayoutRule();
boolean isOnFirstPage = mFolder.mContent.getCurrentPage() == 0;
final List<BubbleTextView> itemsInPreview = isOnFirstPage
? mFolderIcon.getPreviewItems()
: mFolderIcon.getPreviewItemsOnPage(mFolder.mContent.getCurrentPage());
final int numItemsInPreview = itemsInPreview.size();
final int numItemsInFirstPagePreview = isOnFirstPage
- ? numItemsInPreview
- : FolderIcon.NUM_ITEMS_IN_PREVIEW;
+ ? numItemsInPreview : MAX_NUM_ITEMS_IN_PREVIEW;
TimeInterpolator previewItemInterpolator = getPreviewItemInterpolator();
@@ -264,7 +255,7 @@
cwc.setupLp(btv);
// Match scale of icons in the preview of the items on the first page.
- float previewScale = rule.scaleForItem(i, numItemsInFirstPagePreview);
+ float previewScale = rule.scaleForItem(numItemsInFirstPagePreview);
float previewSize = rule.getIconSize() * previewScale;
float iconScale = previewSize / itemsInPreview.get(i).getIconSize();
@@ -299,7 +290,7 @@
scaleAnimator.setInterpolator(previewItemInterpolator);
play(animatorSet, scaleAnimator);
- if (mFolder.getItemCount() > FolderIcon.NUM_ITEMS_IN_PREVIEW) {
+ if (mFolder.getItemCount() > MAX_NUM_ITEMS_IN_PREVIEW) {
// These delays allows the preview items to move as part of the Folder's motion,
// and its only necessary for large folders because of differing interpolators.
int delay = mIsOpening ? mDelay : mDelay * 2;
@@ -349,7 +340,7 @@
}
private TimeInterpolator getPreviewItemInterpolator() {
- if (mFolder.getItemCount() > FolderIcon.NUM_ITEMS_IN_PREVIEW) {
+ if (mFolder.getItemCount() > MAX_NUM_ITEMS_IN_PREVIEW) {
// With larger folders, we want the preview items to reach their final positions faster
// (when opening) and later (when closing) so that they appear aligned with the rest of
// the folder items when they are both visible.
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 8339bc5..5983029 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -16,8 +16,10 @@
package com.android.launcher3.folder;
+import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
+import static com.android.launcher3.folder.PreviewItemManager.INITIAL_ITEM_ANIMATION_DURATION;
+
import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Canvas;
@@ -34,8 +36,6 @@
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
import com.android.launcher3.Alarm;
@@ -49,7 +49,6 @@
import com.android.launcher3.FolderInfo.FolderListener;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.OnAlarmListener;
import com.android.launcher3.R;
@@ -58,9 +57,9 @@
import com.android.launcher3.StylusEventHelper;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.badge.BadgeRenderer;
import com.android.launcher3.badge.FolderBadgeInfo;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.BaseItemDragListener;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragView;
@@ -71,8 +70,6 @@
import java.util.ArrayList;
import java.util.List;
-import static com.android.launcher3.folder.PreviewItemManager.INITIAL_ITEM_ANIMATION_DURATION;
-
/**
* An icon that can appear on in the workspace representing an {@link Folder}.
*/
@@ -82,10 +79,6 @@
private FolderInfo mInfo;
@Thunk static boolean sStaticValuesDirty = true;
- public static final int NUM_ITEMS_IN_PREVIEW = FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON ?
- StackFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW :
- ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
-
private CheckLongPressHelper mLongPressHelper;
private StylusEventHelper mStylusEventHelper;
@@ -103,9 +96,10 @@
private boolean mBackgroundIsVisible = true;
FolderIconPreviewVerifier mPreviewVerifier;
- PreviewLayoutRule mPreviewLayoutRule;
+ ClippedFolderIconLayoutRule mPreviewLayoutRule;
private PreviewItemManager mPreviewItemManager;
private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
+ private List<BubbleTextView> mCurrentPreviewItems = new ArrayList<>();
boolean mAnimating = false;
private Rect mTempBounds = new Rect();
@@ -146,9 +140,7 @@
private void init() {
mLongPressHelper = new CheckLongPressHelper(this);
mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
- mPreviewLayoutRule = FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON ?
- new StackFolderIconLayoutRule() :
- new ClippedFolderIconLayoutRule();
+ mPreviewLayoutRule = new ClippedFolderIconLayoutRule();
mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
mPreviewItemManager = new PreviewItemManager(this);
}
@@ -206,7 +198,7 @@
private void setFolder(Folder folder) {
mFolder = folder;
mPreviewVerifier = new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv);
- mPreviewItemManager.updateItemDrawingParams(false);
+ updatePreviewItems(false);
}
private boolean willAcceptItem(ItemInfo item) {
@@ -261,7 +253,7 @@
public void performCreateAnimation(final ShortcutInfo destInfo, final View destView,
final ShortcutInfo srcInfo, final DragView srcView, Rect dstRect,
- float scaleRelativeToDragLayer, Runnable postAnimationRunnable) {
+ float scaleRelativeToDragLayer) {
prepareCreateAnimation(destView);
addItem(destInfo);
// This will animate the first item from it's position as an icon into its
@@ -270,7 +262,8 @@
.start();
// This will animate the dragView (srcView) into the new folder
- onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable);
+ onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1,
+ false /* itemReturnedOnFailedDrop */);
}
public void performDestroyAnimation(Runnable onCompleteRunnable) {
@@ -285,7 +278,8 @@
}
private void onDrop(final ShortcutInfo item, DragView animateView, Rect finalRect,
- float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable) {
+ float scaleRelativeToDragLayer, int index,
+ boolean itemReturnedOnFailedDrop) {
item.cellX = -1;
item.cellY = -1;
@@ -301,7 +295,7 @@
to = new Rect();
Workspace workspace = mLauncher.getWorkspace();
// Set cellLayout and this to it's final state to compute final animation locations
- workspace.setFinalTransitionTransform((CellLayout) getParent().getParent());
+ workspace.setFinalTransitionTransform();
float scaleX = getScaleX();
float scaleY = getScaleY();
setScaleX(1.0f);
@@ -310,25 +304,28 @@
// Finished computing final animation locations, restore current state
setScaleX(scaleX);
setScaleY(scaleY);
- workspace.resetTransitionTransform((CellLayout) getParent().getParent());
+ workspace.resetTransitionTransform();
}
+ int numItemsInPreview = Math.min(MAX_NUM_ITEMS_IN_PREVIEW, index + 1);
boolean itemAdded = false;
- if (index >= mPreviewLayoutRule.maxNumItems()
- && mPreviewLayoutRule.hasEnterExitIndices()) {
- List<BubbleTextView> oldPreviewItems = getPreviewItemsOnPage(0);
+ if (itemReturnedOnFailedDrop || index >= MAX_NUM_ITEMS_IN_PREVIEW) {
+ List<BubbleTextView> oldPreviewItems = new ArrayList<>(mCurrentPreviewItems);
addItem(item, false);
- List<BubbleTextView> newPreviewItems = getPreviewItemsOnPage(0);
+ mCurrentPreviewItems.clear();
+ mCurrentPreviewItems.addAll(getPreviewItems());
- if (!oldPreviewItems.containsAll(newPreviewItems)) {
- for (int i = 0; i < newPreviewItems.size(); ++i) {
- if (newPreviewItems.get(i).getTag().equals(item)) {
+ if (!oldPreviewItems.equals(mCurrentPreviewItems)) {
+ for (int i = 0; i < mCurrentPreviewItems.size(); ++i) {
+ if (mCurrentPreviewItems.get(i).getTag().equals(item)) {
// If the item dropped is going to be in the preview, we update the
// index here to reflect its position in the preview.
index = i;
}
}
- mPreviewItemManager.onDrop(oldPreviewItems, newPreviewItems, item);
+
+ mPreviewItemManager.hidePreviewItem(index, true);
+ mPreviewItemManager.onDrop(oldPreviewItems, mCurrentPreviewItems, item);
itemAdded = true;
} else {
removeItem(item, false);
@@ -340,20 +337,20 @@
}
int[] center = new int[2];
- float scale = getLocalCenterForIndex(index, index + 1, center);
+ float scale = getLocalCenterForIndex(index, numItemsInPreview, center);
center[0] = (int) Math.round(scaleRelativeToDragLayer * center[0]);
center[1] = (int) Math.round(scaleRelativeToDragLayer * center[1]);
to.offset(center[0] - animateView.getMeasuredWidth() / 2,
center[1] - animateView.getMeasuredHeight() / 2);
- float finalAlpha = index < mPreviewLayoutRule.maxNumItems() ? 0.5f : 0f;
+ float finalAlpha = index < MAX_NUM_ITEMS_IN_PREVIEW ? 0.5f : 0f;
float finalScale = scale * scaleRelativeToDragLayer;
dragLayer.animateView(animateView, from, to, finalAlpha,
1, 1, finalScale, finalScale, DROP_IN_ANIMATION_DURATION,
- new DecelerateInterpolator(2), new AccelerateInterpolator(2),
- postAnimationRunnable, DragLayer.ANIMATION_END_DISAPPEAR, null);
+ Interpolators.DEACCEL_2, Interpolators.ACCEL_2,
+ null, DragLayer.ANIMATION_END_DISAPPEAR, null);
mFolder.hideItem(item);
@@ -371,7 +368,7 @@
}
}
- public void onDrop(DragObject d) {
+ public void onDrop(DragObject d, boolean itemReturnedOnFailedDrop) {
ShortcutInfo item;
if (d.dragInfo instanceof AppInfo) {
// Came from all apps -- make a copy
@@ -383,7 +380,8 @@
item = (ShortcutInfo) d.dragInfo;
}
mFolder.notifyDrop();
- onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable);
+ onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(),
+ itemReturnedOnFailedDrop);
}
public void setBadgeInfo(FolderBadgeInfo badgeInfo) {
@@ -391,7 +389,7 @@
mBadgeInfo = badgeInfo;
}
- public PreviewLayoutRule getLayoutRule() {
+ public ClippedFolderIconLayoutRule getLayoutRule() {
return mPreviewLayoutRule;
}
@@ -420,7 +418,7 @@
private float getLocalCenterForIndex(int index, int curNumItems, int[] center) {
mTmpParams = mPreviewItemManager.computePreviewItemDrawingParams(
- Math.min(mPreviewLayoutRule.maxNumItems(), index), curNumItems, mTmpParams);
+ Math.min(MAX_NUM_ITEMS_IN_PREVIEW, index), curNumItems, mTmpParams);
mTmpParams.transX += mBackground.basePreviewOffsetX;
mTmpParams.transY += mBackground.basePreviewOffsetY;
@@ -474,19 +472,17 @@
Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
} else {
saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
- if (mPreviewLayoutRule.clipToBackground()) {
- canvas.clipPath(mBackground.getClipPath(), Region.Op.INTERSECT);
- }
+ canvas.clipPath(mBackground.getClipPath(), Region.Op.INTERSECT);
}
mPreviewItemManager.draw(canvas);
- if (mPreviewLayoutRule.clipToBackground() && canvas.isHardwareAccelerated()) {
+ if (canvas.isHardwareAccelerated()) {
mBackground.clipCanvasHardware(canvas);
}
canvas.restoreToCount(saveCount);
- if (mPreviewLayoutRule.clipToBackground() && !mBackground.drawingDelegated()) {
+ if (!mBackground.drawingDelegated()) {
mBackground.drawBackgroundStroke(canvas);
}
@@ -542,7 +538,7 @@
itemsToDisplay.add(itemsOnPage.get(rank));
}
- if (itemsToDisplay.size() == FolderIcon.NUM_ITEMS_IN_PREVIEW) {
+ if (itemsToDisplay.size() == MAX_NUM_ITEMS_IN_PREVIEW) {
break;
}
}
@@ -556,11 +552,17 @@
@Override
public void onItemsChanged(boolean animate) {
- mPreviewItemManager.updateItemDrawingParams(animate);
+ updatePreviewItems(animate);
invalidate();
requestLayout();
}
+ private void updatePreviewItems(boolean animate) {
+ mPreviewItemManager.updatePreviewItems(animate);
+ mCurrentPreviewItems.clear();
+ mCurrentPreviewItems.addAll(getPreviewItems());
+ }
+
@Override
public void prepareAutoUpdate() {
}
@@ -631,30 +633,6 @@
mInfo.removeListener(mFolder);
}
- public void shrinkAndFadeIn(boolean animate) {
- // We remove and re-draw the FolderIcon in-case it has changed
- final PreviewImageView previewImage = PreviewImageView.get(getContext());
- previewImage.removeFromParent();
- copyToPreview(previewImage);
-
- clearLeaveBehindIfExists();
-
- ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(previewImage, 1, 1, 1);
- oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
- oa.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- // Remove the ImageView copy of the FolderIcon and make the original visible.
- previewImage.removeFromParent();
- setVisibility(View.VISIBLE);
- }
- });
- oa.start();
- if (!animate) {
- oa.end();
- }
- }
-
public void clearLeaveBehindIfExists() {
((CellLayout.LayoutParams) getLayoutParams()).canReorder = true;
if (mInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
@@ -673,48 +651,7 @@
}
}
- public void growAndFadeOut() {
- drawLeaveBehindIfExists();
-
- // Push an ImageView copy of the FolderIcon into the DragLayer and hide the original
- PreviewImageView previewImage = PreviewImageView.get(getContext());
- copyToPreview(previewImage);
- setVisibility(View.INVISIBLE);
-
- ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(previewImage, 0, 1.5f, 1.5f);
- oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
- oa.start();
- }
-
- /**
- * This method draws the FolderIcon to an ImageView and then adds and positions that ImageView
- * in the DragLayer in the exact absolute location of the original FolderIcon.
- */
- private void copyToPreview(PreviewImageView previewImageView) {
- previewImageView.copy(this);
- if (mFolder != null) {
- previewImageView.setPivotX(mFolder.getPivotXForIconAnimation());
- previewImageView.setPivotY(mFolder.getPivotYForIconAnimation());
- mFolder.bringToFront();
- }
- }
-
public void onFolderClose(int currentPage) {
mPreviewItemManager.onFolderClose(currentPage);
}
-
- interface PreviewLayoutRule {
- PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
- PreviewItemDrawingParams params);
- void init(int availableSpace, float intrinsicIconSize, boolean rtl);
- float scaleForItem(int index, int totalNumItems);
- float getIconSize();
- int maxNumItems();
- boolean clipToBackground();
-
- boolean hasEnterExitIndices();
- int getExitIndex();
- int getEnterIndex();
-
- }
}
diff --git a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
index d054a5d..5a27cd4 100644
--- a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
+++ b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
@@ -18,7 +18,8 @@
import com.android.launcher3.FolderInfo;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.config.FeatureFlags;
+
+import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
/**
* Verifies whether an item in a Folder is displayed in the FolderIcon preview.
@@ -45,9 +46,7 @@
mMaxGridCountY, mMaxItemsPerPage, mGridSize);
mGridCountX = mGridSize[0];
- mDisplayingUpperLeftQuadrant = FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION
- && !FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON
- && numItemsInFolder > FolderIcon.NUM_ITEMS_IN_PREVIEW;
+ mDisplayingUpperLeftQuadrant = numItemsInFolder > MAX_NUM_ITEMS_IN_PREVIEW;
}
/**
@@ -70,6 +69,6 @@
int row = rank / mGridCountX;
return col < 2 && row < 2;
}
- return rank < FolderIcon.NUM_ITEMS_IN_PREVIEW;
+ return rank < MAX_NUM_ITEMS_IN_PREVIEW;
}
}
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index f4ac0a1..9e5bc4f 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -27,7 +27,6 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewDebug;
-import android.view.animation.DecelerateInterpolator;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
@@ -43,6 +42,7 @@
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace.ItemOperator;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.pageindicators.PageIndicator;
import com.android.launcher3.util.Thunk;
@@ -511,7 +511,7 @@
int scroll = getScrollForPage(getNextPage()) + hint;
int delta = scroll - getScrollX();
if (delta != 0) {
- mScroller.setInterpolator(new DecelerateInterpolator());
+ mScroller.setInterpolator(Interpolators.DEACCEL);
mScroller.startScroll(getScrollX(), 0, delta, 0, Folder.SCROLL_HINT_DURATION);
invalidate();
}
diff --git a/src/com/android/launcher3/folder/PreviewImageView.java b/src/com/android/launcher3/folder/PreviewImageView.java
deleted file mode 100644
index 65d9db1..0000000
--- a/src/com/android/launcher3/folder/PreviewImageView.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2016 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.folder;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.view.View;
-import android.widget.ImageView;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-import com.android.launcher3.dragndrop.DragLayer;
-
-/**
- * A temporary view which displays the a bitmap (used for folder icon animation)
- */
-public class PreviewImageView extends ImageView {
-
- private final Rect mTempRect = new Rect();
- private final DragLayer mParent;
-
- private Bitmap mBitmap;
- private Canvas mCanvas;
-
- public PreviewImageView(DragLayer parent) {
- super(parent.getContext());
- mParent = parent;
- }
-
- public void copy(View view) {
- final int width = view.getMeasuredWidth();
- final int height = view.getMeasuredHeight();
-
- if (mBitmap == null || mBitmap.getWidth() != width || mBitmap.getHeight() != height) {
- mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- mCanvas = new Canvas(mBitmap);
- }
-
- DragLayer.LayoutParams lp;
- if (getLayoutParams() instanceof DragLayer.LayoutParams) {
- lp = (DragLayer.LayoutParams) getLayoutParams();
- } else {
- lp = new DragLayer.LayoutParams(width, height);
- }
-
- // The layout from which the folder is being opened may be scaled, adjust the starting
- // view size by this scale factor.
- float scale = mParent.getDescendantRectRelativeToSelf(view, mTempRect);
- lp.customPosition = true;
- lp.x = mTempRect.left;
- lp.y = mTempRect.top;
- lp.width = (int) (scale * width);
- lp.height = (int) (scale * height);
-
- mCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
- view.draw(mCanvas);
- setImageBitmap(mBitmap);
-
- // Just in case this image view is still in the drag layer from a previous animation,
- // we remove it and re-add it.
- removeFromParent();
- mParent.addView(this, lp);
- }
-
- public void removeFromParent() {
- if (mParent.indexOfChild(this) != -1) {
- mParent.removeView(this);
- }
- }
-
- public static PreviewImageView get(Context context) {
- DragLayer dragLayer = Launcher.getLauncher(context).getDragLayer();
- PreviewImageView view = (PreviewImageView) dragLayer.getTag(R.id.preview_image_id);
- if (view == null) {
- view = new PreviewImageView(dragLayer);
- dragLayer.setTag(R.id.preview_image_id, view);
- }
- return view;
- }
-}
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index 2d979a6..06d3eb1 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -29,11 +29,13 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
import java.util.ArrayList;
import java.util.List;
+import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ENTER_INDEX;
+import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.EXIT_INDEX;
+import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
import static com.android.launcher3.folder.FolderIcon.DROP_IN_ANIMATION_DURATION;
/**
@@ -108,7 +110,7 @@
mIcon.mPreviewLayoutRule.init(mIcon.mBackground.previewSize, mIntrinsicIconSize,
Utilities.isRtl(mIcon.getResources()));
- updateItemDrawingParams(false);
+ updatePreviewItems(false);
}
}
@@ -183,6 +185,11 @@
}
public void hidePreviewItem(int index, boolean hidden) {
+ // If there are more params than visible in the preview, they are used for enter/exit
+ // animation purposes and they were added to the front of the list.
+ // To index the params properly, we need to skip these params.
+ index = index + Math.max(mFirstPageParams.size() - MAX_NUM_ITEMS_IN_PREVIEW, 0);
+
PreviewItemDrawingParams params = index < mFirstPageParams.size() ?
mFirstPageParams.get(index) : null;
if (params != null) {
@@ -202,7 +209,7 @@
params.add(new PreviewItemDrawingParams(0, 0, 0, 0));
}
- int numItemsInFirstPagePreview = page == 0 ? items.size() : FolderIcon.NUM_ITEMS_IN_PREVIEW;
+ int numItemsInFirstPagePreview = page == 0 ? items.size() : MAX_NUM_ITEMS_IN_PREVIEW;
for (int i = 0; i < params.size(); i++) {
PreviewItemDrawingParams p = params.get(i);
p.drawable = items.get(i).getCompoundDrawables()[1];
@@ -213,7 +220,7 @@
p.drawable.setCallback(mIcon);
}
- if (!animate || FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON) {
+ if (!animate) {
computePreviewItemDrawingParams(i, numItemsInFirstPagePreview, p);
if (mReferenceDrawable == null) {
mReferenceDrawable = p.drawable;
@@ -264,7 +271,7 @@
}
}
- void updateItemDrawingParams(boolean animate) {
+ void updatePreviewItems(boolean animate) {
buildParamsForPage(0, mFirstPageParams, animate);
}
@@ -308,8 +315,8 @@
int prevIndex = newParams.indexOf(moveIn.get(i));
PreviewItemDrawingParams p = params.get(prevIndex);
computePreviewItemDrawingParams(prevIndex, numItems, p);
- updateTransitionParam(p, moveIn.get(i), mIcon.mPreviewLayoutRule.getEnterIndex(),
- newParams.indexOf(moveIn.get(i)));
+ updateTransitionParam(p, moveIn.get(i), ENTER_INDEX, newParams.indexOf(moveIn.get(i)),
+ numItems);
}
// Items that are moving into new positions within the preview.
@@ -317,7 +324,7 @@
int oldIndex = oldParams.indexOf(newParams.get(newIndex));
if (oldIndex >= 0 && newIndex != oldIndex) {
PreviewItemDrawingParams p = params.get(newIndex);
- updateTransitionParam(p, newParams.get(newIndex), oldIndex, newIndex);
+ updateTransitionParam(p, newParams.get(newIndex), oldIndex, newIndex, numItems);
}
}
@@ -328,7 +335,7 @@
BubbleTextView item = moveOut.get(i);
int oldIndex = oldParams.indexOf(item);
PreviewItemDrawingParams p = computePreviewItemDrawingParams(oldIndex, numItems, null);
- updateTransitionParam(p, item, oldIndex, mIcon.mPreviewLayoutRule.getExitIndex());
+ updateTransitionParam(p, item, oldIndex, EXIT_INDEX, numItems);
params.add(0, p); // We want these items first so that they are on drawn last.
}
@@ -340,7 +347,7 @@
}
private void updateTransitionParam(final PreviewItemDrawingParams p, BubbleTextView btv,
- int prevIndex, int newIndex) {
+ int prevIndex, int newIndex, int numItems) {
p.drawable = btv.getCompoundDrawables()[1];
if (!mIcon.mFolder.isOpen()) {
// Set the callback to FolderIcon as it is responsible to drawing the icon. The
@@ -348,9 +355,8 @@
p.drawable.setCallback(mIcon);
}
- FolderPreviewItemAnim anim = new FolderPreviewItemAnim(this, p, prevIndex,
- FolderIcon.NUM_ITEMS_IN_PREVIEW, newIndex, FolderIcon.NUM_ITEMS_IN_PREVIEW,
- DROP_IN_ANIMATION_DURATION, null);
+ FolderPreviewItemAnim anim = new FolderPreviewItemAnim(this, p, prevIndex, numItems,
+ newIndex, numItems, DROP_IN_ANIMATION_DURATION, null);
if (p.anim != null && !p.anim.hasEqualFinalState(anim)) {
p.anim.cancel();
}
diff --git a/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java b/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java
deleted file mode 100644
index 7d10556..0000000
--- a/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/**
- * Copyright (C) 2015 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.folder;
-
-public class StackFolderIconLayoutRule implements FolderIcon.PreviewLayoutRule {
-
- static final int MAX_NUM_ITEMS_IN_PREVIEW = 3;
-
- // The degree to which the item in the back of the stack is scaled [0...1]
- // (0 means it's not scaled at all, 1 means it's scaled to nothing)
- private static final float PERSPECTIVE_SCALE_FACTOR = 0.35f;
-
- // The amount of vertical spread between items in the stack [0...1]
- private static final float PERSPECTIVE_SHIFT_FACTOR = 0.18f;
-
- private float mBaselineIconScale;
- private int mBaselineIconSize;
- private int mAvailableSpaceInPreview;
- private float mMaxPerspectiveShift;
-
- @Override
- public void init(int availableSpace, float intrinsicIconSize, boolean rtl) {
- mAvailableSpaceInPreview = availableSpace;
-
- // cos(45) = 0.707 + ~= 0.1) = 0.8f
- int adjustedAvailableSpace = (int) ((mAvailableSpaceInPreview / 2) * (1 + 0.8f));
-
- int unscaledHeight = (int) (intrinsicIconSize * (1 + PERSPECTIVE_SHIFT_FACTOR));
-
- mBaselineIconScale = (1.0f * adjustedAvailableSpace / unscaledHeight);
-
- mBaselineIconSize = (int) (intrinsicIconSize * mBaselineIconScale);
- mMaxPerspectiveShift = mBaselineIconSize * PERSPECTIVE_SHIFT_FACTOR;
- }
-
- @Override
- public PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
- PreviewItemDrawingParams params) {
- float scale = scaleForItem(index, curNumItems);
-
- index = MAX_NUM_ITEMS_IN_PREVIEW - index - 1;
- float r = (index * 1.0f) / (MAX_NUM_ITEMS_IN_PREVIEW - 1);
-
- float offset = (1 - r) * mMaxPerspectiveShift;
- float scaledSize = scale * mBaselineIconSize;
- float scaleOffsetCorrection = (1 - scale) * mBaselineIconSize;
-
- // We want to imagine our coordinates from the bottom left, growing up and to the
- // right. This is natural for the x-axis, but for the y-axis, we have to invert things.
- float transY = mAvailableSpaceInPreview - (offset + scaledSize + scaleOffsetCorrection);
- float transX = (mAvailableSpaceInPreview - scaledSize) / 2;
- float totalScale = mBaselineIconScale * scale;
- final float overlayAlpha = (80 * (1 - r)) / 255f;
-
- if (params == null) {
- params = new PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha);
- } else {
- params.update(transX, transY, totalScale);
- params.overlayAlpha = overlayAlpha;
- }
- return params;
- }
-
- @Override
- public int maxNumItems() {
- return MAX_NUM_ITEMS_IN_PREVIEW;
- }
-
- @Override
- public float getIconSize() {
- return mBaselineIconSize;
- }
-
- @Override
- public float scaleForItem(int index, int numItems) {
- // Scale is determined by the position of the icon in the preview.
- index = MAX_NUM_ITEMS_IN_PREVIEW - index - 1;
- float r = (index * 1.0f) / (MAX_NUM_ITEMS_IN_PREVIEW - 1);
- return (1 - PERSPECTIVE_SCALE_FACTOR * (1 - r));
- }
-
- @Override
- public boolean clipToBackground() {
- return false;
- }
-
- @Override
- public boolean hasEnterExitIndices() {
- return false;
- }
-
- @Override
- public int getExitIndex() {
- throw new RuntimeException("hasEnterExitIndices not supported");
- }
-
- @Override
- public int getEnterIndex() {
- throw new RuntimeException("hasEnterExitIndices not supported");
- }
-}
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index 10e91c0..355c231 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -18,10 +18,15 @@
import android.content.Context;
import android.graphics.Bitmap;
+import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.Region.Op;
import android.graphics.drawable.Drawable;
+import android.os.Handler;
import android.view.View;
import com.android.launcher3.BubbleTextView;
@@ -30,6 +35,9 @@
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.util.UiThreadHelper;
+
+import java.nio.ByteBuffer;
/**
* A utility class to generate preview bitmap for dragging.
@@ -45,6 +53,7 @@
protected final int blurSizeOutline;
+ private OutlineGeneratorCallback mOutlineGeneratorCallback;
public Bitmap generatedDragOutline;
public DragPreviewProvider(View view) {
@@ -106,7 +115,7 @@
* Returns a new bitmap to show when the {@link #mView} is being dragged around.
* Responsibility for the bitmap is transferred to the caller.
*/
- public Bitmap createDragBitmap(Canvas canvas) {
+ public Bitmap createDragBitmap() {
float scale = 1f;
int width = mView.getWidth();
int height = mView.getHeight();
@@ -124,7 +133,7 @@
Bitmap b = Bitmap.createBitmap(width + blurSizeOutline, height + blurSizeOutline,
Bitmap.Config.ARGB_8888);
- canvas.setBitmap(b);
+ Canvas canvas = new Canvas(b);
canvas.save();
canvas.scale(scale, scale);
@@ -136,43 +145,13 @@
return b;
}
- public final void generateDragOutline(Canvas canvas) {
- if (FeatureFlags.IS_DOGFOOD_BUILD && generatedDragOutline != null) {
+ public final void generateDragOutline(Bitmap preview) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD && mOutlineGeneratorCallback != null) {
throw new RuntimeException("Drag outline generated twice");
}
- generatedDragOutline = createDragOutline(canvas);
- }
-
- /**
- * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
- * Responsibility for the bitmap is transferred to the caller.
- */
- public Bitmap createDragOutline(Canvas canvas) {
- float scale = 1f;
- int width = mView.getWidth();
- int height = mView.getHeight();
-
- if (mView instanceof LauncherAppWidgetHostView) {
- scale = ((LauncherAppWidgetHostView) mView).getScaleToFit();
- width = (int) Math.floor(mView.getWidth() * scale);
- height = (int) Math.floor(mView.getHeight() * scale);
- }
-
- Bitmap b = Bitmap.createBitmap(width + blurSizeOutline, height + blurSizeOutline,
- Bitmap.Config.ALPHA_8);
- canvas.setBitmap(b);
-
- canvas.save();
- canvas.scale(scale, scale);
- drawDragView(canvas);
- canvas.restore();
-
- HolographicOutlineHelper.getInstance(mView.getContext())
- .applyExpensiveOutlineWithBlur(b, canvas);
-
- canvas.setBitmap(null);
- return b;
+ mOutlineGeneratorCallback = new OutlineGeneratorCallback(preview);
+ new Handler(UiThreadHelper.getBackgroundLooper()).post(mOutlineGeneratorCallback);
}
protected static Rect getDrawableBounds(Drawable d) {
@@ -201,4 +180,89 @@
- previewPadding / 2);
return scale;
}
+
+ protected Bitmap convertPreviewToAlphaBitmap(Bitmap preview) {
+ return preview.copy(Bitmap.Config.ALPHA_8, true);
+ }
+
+ private class OutlineGeneratorCallback implements Runnable {
+
+ private final Bitmap mPreviewSnapshot;
+ private final Context mContext;
+
+ OutlineGeneratorCallback(Bitmap preview) {
+ mPreviewSnapshot = preview;
+ mContext = mView.getContext();
+ }
+
+ @Override
+ public void run() {
+ Bitmap preview = convertPreviewToAlphaBitmap(mPreviewSnapshot);
+
+ // We start by removing most of the alpha channel so as to ignore shadows, and
+ // other types of partial transparency when defining the shape of the object
+ byte[] pixels = new byte[preview.getWidth() * preview.getHeight()];
+ ByteBuffer buffer = ByteBuffer.wrap(pixels);
+ buffer.rewind();
+ preview.copyPixelsToBuffer(buffer);
+
+ for (int i = 0; i < pixels.length; i++) {
+ if ((pixels[i] & 0xFF) < 188) {
+ pixels[i] = 0;
+ }
+ }
+
+ buffer.rewind();
+ preview.copyPixelsFromBuffer(buffer);
+
+ final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+ Canvas canvas = new Canvas();
+
+ // calculate the outer blur first
+ paint.setMaskFilter(new BlurMaskFilter(blurSizeOutline, BlurMaskFilter.Blur.OUTER));
+ int[] outerBlurOffset = new int[2];
+ Bitmap thickOuterBlur = preview.extractAlpha(paint, outerBlurOffset);
+
+ paint.setMaskFilter(new BlurMaskFilter(
+ mContext.getResources().getDimension(R.dimen.blur_size_thin_outline),
+ BlurMaskFilter.Blur.OUTER));
+ int[] brightOutlineOffset = new int[2];
+ Bitmap brightOutline = preview.extractAlpha(paint, brightOutlineOffset);
+
+ // calculate the inner blur
+ canvas.setBitmap(preview);
+ canvas.drawColor(0xFF000000, PorterDuff.Mode.SRC_OUT);
+ paint.setMaskFilter(new BlurMaskFilter(blurSizeOutline, BlurMaskFilter.Blur.NORMAL));
+ int[] thickInnerBlurOffset = new int[2];
+ Bitmap thickInnerBlur = preview.extractAlpha(paint, thickInnerBlurOffset);
+
+ // mask out the inner blur
+ paint.setMaskFilter(null);
+ paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
+ canvas.setBitmap(thickInnerBlur);
+ canvas.drawBitmap(preview, -thickInnerBlurOffset[0],
+ -thickInnerBlurOffset[1], paint);
+ canvas.drawRect(0, 0, -thickInnerBlurOffset[0], thickInnerBlur.getHeight(), paint);
+ canvas.drawRect(0, 0, thickInnerBlur.getWidth(), -thickInnerBlurOffset[1], paint);
+
+ // draw the inner and outer blur
+ paint.setXfermode(null);
+ canvas.setBitmap(preview);
+ canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+ canvas.drawBitmap(thickInnerBlur, thickInnerBlurOffset[0], thickInnerBlurOffset[1],
+ paint);
+ canvas.drawBitmap(thickOuterBlur, outerBlurOffset[0], outerBlurOffset[1], paint);
+
+ // draw the bright outline
+ canvas.drawBitmap(brightOutline, brightOutlineOffset[0], brightOutlineOffset[1], paint);
+
+ // cleanup
+ canvas.setBitmap(null);
+ brightOutline.recycle();
+ thickOuterBlur.recycle();
+ thickInnerBlur.recycle();
+
+ generatedDragOutline = preview;
+ }
+ }
}
diff --git a/src/com/android/launcher3/graphics/GradientView.java b/src/com/android/launcher3/graphics/GradientView.java
index 5455b43..bacb063 100644
--- a/src/com/android/launcher3/graphics/GradientView.java
+++ b/src/com/android/launcher3/graphics/GradientView.java
@@ -29,12 +29,12 @@
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
-import android.view.animation.AccelerateInterpolator;
import android.view.animation.Interpolator;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.dynamicui.WallpaperColorInfo;
import com.android.launcher3.util.Themes;
@@ -64,7 +64,7 @@
private final int mMaskHeight, mMaskWidth;
private final int mAlphaColors;
private final Paint mDebugPaint = DEBUG ? new Paint() : null;
- private final Interpolator mAccelerator = new AccelerateInterpolator();
+ private final Interpolator mAccelerator = Interpolators.ACCEL;
private final float mAlphaStart;
private final WallpaperColorInfo mWallpaperColorInfo;
private final int mScrimColor;
diff --git a/src/com/android/launcher3/graphics/HolographicOutlineHelper.java b/src/com/android/launcher3/graphics/HolographicOutlineHelper.java
index b221828..9e67f56 100644
--- a/src/com/android/launcher3/graphics/HolographicOutlineHelper.java
+++ b/src/com/android/launcher3/graphics/HolographicOutlineHelper.java
@@ -17,7 +17,6 @@
package com.android.launcher3.graphics;
import android.content.Context;
-import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
@@ -31,9 +30,6 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.R;
-import com.android.launcher3.config.FeatureFlags;
-
-import java.nio.ByteBuffer;
/**
* Utility class to generate shadow and outline effect, which are used for click feedback
@@ -44,14 +40,9 @@
private static HolographicOutlineHelper sInstance;
private final Canvas mCanvas = new Canvas();
- private final Paint mDrawPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
private final Paint mBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
private final Paint mErasePaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
- private final BlurMaskFilter mMediumOuterBlurMaskFilter;
- private final BlurMaskFilter mThinOuterBlurMaskFilter;
- private final BlurMaskFilter mMediumInnerBlurMaskFilter;
-
private final float mShadowBitmapShift;
private final BlurMaskFilter mShadowBlurMaskFilter;
@@ -59,18 +50,8 @@
private final SparseArray<Bitmap> mBitmapCache = new SparseArray<>(4);
private HolographicOutlineHelper(Context context) {
- Resources res = context.getResources();
-
- float mediumBlur = res.getDimension(R.dimen.blur_size_medium_outline);
- mMediumOuterBlurMaskFilter = new BlurMaskFilter(mediumBlur, BlurMaskFilter.Blur.OUTER);
- mMediumInnerBlurMaskFilter = new BlurMaskFilter(mediumBlur, BlurMaskFilter.Blur.NORMAL);
-
- mThinOuterBlurMaskFilter = new BlurMaskFilter(
- res.getDimension(R.dimen.blur_size_thin_outline), BlurMaskFilter.Blur.OUTER);
-
- mShadowBitmapShift = res.getDimension(R.dimen.blur_size_click_shadow);
+ mShadowBitmapShift = context.getResources().getDimension(R.dimen.blur_size_click_shadow);
mShadowBlurMaskFilter = new BlurMaskFilter(mShadowBitmapShift, BlurMaskFilter.Blur.NORMAL);
-
mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
}
@@ -81,75 +62,6 @@
return sInstance;
}
- /**
- * Applies a more expensive and accurate outline to whatever is currently drawn in a specified
- * bitmap.
- */
- public void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas) {
- if (FeatureFlags.IS_DOGFOOD_BUILD && srcDst.getConfig() != Bitmap.Config.ALPHA_8) {
- throw new RuntimeException("Outline blue is only supported on alpha bitmaps");
- }
-
- // We start by removing most of the alpha channel so as to ignore shadows, and
- // other types of partial transparency when defining the shape of the object
- byte[] pixels = new byte[srcDst.getWidth() * srcDst.getHeight()];
- ByteBuffer buffer = ByteBuffer.wrap(pixels);
- buffer.rewind();
- srcDst.copyPixelsToBuffer(buffer);
-
- for (int i = 0; i < pixels.length; i++) {
- if ((pixels[i] & 0xFF) < 188) {
- pixels[i] = 0;
- }
- }
-
- buffer.rewind();
- srcDst.copyPixelsFromBuffer(buffer);
-
- // calculate the outer blur first
- mBlurPaint.setMaskFilter(mMediumOuterBlurMaskFilter);
- int[] outerBlurOffset = new int[2];
- Bitmap thickOuterBlur = srcDst.extractAlpha(mBlurPaint, outerBlurOffset);
-
- mBlurPaint.setMaskFilter(mThinOuterBlurMaskFilter);
- int[] brightOutlineOffset = new int[2];
- Bitmap brightOutline = srcDst.extractAlpha(mBlurPaint, brightOutlineOffset);
-
- // calculate the inner blur
- srcDstCanvas.setBitmap(srcDst);
- srcDstCanvas.drawColor(0xFF000000, PorterDuff.Mode.SRC_OUT);
- mBlurPaint.setMaskFilter(mMediumInnerBlurMaskFilter);
- int[] thickInnerBlurOffset = new int[2];
- Bitmap thickInnerBlur = srcDst.extractAlpha(mBlurPaint, thickInnerBlurOffset);
-
- // mask out the inner blur
- srcDstCanvas.setBitmap(thickInnerBlur);
- srcDstCanvas.drawBitmap(srcDst, -thickInnerBlurOffset[0],
- -thickInnerBlurOffset[1], mErasePaint);
- srcDstCanvas.drawRect(0, 0, -thickInnerBlurOffset[0], thickInnerBlur.getHeight(),
- mErasePaint);
- srcDstCanvas.drawRect(0, 0, thickInnerBlur.getWidth(), -thickInnerBlurOffset[1],
- mErasePaint);
-
- // draw the inner and outer blur
- srcDstCanvas.setBitmap(srcDst);
- srcDstCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
- srcDstCanvas.drawBitmap(thickInnerBlur, thickInnerBlurOffset[0], thickInnerBlurOffset[1],
- mDrawPaint);
- srcDstCanvas.drawBitmap(thickOuterBlur, outerBlurOffset[0], outerBlurOffset[1],
- mDrawPaint);
-
- // draw the bright outline
- srcDstCanvas.drawBitmap(brightOutline, brightOutlineOffset[0], brightOutlineOffset[1],
- mDrawPaint);
-
- // cleanup
- srcDstCanvas.setBitmap(null);
- brightOutline.recycle();
- thickOuterBlur.recycle();
- thickInnerBlur.recycle();
- }
-
public Bitmap createMediumDropShadow(BubbleTextView view) {
Drawable drawable = view.getIcon();
if (drawable == null) {
diff --git a/src/com/android/launcher3/graphics/IconNormalizer.java b/src/com/android/launcher3/graphics/IconNormalizer.java
index 28fc423..5ee6a30 100644
--- a/src/com/android/launcher3/graphics/IconNormalizer.java
+++ b/src/com/android/launcher3/graphics/IconNormalizer.java
@@ -28,12 +28,15 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.Utilities;
+import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
+
import java.io.File;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
@@ -231,12 +234,17 @@
*/
public synchronized float getScale(@NonNull Drawable d, @Nullable RectF outBounds,
@Nullable Path path, @Nullable boolean[] outMaskShape) {
- if (Utilities.ATLEAST_OREO && d instanceof AdaptiveIconDrawable &&
- mAdaptiveIconScale != SCALE_NOT_INITIALIZED) {
- if (outBounds != null) {
- outBounds.set(mAdaptiveIconBounds);
+ if (Utilities.ATLEAST_OREO && d instanceof AdaptiveIconDrawable) {
+ if (mAdaptiveIconScale != SCALE_NOT_INITIALIZED) {
+ if (outBounds != null) {
+ outBounds.set(mAdaptiveIconBounds);
+ }
+ return mAdaptiveIconScale;
}
- return mAdaptiveIconScale;
+ if (d instanceof FolderAdaptiveIcon) {
+ // Since we just want the scale, avoid heavy drawing operations
+ d = new AdaptiveIconDrawable(new ColorDrawable(Color.BLACK), null);
+ }
}
int width = d.getIntrinsicWidth();
int height = d.getIntrinsicHeight();
diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java
index d55baf0..d471af6 100644
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ b/src/com/android/launcher3/graphics/LauncherIcons.java
@@ -16,10 +16,12 @@
package com.android.launcher3.graphics;
+import android.annotation.TargetApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.Intent.ShortcutIconResource;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -70,11 +72,15 @@
PackageManager packageManager = context.getPackageManager();
// the resource
try {
- Resources resources = packageManager.getResourcesForApplication(iconRes.packageName);
+ Resources resources = packageManager.getResourcesForApplication(iconRes.resourceName);
if (resources != null) {
final int id = resources.getIdentifier(iconRes.resourceName, null, null);
- return createIconBitmap(resources.getDrawableForDensity(
- id, LauncherAppState.getIDP(context).fillResIconDpi), context);
+ // do not stamp old legacy shortcuts as the app may have already forgotten about it
+ return createBadgedIconBitmap(resources.getDrawableForDensity(
+ id, LauncherAppState.getIDP(context).fillResIconDpi),
+ Process.myUserHandle() /* only available on primary user */,
+ context,
+ 0 /* do not apply legacy treatment */);
}
} catch (Exception e) {
// Icon not found.
@@ -90,11 +96,12 @@
if (iconBitmapSize == icon.getWidth() && iconBitmapSize == icon.getHeight()) {
return icon;
}
- return createIconBitmap(new BitmapDrawable(context.getResources(), icon), context);
+ return createIconBitmap(new BitmapDrawable(context.getResources(), icon), context, 1f);
}
/**
- * Returns a bitmap suitable for the all apps view. The icon is badged for {@param user}.
+ * Returns a bitmap suitable for displaying as an icon at various launcher UIs like all apps
+ * view or workspace. The icon is badged for {@param user}.
* The bitmap is also visually normalized with other icons.
*/
public static Bitmap createBadgedIconBitmap(
@@ -127,24 +134,18 @@
icon instanceof AdaptiveIconDrawable) {
bitmap = ShadowGenerator.getInstance(context).recreateIcon(bitmap);
}
- return badgeIconForUser(bitmap, user, context);
- }
- /**
- * Badges the provided icon with the user badge if required.
- */
- public static Bitmap badgeIconForUser(Bitmap icon, UserHandle user, Context context) {
if (user != null && !Process.myUserHandle().equals(user)) {
- BitmapDrawable drawable = new FixedSizeBitmapDrawable(icon);
+ BitmapDrawable drawable = new FixedSizeBitmapDrawable(bitmap);
Drawable badged = context.getPackageManager().getUserBadgedIcon(
drawable, user);
if (badged instanceof BitmapDrawable) {
return ((BitmapDrawable) badged).getBitmap();
} else {
- return createIconBitmap(badged, context);
+ return createIconBitmap(badged, context, 1f);
}
} else {
- return icon;
+ return bitmap;
}
}
@@ -152,7 +153,8 @@
* Creates a normalized bitmap suitable for the all apps view. The bitmap is also visually
* normalized with other icons and has enough spacing to add shadow.
*/
- public static Bitmap createScaledBitmapWithoutShadow(Drawable icon, Context context, int iconAppTargetSdk) {
+ public static Bitmap createScaledBitmapWithoutShadow(
+ Drawable icon, Context context, int iconAppTargetSdk) {
RectF iconBounds = new RectF();
IconNormalizer normalizer;
float scale = 1f;
@@ -181,21 +183,6 @@
return createIconBitmap(icon, context, scale);
}
- /**
- * Adds a shadow to the provided icon. It assumes that the icon has already been scaled using
- * {@link #createScaledBitmapWithoutShadow(Drawable, Context, int)}
- */
- public static Bitmap addShadowToIcon(Bitmap icon, Context context) {
- return ShadowGenerator.getInstance(context).recreateIcon(icon);
- }
-
- /**
- * Adds the {@param badge} on top of {@param srcTgt} using the badge dimensions.
- */
- public static Bitmap badgeWithBitmap(Bitmap srcTgt, Bitmap badge, Context context) {
- return badgeWithDrawable(srcTgt, new FastBitmapDrawable(badge), context);
- }
-
public static Bitmap badgeWithDrawable(Bitmap srcTgt, Drawable badge, Context context) {
int badgeSize = context.getResources().getDimensionPixelSize(R.dimen.profile_badge_size);
synchronized (sCanvas) {
@@ -209,26 +196,9 @@
}
/**
- * Returns a bitmap suitable for the all apps view.
- */
- public static Bitmap createIconBitmap(Drawable icon, Context context) {
- float scale = 1f;
- if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.ATLEAST_OREO &&
- icon instanceof AdaptiveIconDrawable) {
- scale = ShadowGenerator.getScaleForBounds(new RectF(0, 0, 0, 0));
- }
- Bitmap bitmap = createIconBitmap(icon, context, scale);
- if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.ATLEAST_OREO &&
- icon instanceof AdaptiveIconDrawable) {
- bitmap = ShadowGenerator.getInstance(context).recreateIcon(bitmap);
- }
- return bitmap;
- }
-
- /**
* @param scale the scale to apply before drawing {@param icon} on the canvas
*/
- public static Bitmap createIconBitmap(Drawable icon, Context context, float scale) {
+ private static Bitmap createIconBitmap(Drawable icon, Context context, float scale) {
synchronized (sCanvas) {
final int iconBitmapSize = LauncherAppState.getIDP(context).iconBitmapSize;
int width = iconBitmapSize;
@@ -295,7 +265,9 @@
* shrink the legacy icon and set it as foreground. Use color drawable as background to
* create AdaptiveIconDrawable.
*/
- static Drawable wrapToAdaptiveIconDrawable(Context context, Drawable drawable, float scale) {
+ @TargetApi(Build.VERSION_CODES.O)
+ private static Drawable wrapToAdaptiveIconDrawable(
+ Context context, Drawable drawable, float scale) {
if (!(FeatureFlags.LEGACY_ICON_TREATMENT && Utilities.ATLEAST_OREO)) {
return drawable;
}
@@ -307,7 +279,7 @@
FixedScaleDrawable fsd = ((FixedScaleDrawable) iconWrapper.getForeground());
fsd.setDrawable(drawable);
fsd.setScale(scale);
- return (Drawable) iconWrapper;
+ return iconWrapper;
}
} catch (Exception e) {
return drawable;
@@ -326,15 +298,9 @@
public static Bitmap createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context,
final Bitmap fallbackIcon) {
- Provider<Bitmap> fallbackIconProvider = new Provider<Bitmap>() {
- @Override
- public Bitmap get() {
- // If the shortcut is pinned but no longer has an icon in the system,
- // keep the current icon instead of reverting to the default icon.
- return fallbackIcon;
- }
- };
- return createShortcutIcon(shortcutInfo, context, true, fallbackIconProvider);
+ // If the shortcut is pinned but no longer has an icon in the system,
+ // keep the current icon instead of reverting to the default icon.
+ return createShortcutIcon(shortcutInfo, context, true, Provider.of(fallbackIcon));
}
public static Bitmap createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context,
@@ -360,8 +326,9 @@
if (!badged) {
return unbadgedBitmap;
}
- unbadgedBitmap = LauncherIcons.addShadowToIcon(unbadgedBitmap, context);
- return badgeWithBitmap(unbadgedBitmap, getShortcutInfoBadge(shortcutInfo, cache), context);
+ unbadgedBitmap = ShadowGenerator.getInstance(context).recreateIcon(unbadgedBitmap);
+ return badgeWithDrawable(unbadgedBitmap,
+ new FastBitmapDrawable(getShortcutInfoBadge(shortcutInfo, cache)), context);
}
public static Bitmap getShortcutInfoBadge(ShortcutInfoCompat shortcutInfo, IconCache cache) {
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index 06dc7ac..6d486ee 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -30,9 +30,9 @@
import android.graphics.Rect;
import android.util.Property;
import android.util.SparseArray;
-import android.view.animation.LinearInterpolator;
import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.anim.Interpolators;
import java.lang.ref.WeakReference;
@@ -226,7 +226,7 @@
mCurrentAnim = ObjectAnimator.ofFloat(this, INTERNAL_STATE, finalProgress);
mCurrentAnim.setDuration(
(long) ((finalProgress - mInternalStateProgress) * DURATION_SCALE));
- mCurrentAnim.setInterpolator(new LinearInterpolator());
+ mCurrentAnim.setInterpolator(Interpolators.LINEAR);
if (isFinish) {
mCurrentAnim.addListener(new AnimatorListenerAdapter() {
@Override
@@ -237,7 +237,6 @@
}
mCurrentAnim.start();
}
-
}
/**
diff --git a/src/com/android/launcher3/keyboard/CustomActionsPopup.java b/src/com/android/launcher3/keyboard/CustomActionsPopup.java
index 938955c..150522e 100644
--- a/src/com/android/launcher3/keyboard/CustomActionsPopup.java
+++ b/src/com/android/launcher3/keyboard/CustomActionsPopup.java
@@ -27,7 +27,7 @@
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
-import com.android.launcher3.popup.PopupContainerWithArrow;
+import com.android.launcher3.popup.BaseActionPopup;
import java.util.ArrayList;
import java.util.Collections;
@@ -46,7 +46,7 @@
public CustomActionsPopup(Launcher launcher, View icon) {
mLauncher = launcher;
mIcon = icon;
- PopupContainerWithArrow container = PopupContainerWithArrow.getOpen(launcher);
+ BaseActionPopup container = BaseActionPopup.getOpen(launcher);
if (container != null) {
mDelegate = container.getAccessibilityDelegate();
} else {
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index 81333b1..00ee009 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -91,7 +91,8 @@
return getFieldName(t.controlType, ControlType.class);
case Target.Type.CONTAINER:
String str = getFieldName(t.containerType, ContainerType.class);
- if (t.containerType == ContainerType.WORKSPACE) {
+ if (t.containerType == ContainerType.WORKSPACE ||
+ t.containerType == ContainerType.HOTSEAT) {
str += " id=" + t.pageIndex;
} else if (t.containerType == ContainerType.FOLDER) {
str += " grid(" + t.gridX + "," + t.gridY+ ")";
@@ -105,13 +106,16 @@
private static String getItemStr(Target t) {
String typeStr = getFieldName(t.itemType, ItemType.class);
if (t.packageNameHash != 0) {
- typeStr += ", packageHash=" + t.packageNameHash + ", predictiveRank=" + t.predictedRank;
+ typeStr += ", packageHash=" + t.packageNameHash;
}
if (t.componentHash != 0) {
- typeStr += ", componentHash=" + t.componentHash + ", predictiveRank=" + t.predictedRank;
+ typeStr += ", componentHash=" + t.componentHash;
}
if (t.intentHash != 0) {
- typeStr += ", intentHash=" + t.intentHash + ", predictiveRank=" + t.predictedRank;
+ typeStr += ", intentHash=" + t.intentHash;
+ }
+ if (t.packageNameHash != 0 || t.componentHash != 0 || t.intentHash != 0) {
+ typeStr += ", predictiveRank=" + t.predictedRank;
}
return typeStr + ", grid(" + t.gridX + "," + t.gridY + "), span(" + t.spanX + "," + t.spanY
+ "), pageIdx=" + t.pageIndex;
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index d5c6515..243dbea 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -177,13 +177,11 @@
}
public void logActionCommand(int command, int containerType) {
- logActionCommand(command, containerType, 0);
+ logActionCommand(command, newContainerTarget(containerType));
}
- public void logActionCommand(int command, int containerType, int pageIndex) {
- LauncherEvent event = newLauncherEvent(
- newCommandAction(command), newContainerTarget(containerType));
- event.srcTarget[0].pageIndex = pageIndex;
+ public void logActionCommand(int command, Target target) {
+ LauncherEvent event = newLauncherEvent(newCommandAction(command), target);
dispatchUserEvent(event, null);
}
@@ -234,6 +232,10 @@
event.action.dir = dir;
event.srcTarget[0].pageIndex = pageIndex;
dispatchUserEvent(event, null);
+
+ if (action == Action.Touch.SWIPE) {
+ resetElapsedContainerMillis();
+ }
}
public void logActionOnItem(int action, int dir, int itemType) {
@@ -325,6 +327,7 @@
ev.actionDurationMillis);
log += "\n isInLandscapeMode " + ev.isInLandscapeMode;
log += "\n isInMultiWindowMode " + ev.isInMultiWindowMode;
+ log += "\n";
Log.d(TAG, log);
}
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index 42926fa..a33a039 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -38,7 +38,6 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.ManagedProfileHeuristic.UserFolderInfo;
-import com.android.launcher3.util.Provider;
import java.util.ArrayList;
import java.util.List;
@@ -47,19 +46,18 @@
*/
public class AddWorkspaceItemsTask extends BaseModelUpdateTask {
- private final Provider<List<Pair<ItemInfo, Object>>> mAppsProvider;
+ private final List<Pair<ItemInfo, Object>> mItemList;
/**
- * @param appsProvider items to add on the workspace
+ * @param itemList items to add on the workspace
*/
- public AddWorkspaceItemsTask(Provider<List<Pair<ItemInfo, Object>>> appsProvider) {
- mAppsProvider = appsProvider;
+ public AddWorkspaceItemsTask(List<Pair<ItemInfo, Object>> itemList) {
+ mItemList = itemList;
}
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
- List<Pair<ItemInfo, Object>> workspaceApps = mAppsProvider.get();
- if (workspaceApps.isEmpty()) {
+ if (mItemList.isEmpty()) {
return;
}
Context context = app.getContext();
@@ -75,7 +73,7 @@
synchronized(dataModel) {
List<ItemInfo> filteredItems = new ArrayList<>();
- for (Pair<ItemInfo, Object> entry : workspaceApps) {
+ for (Pair<ItemInfo, Object> entry : mItemList) {
ItemInfo item = entry.first;
if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
index d5b5aa7..9aa30e7 100644
--- a/src/com/android/launcher3/model/BaseModelUpdateTask.java
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -28,6 +28,8 @@
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.MultiHashMap;
+import com.android.launcher3.widget.WidgetListRowEntry;
+import com.android.launcher3.widget.WidgetsListAdapter;
import java.util.ArrayList;
import java.util.concurrent.Executor;
@@ -117,8 +119,8 @@
}
public void bindUpdatedWidgets(BgDataModel dataModel) {
- final MultiHashMap<PackageItemInfo, WidgetItem> widgets
- = dataModel.widgetsModel.getWidgetsMap();
+ final ArrayList<WidgetListRowEntry> widgets =
+ dataModel.widgetsModel.getWidgetsList(mApp.getContext());
scheduleCallbackTask(new CallbackTask() {
@Override
public void execute(Callbacks callbacks) {
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index 8de0de0..d9b1a3f 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -726,7 +726,7 @@
mContext).getLauncherAppWidgetInfo(widgetId);
Point spans = null;
if (pInfo != null) {
- spans = pInfo.getMinSpans(mIdp, mContext);
+ spans = pInfo.getMinSpans();
}
if (spans != null) {
entry.minSpanX = spans.x > 0 ? spans.x : entry.spanX;
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index bc7da9b..47f370a 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -16,11 +16,15 @@
package com.android.launcher3.model;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_SYSTEM_NO;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_SYSTEM_YES;
+
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.Intent.ShortcutIconResource;
+import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
import android.database.Cursor;
import android.database.CursorWrapper;
@@ -274,8 +278,13 @@
info.iconBitmap = icon != null ? icon : info.iconBitmap;
}
- if (lai != null && PackageManagerHelper.isAppSuspended(lai.getApplicationInfo())) {
- info.isDisabled = ShortcutInfo.FLAG_DISABLED_SUSPENDED;
+ if (lai != null) {
+ ApplicationInfo appInfo = lai.getApplicationInfo();
+ if (PackageManagerHelper.isAppSuspended(appInfo)) {
+ info.runtimeStatusFlags |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
+ }
+ info.runtimeStatusFlags |= (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0
+ ? FLAG_SYSTEM_NO : FLAG_SYSTEM_YES;
}
// from the db
diff --git a/src/com/android/launcher3/model/LoaderResults.java b/src/com/android/launcher3/model/LoaderResults.java
index b7a6b68..24e5b9c 100644
--- a/src/com/android/launcher3/model/LoaderResults.java
+++ b/src/com/android/launcher3/model/LoaderResults.java
@@ -36,6 +36,8 @@
import com.android.launcher3.util.LooperIdleLock;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.ViewOnDrawExecutor;
+import com.android.launcher3.widget.WidgetListRowEntry;
+import com.android.launcher3.widget.WidgetsListAdapter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -362,8 +364,8 @@
}
public void bindWidgets() {
- final MultiHashMap<PackageItemInfo, WidgetItem> widgets
- = mBgDataModel.widgetsModel.getWidgetsMap();
+ final ArrayList<WidgetListRowEntry> widgets =
+ mBgDataModel.widgetsModel.getWidgetsList(mApp.getContext());
Runnable r = new Runnable() {
public void run() {
Callbacks callbacks = mCallbacks.get();
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 4756edc..c2cfebb 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -27,8 +27,6 @@
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Process;
-import android.os.SystemClock;
-import android.os.Trace;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
@@ -53,7 +51,6 @@
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
-import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.folder.FolderIconPreviewVerifier;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.logging.FileLog;
@@ -67,6 +64,7 @@
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Provider;
+import com.android.launcher3.util.TraceHelper;
import java.util.ArrayList;
import java.util.Collections;
@@ -76,6 +74,11 @@
import java.util.Map;
import java.util.concurrent.CancellationException;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
+import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
+
/**
* Runnable for the thread that loads the contents of the launcher:
* - workspace icons
@@ -84,7 +87,6 @@
* - deep shortcuts within apps
*/
public class LoaderTask implements Runnable {
- private static final boolean DEBUG_LOADERS = false;
private static final String TAG = "LoaderTask";
private final LauncherAppState mApp;
@@ -141,73 +143,64 @@
}
}
+ TraceHelper.beginSection(TAG);
try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
- long now = 0;
- if (DEBUG_LOADERS) Log.d(TAG, "step 1.1: loading workspace");
+ TraceHelper.partitionSection(TAG, "step 1.1: loading workspace");
loadWorkspace();
verifyNotStopped();
- if (DEBUG_LOADERS) Log.d(TAG, "step 1.2: bind workspace workspace");
+ TraceHelper.partitionSection(TAG, "step 1.2: bind workspace workspace");
mResults.bindWorkspace();
// Take a break
- if (DEBUG_LOADERS) {
- Log.d(TAG, "step 1 completed, wait for idle");
- now = SystemClock.uptimeMillis();
- }
+ TraceHelper.partitionSection(TAG, "step 1 completed, wait for idle");
waitForIdle();
- if (DEBUG_LOADERS) Log.d(TAG, "Waited " + (SystemClock.uptimeMillis() - now) + "ms");
verifyNotStopped();
// second step
- if (DEBUG_LOADERS) Log.d(TAG, "step 2.1: loading all apps");
+ TraceHelper.partitionSection(TAG, "step 2.1: loading all apps");
loadAllApps();
- if (DEBUG_LOADERS) Log.d(TAG, "step 2.2: Binding all apps");
+ TraceHelper.partitionSection(TAG, "step 2.2: Binding all apps");
verifyNotStopped();
mResults.bindAllApps();
verifyNotStopped();
- if (DEBUG_LOADERS) Log.d(TAG, "step 2.3: Update icon cache");
+ TraceHelper.partitionSection(TAG, "step 2.3: Update icon cache");
updateIconCache();
// Take a break
- if (DEBUG_LOADERS) {
- Log.d(TAG, "step 2 completed, wait for idle");
- now = SystemClock.uptimeMillis();
- }
+ TraceHelper.partitionSection(TAG, "step 2 completed, wait for idle");
waitForIdle();
- if (DEBUG_LOADERS) Log.d(TAG, "Waited " + (SystemClock.uptimeMillis() - now) + "ms");
verifyNotStopped();
// third step
- if (DEBUG_LOADERS) Log.d(TAG, "step 3.1: loading deep shortcuts");
+ TraceHelper.partitionSection(TAG, "step 3.1: loading deep shortcuts");
loadDeepShortcuts();
verifyNotStopped();
- if (DEBUG_LOADERS) Log.d(TAG, "step 3.2: bind deep shortcuts");
+ TraceHelper.partitionSection(TAG, "step 3.2: bind deep shortcuts");
mResults.bindDeepShortcuts();
// Take a break
- if (DEBUG_LOADERS) Log.d(TAG, "step 3 completed, wait for idle");
+ TraceHelper.partitionSection(TAG, "step 3 completed, wait for idle");
waitForIdle();
verifyNotStopped();
// fourth step
- if (DEBUG_LOADERS) Log.d(TAG, "step 4.1: loading widgets");
+ TraceHelper.partitionSection(TAG, "step 4.1: loading widgets");
mBgDataModel.widgetsModel.update(mApp, null);
verifyNotStopped();
- if (DEBUG_LOADERS) Log.d(TAG, "step 4.2: Binding widgets");
+ TraceHelper.partitionSection(TAG, "step 4.2: Binding widgets");
mResults.bindWidgets();
transaction.commit();
} catch (CancellationException e) {
// Loader stopped, ignore
- if (DEBUG_LOADERS) {
- Log.d(TAG, "Loader cancelled", e);
- }
+ TraceHelper.partitionSection(TAG, "Cancelled");
}
+ TraceHelper.endSection(TAG);
}
public synchronized void stopLocked() {
@@ -216,10 +209,6 @@
}
private void loadWorkspace() {
- if (LauncherAppState.PROFILE_STARTUP) {
- Trace.beginSection("Loading Workspace");
- }
-
final Context context = mApp.getContext();
final ContentResolver contentResolver = context.getContentResolver();
final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
@@ -478,13 +467,13 @@
true /* badged */, fallbackIconProvider);
if (pmHelper.isAppSuspended(
pinnedShortcut.getPackage(), info.user)) {
- info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
+ info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED;
}
intent = info.intent;
} else {
// Create a shortcut info in disabled mode for now.
info = c.loadSimpleShortcut();
- info.isDisabled |= ShortcutInfo.FLAG_DISABLED_LOCKED_USER;
+ info.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
}
} else { // item type == ITEM_TYPE_SHORTCUT
info = c.loadSimpleShortcut();
@@ -492,7 +481,7 @@
// Shortcuts are only available on the primary profile
if (!TextUtils.isEmpty(targetPkg)
&& pmHelper.isAppSuspended(targetPkg, c.user)) {
- disabledState |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
+ disabledState |= FLAG_DISABLED_SUSPENDED;
}
// App shortcuts that used to be automatically added to Launcher
@@ -515,9 +504,9 @@
info.rank = c.getInt(rankIndex);
info.spanX = 1;
info.spanY = 1;
- info.isDisabled |= disabledState;
+ info.runtimeStatusFlags |= disabledState;
if (isSafeMode && !Utilities.isSystemApp(context, intent)) {
- info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE;
+ info.runtimeStatusFlags |= FLAG_DISABLED_SAFEMODE;
}
if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
@@ -734,7 +723,7 @@
numItemsInPreview++;
}
- if (numItemsInPreview >= FolderIcon.NUM_ITEMS_IN_PREVIEW) {
+ if (numItemsInPreview >= MAX_NUM_ITEMS_IN_PREVIEW) {
break;
}
}
@@ -765,9 +754,6 @@
LauncherModel.updateWorkspaceScreenOrder(context, mBgDataModel.workspaceScreens);
}
}
- if (LauncherAppState.PROFILE_STARTUP) {
- Trace.endSection();
- }
}
private void updateIconCache() {
@@ -792,21 +778,13 @@
}
private void loadAllApps() {
- final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
-
final List<UserHandle> profiles = mUserManager.getUserProfiles();
// Clear the list of apps
mBgAllAppsList.clear();
for (UserHandle user : profiles) {
// Query for the set of apps
- final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
- if (DEBUG_LOADERS) {
- Log.d(TAG, "getActivityList took "
- + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user);
- Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user);
- }
// Fail if we don't have any apps
// TODO: Fix this. Only fail for the current user.
if (apps == null || apps.isEmpty()) {
@@ -833,10 +811,6 @@
}
mBgAllAppsList.added = new ArrayList<>();
- if (DEBUG_LOADERS) {
- Log.d(TAG, "All apps loaded in in "
- + (SystemClock.uptimeMillis() - loadTime) + "ms");
- }
}
private void loadDeepShortcuts() {
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 78ecbc6..470dadf 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -244,9 +244,9 @@
infoUpdated = true;
}
- int oldDisabledFlags = si.isDisabled;
- si.isDisabled = flagOp.apply(si.isDisabled);
- if (si.isDisabled != oldDisabledFlags) {
+ int oldRuntimeFlags = si.runtimeStatusFlags;
+ si.runtimeStatusFlags = flagOp.apply(si.runtimeStatusFlags);
+ if (si.runtimeStatusFlags != oldRuntimeFlags) {
shortcutUpdated = true;
}
}
@@ -336,17 +336,7 @@
});
}
- // Notify launcher of widget update. From marshmallow onwards we use AppWidgetHost to
- // get widget update signals.
- if (!Utilities.ATLEAST_MARSHMALLOW &&
- (mOp == OP_ADD || mOp == OP_REMOVE || mOp == OP_UPDATE)) {
- scheduleCallbackTask(new CallbackTask() {
- @Override
- public void execute(Callbacks callbacks) {
- callbacks.notifyWidgetProvidersChanged();
- }
- });
- } else if (Utilities.ATLEAST_OREO && mOp == OP_ADD) {
+ if (Utilities.ATLEAST_OREO && mOp == OP_ADD) {
// Load widgets for the new package. Changes due to app updates are handled through
// AppWidgetHost events, this is just to initialize the long-press options.
for (int i = 0; i < N; i++) {
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 8170f9a..2e9ac72 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.model;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
+
import android.content.Context;
import android.os.UserHandle;
@@ -87,12 +89,12 @@
removedKeys.add(key);
continue;
}
- si.isDisabled &= ~ShortcutInfo.FLAG_DISABLED_LOCKED_USER;
+ si.runtimeStatusFlags &= ~FLAG_DISABLED_LOCKED_USER;
si.updateFromDeepShortcutInfo(shortcut, context);
si.iconBitmap = LauncherIcons.createShortcutIcon(shortcut, context,
si.iconBitmap);
} else {
- si.isDisabled |= ShortcutInfo.FLAG_DISABLED_LOCKED_USER;
+ si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
}
updatedShortcutInfos.add(si);
}
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index ed900bf..1ff0dac 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -15,6 +15,7 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.AlphabeticIndexCompat;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.ShortcutConfigActivityInfo;
@@ -22,10 +23,14 @@
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.widget.WidgetItemComparator;
+import com.android.launcher3.widget.WidgetListRowEntry;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.Map;
/**
* Widgets data model that is used by the adapters of the widget views and controllers.
@@ -42,8 +47,26 @@
private AppFilter mAppFilter;
- public synchronized MultiHashMap<PackageItemInfo, WidgetItem> getWidgetsMap() {
- return mWidgetsList.clone();
+ /**
+ * Returns a list of {@link WidgetListRowEntry}. All {@link WidgetItem} in a single row
+ * are sorted (based on label and user), but the overall list of {@link WidgetListRowEntry}s
+ * is not sorted. This list is sorted at the UI when using
+ * {@link com.android.launcher3.widget.WidgetsDiffReporter}
+ *
+ * @see com.android.launcher3.widget.WidgetsListAdapter#setWidgets(ArrayList)
+ */
+ public synchronized ArrayList<WidgetListRowEntry> getWidgetsList(Context context) {
+ ArrayList<WidgetListRowEntry> result = new ArrayList<>();
+ AlphabeticIndexCompat indexer = new AlphabeticIndexCompat(context);
+
+ WidgetItemComparator widgetComparator = new WidgetItemComparator();
+ for (Map.Entry<PackageItemInfo, ArrayList<WidgetItem>> entry : mWidgetsList.entrySet()) {
+ WidgetListRowEntry row = new WidgetListRowEntry(entry.getKey(), entry.getValue());
+ row.titleSectionName = indexer.computeSectionName(row.pkgItem.title);
+ Collections.sort(row.widgets, widgetComparator);
+ result.add(row);
+ }
+ return result;
}
/**
diff --git a/src/com/android/launcher3/notification/Interpolators.java b/src/com/android/launcher3/notification/Interpolators.java
deleted file mode 100644
index 5c3b22a..0000000
--- a/src/com/android/launcher3/notification/Interpolators.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2017 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.notification;
-
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
-
-/**
- * Utility class to receive interpolators from.
- *
- * This class was copied from com.android.systemui.
- */
-public class Interpolators {
- public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
- public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
- public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
-
- /**
- * Interpolator to be used when animating a move based on a click. Pair with enough duration.
- */
- public static final Interpolator TOUCH_RESPONSE =
- new PathInterpolator(0.3f, 0f, 0.1f, 1f);
-}
diff --git a/src/com/android/launcher3/notification/NotificationFooterLayout.java b/src/com/android/launcher3/notification/NotificationFooterLayout.java
index ad07d37..3cf3ff6 100644
--- a/src/com/android/launcher3/notification/NotificationFooterLayout.java
+++ b/src/com/android/launcher3/notification/NotificationFooterLayout.java
@@ -37,6 +37,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.anim.PropertyResetListener;
+import com.android.launcher3.popup.BaseActionPopup;
import com.android.launcher3.popup.PopupContainerWithArrow;
import java.util.ArrayList;
@@ -193,16 +194,17 @@
private void removeViewFromIconRow(View child) {
mIconRow.removeView(child);
- mNotifications.remove((NotificationInfo) child.getTag());
+ mNotifications.remove(child.getTag());
updateOverflowEllipsisVisibility();
if (mIconRow.getChildCount() == 0) {
// There are no more icons in the footer, so hide it.
- PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(
+ BaseActionPopup popup = BaseActionPopup.getOpen(
Launcher.getLauncher(getContext()));
- if (popup != null) {
+ if (popup instanceof PopupContainerWithArrow) {
final int newHeight = getResources().getDimensionPixelSize(
R.dimen.notification_empty_footer_height);
- Animator collapseFooter = popup.reduceNotificationViewHeight(getHeight() - newHeight,
+ Animator collapseFooter = ((PopupContainerWithArrow) popup)
+ .reduceNotificationViewHeight(getHeight() - newHeight,
getResources().getInteger(R.integer.config_removeNotificationViewDuration));
collapseFooter.addListener(new AnimatorListenerAdapter() {
@Override
diff --git a/src/com/android/launcher3/notification/NotificationGroup.java b/src/com/android/launcher3/notification/NotificationGroup.java
new file mode 100644
index 0000000..bce2117
--- /dev/null
+++ b/src/com/android/launcher3/notification/NotificationGroup.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Contains data related to a group of notifications, like the group summary key and the child keys.
+ */
+public class NotificationGroup {
+ private String mGroupSummaryKey;
+ private Set<String> mChildKeys;
+
+ public NotificationGroup() {
+ mChildKeys = new HashSet<>();
+ }
+
+ public void setGroupSummaryKey(String groupSummaryKey) {
+ mGroupSummaryKey = groupSummaryKey;
+ }
+
+ public String getGroupSummaryKey() {
+ return mGroupSummaryKey;
+ }
+
+ public void addChildKey(String childKey) {
+ mChildKeys.add(childKey);
+ }
+
+ public void removeChildKey(String childKey) {
+ mChildKeys.remove(childKey);
+ }
+
+ public boolean isEmpty() {
+ return mChildKeys.isEmpty();
+ }
+}
diff --git a/src/com/android/launcher3/notification/NotificationInfo.java b/src/com/android/launcher3/notification/NotificationInfo.java
index 26f9346..120de04 100644
--- a/src/com/android/launcher3/notification/NotificationInfo.java
+++ b/src/com/android/launcher3/notification/NotificationInfo.java
@@ -31,7 +31,6 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.graphics.IconPalette;
-import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.util.PackageUserKey;
/**
@@ -110,7 +109,7 @@
launcher.getPopupDataProvider().cancelNotification(notificationKey);
}
AbstractFloatingView.closeOpenContainer(launcher, AbstractFloatingView
- .TYPE_POPUP_CONTAINER_WITH_ARROW);
+ .TYPE_ACTION_POPUP);
}
public Drawable getIconForBackground(Context context, int background) {
diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index 9126626..7b70df7 100644
--- a/src/com/android/launcher3/notification/NotificationListener.java
+++ b/src/com/android/launcher3/notification/NotificationListener.java
@@ -38,7 +38,9 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import static com.android.launcher3.SettingsActivity.NOTIFICATION_BADGING;
@@ -66,6 +68,8 @@
private final Handler mWorkerHandler;
private final Handler mUiHandler;
private final Ranking mTempRanking = new Ranking();
+ /** Maps groupKey's to the corresponding group of notifications. */
+ private final Map<String, NotificationGroup> mNotificationGroupMap = new HashMap<>();
private SettingsObserver mNotificationBadgingObserver;
@@ -227,6 +231,15 @@
NotificationKeyData.fromNotification(sbn));
mWorkerHandler.obtainMessage(MSG_NOTIFICATION_REMOVED, packageUserKeyAndNotificationKey)
.sendToTarget();
+
+ NotificationGroup notificationGroup = mNotificationGroupMap.get(sbn.getGroupKey());
+ if (notificationGroup != null) {
+ notificationGroup.removeChildKey(sbn.getKey());
+ if (notificationGroup.isEmpty()) {
+ cancelNotification(notificationGroup.getGroupSummaryKey());
+ mNotificationGroupMap.remove(sbn.getGroupKey());
+ }
+ }
}
/** This makes a potentially expensive binder call and should be run on a background thread. */
@@ -264,18 +277,34 @@
}
private boolean shouldBeFilteredOut(StatusBarNotification sbn) {
+ Notification notification = sbn.getNotification();
+
+ boolean isGroupHeader = (notification.flags & Notification.FLAG_GROUP_SUMMARY) != 0;
+ if (sbn.isGroup()) {
+ // Maintain group info so we can cancel the summary when the last child is canceled.
+ NotificationGroup notificationGroup = mNotificationGroupMap.get(sbn.getGroupKey());
+ if (notificationGroup == null) {
+ notificationGroup = new NotificationGroup();
+ mNotificationGroupMap.put(sbn.getGroupKey(), notificationGroup);
+ }
+ if (isGroupHeader) {
+ notificationGroup.setGroupSummaryKey(sbn.getKey());
+ } else {
+ notificationGroup.addChildKey(sbn.getKey());
+ }
+ }
+
getCurrentRanking().getRanking(sbn.getKey(), mTempRanking);
if (!mTempRanking.canShowBadge()) {
return true;
}
- Notification notification = sbn.getNotification();
if (mTempRanking.getChannel().getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID)) {
// Special filtering for the default, legacy "Miscellaneous" channel.
if ((notification.flags & Notification.FLAG_ONGOING_EVENT) != 0) {
return true;
}
}
- boolean isGroupHeader = (notification.flags & Notification.FLAG_GROUP_SUMMARY) != 0;
+
CharSequence title = notification.extras.getCharSequence(Notification.EXTRA_TITLE);
CharSequence text = notification.extras.getCharSequence(Notification.EXTRA_TEXT);
boolean missingTitleAndText = TextUtils.isEmpty(title) && TextUtils.isEmpty(text);
diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java
index 5aff28d..c8e1fdb 100644
--- a/src/com/android/launcher3/notification/NotificationMainView.java
+++ b/src/com/android/launcher3/notification/NotificationMainView.java
@@ -16,6 +16,8 @@
package com.android.launcher3.notification;
+import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
+
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -25,7 +27,6 @@
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewPropertyAnimator;
import android.widget.FrameLayout;
import android.widget.TextView;
@@ -167,14 +168,11 @@
endTranslation = 0;
}
- SwipeDetector.ScrollInterpolator interpolator = new SwipeDetector.ScrollInterpolator();
- interpolator.setVelocityAtZero(velocity);
-
long duration = SwipeDetector.calculateDuration(velocity,
(endTranslation - getTranslationX()) / getWidth());
animate()
.setDuration(duration)
- .setInterpolator(interpolator)
+ .setInterpolator(scrollInterpolatorForVelocity(velocity))
.translationX(endTranslation)
.withEndAction(new Runnable() {
@Override
diff --git a/src/com/android/launcher3/pageindicators/CaretDrawable.java b/src/com/android/launcher3/pageindicators/CaretDrawable.java
deleted file mode 100644
index 5ade497..0000000
--- a/src/com/android/launcher3/pageindicators/CaretDrawable.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2016 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.pageindicators;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.Drawable;
-
-import com.android.launcher3.R;
-import com.android.launcher3.util.Themes;
-
-public class CaretDrawable extends Drawable {
- public static final float PROGRESS_CARET_POINTING_UP = -1f;
- public static final float PROGRESS_CARET_POINTING_DOWN = 1f;
- public static final float PROGRESS_CARET_NEUTRAL = 0;
-
- private float mCaretProgress = PROGRESS_CARET_NEUTRAL;
-
- private Paint mShadowPaint = new Paint();
- private Paint mCaretPaint = new Paint();
- private Path mPath = new Path();
- private final int mCaretSizePx;
- private final boolean mUseShadow;
-
- public CaretDrawable(Context context) {
- final Resources res = context.getResources();
-
- final int strokeWidth = res.getDimensionPixelSize(R.dimen.all_apps_caret_stroke_width);
- final int shadowSpread = res.getDimensionPixelSize(R.dimen.all_apps_caret_shadow_spread);
-
- mCaretPaint.setColor(Themes.getAttrColor(context, R.attr.workspaceTextColor));
- mCaretPaint.setAntiAlias(true);
- mCaretPaint.setStrokeWidth(strokeWidth);
- mCaretPaint.setStyle(Paint.Style.STROKE);
- mCaretPaint.setStrokeCap(Paint.Cap.ROUND);
- mCaretPaint.setStrokeJoin(Paint.Join.ROUND);
-
- mShadowPaint.setColor(res.getColor(R.color.default_shadow_color_no_alpha));
- mShadowPaint.setAlpha(Themes.getAlpha(context, android.R.attr.spotShadowAlpha));
- mShadowPaint.setAntiAlias(true);
- mShadowPaint.setStrokeWidth(strokeWidth + (shadowSpread * 2));
- mShadowPaint.setStyle(Paint.Style.STROKE);
- mShadowPaint.setStrokeCap(Paint.Cap.ROUND);
- mShadowPaint.setStrokeJoin(Paint.Join.ROUND);
-
- mUseShadow = !Themes.getAttrBoolean(context, R.attr.isWorkspaceDarkText);
- mCaretSizePx = res.getDimensionPixelSize(R.dimen.all_apps_caret_size);
- }
-
- @Override
- public int getIntrinsicHeight() {
- return mCaretSizePx;
- }
-
- @Override
- public int getIntrinsicWidth() {
- return mCaretSizePx;
- }
-
- @Override
- public void draw(Canvas canvas) {
- // Assumes caret paint is more important than shadow paint
- if (Float.compare(mCaretPaint.getAlpha(), 0f) == 0) {
- return;
- }
-
- // Assumes shadow stroke width is larger
- final float width = getBounds().width() - mShadowPaint.getStrokeWidth();
- final float height = getBounds().height() - mShadowPaint.getStrokeWidth();
- final float left = getBounds().left + (mShadowPaint.getStrokeWidth() / 2);
- final float top = getBounds().top + (mShadowPaint.getStrokeWidth() / 2);
-
- // When the bounds are square, this will result in a caret with a right angle
- final float verticalInset = (height / 4);
- final float caretHeight = (height - (verticalInset * 2));
-
- mPath.reset();
- mPath.moveTo(left, top + caretHeight * (1 - getNormalizedCaretProgress()));
- mPath.lineTo(left + (width / 2), top + caretHeight * getNormalizedCaretProgress());
- mPath.lineTo(left + width, top + caretHeight * (1 - getNormalizedCaretProgress()));
- if (mUseShadow) {
- canvas.drawPath(mPath, mShadowPaint);
- }
- canvas.drawPath(mPath, mCaretPaint);
- }
-
- /**
- * Sets the caret progress
- *
- * @param progress The progress ({@value #PROGRESS_CARET_POINTING_UP} for pointing up,
- * {@value #PROGRESS_CARET_POINTING_DOWN} for pointing down, {@value #PROGRESS_CARET_NEUTRAL}
- * for neutral)
- */
- public void setCaretProgress(float progress) {
- mCaretProgress = progress;
- invalidateSelf();
- }
-
- /**
- * Returns the caret progress
- *
- * @return The progress
- */
- public float getCaretProgress() {
- return mCaretProgress;
- }
-
- /**
- * Returns the caret progress normalized to [0..1]
- *
- * @return The normalized progress
- */
- public float getNormalizedCaretProgress() {
- return (mCaretProgress - PROGRESS_CARET_POINTING_UP) /
- (PROGRESS_CARET_POINTING_DOWN - PROGRESS_CARET_POINTING_UP);
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
- }
-
- @Override
- public void setAlpha(int alpha) {
- mCaretPaint.setAlpha(alpha);
- mShadowPaint.setAlpha(alpha);
- invalidateSelf();
- }
-
- @Override
- public void setColorFilter(ColorFilter cf) {
- // no-op
- }
-}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicator.java b/src/com/android/launcher3/pageindicators/PageIndicator.java
index 47c2ffb..be6bcc5 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicator.java
@@ -16,17 +16,13 @@
package com.android.launcher3.pageindicators;
import android.content.Context;
-import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.FrameLayout;
-import com.android.launcher3.dynamicui.ExtractedColors;
-
/**
* Base class for a page indicator.
*/
public abstract class PageIndicator extends FrameLayout {
- private CaretDrawable mCaretDrawable;
protected int mNumPages = 1;
@@ -54,30 +50,7 @@
onPageCountChanged();
}
- public CaretDrawable getCaretDrawable() {
- return mCaretDrawable;
- }
-
- public void setCaretDrawable(CaretDrawable caretDrawable) {
- if (mCaretDrawable != null) {
- mCaretDrawable.setCallback(null);
- }
-
- mCaretDrawable = caretDrawable;
-
- if (mCaretDrawable != null) {
- mCaretDrawable.setCallback(this);
- }
- }
-
protected void onPageCountChanged() {}
public void setShouldAutoHide(boolean shouldAutoHide) {}
-
- public void updateColor(ExtractedColors extractedColors) {}
-
- @Override
- protected boolean verifyDrawable(Drawable who) {
- return super.verifyDrawable(who) || who == getCaretDrawable();
- }
}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java b/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java
deleted file mode 100644
index 911be93..0000000
--- a/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2016 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.pageindicators;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-
-/**
- * Simply draws the caret drawable bottom-right aligned in the view. This ensures that we can have
- * a view with as large an area as we want (for touching) while maintaining a caret of size
- * all_apps_caret_size. Used only for the landscape layout.
- */
-public class PageIndicatorCaretLandscape extends PageIndicator {
- // all apps pull up handle drawable.
-
- public PageIndicatorCaretLandscape(Context context) {
- this(context, null);
- }
-
- public PageIndicatorCaretLandscape(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public PageIndicatorCaretLandscape(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- int caretSize = context.getResources().getDimensionPixelSize(R.dimen.all_apps_caret_size);
- CaretDrawable caretDrawable = new CaretDrawable(context);
- caretDrawable.setBounds(0, 0, caretSize, caretSize);
- setCaretDrawable(caretDrawable);
-
- Launcher l = Launcher.getLauncher(context);
- setOnClickListener(l);
- setOnFocusChangeListener(l.mFocusHandler);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- Rect drawableBounds = getCaretDrawable().getBounds();
- int count = canvas.save();
- canvas.translate((getWidth() - drawableBounds.width()) / 2,
- getHeight() - drawableBounds.height());
- getCaretDrawable().draw(canvas);
- canvas.restoreToCount(count);
- }
-}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorLandscape.java b/src/com/android/launcher3/pageindicators/PageIndicatorLandscape.java
new file mode 100644
index 0000000..7325235
--- /dev/null
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorLandscape.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 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.pageindicators;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.android.launcher3.Launcher;
+
+/**
+ * Simply draws the caret drawable bottom-right aligned in the view. This ensures that we can have
+ * a view with as large an area as we want (for touching) while maintaining a caret of size
+ * all_apps_caret_size. Used only for the landscape layout.
+ */
+public class PageIndicatorLandscape extends PageIndicator {
+ // all apps pull up handle drawable.
+
+ public PageIndicatorLandscape(Context context) {
+ this(context, null);
+ }
+
+ public PageIndicatorLandscape(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public PageIndicatorLandscape(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ Launcher l = Launcher.getLauncher(context);
+ setOnClickListener(l);
+ setOnFocusChangeListener(l.mFocusHandler);
+ }
+}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java b/src/com/android/launcher3/pageindicators/PageIndicatorLine.java
similarity index 73%
rename from src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
rename to src/com/android/launcher3/pageindicators/PageIndicatorLine.java
index 6281fec..09a06b0 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorLine.java
@@ -11,9 +11,7 @@
import android.graphics.Paint;
import android.os.Handler;
import android.os.Looper;
-import android.support.v4.graphics.ColorUtils;
import android.util.AttributeSet;
-import android.util.Log;
import android.util.Property;
import android.view.ViewConfiguration;
import android.widget.ImageView;
@@ -21,8 +19,6 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.dynamicui.ExtractedColors;
import com.android.launcher3.dynamicui.WallpaperColorInfo;
/**
@@ -30,10 +26,7 @@
*
* The fraction is 1 / number of pages and the position is based on the progress of the page scroll.
*/
-public class PageIndicatorLineCaret extends PageIndicator {
- private static final String TAG = "PageIndicatorLine";
-
- private static final int[] sTempCoords = new int[2];
+public class PageIndicatorLine extends PageIndicator {
private static final int LINE_ANIMATE_DURATION = ViewConfiguration.getScrollBarFadeDuration();
private static final int LINE_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay();
@@ -63,43 +56,43 @@
private final int mLineHeight;
private ImageView mAllAppsHandle;
- private static final Property<PageIndicatorLineCaret, Integer> PAINT_ALPHA
- = new Property<PageIndicatorLineCaret, Integer>(Integer.class, "paint_alpha") {
+ private static final Property<PageIndicatorLine, Integer> PAINT_ALPHA
+ = new Property<PageIndicatorLine, Integer>(Integer.class, "paint_alpha") {
@Override
- public Integer get(PageIndicatorLineCaret obj) {
+ public Integer get(PageIndicatorLine obj) {
return obj.mLinePaint.getAlpha();
}
@Override
- public void set(PageIndicatorLineCaret obj, Integer alpha) {
+ public void set(PageIndicatorLine obj, Integer alpha) {
obj.mLinePaint.setAlpha(alpha);
obj.invalidate();
}
};
- private static final Property<PageIndicatorLineCaret, Float> NUM_PAGES
- = new Property<PageIndicatorLineCaret, Float>(Float.class, "num_pages") {
+ private static final Property<PageIndicatorLine, Float> NUM_PAGES
+ = new Property<PageIndicatorLine, Float>(Float.class, "num_pages") {
@Override
- public Float get(PageIndicatorLineCaret obj) {
+ public Float get(PageIndicatorLine obj) {
return obj.mNumPagesFloat;
}
@Override
- public void set(PageIndicatorLineCaret obj, Float numPages) {
+ public void set(PageIndicatorLine obj, Float numPages) {
obj.mNumPagesFloat = numPages;
obj.invalidate();
}
};
- private static final Property<PageIndicatorLineCaret, Integer> TOTAL_SCROLL
- = new Property<PageIndicatorLineCaret, Integer>(Integer.class, "total_scroll") {
+ private static final Property<PageIndicatorLine, Integer> TOTAL_SCROLL
+ = new Property<PageIndicatorLine, Integer>(Integer.class, "total_scroll") {
@Override
- public Integer get(PageIndicatorLineCaret obj) {
+ public Integer get(PageIndicatorLine obj) {
return obj.mTotalScroll;
}
@Override
- public void set(PageIndicatorLineCaret obj, Integer totalScroll) {
+ public void set(PageIndicatorLine obj, Integer totalScroll) {
obj.mTotalScroll = totalScroll;
obj.invalidate();
}
@@ -112,15 +105,15 @@
}
};
- public PageIndicatorLineCaret(Context context) {
+ public PageIndicatorLine(Context context) {
this(context, null);
}
- public PageIndicatorLineCaret(Context context, AttributeSet attrs) {
+ public PageIndicatorLine(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
- public PageIndicatorLineCaret(Context context, AttributeSet attrs, int defStyle) {
+ public PageIndicatorLine(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
Resources res = context.getResources();
@@ -129,7 +122,6 @@
mLauncher = Launcher.getLauncher(context);
mLineHeight = res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_line_height);
- setCaretDrawable(new CaretDrawable(context));
boolean darkText = WallpaperColorInfo.getInstance(context).supportsDarkText();
mActiveAlpha = darkText ? BLACK_ALPHA : WHITE_ALPHA;
@@ -139,8 +131,7 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mAllAppsHandle = (ImageView) findViewById(R.id.all_apps_handle);
- mAllAppsHandle.setImageDrawable(getCaretDrawable());
+ mAllAppsHandle = findViewById(R.id.all_apps_handle);
mAllAppsHandle.setOnClickListener(mLauncher);
mAllAppsHandle.setOnFocusChangeListener(mLauncher.mFocusHandler);
mLauncher.setAllAppsButton(mAllAppsHandle);
@@ -218,32 +209,6 @@
}
}
- /**
- * The line's color will be:
- * - mostly opaque white if the hotseat is white (ignoring alpha)
- * - mostly opaque black if the hotseat is black (ignoring alpha)
- */
- public void updateColor(ExtractedColors extractedColors) {
- if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
- return;
- }
- int originalLineAlpha = mLinePaint.getAlpha();
- int color = extractedColors.getColor(ExtractedColors.HOTSEAT_INDEX);
- if (color != Color.TRANSPARENT) {
- color = ColorUtils.setAlphaComponent(color, 255);
- if (color == Color.BLACK) {
- mActiveAlpha = BLACK_ALPHA;
- } else if (color == Color.WHITE) {
- mActiveAlpha = WHITE_ALPHA;
- } else {
- Log.e(TAG, "Setting workspace page indicators to an unsupported color: #"
- + Integer.toHexString(color));
- }
- mLinePaint.setColor(color);
- mLinePaint.setAlpha(originalLineAlpha);
- }
- }
-
private void animateLineToAlpha(int alpha) {
if (alpha == mToAlpha) {
// Ignore the new animation if it is going to the same alpha as the current animation.
diff --git a/src/com/android/launcher3/popup/BaseActionPopup.java b/src/com/android/launcher3/popup/BaseActionPopup.java
new file mode 100644
index 0000000..7ffe2ef
--- /dev/null
+++ b/src/com/android/launcher3/popup/BaseActionPopup.java
@@ -0,0 +1,599 @@
+/*
+ * Copyright (C) 2017 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.popup;
+
+import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS_IF_NOTIFICATIONS;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.CornerPathEffect;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.drawable.ShapeDrawable;
+import android.os.Build;
+import android.support.annotation.IntDef;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.widget.TextView;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
+import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate;
+import com.android.launcher3.anim.PropertyListBuilder;
+import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.graphics.TriangleShape;
+import com.android.launcher3.logging.LoggerUtils;
+import com.android.launcher3.notification.NotificationItemView;
+import com.android.launcher3.shortcuts.DeepShortcutView;
+import com.android.launcher3.shortcuts.ShortcutsItemView;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.Themes;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Set;
+
+/**
+ * Base popup container for showing shortcuts to deep links within apps.
+ */
+@TargetApi(Build.VERSION_CODES.N)
+public class BaseActionPopup<V extends TextView> extends AbstractFloatingView {
+
+ public static final int ROUNDED_TOP_CORNERS = 1 << 0;
+ public static final int ROUNDED_BOTTOM_CORNERS = 1 << 1;
+
+ @IntDef(flag = true, value = {
+ ROUNDED_TOP_CORNERS,
+ ROUNDED_BOTTOM_CORNERS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RoundedCornerFlags {}
+
+ protected final Launcher mLauncher;
+ protected final LauncherAccessibilityDelegate mAccessibilityDelegate;
+ private final boolean mIsRtl;
+
+ public ShortcutsItemView mShortcutsItemView;
+
+ protected V mOriginalIcon;
+ private final Rect mTempRect = new Rect();
+ private PointF mInterceptTouchDown = new PointF();
+ private boolean mIsLeftAligned;
+ protected boolean mIsAboveIcon;
+ protected View mArrow;
+ private int mGravity;
+
+ protected Animator mOpenCloseAnimator;
+ protected boolean mDeferContainerRemoval;
+ private final Rect mStartRect = new Rect();
+ private final Rect mEndRect = new Rect();
+
+ public BaseActionPopup(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mLauncher = Launcher.getLauncher(context);
+
+ mAccessibilityDelegate = new ShortcutMenuAccessibilityDelegate(mLauncher);
+ mIsRtl = Utilities.isRtl(getResources());
+ }
+
+ public BaseActionPopup(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public BaseActionPopup(Context context) {
+ this(context, null, 0);
+ }
+
+ public LauncherAccessibilityDelegate getAccessibilityDelegate() {
+ return mAccessibilityDelegate;
+ }
+
+ protected PopupItemView getItemViewAt(int index) {
+ if (!mIsAboveIcon) {
+ // Opening down, so arrow is the first view.
+ index++;
+ }
+ return (PopupItemView) getChildAt(index);
+ }
+
+ protected int getItemCount() {
+ // All children except the arrow are items.
+ return getChildCount() - 1;
+ }
+
+ protected void animateOpen() {
+ setVisibility(View.VISIBLE);
+ mIsOpen = true;
+
+ final AnimatorSet openAnim = LauncherAnimUtils.createAnimatorSet();
+ final Resources res = getResources();
+ final long revealDuration = (long) res.getInteger(R.integer.config_popupOpenCloseDuration);
+ final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator();
+
+ // Rectangular reveal.
+ int itemsTotalHeight = 0;
+ for (int i = 0; i < getItemCount(); i++) {
+ itemsTotalHeight += getItemViewAt(i).getMeasuredHeight();
+ }
+ Point startPoint = computeAnimStartPoint(itemsTotalHeight);
+ int top = mIsAboveIcon ? getPaddingTop() : startPoint.y;
+ float radius = getItemViewAt(0).getBackgroundRadius();
+ mStartRect.set(startPoint.x, startPoint.y, startPoint.x, startPoint.y);
+ mEndRect.set(0, top, getMeasuredWidth(), top + itemsTotalHeight);
+ final ValueAnimator revealAnim = new RoundedRectRevealOutlineProvider
+ (radius, radius, mStartRect, mEndRect).createRevealAnimator(this, false);
+ revealAnim.setDuration(revealDuration);
+ revealAnim.setInterpolator(revealInterpolator);
+
+ Animator fadeIn = ObjectAnimator.ofFloat(this, ALPHA, 0, 1);
+ fadeIn.setDuration(revealDuration);
+ fadeIn.setInterpolator(revealInterpolator);
+ openAnim.play(fadeIn);
+
+ // Animate the arrow.
+ mArrow.setScaleX(0);
+ mArrow.setScaleY(0);
+ Animator arrowScale = createArrowScaleAnim(1).setDuration(res.getInteger(
+ R.integer.config_popupArrowOpenDuration));
+
+ openAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mOpenCloseAnimator = null;
+ Utilities.sendCustomAccessibilityEvent(
+ BaseActionPopup.this,
+ AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
+ getContext().getString(R.string.action_deep_shortcut));
+ }
+ });
+
+ mOpenCloseAnimator = openAnim;
+ openAnim.playSequentially(revealAnim, arrowScale);
+ openAnim.start();
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ enforceContainedWithinScreen(l, r);
+ }
+
+ private void enforceContainedWithinScreen(int left, int right) {
+ DragLayer dragLayer = mLauncher.getDragLayer();
+ if (getTranslationX() + left < 0 ||
+ getTranslationX() + right > dragLayer.getWidth()) {
+ // If we are still off screen, center horizontally too.
+ mGravity |= Gravity.CENTER_HORIZONTAL;
+ }
+
+ if (Gravity.isHorizontal(mGravity)) {
+ setX(dragLayer.getWidth() / 2 - getMeasuredWidth() / 2);
+ }
+ if (Gravity.isVertical(mGravity)) {
+ setY(dragLayer.getHeight() / 2 - getMeasuredHeight() / 2);
+ }
+ }
+
+ /**
+ * Returns the point at which the center of the arrow merges with the first popup item.
+ */
+ private Point computeAnimStartPoint(int itemsTotalHeight) {
+ int arrowCenterX = getResources().getDimensionPixelSize(mIsLeftAligned ^ mIsRtl ?
+ R.dimen.popup_arrow_horizontal_center_start:
+ R.dimen.popup_arrow_horizontal_center_end);
+ if (!mIsLeftAligned) {
+ arrowCenterX = getMeasuredWidth() - arrowCenterX;
+ }
+ int arrowHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom()
+ - itemsTotalHeight;
+ // The y-coordinate of edge between the arrow and the first popup item.
+ int arrowEdge = getPaddingTop() + (mIsAboveIcon ? itemsTotalHeight : arrowHeight);
+ return new Point(arrowCenterX, arrowEdge);
+ }
+
+ /**
+ * Orients this container above or below the given icon, aligning with the left or right.
+ *
+ * These are the preferred orientations, in order (RTL prefers right-aligned over left):
+ * - Above and left-aligned
+ * - Above and right-aligned
+ * - Below and left-aligned
+ * - Below and right-aligned
+ *
+ * So we always align left if there is enough horizontal space
+ * and align above if there is enough vertical space.
+ */
+ protected void orientAboutIcon(int arrowHeight) {
+ int width = getMeasuredWidth();
+ int height = getMeasuredHeight() + arrowHeight;
+
+ DragLayer dragLayer = mLauncher.getDragLayer();
+ dragLayer.getDescendantRectRelativeToSelf(mOriginalIcon, mTempRect);
+ Rect insets = dragLayer.getInsets();
+
+ // Align left (right in RTL) if there is room.
+ int leftAlignedX = mTempRect.left + mOriginalIcon.getPaddingLeft();
+ int rightAlignedX = mTempRect.right - width - mOriginalIcon.getPaddingRight();
+ int x = leftAlignedX;
+ boolean canBeLeftAligned = leftAlignedX + width + insets.left
+ < dragLayer.getRight() - insets.right;
+ boolean canBeRightAligned = rightAlignedX > dragLayer.getLeft() + insets.left;
+ if (!canBeLeftAligned || (mIsRtl && canBeRightAligned)) {
+ x = rightAlignedX;
+ }
+ mIsLeftAligned = x == leftAlignedX;
+ if (mIsRtl) {
+ x -= dragLayer.getWidth() - width;
+ }
+
+ // Offset x so that the arrow and shortcut icons are center-aligned with the original icon.
+ int iconWidth = mOriginalIcon.getWidth()
+ - mOriginalIcon.getTotalPaddingLeft() - mOriginalIcon.getTotalPaddingRight();
+ iconWidth *= mOriginalIcon.getScaleX();
+ Resources resources = getResources();
+ int xOffset;
+ if (isAlignedWithStart()) {
+ // Aligning with the shortcut icon.
+ int shortcutIconWidth = resources.getDimensionPixelSize(R.dimen.deep_shortcut_icon_size);
+ int shortcutPaddingStart = resources.getDimensionPixelSize(
+ R.dimen.popup_padding_start);
+ xOffset = iconWidth / 2 - shortcutIconWidth / 2 - shortcutPaddingStart;
+ } else {
+ // Aligning with the drag handle.
+ int shortcutDragHandleWidth = resources.getDimensionPixelSize(
+ R.dimen.deep_shortcut_drag_handle_size);
+ int shortcutPaddingEnd = resources.getDimensionPixelSize(
+ R.dimen.popup_padding_end);
+ xOffset = iconWidth / 2 - shortcutDragHandleWidth / 2 - shortcutPaddingEnd;
+ }
+ x += mIsLeftAligned ? xOffset : -xOffset;
+
+ // Open above icon if there is room.
+ int iconHeight = getIconHeightForPopupPlacement();
+ int y = mTempRect.top + mOriginalIcon.getPaddingTop() - height;
+ mIsAboveIcon = y > dragLayer.getTop() + insets.top;
+ if (!mIsAboveIcon) {
+ y = mTempRect.top + mOriginalIcon.getPaddingTop() + iconHeight;
+ }
+
+ // Insets are added later, so subtract them now.
+ if (mIsRtl) {
+ x += insets.right;
+ } else {
+ x -= insets.left;
+ }
+ y -= insets.top;
+
+ mGravity = 0;
+ if (y + height > dragLayer.getBottom() - insets.bottom) {
+ // The container is opening off the screen, so just center it in the drag layer instead.
+ mGravity = Gravity.CENTER_VERTICAL;
+ // Put the container next to the icon, preferring the right side in ltr (left in rtl).
+ int rightSide = leftAlignedX + iconWidth - insets.left;
+ int leftSide = rightAlignedX - iconWidth - insets.left;
+ if (!mIsRtl) {
+ if (rightSide + width < dragLayer.getRight()) {
+ x = rightSide;
+ mIsLeftAligned = true;
+ } else {
+ x = leftSide;
+ mIsLeftAligned = false;
+ }
+ } else {
+ if (leftSide > dragLayer.getLeft()) {
+ x = leftSide;
+ mIsLeftAligned = false;
+ } else {
+ x = rightSide;
+ mIsLeftAligned = true;
+ }
+ }
+ mIsAboveIcon = true;
+ }
+
+ setX(x);
+ setY(y);
+ }
+
+ protected int getIconHeightForPopupPlacement() {
+ return mOriginalIcon.getHeight();
+ }
+
+ protected boolean isAlignedWithStart() {
+ return mIsLeftAligned && !mIsRtl || !mIsLeftAligned && mIsRtl;
+ }
+
+ /**
+ * Adds an arrow view pointing at the original icon.
+ * @param horizontalOffset the horizontal offset of the arrow, so that it
+ * points at the center of the original icon
+ */
+ protected View addArrowView(int horizontalOffset, int verticalOffset, int width, int height) {
+ LayoutParams layoutParams = new LayoutParams(width, height);
+ if (mIsLeftAligned) {
+ layoutParams.gravity = Gravity.LEFT;
+ layoutParams.leftMargin = horizontalOffset;
+ } else {
+ layoutParams.gravity = Gravity.RIGHT;
+ layoutParams.rightMargin = horizontalOffset;
+ }
+ if (mIsAboveIcon) {
+ layoutParams.topMargin = verticalOffset;
+ } else {
+ layoutParams.bottomMargin = verticalOffset;
+ }
+
+ View arrowView = new View(getContext());
+ if (Gravity.isVertical(mGravity)) {
+ // This is only true if there wasn't room for the container next to the icon,
+ // so we centered it instead. In that case we don't want to show the arrow.
+ arrowView.setVisibility(INVISIBLE);
+ } else {
+ ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
+ width, height, !mIsAboveIcon));
+ Paint arrowPaint = arrowDrawable.getPaint();
+ arrowPaint.setColor(Themes.getAttrColor(mLauncher, R.attr.popupColorPrimary));
+ // The corner path effect won't be reflected in the shadow, but shouldn't be noticeable.
+ int radius = getResources().getDimensionPixelSize(R.dimen.popup_arrow_corner_radius);
+ arrowPaint.setPathEffect(new CornerPathEffect(radius));
+ arrowView.setBackground(arrowDrawable);
+ arrowView.setElevation(getElevation());
+ }
+ addView(arrowView, mIsAboveIcon ? getChildCount() : 0, layoutParams);
+ return arrowView;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mInterceptTouchDown.set(ev.getX(), ev.getY());
+ return false;
+ }
+ // Stop sending touch events to deep shortcut views if user moved beyond touch slop.
+ return Math.hypot(mInterceptTouchDown.x - ev.getX(), mInterceptTouchDown.y - ev.getY())
+ > ViewConfiguration.get(getContext()).getScaledTouchSlop();
+ }
+
+ protected ObjectAnimator createArrowScaleAnim(float scale) {
+ return LauncherAnimUtils.ofPropertyValuesHolder(
+ mArrow, new PropertyListBuilder().scale(scale).build());
+ }
+
+ @Override
+ protected void handleClose(boolean animate) {
+ if (animate) {
+ animateClose();
+ } else {
+ closeComplete();
+ }
+ }
+
+ protected void animateClose() {
+ if (!mIsOpen) {
+ return;
+ }
+ mEndRect.setEmpty();
+ if (mOpenCloseAnimator != null) {
+ Outline outline = new Outline();
+ getOutlineProvider().getOutline(this, outline);
+ outline.getRect(mEndRect);
+ mOpenCloseAnimator.cancel();
+ }
+ mIsOpen = false;
+
+ final AnimatorSet closeAnim = LauncherAnimUtils.createAnimatorSet();
+ prepareCloseAnimator(closeAnim);
+
+ closeAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mOpenCloseAnimator = null;
+ if (mDeferContainerRemoval) {
+ setVisibility(INVISIBLE);
+ } else {
+ closeComplete();
+ }
+ }
+ });
+ mOpenCloseAnimator = closeAnim;
+ closeAnim.start();
+ }
+
+ protected void prepareCloseAnimator(AnimatorSet closeAnim) {
+ final Resources res = getResources();
+ final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator();
+
+ // Rectangular reveal (reversed).
+ int itemsTotalHeight = 0;
+ for (int i = 0; i < getItemCount(); i++) {
+ itemsTotalHeight += getItemViewAt(i).getMeasuredHeight();
+ }
+ Point startPoint = computeAnimStartPoint(itemsTotalHeight);
+ int top = mIsAboveIcon ? getPaddingTop() : startPoint.y;
+ float radius = getItemViewAt(0).getBackgroundRadius();
+ mStartRect.set(startPoint.x, startPoint.y, startPoint.x, startPoint.y);
+ if (mEndRect.isEmpty()) {
+ mEndRect.set(0, top, getMeasuredWidth(), top + itemsTotalHeight);
+ }
+ final ValueAnimator revealAnim = new RoundedRectRevealOutlineProvider(
+ radius, radius, mStartRect, mEndRect).createRevealAnimator(this, true);
+ revealAnim.setInterpolator(revealInterpolator);
+ closeAnim.play(revealAnim);
+
+ Animator fadeOut = ObjectAnimator.ofFloat(this, ALPHA, 0);
+ fadeOut.setInterpolator(revealInterpolator);
+ closeAnim.play(fadeOut);
+ closeAnim.setDuration((long) res.getInteger(R.integer.config_popupOpenCloseDuration));
+ }
+
+ /**
+ * Closes the folder without animation.
+ */
+ protected void closeComplete() {
+ if (mOpenCloseAnimator != null) {
+ mOpenCloseAnimator.cancel();
+ mOpenCloseAnimator = null;
+ }
+ mIsOpen = false;
+ mDeferContainerRemoval = false;
+ mLauncher.getDragLayer().removeView(this);
+ }
+
+ @Override
+ protected boolean isOfType(int type) {
+ return (type & TYPE_ACTION_POPUP) != 0;
+ }
+
+ /**
+ * Returns a DeepShortcutsContainer which is already open or null
+ */
+ public static BaseActionPopup getOpen(Launcher launcher) {
+ return getOpenView(launcher, TYPE_ACTION_POPUP);
+ }
+
+ @Override
+ public void logActionCommand(int command) {
+ mLauncher.getUserEventDispatcher().logActionCommand(
+ command, mOriginalIcon, ContainerType.DEEPSHORTCUTS);
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ DragLayer dl = mLauncher.getDragLayer();
+ if (!dl.isEventOverView(this, ev)) {
+ mLauncher.getUserEventDispatcher().logActionTapOutside(
+ LoggerUtils.newContainerTarget(ContainerType.DEEPSHORTCUTS));
+ close(true);
+
+ // We let touches on the original icon go through so that users can launch
+ // the app with one tap if they don't find a shortcut they want.
+ return mOriginalIcon == null || !dl.isEventOverView(mOriginalIcon, ev);
+ }
+ }
+ return false;
+ }
+
+ public void populateAndShow(V originalIcon, PopupPopulator.Item[] itemsToPopulate) {
+ setVisibility(View.INVISIBLE);
+ mLauncher.getDragLayer().addView(this);
+
+ final Resources resources = getResources();
+ final int arrowWidth = resources.getDimensionPixelSize(R.dimen.popup_arrow_width);
+ final int arrowHeight = resources.getDimensionPixelSize(R.dimen.popup_arrow_height);
+ final int arrowVerticalOffset = resources.getDimensionPixelSize(
+ R.dimen.popup_arrow_vertical_offset);
+
+ mOriginalIcon = originalIcon;
+
+ // Add dummy views first, and populate with real info when ready.
+ addDummyViews(itemsToPopulate);
+
+ measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ orientAboutIcon(arrowHeight + arrowVerticalOffset);
+
+ boolean reverseOrder = mIsAboveIcon;
+ if (reverseOrder) {
+ removeAllViews();
+ mShortcutsItemView = null;
+ itemsToPopulate = PopupPopulator.reverseItems(itemsToPopulate);
+ addDummyViews(itemsToPopulate);
+
+ measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ orientAboutIcon(arrowHeight + arrowVerticalOffset);
+ }
+
+ // Add the arrow.
+ final int arrowHorizontalOffset = resources.getDimensionPixelSize(isAlignedWithStart() ?
+ R.dimen.popup_arrow_horizontal_offset_start :
+ R.dimen.popup_arrow_horizontal_offset_end);
+ mArrow = addArrowView(arrowHorizontalOffset, arrowVerticalOffset, arrowWidth, arrowHeight);
+ mArrow.setPivotX(arrowWidth / 2);
+ mArrow.setPivotY(mIsAboveIcon ? 0 : arrowHeight);
+
+ measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ animateOpen();
+ }
+
+ protected void addDummyViews(PopupPopulator.Item[] itemTypesToPopulate) {
+ final LayoutInflater inflater = mLauncher.getLayoutInflater();
+ int shortcutsItemRoundedCorners = ROUNDED_TOP_CORNERS | ROUNDED_BOTTOM_CORNERS;
+ int numItems = itemTypesToPopulate.length;
+ for (int i = 0; i < numItems; i++) {
+ PopupPopulator.Item itemTypeToPopulate = itemTypesToPopulate[i];
+ PopupPopulator.Item prevItemTypeToPopulate =
+ i > 0 ? itemTypesToPopulate[i - 1] : null;
+ PopupPopulator.Item nextItemTypeToPopulate =
+ i < numItems - 1 ? itemTypesToPopulate[i + 1] : null;
+ final View item = inflater.inflate(itemTypeToPopulate.layoutId, this, false);
+
+ boolean shouldUnroundTopCorners = prevItemTypeToPopulate != null
+ && itemTypeToPopulate.isShortcut ^ prevItemTypeToPopulate.isShortcut;
+ boolean shouldUnroundBottomCorners = nextItemTypeToPopulate != null
+ && itemTypeToPopulate.isShortcut ^ nextItemTypeToPopulate.isShortcut;
+
+ onViewInflated(item, itemTypeToPopulate,
+ shouldUnroundTopCorners, shouldUnroundBottomCorners);
+
+ if (itemTypeToPopulate.isShortcut) {
+ if (mShortcutsItemView == null) {
+ mShortcutsItemView = (ShortcutsItemView) inflater.inflate(
+ R.layout.shortcuts_item, this, false);
+ addView(mShortcutsItemView);
+ if (shouldUnroundTopCorners) {
+ shortcutsItemRoundedCorners &= ~ROUNDED_TOP_CORNERS;
+ }
+ }
+ mShortcutsItemView.addShortcutView(item, itemTypeToPopulate);
+ if (shouldUnroundBottomCorners) {
+ shortcutsItemRoundedCorners &= ~ROUNDED_BOTTOM_CORNERS;
+ }
+ } else {
+ addView(item);
+ }
+ }
+ int backgroundColor = Themes.getAttrColor(mLauncher, R.attr.popupColorPrimary);
+ mShortcutsItemView.setBackgroundWithCorners(backgroundColor, shortcutsItemRoundedCorners);
+ }
+
+ protected void onViewInflated(View view, PopupPopulator.Item itemType,
+ boolean shouldUnroundTopCorners, boolean shouldUnroundBottomCorners) {
+
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 8441598..4435afb 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -16,123 +16,71 @@
package com.android.launcher3.popup;
+import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS_IF_NOTIFICATIONS;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
-import android.graphics.CornerPathEffect;
-import android.graphics.Outline;
-import android.graphics.Paint;
-import android.graphics.Point;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.drawable.ShapeDrawable;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
-import android.support.annotation.IntDef;
import android.util.AttributeSet;
-import android.view.Gravity;
import android.view.LayoutInflater;
-import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
+import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
-import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate;
-import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.anim.PropertyResetListener;
-import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.launcher3.badge.BadgeInfo;
import com.android.launcher3.dragndrop.DragController;
-import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.graphics.IconPalette;
-import com.android.launcher3.graphics.TriangleShape;
import com.android.launcher3.notification.NotificationItemView;
import com.android.launcher3.notification.NotificationKeyData;
+import com.android.launcher3.popup.PopupPopulator.Item;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.shortcuts.ShortcutsItemView;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Themes;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS_IF_NOTIFICATIONS;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-
/**
- * A container for shortcuts to deep links within apps.
+ * A container for shortcuts to deep links and notifications associated with an app.
*/
@TargetApi(Build.VERSION_CODES.N)
-public class PopupContainerWithArrow extends AbstractFloatingView implements DragSource,
+public class PopupContainerWithArrow extends BaseActionPopup<BubbleTextView> implements DragSource,
DragController.DragListener {
- public static final int ROUNDED_TOP_CORNERS = 1 << 0;
- public static final int ROUNDED_BOTTOM_CORNERS = 1 << 1;
-
- @IntDef(flag = true, value = {
- ROUNDED_TOP_CORNERS,
- ROUNDED_BOTTOM_CORNERS
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface RoundedCornerFlags {}
-
- protected final Launcher mLauncher;
private final int mStartDragThreshold;
- private LauncherAccessibilityDelegate mAccessibilityDelegate;
- private final boolean mIsRtl;
- public ShortcutsItemView mShortcutsItemView;
private NotificationItemView mNotificationItemView;
-
- protected BubbleTextView mOriginalIcon;
- private final Rect mTempRect = new Rect();
- private PointF mInterceptTouchDown = new PointF();
- private boolean mIsLeftAligned;
- protected boolean mIsAboveIcon;
- private View mArrow;
- private int mGravity;
-
- protected Animator mOpenCloseAnimator;
- private boolean mDeferContainerRemoval;
private AnimatorSet mReduceHeightAnimatorSet;
- private final Rect mStartRect = new Rect();
- private final Rect mEndRect = new Rect();
+ private int mNumNotifications;
public PopupContainerWithArrow(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mLauncher = Launcher.getLauncher(context);
-
mStartDragThreshold = getResources().getDimensionPixelSize(
R.dimen.deep_shortcuts_start_drag_threshold);
- mAccessibilityDelegate = new ShortcutMenuAccessibilityDelegate(mLauncher);
- mIsRtl = Utilities.isRtl(getResources());
}
public PopupContainerWithArrow(Context context, AttributeSet attrs) {
@@ -143,10 +91,6 @@
this(context, null, 0);
}
- public LauncherAccessibilityDelegate getAccessibilityDelegate() {
- return mAccessibilityDelegate;
- }
-
/**
* Shows the notifications and deep shortcuts associated with {@param icon}.
* @return the container if shown or null.
@@ -173,49 +117,24 @@
final PopupContainerWithArrow container =
(PopupContainerWithArrow) launcher.getLayoutInflater().inflate(
R.layout.popup_container, launcher.getDragLayer(), false);
- container.setVisibility(View.INVISIBLE);
- launcher.getDragLayer().addView(container);
container.populateAndShow(icon, shortcutIds, notificationKeys, systemShortcuts);
return container;
}
- public void populateAndShow(final BubbleTextView originalIcon, final List<String> shortcutIds,
+ private void populateAndShow(final BubbleTextView originalIcon, final List<String> shortcutIds,
final List<NotificationKeyData> notificationKeys, List<SystemShortcut> systemShortcuts) {
- final Resources resources = getResources();
- final int arrowWidth = resources.getDimensionPixelSize(R.dimen.popup_arrow_width);
- final int arrowHeight = resources.getDimensionPixelSize(R.dimen.popup_arrow_height);
- final int arrowVerticalOffset = resources.getDimensionPixelSize(
- R.dimen.popup_arrow_vertical_offset);
-
- mOriginalIcon = originalIcon;
-
- // Add dummy views first, and populate with real info when ready.
+ mNumNotifications = notificationKeys.size();
PopupPopulator.Item[] itemsToPopulate = PopupPopulator
.getItemsToPopulate(shortcutIds, notificationKeys, systemShortcuts);
- addDummyViews(itemsToPopulate, notificationKeys.size());
-
- measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- orientAboutIcon(originalIcon, arrowHeight + arrowVerticalOffset);
-
- boolean reverseOrder = mIsAboveIcon;
- if (reverseOrder) {
- removeAllViews();
- mNotificationItemView = null;
- mShortcutsItemView = null;
- itemsToPopulate = PopupPopulator.reverseItems(itemsToPopulate);
- addDummyViews(itemsToPopulate, notificationKeys.size());
-
- measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- orientAboutIcon(originalIcon, arrowHeight + arrowVerticalOffset);
- }
+ populateAndShow(originalIcon, itemsToPopulate);
ItemInfo originalItemInfo = (ItemInfo) originalIcon.getTag();
List<DeepShortcutView> shortcutViews = mShortcutsItemView == null
? Collections.EMPTY_LIST
- : mShortcutsItemView.getDeepShortcutViews(reverseOrder);
+ : mShortcutsItemView.getDeepShortcutViews(mIsAboveIcon);
List<View> systemShortcutViews = mShortcutsItemView == null
? Collections.EMPTY_LIST
- : mShortcutsItemView.getSystemShortcutViews(reverseOrder);
+ : mShortcutsItemView.getSystemShortcutViews(mIsAboveIcon);
if (mNotificationItemView != null) {
updateNotificationHeader();
}
@@ -231,17 +150,6 @@
numNotifications, originalIcon.getContentDescription().toString()));
}
- // Add the arrow.
- final int arrowHorizontalOffset = resources.getDimensionPixelSize(isAlignedWithStart() ?
- R.dimen.popup_arrow_horizontal_offset_start :
- R.dimen.popup_arrow_horizontal_offset_end);
- mArrow = addArrowView(arrowHorizontalOffset, arrowVerticalOffset, arrowWidth, arrowHeight);
- mArrow.setPivotX(arrowWidth / 2);
- mArrow.setPivotY(mIsAboveIcon ? 0 : arrowHeight);
-
- measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- animateOpen();
-
mLauncher.getDragController().addDragListener(this);
mOriginalIcon.forceHideBadge(true);
@@ -253,6 +161,60 @@
systemShortcuts, systemShortcutViews));
}
+ @Override
+ protected void addDummyViews(Item[] itemTypesToPopulate) {
+ mNotificationItemView = null;
+ super.addDummyViews(itemTypesToPopulate);
+ if (mNumNotifications > 0) {
+ mShortcutsItemView.hideShortcuts(mIsAboveIcon, MAX_SHORTCUTS_IF_NOTIFICATIONS);
+ }
+ }
+
+ @Override
+ protected void onViewInflated(View view, Item itemType,
+ boolean shouldUnroundTopCorners, boolean shouldUnroundBottomCorners) {
+ if (itemType == PopupPopulator.Item.NOTIFICATION) {
+ mNotificationItemView = (NotificationItemView) view;
+ boolean notificationFooterHasIcons = mNumNotifications > 1;
+ int footerHeight = getResources().getDimensionPixelSize(
+ notificationFooterHasIcons ? R.dimen.notification_footer_height
+ : R.dimen.notification_empty_footer_height);
+ view.findViewById(R.id.footer).getLayoutParams().height = footerHeight;
+ if (notificationFooterHasIcons) {
+ mNotificationItemView.findViewById(R.id.divider).setVisibility(VISIBLE);
+ }
+
+ int roundedCorners = ROUNDED_TOP_CORNERS | ROUNDED_BOTTOM_CORNERS;
+ if (shouldUnroundTopCorners) {
+ roundedCorners &= ~ROUNDED_TOP_CORNERS;
+ mNotificationItemView.findViewById(R.id.gutter_top).setVisibility(VISIBLE);
+ }
+ if (shouldUnroundBottomCorners) {
+ roundedCorners &= ~ROUNDED_BOTTOM_CORNERS;
+ mNotificationItemView.findViewById(R.id.gutter_bottom).setVisibility(VISIBLE);
+ }
+ int backgroundColor = Themes.getAttrColor(mLauncher, R.attr.popupColorTertiary);
+ mNotificationItemView.setBackgroundWithCorners(backgroundColor, roundedCorners);
+
+ mNotificationItemView.getMainView().setAccessibilityDelegate(mAccessibilityDelegate);
+ } else if (itemType == PopupPopulator.Item.SHORTCUT) {
+ view.setAccessibilityDelegate(mAccessibilityDelegate);
+ }
+
+ if (itemType != PopupPopulator.Item.SYSTEM_SHORTCUT_ICON && itemType.isShortcut
+ && mNumNotifications > 0) {
+ int prevHeight = view.getLayoutParams().height;
+ // Condense shortcuts height when there are notifications.
+ view.getLayoutParams().height = getResources().getDimensionPixelSize(
+ R.dimen.bg_popup_item_condensed_height);
+ if (view instanceof DeepShortcutView) {
+ float iconScale = (float) view.getLayoutParams().height / prevHeight;
+ ((DeepShortcutView) view).getIconView().setScaleX(iconScale);
+ ((DeepShortcutView) view).getIconView().setScaleY(iconScale);
+ }
+ }
+ }
+
private void addDummyViews(PopupPopulator.Item[] itemTypesToPopulate, int numNotifications) {
final Resources res = getResources();
final LayoutInflater inflater = mLauncher.getLayoutInflater();
@@ -336,266 +298,18 @@
}
}
- protected PopupItemView getItemViewAt(int index) {
- if (!mIsAboveIcon) {
- // Opening down, so arrow is the first view.
- index++;
+ @Override
+ protected void onWidgetsBound() {
+ if (mShortcutsItemView != null) {
+ mShortcutsItemView.enableWidgetsIfExist(mOriginalIcon);
}
- return (PopupItemView) getChildAt(index);
- }
-
- protected int getItemCount() {
- // All children except the arrow are items.
- return getChildCount() - 1;
- }
-
- private void animateOpen() {
- setVisibility(View.VISIBLE);
- mIsOpen = true;
-
- final AnimatorSet openAnim = LauncherAnimUtils.createAnimatorSet();
- final Resources res = getResources();
- final long revealDuration = (long) res.getInteger(R.integer.config_popupOpenCloseDuration);
- final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator();
-
- // Rectangular reveal.
- int itemsTotalHeight = 0;
- for (int i = 0; i < getItemCount(); i++) {
- itemsTotalHeight += getItemViewAt(i).getMeasuredHeight();
- }
- Point startPoint = computeAnimStartPoint(itemsTotalHeight);
- int top = mIsAboveIcon ? getPaddingTop() : startPoint.y;
- float radius = getItemViewAt(0).getBackgroundRadius();
- mStartRect.set(startPoint.x, startPoint.y, startPoint.x, startPoint.y);
- mEndRect.set(0, top, getMeasuredWidth(), top + itemsTotalHeight);
- final ValueAnimator revealAnim = new RoundedRectRevealOutlineProvider
- (radius, radius, mStartRect, mEndRect).createRevealAnimator(this, false);
- revealAnim.setDuration(revealDuration);
- revealAnim.setInterpolator(revealInterpolator);
-
- Animator fadeIn = ObjectAnimator.ofFloat(this, ALPHA, 0, 1);
- fadeIn.setDuration(revealDuration);
- fadeIn.setInterpolator(revealInterpolator);
- openAnim.play(fadeIn);
-
- // Animate the arrow.
- mArrow.setScaleX(0);
- mArrow.setScaleY(0);
- Animator arrowScale = createArrowScaleAnim(1).setDuration(res.getInteger(
- R.integer.config_popupArrowOpenDuration));
-
- openAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mOpenCloseAnimator = null;
- Utilities.sendCustomAccessibilityEvent(
- PopupContainerWithArrow.this,
- AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
- getContext().getString(R.string.action_deep_shortcut));
- }
- });
-
- mOpenCloseAnimator = openAnim;
- openAnim.playSequentially(revealAnim, arrowScale);
- openAnim.start();
}
@Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
- enforceContainedWithinScreen(l, r);
-
- }
-
- private void enforceContainedWithinScreen(int left, int right) {
- DragLayer dragLayer = mLauncher.getDragLayer();
- if (getTranslationX() + left < 0 ||
- getTranslationX() + right > dragLayer.getWidth()) {
- // If we are still off screen, center horizontally too.
- mGravity |= Gravity.CENTER_HORIZONTAL;
- }
-
- if (Gravity.isHorizontal(mGravity)) {
- setX(dragLayer.getWidth() / 2 - getMeasuredWidth() / 2);
- }
- if (Gravity.isVertical(mGravity)) {
- setY(dragLayer.getHeight() / 2 - getMeasuredHeight() / 2);
- }
- }
-
- /**
- * Returns the point at which the center of the arrow merges with the first popup item.
- */
- private Point computeAnimStartPoint(int itemsTotalHeight) {
- int arrowCenterX = getResources().getDimensionPixelSize(mIsLeftAligned ^ mIsRtl ?
- R.dimen.popup_arrow_horizontal_center_start:
- R.dimen.popup_arrow_horizontal_center_end);
- if (!mIsLeftAligned) {
- arrowCenterX = getMeasuredWidth() - arrowCenterX;
- }
- int arrowHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom()
- - itemsTotalHeight;
- // The y-coordinate of edge between the arrow and the first popup item.
- int arrowEdge = getPaddingTop() + (mIsAboveIcon ? itemsTotalHeight : arrowHeight);
- return new Point(arrowCenterX, arrowEdge);
- }
-
- /**
- * Orients this container above or below the given icon, aligning with the left or right.
- *
- * These are the preferred orientations, in order (RTL prefers right-aligned over left):
- * - Above and left-aligned
- * - Above and right-aligned
- * - Below and left-aligned
- * - Below and right-aligned
- *
- * So we always align left if there is enough horizontal space
- * and align above if there is enough vertical space.
- */
- private void orientAboutIcon(BubbleTextView icon, int arrowHeight) {
- int width = getMeasuredWidth();
- int height = getMeasuredHeight() + arrowHeight;
-
- DragLayer dragLayer = mLauncher.getDragLayer();
- dragLayer.getDescendantRectRelativeToSelf(icon, mTempRect);
- Rect insets = dragLayer.getInsets();
-
- // Align left (right in RTL) if there is room.
- int leftAlignedX = mTempRect.left + icon.getPaddingLeft();
- int rightAlignedX = mTempRect.right - width - icon.getPaddingRight();
- int x = leftAlignedX;
- boolean canBeLeftAligned = leftAlignedX + width + insets.left
- < dragLayer.getRight() - insets.right;
- boolean canBeRightAligned = rightAlignedX > dragLayer.getLeft() + insets.left;
- if (!canBeLeftAligned || (mIsRtl && canBeRightAligned)) {
- x = rightAlignedX;
- }
- mIsLeftAligned = x == leftAlignedX;
- if (mIsRtl) {
- x -= dragLayer.getWidth() - width;
- }
-
- // Offset x so that the arrow and shortcut icons are center-aligned with the original icon.
- int iconWidth = icon.getWidth() - icon.getTotalPaddingLeft() - icon.getTotalPaddingRight();
- iconWidth *= icon.getScaleX();
- Resources resources = getResources();
- int xOffset;
- if (isAlignedWithStart()) {
- // Aligning with the shortcut icon.
- int shortcutIconWidth = resources.getDimensionPixelSize(R.dimen.deep_shortcut_icon_size);
- int shortcutPaddingStart = resources.getDimensionPixelSize(
- R.dimen.popup_padding_start);
- xOffset = iconWidth / 2 - shortcutIconWidth / 2 - shortcutPaddingStart;
- } else {
- // Aligning with the drag handle.
- int shortcutDragHandleWidth = resources.getDimensionPixelSize(
- R.dimen.deep_shortcut_drag_handle_size);
- int shortcutPaddingEnd = resources.getDimensionPixelSize(
- R.dimen.popup_padding_end);
- xOffset = iconWidth / 2 - shortcutDragHandleWidth / 2 - shortcutPaddingEnd;
- }
- x += mIsLeftAligned ? xOffset : -xOffset;
-
- // Open above icon if there is room.
- int iconHeight = icon.getIcon() != null
- ? icon.getIcon().getBounds().height()
- : icon.getHeight();
- int y = mTempRect.top + icon.getPaddingTop() - height;
- mIsAboveIcon = y > dragLayer.getTop() + insets.top;
- if (!mIsAboveIcon) {
- y = mTempRect.top + icon.getPaddingTop() + iconHeight;
- }
-
- // Insets are added later, so subtract them now.
- if (mIsRtl) {
- x += insets.right;
- } else {
- x -= insets.left;
- }
- y -= insets.top;
-
- mGravity = 0;
- if (y + height > dragLayer.getBottom() - insets.bottom) {
- // The container is opening off the screen, so just center it in the drag layer instead.
- mGravity = Gravity.CENTER_VERTICAL;
- // Put the container next to the icon, preferring the right side in ltr (left in rtl).
- int rightSide = leftAlignedX + iconWidth - insets.left;
- int leftSide = rightAlignedX - iconWidth - insets.left;
- if (!mIsRtl) {
- if (rightSide + width < dragLayer.getRight()) {
- x = rightSide;
- mIsLeftAligned = true;
- } else {
- x = leftSide;
- mIsLeftAligned = false;
- }
- } else {
- if (leftSide > dragLayer.getLeft()) {
- x = leftSide;
- mIsLeftAligned = false;
- } else {
- x = rightSide;
- mIsLeftAligned = true;
- }
- }
- mIsAboveIcon = true;
- }
-
- setX(x);
- setY(y);
- }
-
- private boolean isAlignedWithStart() {
- return mIsLeftAligned && !mIsRtl || !mIsLeftAligned && mIsRtl;
- }
-
- /**
- * Adds an arrow view pointing at the original icon.
- * @param horizontalOffset the horizontal offset of the arrow, so that it
- * points at the center of the original icon
- */
- private View addArrowView(int horizontalOffset, int verticalOffset, int width, int height) {
- LayoutParams layoutParams = new LayoutParams(width, height);
- if (mIsLeftAligned) {
- layoutParams.gravity = Gravity.LEFT;
- layoutParams.leftMargin = horizontalOffset;
- } else {
- layoutParams.gravity = Gravity.RIGHT;
- layoutParams.rightMargin = horizontalOffset;
- }
- if (mIsAboveIcon) {
- layoutParams.topMargin = verticalOffset;
- } else {
- layoutParams.bottomMargin = verticalOffset;
- }
-
- View arrowView = new View(getContext());
- if (Gravity.isVertical(mGravity)) {
- // This is only true if there wasn't room for the container next to the icon,
- // so we centered it instead. In that case we don't want to show the arrow.
- arrowView.setVisibility(INVISIBLE);
- } else {
- ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
- width, height, !mIsAboveIcon));
- Paint arrowPaint = arrowDrawable.getPaint();
- // Note that we have to use getChildAt() instead of getItemViewAt(),
- // since the latter expects the arrow which hasn't been added yet.
- PopupItemView itemAttachedToArrow = (PopupItemView)
- (getChildAt(mIsAboveIcon ? getChildCount() - 1 : 0));
- arrowPaint.setColor(Themes.getAttrColor(mLauncher, R.attr.popupColorPrimary));
- // The corner path effect won't be reflected in the shadow, but shouldn't be noticeable.
- int radius = getResources().getDimensionPixelSize(R.dimen.popup_arrow_corner_radius);
- arrowPaint.setPathEffect(new CornerPathEffect(radius));
- arrowView.setBackground(arrowDrawable);
- arrowView.setElevation(getElevation());
- }
- addView(arrowView, mIsAboveIcon ? getChildCount() : 0, layoutParams);
- return arrowView;
- }
-
- @Override
- public View getExtendedTouchView() {
- return mOriginalIcon;
+ protected int getIconHeightForPopupPlacement() {
+ return mOriginalIcon.getIcon() != null
+ ? mOriginalIcon.getIcon().getBounds().height()
+ : mOriginalIcon.getHeight();
}
/**
@@ -642,17 +356,6 @@
};
}
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mInterceptTouchDown.set(ev.getX(), ev.getY());
- return false;
- }
- // Stop sending touch events to deep shortcut views if user moved beyond touch slop.
- return Math.hypot(mInterceptTouchDown.x - ev.getX(), mInterceptTouchDown.y - ev.getY())
- > ViewConfiguration.get(getContext()).getScaledTouchSlop();
- }
-
/**
* Updates the notification header if the original icon's badge updated.
*/
@@ -723,18 +426,6 @@
badgeInfo.getNotificationKeys()));
}
- @Override
- protected void onWidgetsBound() {
- if (mShortcutsItemView != null) {
- mShortcutsItemView.enableWidgetsIfExist(mOriginalIcon);
- }
- }
-
- private ObjectAnimator createArrowScaleAnim(float scale) {
- return LauncherAnimUtils.ofPropertyValuesHolder(
- mArrow, new PropertyListBuilder().scale(scale).build());
- }
-
public Animator reduceNotificationViewHeight(int heightToRemove, int duration) {
return adjustItemHeights(heightToRemove, 0, duration);
}
@@ -797,29 +488,7 @@
}
@Override
- public boolean supportsAppInfoDropTarget() {
- return true;
- }
-
- @Override
- public boolean supportsDeleteDropTarget() {
- return false;
- }
-
- @Override
- public float getIntrinsicIconScaleFactor() {
- return 1f;
- }
-
- @Override
- public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete,
- boolean success) {
- if (!success) {
- d.dragView.remove();
- mLauncher.showWorkspace(true);
- mLauncher.getDropTargetBar().onDragEnd();
- }
- }
+ public void onDropCompleted(View target, DragObject d, boolean success) { }
@Override
public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
@@ -851,106 +520,20 @@
}
@Override
- protected void handleClose(boolean animate) {
- if (animate) {
- animateClose();
- } else {
- closeComplete();
- }
- }
-
- protected void animateClose() {
- if (!mIsOpen) {
- return;
- }
- mEndRect.setEmpty();
- if (mOpenCloseAnimator != null) {
- Outline outline = new Outline();
- getOutlineProvider().getOutline(this, outline);
- outline.getRect(mEndRect);
- mOpenCloseAnimator.cancel();
- }
- mIsOpen = false;
-
- final AnimatorSet closeAnim = LauncherAnimUtils.createAnimatorSet();
- final Resources res = getResources();
- final long revealDuration = (long) res.getInteger(R.integer.config_popupOpenCloseDuration);
- final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator();
-
- // Rectangular reveal (reversed).
- int itemsTotalHeight = 0;
- for (int i = 0; i < getItemCount(); i++) {
- itemsTotalHeight += getItemViewAt(i).getMeasuredHeight();
- }
- Point startPoint = computeAnimStartPoint(itemsTotalHeight);
- int top = mIsAboveIcon ? getPaddingTop() : startPoint.y;
- float radius = getItemViewAt(0).getBackgroundRadius();
- mStartRect.set(startPoint.x, startPoint.y, startPoint.x, startPoint.y);
- if (mEndRect.isEmpty()) {
- mEndRect.set(0, top, getMeasuredWidth(), top + itemsTotalHeight);
- }
- final ValueAnimator revealAnim = new RoundedRectRevealOutlineProvider(
- radius, radius, mStartRect, mEndRect).createRevealAnimator(this, true);
- revealAnim.setDuration(revealDuration);
- revealAnim.setInterpolator(revealInterpolator);
- closeAnim.play(revealAnim);
-
- Animator fadeOut = ObjectAnimator.ofFloat(this, ALPHA, 0);
- fadeOut.setDuration(revealDuration);
- fadeOut.setInterpolator(revealInterpolator);
- closeAnim.play(fadeOut);
-
+ protected void prepareCloseAnimator(AnimatorSet closeAnim) {
// Animate original icon's text back in.
- Animator fadeText = mOriginalIcon.createTextAlphaAnimator(true /* fadeIn */);
- fadeText.setDuration(revealDuration);
- closeAnim.play(fadeText);
+ closeAnim.play(mOriginalIcon.createTextAlphaAnimator(true /* fadeIn */));
- closeAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mOpenCloseAnimator = null;
- if (mDeferContainerRemoval) {
- setVisibility(INVISIBLE);
- } else {
- closeComplete();
- }
- }
- });
- mOpenCloseAnimator = closeAnim;
- closeAnim.start();
mOriginalIcon.forceHideBadge(false);
+ super.prepareCloseAnimator(closeAnim);
}
- /**
- * Closes the folder without animation.
- */
+ @Override
protected void closeComplete() {
- if (mOpenCloseAnimator != null) {
- mOpenCloseAnimator.cancel();
- mOpenCloseAnimator = null;
- }
- mIsOpen = false;
- mDeferContainerRemoval = false;
mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible());
mOriginalIcon.forceHideBadge(false);
+
mLauncher.getDragController().removeDragListener(this);
- mLauncher.getDragLayer().removeView(this);
- }
-
- @Override
- protected boolean isOfType(int type) {
- return (type & TYPE_POPUP_CONTAINER_WITH_ARROW) != 0;
- }
-
- /**
- * Returns a DeepShortcutsContainer which is already open or null
- */
- public static PopupContainerWithArrow getOpen(Launcher launcher) {
- return getOpenView(launcher, TYPE_POPUP_CONTAINER_WITH_ARROW);
- }
-
- @Override
- public int getLogContainerType() {
- return ContainerType.DEEPSHORTCUTS;
+ super.closeComplete();
}
}
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index de9f25e..070ac39 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -25,6 +25,8 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
import com.android.launcher3.badge.BadgeInfo;
+import com.android.launcher3.model.PackageItemInfo;
+import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.notification.NotificationInfo;
import com.android.launcher3.notification.NotificationKeyData;
import com.android.launcher3.notification.NotificationListener;
@@ -32,6 +34,7 @@
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.widget.WidgetListRowEntry;
import java.util.ArrayList;
import java.util.Collections;
@@ -53,6 +56,7 @@
private static final SystemShortcut[] SYSTEM_SHORTCUTS = new SystemShortcut[] {
new SystemShortcut.AppInfo(),
new SystemShortcut.Widgets(),
+ new SystemShortcut.Install()
};
private final Launcher mLauncher;
@@ -61,6 +65,8 @@
private MultiHashMap<ComponentKey, String> mDeepShortcutMap = new MultiHashMap<>();
/** Maps packages to their BadgeInfo's . */
private Map<PackageUserKey, BadgeInfo> mPackageUserToBadgeInfos = new HashMap<>();
+ /** Maps packages to their Widgets */
+ private ArrayList<WidgetListRowEntry> mAllWidgets = new ArrayList<>();
public PopupDataProvider(Launcher launcher) {
mLauncher = launcher;
@@ -101,11 +107,7 @@
mPackageUserToBadgeInfos.remove(removedPackageUserKey);
}
updateLauncherIconBadges(Utilities.singletonHashSet(removedPackageUserKey));
-
- PopupContainerWithArrow openContainer = PopupContainerWithArrow.getOpen(mLauncher);
- if (openContainer != null) {
- openContainer.trimNotifications(mPackageUserToBadgeInfos);
- }
+ trimNotifications(mPackageUserToBadgeInfos);
}
}
@@ -142,10 +144,13 @@
if (!updatedBadges.isEmpty()) {
updateLauncherIconBadges(updatedBadges.keySet());
}
+ trimNotifications(updatedBadges);
+ }
- PopupContainerWithArrow openContainer = PopupContainerWithArrow.getOpen(mLauncher);
- if (openContainer != null) {
- openContainer.trimNotifications(updatedBadges);
+ private void trimNotifications(Map<PackageUserKey, BadgeInfo> updatedBadges) {
+ BaseActionPopup openContainer = BaseActionPopup.getOpen(mLauncher);
+ if (openContainer instanceof PopupContainerWithArrow) {
+ ((PopupContainerWithArrow) openContainer).trimNotifications(updatedBadges);
}
}
@@ -265,4 +270,29 @@
}
notificationListener.cancelNotification(notificationKey);
}
+
+ public void setAllWidgets(ArrayList<WidgetListRowEntry> allWidgets) {
+ mAllWidgets = allWidgets;
+ }
+
+ public ArrayList<WidgetListRowEntry> getAllWidgets() {
+ return mAllWidgets;
+ }
+
+ public List<WidgetItem> getWidgetsForPackageUser(PackageUserKey packageUserKey) {
+ for (WidgetListRowEntry entry : mAllWidgets) {
+ if (entry.pkgItem.packageName.equals(packageUserKey.mPackageName)) {
+ ArrayList<WidgetItem> widgets = new ArrayList<>(entry.widgets);
+ // Remove widgets not associated with the correct user.
+ Iterator<WidgetItem> iterator = widgets.iterator();
+ while (iterator.hasNext()) {
+ if (!iterator.next().user.equals(packageUserKey.mUser)) {
+ iterator.remove();
+ }
+ }
+ return widgets.isEmpty() ? null : widgets;
+ }
+ }
+ return null;
+ }
}
diff --git a/src/com/android/launcher3/popup/PopupItemView.java b/src/com/android/launcher3/popup/PopupItemView.java
index 8ec051b..75c3f26 100644
--- a/src/com/android/launcher3/popup/PopupItemView.java
+++ b/src/com/android/launcher3/popup/PopupItemView.java
@@ -32,7 +32,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.popup.PopupContainerWithArrow.RoundedCornerFlags;
+import com.android.launcher3.popup.BaseActionPopup.RoundedCornerFlags;
import static com.android.launcher3.popup.PopupContainerWithArrow.ROUNDED_BOTTOM_CORNERS;
import static com.android.launcher3.popup.PopupContainerWithArrow.ROUNDED_TOP_CORNERS;
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 6254d2d..e709b93 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -1,6 +1,7 @@
package com.android.launcher3.popup;
import android.content.Context;
+import android.content.Intent;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -11,7 +12,10 @@
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
+import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.util.InstantAppResolver;
+import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.widget.WidgetsBottomSheet;
@@ -55,8 +59,9 @@
@Override
public View.OnClickListener getOnClickListener(final Launcher launcher,
final ItemInfo itemInfo) {
- final List<WidgetItem> widgets = launcher.getWidgetsForPackageUser(new PackageUserKey(
- itemInfo.getTargetComponent().getPackageName(), itemInfo.user));
+ final List<WidgetItem> widgets =
+ launcher.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey(
+ itemInfo.getTargetComponent().getPackageName(), itemInfo.user));
if (widgets == null) {
return null;
}
@@ -88,11 +93,42 @@
public void onClick(View view) {
Rect sourceBounds = launcher.getViewBounds(view);
Bundle opts = launcher.getActivityLaunchOptions(view);
- InfoDropTarget.startDetailsActivityForInfo(itemInfo, launcher, null, sourceBounds, opts);
+ InfoDropTarget.startDetailsActivityForInfo(itemInfo, launcher, sourceBounds, opts);
launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
ControlType.APPINFO_TARGET, view);
}
};
}
}
+
+ public static class Install extends SystemShortcut {
+ public Install() {
+ super(R.drawable.ic_install_no_shadow, R.string.install_drop_target_label);
+ }
+
+ @Override
+ public View.OnClickListener getOnClickListener(final Launcher launcher,
+ final ItemInfo itemInfo) {
+ boolean supportsWebUI = (itemInfo instanceof ShortcutInfo) &&
+ ((ShortcutInfo) itemInfo).hasStatusFlag(ShortcutInfo.FLAG_SUPPORTS_WEB_UI);
+ boolean isInstantApp = false;
+ if (itemInfo instanceof com.android.launcher3.AppInfo) {
+ com.android.launcher3.AppInfo appInfo = (com.android.launcher3.AppInfo) itemInfo;
+ isInstantApp = InstantAppResolver.newInstance(launcher).isInstantApp(appInfo);
+ }
+ boolean enabled = supportsWebUI || isInstantApp;
+ if (!enabled) {
+ return null;
+ }
+ return new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Intent intent = PackageManagerHelper.getMarketIntent(itemInfo
+ .getTargetComponent().getPackageName());
+ launcher.startActivitySafely(view, intent, itemInfo);
+ AbstractFloatingView.closeAllOpenViews(launcher);
+ }
+ };
+ }
+ }
}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
index e9d2b50..cfb9258 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
@@ -26,7 +26,6 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
import com.android.launcher3.graphics.DragPreviewProvider;
-import com.android.launcher3.graphics.HolographicOutlineHelper;
/**
* Extension of {@link DragPreviewProvider} which generates bitmaps scaled to the default icon size.
@@ -40,35 +39,17 @@
mPositionShift = shift;
}
- @Override
- public Bitmap createDragOutline(Canvas canvas) {
- Bitmap b = drawScaledPreview(canvas, Bitmap.Config.ALPHA_8);
-
- HolographicOutlineHelper.getInstance(mView.getContext())
- .applyExpensiveOutlineWithBlur(b, canvas);
- canvas.setBitmap(null);
- return b;
- }
-
- @Override
- public Bitmap createDragBitmap(Canvas canvas) {
- Bitmap b = drawScaledPreview(canvas, Bitmap.Config.ARGB_8888);
- canvas.setBitmap(null);
- return b;
- }
-
- private Bitmap drawScaledPreview(Canvas canvas, Bitmap.Config config) {
+ public Bitmap createDragBitmap() {
Drawable d = mView.getBackground();
Rect bounds = getDrawableBounds(d);
int size = Launcher.getLauncher(mView.getContext()).getDeviceProfile().iconSizePx;
-
final Bitmap b = Bitmap.createBitmap(
size + blurSizeOutline,
size + blurSizeOutline,
- config);
+ Bitmap.Config.ARGB_8888);
- canvas.setBitmap(b);
+ Canvas canvas = new Canvas(b);
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.translate(blurSizeOutline / 2, blurSizeOutline / 2);
canvas.scale(((float) size) / bounds.width(), ((float) size) / bounds.height(), 0, 0);
diff --git a/src/com/android/launcher3/states/AllAppsState.java b/src/com/android/launcher3/states/AllAppsState.java
new file mode 100644
index 0000000..ed3023a
--- /dev/null
+++ b/src/com/android/launcher3/states/AllAppsState.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 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.states;
+
+import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS;
+
+import android.view.View;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.R;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+
+/**
+ * Definition for AllApps state
+ */
+public class AllAppsState extends LauncherState {
+
+ public static final String APPS_VIEW_SHOWN = "launcher.apps_view_shown";
+
+ private static final int STATE_FLAGS = FLAG_DISABLE_ACCESSIBILITY;
+
+ public AllAppsState(int id) {
+ super(id, ContainerType.ALLAPPS, ALL_APPS_TRANSITION_MS, 0f, STATE_FLAGS);
+ }
+
+ @Override
+ public void onStateEnabled(Launcher launcher) {
+ if (!launcher.getSharedPrefs().getBoolean(APPS_VIEW_SHOWN, false)) {
+ launcher.getSharedPrefs().edit().putBoolean(APPS_VIEW_SHOWN, true).apply();
+ }
+
+ AbstractFloatingView.closeAllOpenViews(launcher);
+ dispatchWindowStateChanged(launcher);
+ }
+
+ @Override
+ public String getDescription(Launcher launcher) {
+ return launcher.getString(R.string.all_apps_button_label);
+ }
+
+ @Override
+ public View getFinalFocus(Launcher launcher) {
+ return launcher.getAppsView();
+ }
+}
diff --git a/src/com/android/launcher3/states/OverviewState.java b/src/com/android/launcher3/states/OverviewState.java
new file mode 100644
index 0000000..344a4f9
--- /dev/null
+++ b/src/com/android/launcher3/states/OverviewState.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2017 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.states;
+
+import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
+import static com.android.launcher3.Utilities.isAccessibilityEnabled;
+
+import android.graphics.Rect;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+
+/**
+ * Definition for overview state
+ */
+public class OverviewState extends LauncherState {
+
+ // The percent to shrink the workspace during overview mode
+ public static final float SCALE_FACTOR = 0.7f;
+
+ private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE | FLAG_HIDE_HOTSEAT;
+
+ public OverviewState(int id) {
+ super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, 1f, STATE_FLAGS);
+ }
+
+ @Override
+ public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
+ DeviceProfile grid = launcher.getDeviceProfile();
+ Workspace ws = launcher.getWorkspace();
+ Rect insets = launcher.getDragLayer().getInsets();
+
+ int overviewButtonBarHeight = grid.getOverviewModeButtonBarHeight();
+ int scaledHeight = (int) (SCALE_FACTOR * ws.getNormalChildHeight());
+ Rect workspacePadding = grid.getWorkspacePadding(null);
+ int workspaceTop = insets.top + workspacePadding.top;
+ int workspaceBottom = ws.getViewportHeight() - insets.bottom - workspacePadding.bottom;
+ int overviewTop = insets.top;
+ int overviewBottom = ws.getViewportHeight() - insets.bottom - overviewButtonBarHeight;
+ int workspaceOffsetTopEdge =
+ workspaceTop + ((workspaceBottom - workspaceTop) - scaledHeight) / 2;
+ int overviewOffsetTopEdge = overviewTop + (overviewBottom - overviewTop - scaledHeight) / 2;
+ return new float[] {SCALE_FACTOR, -workspaceOffsetTopEdge + overviewOffsetTopEdge };
+ }
+
+ @Override
+ public void onStateEnabled(Launcher launcher) {
+ launcher.getWorkspace().setPageRearrangeEnabled(true);
+
+ if (isAccessibilityEnabled(launcher)) {
+ launcher.getOverviewPanel().getChildAt(0).performAccessibilityAction(
+ AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
+ }
+ }
+
+ @Override
+ public void onStateDisabled(Launcher launcher) {
+ launcher.getWorkspace().setPageRearrangeEnabled(false);
+ }
+
+ @Override
+ public View getFinalFocus(Launcher launcher) {
+ return launcher.getOverviewPanel();
+ }
+}
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
new file mode 100644
index 0000000..3864e3a
--- /dev/null
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2017 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.states;
+
+import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS;
+
+import android.content.pm.ActivityInfo;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.view.View;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InstallShortcutReceiver;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+
+/**
+ * Definition for spring loaded state used during drag and drop.
+ */
+public class SpringLoadedState extends LauncherState {
+
+ private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE |
+ FLAG_DISABLE_ACCESSIBILITY | FLAG_DO_NOT_RESTORE;
+
+ // Determines how long to wait after a rotation before restoring the screen orientation to
+ // match the sensor state.
+ private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500;
+
+ public SpringLoadedState(int id) {
+ super(id, ContainerType.OVERVIEW, SPRING_LOADED_TRANSITION_MS, 1f, STATE_FLAGS);
+ }
+
+ @Override
+ public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
+ DeviceProfile grid = launcher.getDeviceProfile();
+ Workspace ws = launcher.getWorkspace();
+ if (grid.isVerticalBarLayout() || ws.getChildCount() == 0) {
+ return super.getWorkspaceScaleAndTranslation(launcher);
+ }
+
+ float scale = grid.workspaceSpringLoadShrinkFactor;
+ Rect insets = launcher.getDragLayer().getInsets();
+
+ float scaledHeight = scale * ws.getNormalChildHeight();
+ float shrunkTop = insets.top + grid.dropTargetBarSizePx;
+ float shrunkBottom = ws.getViewportHeight() - insets.bottom
+ - grid.getWorkspacePadding(null).bottom
+ - grid.workspaceSpringLoadedBottomSpace;
+ float totalShrunkSpace = shrunkBottom - shrunkTop;
+
+ float desiredCellTop = shrunkTop + (totalShrunkSpace - scaledHeight) / 2;
+
+ float halfHeight = ws.getHeight() / 2;
+ float myCenter = ws.getTop() + halfHeight;
+ float cellTopFromCenter = halfHeight - ws.getChildAt(0).getTop();
+ float actualCellTop = myCenter - cellTopFromCenter * scale;
+ return new float[] { scale, (desiredCellTop - actualCellTop) / scale};
+ }
+
+ @Override
+ public void onStateEnabled(Launcher launcher) {
+ Workspace ws = launcher.getWorkspace();
+ ws.showPageIndicatorAtCurrentScroll();
+ ws.getPageIndicator().setShouldAutoHide(false);
+
+ // Lock the orientation:
+ if (launcher.isRotationEnabled()) {
+ launcher.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
+ }
+
+ // Prevent any Un/InstallShortcutReceivers from updating the db while we are
+ // in spring loaded mode
+ InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_DRAG_AND_DROP);
+ }
+
+ @Override
+ public void onStateDisabled(final Launcher launcher) {
+ launcher.getWorkspace().getPageIndicator().setShouldAutoHide(true);
+
+ // Unlock rotation lock
+ if (launcher.isRotationEnabled()) {
+ new Handler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ launcher.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
+ }
+ }, RESTORE_SCREEN_ORIENTATION_DELAY);
+ }
+
+ // Re-enable any Un/InstallShortcutReceiver and now process any queued items
+ InstallShortcutReceiver.disableAndFlushInstallQueue(
+ InstallShortcutReceiver.FLAG_DRAG_AND_DROP, launcher);
+ }
+
+ @Override
+ public View getFinalFocus(Launcher launcher) {
+ return null;
+ }
+}
diff --git a/src/com/android/launcher3/testing/DummyWidget.java b/src/com/android/launcher3/testing/DummyWidget.java
deleted file mode 100644
index df887ac..0000000
--- a/src/com/android/launcher3/testing/DummyWidget.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.android.launcher3.testing;
-
-import android.appwidget.AppWidgetProviderInfo;
-
-import com.android.launcher3.CustomAppWidget;
-import com.android.launcher3.R;
-
-public class DummyWidget implements CustomAppWidget {
- @Override
- public String getLabel() {
- return "Dumb Launcher Widget";
- }
-
- @Override
- public int getPreviewImage() {
- return 0;
- }
-
- @Override
- public int getIcon() {
- return 0;
- }
-
- @Override
- public int getWidgetLayout() {
- return R.layout.zzz_dummy_widget;
- }
-
- @Override
- public int getSpanX() {
- return 2;
- }
-
- @Override
- public int getSpanY() {
- return 2;
- }
-
- @Override
- public int getMinSpanX() {
- return 1;
- }
-
- @Override
- public int getMinSpanY() {
- return 1;
- }
-
- @Override
- public int getResizeMode() {
- return AppWidgetProviderInfo.RESIZE_BOTH;
- }
-}
diff --git a/src/com/android/launcher3/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java
index 5cb8c4c..c40e1fb 100644
--- a/src/com/android/launcher3/testing/LauncherExtension.java
+++ b/src/com/android/launcher3/testing/LauncherExtension.java
@@ -1,22 +1,16 @@
package com.android.launcher3.testing;
import android.content.Intent;
-import android.graphics.Color;
import android.os.Bundle;
import android.view.Menu;
-import android.view.View;
-import android.widget.FrameLayout;
import com.android.launcher3.AppInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherCallbacks;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.ComponentKeyMapper;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.List;
/**
* This class represents a very trivial LauncherExtension. It primarily serves as a simple
@@ -42,10 +36,6 @@
}
@Override
- public void preOnResume() {
- }
-
- @Override
public void onResume() {
}
@@ -70,10 +60,6 @@
}
@Override
- public void onPostCreate(Bundle savedInstanceState) {
- }
-
- @Override
public void onNewIntent(Intent intent) {
}
@@ -117,98 +103,21 @@
}
@Override
- public void finishBindingItems(boolean upgradePath) {
- }
-
- @Override
public void bindAllApplications(ArrayList<AppInfo> apps) {
}
@Override
- public void onWorkspaceLockedChanged() {
- }
-
- @Override
- public void onInteractionBegin() {
- }
-
- @Override
- public void onInteractionEnd() {
- }
-
- @Override
public boolean startSearch(String initialQuery, boolean selectInitialQuery,
Bundle appSearchData) {
return false;
}
- CustomContentCallbacks mCustomContentCallbacks = new CustomContentCallbacks() {
-
- // Custom content is completely shown. {@code fromResume} indicates whether this was caused
- // by a onResume or by scrolling otherwise.
- public void onShow(boolean fromResume) {
- }
-
- // Custom content is completely hidden
- public void onHide() {
- }
-
- // Custom content scroll progress changed. From 0 (not showing) to 1 (fully showing).
- public void onScrollProgressChanged(float progress) {
-
- }
-
- // Indicates whether the user is allowed to scroll away from the custom content.
- public boolean isScrollingAllowed() {
- return true;
- }
-
- };
-
- @Override
- public boolean hasCustomContentToLeft() {
- return true;
- }
-
- @Override
- public void populateCustomContentContainer() {
- FrameLayout customContent = new FrameLayout(LauncherExtension.this);
- customContent.setBackgroundColor(Color.GRAY);
- addToCustomContentPage(customContent, mCustomContentCallbacks, "");
- }
-
- @Override
- public View getQsbBar() {
- return null;
- }
-
- @Override
- public Bundle getAdditionalSearchWidgetOptions() {
- return new Bundle();
- }
-
- @Override
- public boolean shouldMoveToDefaultScreenOnHomeIntent() {
- return true;
- }
-
@Override
public boolean hasSettings() {
return false;
}
@Override
- public List<ComponentKeyMapper<AppInfo>> getPredictedApps() {
- // To debug app predictions, enable AlphabeticalAppsList#DEBUG_PREDICTIONS
- return new ArrayList<>();
- }
-
- @Override
- public int getSearchBarHeight() {
- return SEARCH_BAR_HEIGHT_NORMAL;
- }
-
- @Override
public void onAttachedToWindow() {
}
diff --git a/src/com/android/launcher3/touch/SwipeDetector.java b/src/com/android/launcher3/touch/SwipeDetector.java
index be4648e..351f88d 100644
--- a/src/com/android/launcher3/touch/SwipeDetector.java
+++ b/src/com/android/launcher3/touch/SwipeDetector.java
@@ -16,6 +16,7 @@
package com.android.launcher3.touch;
import static android.view.MotionEvent.INVALID_POINTER_ID;
+
import android.content.Context;
import android.graphics.PointF;
import android.support.annotation.NonNull;
@@ -23,7 +24,6 @@
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
-import android.view.animation.Interpolator;
/**
* One dimensional scroll/drag/swipe gesture detector.
@@ -43,7 +43,6 @@
public static final int DIRECTION_BOTH = DIRECTION_NEGATIVE | DIRECTION_POSITIVE;
private static final float ANIMATION_DURATION = 1200;
- private static final float FAST_FLING_PX_MS = 10;
protected int mActivePointerId = INVALID_POINTER_ID;
@@ -351,22 +350,4 @@
}
return duration;
}
-
- public static class ScrollInterpolator implements Interpolator {
-
- boolean mSteeper;
-
- public void setVelocityAtZero(float velocity) {
- mSteeper = velocity > FAST_FLING_PX_MS;
- }
-
- public float getInterpolation(float t) {
- t -= 1.0f;
- float output = t * t * t;
- if (mSteeper) {
- output *= t * t; // Make interpolation initial slope steeper
- }
- return output + 1;
- }
- }
}
diff --git a/src/com/android/launcher3/util/FlingAnimation.java b/src/com/android/launcher3/util/FlingAnimation.java
index d475ee9..fe0571b 100644
--- a/src/com/android/launcher3/util/FlingAnimation.java
+++ b/src/com/android/launcher3/util/FlingAnimation.java
@@ -1,5 +1,7 @@
package com.android.launcher3.util;
+import static com.android.launcher3.LauncherState.NORMAL;
+
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
@@ -95,7 +97,7 @@
Runnable onAnimationEndRunnable = new Runnable() {
@Override
public void run() {
- mLauncher.exitSpringLoadedDragMode();
+ mLauncher.getStateManager().goToState(NORMAL);
mDropTarget.completeDrop(mDragObject);
}
};
diff --git a/src/com/android/launcher3/util/InstantAppResolver.java b/src/com/android/launcher3/util/InstantAppResolver.java
index e60d768..99ce7ca 100644
--- a/src/com/android/launcher3/util/InstantAppResolver.java
+++ b/src/com/android/launcher3/util/InstantAppResolver.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import com.android.launcher3.AppInfo;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -39,6 +40,10 @@
return false;
}
+ public boolean isInstantApp(AppInfo info) {
+ return false;
+ }
+
public List<ApplicationInfo> getInstantApps() {
return Collections.emptyList();
}
diff --git a/src/com/android/launcher3/util/TestingUtils.java b/src/com/android/launcher3/util/TestingUtils.java
index a7cc42b..d927dc3 100644
--- a/src/com/android/launcher3/util/TestingUtils.java
+++ b/src/com/android/launcher3/util/TestingUtils.java
@@ -3,18 +3,14 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.widget.FrameLayout;
-import com.android.launcher3.CustomAppWidget;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import java.util.HashMap;
-
public class TestingUtils {
public static final String MEMORY_TRACKER = "com.android.launcher3.testing.MemoryTracker";
@@ -23,9 +19,6 @@
public static final boolean MEMORY_DUMP_ENABLED = false;
public static final String SHOW_WEIGHT_WATCHER = "debug.show_mem";
- public static final boolean ENABLE_CUSTOM_WIDGET_TEST = false;
- public static final String DUMMY_WIDGET = "com.android.launcher3.testing.DummyWidget";
-
public static void startTrackingMemory(Context context) {
if (MEMORY_DUMP_ENABLED) {
context.startService(new Intent()
@@ -55,16 +48,4 @@
launcher.mWeightWatcher = watcher;
}
}
-
- public static void addDummyWidget(HashMap<String, CustomAppWidget> set) {
- if (ENABLE_CUSTOM_WIDGET_TEST) {
- try {
- Class<?> clazz = Class.forName(DUMMY_WIDGET);
- CustomAppWidget widget = (CustomAppWidget) clazz.newInstance();
- set.put(widget.getClass().getName(), widget);
- } catch (Exception e) {
- Log.e("TestingUtils", "Error adding dummy widget", e);
- }
- }
- }
}
diff --git a/src/com/android/launcher3/util/TraceHelper.java b/src/com/android/launcher3/util/TraceHelper.java
new file mode 100644
index 0000000..5b66fcd
--- /dev/null
+++ b/src/com/android/launcher3/util/TraceHelper.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 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.util;
+
+import android.os.SystemClock;
+import android.os.Trace;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.MutableLong;
+
+import com.android.launcher3.config.FeatureFlags;
+
+/**
+ * A wrapper around {@link Trace} to allow easier proguarding for production builds.
+ *
+ * To enable any tracing log, execute the following command:
+ * $ adb shell setprop log.tag.TAGNAME VERBOSE
+ */
+public class TraceHelper {
+
+ private static final boolean ENABLED = FeatureFlags.IS_DOGFOOD_BUILD;
+
+ private static final boolean SYSTEM_TRACE = true;
+ private static final ArrayMap<String, MutableLong> sUpTimes =
+ ENABLED ? new ArrayMap<String, MutableLong>() : null;
+
+ public static void beginSection(String sectionName) {
+ if (ENABLED) {
+ MutableLong time = sUpTimes.get(sectionName);
+ if (time == null) {
+ time = new MutableLong(Log.isLoggable(sectionName, Log.VERBOSE) ? 0 : -1);
+ sUpTimes.put(sectionName, time);
+ }
+ if (time.value >= 0) {
+ if (SYSTEM_TRACE) {
+ Trace.beginSection(sectionName);
+ }
+ time.value = SystemClock.uptimeMillis();
+ }
+ }
+ }
+
+ public static void partitionSection(String sectionName, String partition) {
+ if (ENABLED) {
+ MutableLong time = sUpTimes.get(sectionName);
+ if (time.value >= 0) {
+
+ if (SYSTEM_TRACE) {
+ Trace.endSection();
+ Trace.beginSection(sectionName);
+ }
+
+ long now = SystemClock.uptimeMillis();
+ Log.d(sectionName, partition + " : " + (now - time.value));
+ time.value = now;
+ }
+ }
+ }
+
+ public static void endSection(String sectionName) {
+ if (ENABLED) {
+ endSection(sectionName, "End");
+ }
+ }
+
+ public static void endSection(String sectionName, String msg) {
+ if (ENABLED) {
+ MutableLong time = sUpTimes.get(sectionName);
+ if (time.value >= 0) {
+ if (SYSTEM_TRACE) {
+ Trace.endSection();
+ }
+ Log.d(sectionName, msg + " : " + (SystemClock.uptimeMillis() - time.value));
+ }
+ }
+ }
+}
diff --git a/src/com/android/launcher3/util/UiThreadHelper.java b/src/com/android/launcher3/util/UiThreadHelper.java
new file mode 100644
index 0000000..27140a1
--- /dev/null
+++ b/src/com/android/launcher3/util/UiThreadHelper.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 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.util;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.view.inputmethod.InputMethodManager;
+
+/**
+ * Utility class for offloading some class from UI thread
+ */
+public class UiThreadHelper {
+
+ private static HandlerThread sHandlerThread;
+ private static Handler sHandler;
+
+ private static final int MSG_HIDE_KEYBOARD = 1;
+
+ public static Looper getBackgroundLooper() {
+ if (sHandlerThread == null) {
+ sHandlerThread =
+ new HandlerThread("UiThreadHelper", Process.THREAD_PRIORITY_FOREGROUND);
+ sHandlerThread.start();
+ }
+ return sHandlerThread.getLooper();
+ }
+
+ private static Handler getHandler(Context context) {
+ if (sHandler == null) {
+ sHandler = new Handler(getBackgroundLooper(),
+ new UiCallbacks(context.getApplicationContext()));
+ }
+ return sHandler;
+ }
+
+ public static void hideKeyboardAsync(Context context, IBinder token) {
+ Message.obtain(getHandler(context), MSG_HIDE_KEYBOARD, token).sendToTarget();
+ }
+
+ private static class UiCallbacks implements Handler.Callback {
+
+ private final InputMethodManager mIMM;
+
+ UiCallbacks(Context context) {
+ mIMM = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+ }
+
+ @Override
+ public boolean handleMessage(Message message) {
+ switch (message.what) {
+ case MSG_HIDE_KEYBOARD:
+ mIMM.hideSoftInputFromWindow((IBinder) message.obj, 0);
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/util/VerticalFlingDetector.java b/src/com/android/launcher3/util/VerticalFlingDetector.java
deleted file mode 100644
index 7236c2d..0000000
--- a/src/com/android/launcher3/util/VerticalFlingDetector.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2016 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.util;
-
-import android.content.Context;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-
-public class VerticalFlingDetector implements View.OnTouchListener {
-
- private static final float CUSTOM_SLOP_MULTIPLIER = 2.2f;
- private static final int SEC_IN_MILLIS = 1000;
-
- private VelocityTracker mVelocityTracker;
- private float mMinimumFlingVelocity;
- private float mMaximumFlingVelocity;
- private float mDownX, mDownY;
- private boolean mShouldCheckFling;
- private double mCustomTouchSlop;
-
- public VerticalFlingDetector(Context context) {
- ViewConfiguration vc = ViewConfiguration.get(context);
- mMinimumFlingVelocity = vc.getScaledMinimumFlingVelocity();
- mMaximumFlingVelocity = vc.getScaledMaximumFlingVelocity();
- mCustomTouchSlop = CUSTOM_SLOP_MULTIPLIER * vc.getScaledTouchSlop();
- }
-
- @Override
- public boolean onTouch(View v, MotionEvent ev) {
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(ev);
- switch (ev.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mDownX = ev.getX();
- mDownY = ev.getY();
- mShouldCheckFling = false;
- break;
- case MotionEvent.ACTION_MOVE:
- if (mShouldCheckFling) {
- break;
- }
- if (Math.abs(ev.getY() - mDownY) > mCustomTouchSlop &&
- Math.abs(ev.getY() - mDownY) > Math.abs(ev.getX() - mDownX)) {
- mShouldCheckFling = true;
- }
- break;
- case MotionEvent.ACTION_UP:
- if (mShouldCheckFling) {
- mVelocityTracker.computeCurrentVelocity(SEC_IN_MILLIS, mMaximumFlingVelocity);
- // only when fling is detected in down direction
- if (mVelocityTracker.getYVelocity() > mMinimumFlingVelocity) {
- cleanUp();
- return true;
- }
- }
- // fall through.
- case MotionEvent.ACTION_CANCEL:
- cleanUp();
- }
- return false;
- }
-
- private void cleanUp() {
- if (mVelocityTracker == null) {
- return;
- }
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
-}
diff --git a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
index 3926974..5c24687 100644
--- a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
+++ b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
@@ -1,72 +1,48 @@
package com.android.launcher3.util;
import android.app.WallpaperManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Message;
+import android.os.SystemClock;
import android.util.Log;
-import android.view.Choreographer;
-import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
+import com.android.launcher3.anim.Interpolators;
/**
* Utility class to handle wallpaper scrolling along with workspace.
*/
-public class WallpaperOffsetInterpolator implements Choreographer.FrameCallback {
+public class WallpaperOffsetInterpolator extends BroadcastReceiver {
+
+ private static final int[] sTempInt = new int[2];
private static final String TAG = "WPOffsetInterpolator";
private static final int ANIMATION_DURATION = 250;
// Don't use all the wallpaper for parallax until you have at least this many pages
private static final int MIN_PARALLAX_PAGE_SPAN = 4;
- private final Choreographer mChoreographer;
- private final Interpolator mInterpolator;
- private final WallpaperManager mWallpaperManager;
private final Workspace mWorkspace;
private final boolean mIsRtl;
+ private final Handler mHandler;
+ private boolean mRegistered = false;
private IBinder mWindowToken;
private boolean mWallpaperIsLiveWallpaper;
- private float mLastSetWallpaperOffsetSteps = 0;
- private float mFinalOffset = 0.0f;
- private float mCurrentOffset = 0.5f; // to force an initial update
- private boolean mWaitingForUpdate;
private boolean mLockedToDefaultPage;
-
- private boolean mAnimating;
- private long mAnimationStartTime;
- private float mAnimationStartOffset;
- int mNumScreens;
- int mNumPagesForWallpaperParallax;
+ private int mNumScreens;
public WallpaperOffsetInterpolator(Workspace workspace) {
- mChoreographer = Choreographer.getInstance();
- mInterpolator = new DecelerateInterpolator(1.5f);
-
mWorkspace = workspace;
- mWallpaperManager = WallpaperManager.getInstance(workspace.getContext());
mIsRtl = Utilities.isRtl(workspace.getResources());
- }
-
- @Override
- public void doFrame(long frameTimeNanos) {
- updateOffset(false);
- }
-
- private void updateOffset(boolean force) {
- if (mWaitingForUpdate || force) {
- mWaitingForUpdate = false;
- if (computeScrollOffset() && mWindowToken != null) {
- try {
- mWallpaperManager.setWallpaperOffsets(mWindowToken, getCurrX(), 0.5f);
- setWallpaperOffsetSteps();
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Error updating wallpaper offset: " + e);
- }
- }
- }
+ mHandler = new OffsetHandler(workspace.getContext());
}
/**
@@ -80,55 +56,34 @@
return mLockedToDefaultPage;
}
- public boolean computeScrollOffset() {
- final float oldOffset = mCurrentOffset;
- if (mAnimating) {
- long durationSinceAnimation = System.currentTimeMillis() - mAnimationStartTime;
- float t0 = durationSinceAnimation / (float) ANIMATION_DURATION;
- float t1 = mInterpolator.getInterpolation(t0);
- mCurrentOffset = mAnimationStartOffset +
- (mFinalOffset - mAnimationStartOffset) * t1;
- mAnimating = durationSinceAnimation < ANIMATION_DURATION;
- } else {
- mCurrentOffset = mFinalOffset;
- }
-
- if (Math.abs(mCurrentOffset - mFinalOffset) > 0.0000001f) {
- scheduleUpdate();
- }
- if (Math.abs(oldOffset - mCurrentOffset) > 0.0000001f) {
- return true;
- }
- return false;
- }
-
/**
+ * Computes the wallpaper offset as an int ratio (out[0] / out[1])
+ *
* TODO: do different behavior if it's a live wallpaper?
*/
- public float wallpaperOffsetForScroll(int scroll) {
+ private void wallpaperOffsetForScroll(int scroll, int numScrollingPages, final int[] out) {
+ out[1] = 1;
+
// To match the default wallpaper behavior in the system, we default to either the left
// or right edge on initialization
- int numScrollingPages = getNumScreensExcludingEmptyAndCustom();
if (mLockedToDefaultPage || numScrollingPages <= 1) {
- return mIsRtl ? 1f : 0f;
+ out[0] = mIsRtl ? 1 : 0;
+ return;
}
// Distribute the wallpaper parallax over a minimum of MIN_PARALLAX_PAGE_SPAN workspace
// screens, not including the custom screen, and empty screens (if > MIN_PARALLAX_PAGE_SPAN)
- if (mWallpaperIsLiveWallpaper) {
- mNumPagesForWallpaperParallax = numScrollingPages;
- } else {
- mNumPagesForWallpaperParallax = Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollingPages);
- }
+ int numPagesForWallpaperParallax = mWallpaperIsLiveWallpaper ? numScrollingPages :
+ Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollingPages);
// Offset by the custom screen
int leftPageIndex;
int rightPageIndex;
if (mIsRtl) {
- rightPageIndex = mWorkspace.numCustomPages();
+ rightPageIndex = 0;
leftPageIndex = rightPageIndex + numScrollingPages - 1;
} else {
- leftPageIndex = mWorkspace.numCustomPages();
+ leftPageIndex = 0;
rightPageIndex = leftPageIndex + numScrollingPages - 1;
}
@@ -136,106 +91,184 @@
int leftPageScrollX = mWorkspace.getScrollForPage(leftPageIndex);
int rightPageScrollX = mWorkspace.getScrollForPage(rightPageIndex);
int scrollRange = rightPageScrollX - leftPageScrollX;
- if (scrollRange == 0) {
- return 0f;
+ if (scrollRange <= 0) {
+ out[0] = 0;
+ return;
}
// Sometimes the left parameter of the pages is animated during a layout transition;
// this parameter offsets it to keep the wallpaper from animating as well
int adjustedScroll = scroll - leftPageScrollX -
mWorkspace.getLayoutTransitionOffsetForPage(0);
- float offset = Utilities.boundToRange((float) adjustedScroll / scrollRange, 0f, 1f);
+ adjustedScroll = Utilities.boundToRange(adjustedScroll, 0, scrollRange);
+ out[1] = (numPagesForWallpaperParallax - 1) * scrollRange;
// The offset is now distributed 0..1 between the left and right pages that we care about,
// so we just map that between the pages that we are using for parallax
- float rtlOffset = 0;
+ int rtlOffset = 0;
if (mIsRtl) {
// In RTL, the pages are right aligned, so adjust the offset from the end
- rtlOffset = (float) ((mNumPagesForWallpaperParallax - 1) - (numScrollingPages - 1)) /
- (mNumPagesForWallpaperParallax - 1);
+ rtlOffset = out[1] - (numScrollingPages - 1) * scrollRange;
}
- return rtlOffset + offset *
- ((float) (numScrollingPages - 1) / (mNumPagesForWallpaperParallax - 1));
+ out[0] = rtlOffset + adjustedScroll * (numScrollingPages - 1);
}
- private float wallpaperOffsetForCurrentScroll() {
- return wallpaperOffsetForScroll(mWorkspace.getScrollX());
+ public float wallpaperOffsetForScroll(int scroll) {
+ wallpaperOffsetForScroll(scroll, getNumScreensExcludingEmpty(), sTempInt);
+ return ((float) sTempInt[0]) / sTempInt[1];
}
- private int numEmptyScreensToIgnore() {
- int numScrollingPages = mWorkspace.getChildCount() - mWorkspace.numCustomPages();
+ private int getNumScreensExcludingEmpty() {
+ int numScrollingPages = mWorkspace.getChildCount();
if (numScrollingPages >= MIN_PARALLAX_PAGE_SPAN && mWorkspace.hasExtraEmptyScreen()) {
- return 1;
+ return numScrollingPages - 1;
} else {
- return 0;
+ return numScrollingPages;
}
}
- private int getNumScreensExcludingEmptyAndCustom() {
- return mWorkspace.getChildCount() - numEmptyScreensToIgnore() - mWorkspace.numCustomPages();
- }
-
public void syncWithScroll() {
- float offset = wallpaperOffsetForCurrentScroll();
- setFinalX(offset);
- updateOffset(true);
- }
-
- public float getCurrX() {
- return mCurrentOffset;
- }
-
- public float getFinalX() {
- return mFinalOffset;
- }
-
- private void animateToFinal() {
- mAnimating = true;
- mAnimationStartOffset = mCurrentOffset;
- mAnimationStartTime = System.currentTimeMillis();
- }
-
- private void setWallpaperOffsetSteps() {
- // Set wallpaper offset steps (1 / (number of screens - 1))
- float xOffset = 1.0f / (mNumPagesForWallpaperParallax - 1);
- if (xOffset != mLastSetWallpaperOffsetSteps) {
- mWallpaperManager.setWallpaperOffsetSteps(xOffset, 1.0f);
- mLastSetWallpaperOffsetSteps = xOffset;
- }
- }
-
- public void setFinalX(float x) {
- scheduleUpdate();
- mFinalOffset = Math.max(0f, Math.min(x, 1f));
- if (getNumScreensExcludingEmptyAndCustom() != mNumScreens) {
- if (mNumScreens > 0 && Float.compare(mCurrentOffset, mFinalOffset) != 0) {
- // Don't animate if we're going from 0 screens, or if the final offset is the same
- // as the current offset
- animateToFinal();
+ int numScreens = getNumScreensExcludingEmpty();
+ wallpaperOffsetForScroll(mWorkspace.getScrollX(), numScreens, sTempInt);
+ Message msg = Message.obtain(mHandler, MSG_UPDATE_OFFSET, sTempInt[0], sTempInt[1],
+ mWindowToken);
+ if (numScreens != mNumScreens) {
+ if (mNumScreens > 0) {
+ // Don't animate if we're going from 0 screens
+ msg.what = MSG_START_ANIMATION;
}
- mNumScreens = getNumScreensExcludingEmptyAndCustom();
+ mNumScreens = numScreens;
+ updateOffset();
}
+ msg.sendToTarget();
}
- private void scheduleUpdate() {
- if (!mWaitingForUpdate) {
- mChoreographer.postFrameCallback(this);
- mWaitingForUpdate = true;
+ private void updateOffset() {
+ int numPagesForWallpaperParallax;
+ if (mWallpaperIsLiveWallpaper) {
+ numPagesForWallpaperParallax = mNumScreens;
+ } else {
+ numPagesForWallpaperParallax = Math.max(MIN_PARALLAX_PAGE_SPAN, mNumScreens);
}
+ Message.obtain(mHandler, MSG_SET_NUM_PARALLAX, numPagesForWallpaperParallax, 0,
+ mWindowToken).sendToTarget();
}
public void jumpToFinal() {
- mCurrentOffset = mFinalOffset;
- }
-
- public void onResume() {
- mWallpaperIsLiveWallpaper = mWallpaperManager.getWallpaperInfo() != null;
- // Force the wallpaper offset steps to be set again, because another app might have changed
- // them
- mLastSetWallpaperOffsetSteps = 0f;
+ Message.obtain(mHandler, MSG_JUMP_TO_FINAL, mWindowToken).sendToTarget();
}
public void setWindowToken(IBinder token) {
mWindowToken = token;
+ if (mWindowToken == null && mRegistered) {
+ mWorkspace.getContext().unregisterReceiver(this);
+ mRegistered = false;
+ } else if (mWindowToken != null && !mRegistered) {
+ mWorkspace.getContext()
+ .registerReceiver(this, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
+ onReceive(mWorkspace.getContext(), null);
+ mRegistered = true;
+ }
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mWallpaperIsLiveWallpaper =
+ WallpaperManager.getInstance(mWorkspace.getContext()).getWallpaperInfo() != null;
+ updateOffset();
+ }
+
+ private static final int MSG_START_ANIMATION = 1;
+ private static final int MSG_UPDATE_OFFSET = 2;
+ private static final int MSG_APPLY_OFFSET = 3;
+ private static final int MSG_SET_NUM_PARALLAX = 4;
+ private static final int MSG_JUMP_TO_FINAL = 5;
+
+ private static class OffsetHandler extends Handler {
+
+ private final Interpolator mInterpolator;
+ private final WallpaperManager mWM;
+
+ private float mCurrentOffset = 0.5f; // to force an initial update
+ private boolean mAnimating;
+ private long mAnimationStartTime;
+ private float mAnimationStartOffset;
+
+ private float mFinalOffset;
+ private float mOffsetX;
+
+ public OffsetHandler(Context context) {
+ super(UiThreadHelper.getBackgroundLooper());
+ mInterpolator = Interpolators.DEACCEL_1_5;
+ mWM = WallpaperManager.getInstance(context);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ final IBinder token = (IBinder) msg.obj;
+ if (token == null) {
+ return;
+ }
+
+ switch (msg.what) {
+ case MSG_START_ANIMATION: {
+ mAnimating = true;
+ mAnimationStartOffset = mCurrentOffset;
+ mAnimationStartTime = msg.getWhen();
+ // Follow through
+ }
+ case MSG_UPDATE_OFFSET:
+ mFinalOffset = ((float) msg.arg1) / msg.arg2;
+ // Follow through
+ case MSG_APPLY_OFFSET: {
+ float oldOffset = mCurrentOffset;
+ if (mAnimating) {
+ long durationSinceAnimation = SystemClock.uptimeMillis()
+ - mAnimationStartTime;
+ float t0 = durationSinceAnimation / (float) ANIMATION_DURATION;
+ float t1 = mInterpolator.getInterpolation(t0);
+ mCurrentOffset = mAnimationStartOffset +
+ (mFinalOffset - mAnimationStartOffset) * t1;
+ mAnimating = durationSinceAnimation < ANIMATION_DURATION;
+ } else {
+ mCurrentOffset = mFinalOffset;
+ }
+
+ if (Float.compare(mCurrentOffset, oldOffset) != 0) {
+ setOffsetSafely(token);
+ // Force the wallpaper offset steps to be set again, because another app
+ // might have changed them
+ mWM.setWallpaperOffsetSteps(mOffsetX, 1.0f);
+ }
+ if (mAnimating) {
+ // If we are animating, keep updating the offset
+ Message.obtain(this, MSG_APPLY_OFFSET, token).sendToTarget();
+ }
+ return;
+ }
+ case MSG_SET_NUM_PARALLAX: {
+ // Set wallpaper offset steps (1 / (number of screens - 1))
+ mOffsetX = 1.0f / (msg.arg1 - 1);
+ mWM.setWallpaperOffsetSteps(mOffsetX, 1.0f);
+ return;
+ }
+ case MSG_JUMP_TO_FINAL: {
+ if (Float.compare(mCurrentOffset, mFinalOffset) != 0) {
+ mCurrentOffset = mFinalOffset;
+ setOffsetSafely(token);
+ }
+ mAnimating = false;
+ return;
+ }
+ }
+ }
+
+ private void setOffsetSafely(IBinder token) {
+ try {
+ mWM.setWallpaperOffsets(token, mCurrentOffset, 0.5f);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Error updating wallpaper offset: " + e);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 7b5bcdb..8c9a441 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -285,7 +285,7 @@
return;
}
int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
- canvas.translate(getWidth() / 2, mRv.getPaddingTop());
+ canvas.translate(getWidth() / 2, mRv.getScrollBarTop());
// Draw the track
float halfW = mWidth / 2;
canvas.drawRoundRect(-halfW, 0, halfW, mRv.getScrollbarTrackHeight(),
@@ -317,7 +317,7 @@
* Returns whether the specified point is inside the thumb bounds.
*/
private boolean isNearThumb(int x, int y) {
- int offset = y - mRv.getPaddingTop() - mThumbOffsetY;
+ int offset = y - mRv.getScrollBarTop() - mThumbOffsetY;
return x >= 0 && x < getWidth() && offset >= 0 && offset <= mThumbHeight;
}
@@ -348,7 +348,7 @@
private void updatePopupY(int lastTouchY) {
int height = mPopupView.getHeight();
float top = lastTouchY - (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * height)
- + mRv.getPaddingTop();
+ + mRv.getScrollBarTop();
top = Utilities.boundToRange(top,
mMaxWidth, mRv.getScrollbarTrackHeight() - mMaxWidth - height);
mPopupView.setTranslationY(top);
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
new file mode 100644
index 0000000..e328759
--- /dev/null
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2017 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.widget;
+
+import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
+import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.content.Context;
+import android.graphics.Point;
+import android.util.AttributeSet;
+import android.util.Property;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
+import android.view.animation.Interpolator;
+import android.widget.Toast;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.graphics.GradientView;
+import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.util.SystemUiController;
+import com.android.launcher3.util.Themes;
+
+/**
+ * Base class for various widgets popup
+ */
+abstract class BaseWidgetSheet extends AbstractFloatingView
+ implements OnClickListener, OnLongClickListener, DragSource, SwipeDetector.Listener {
+
+
+ protected static Property<BaseWidgetSheet, Float> TRANSLATION_SHIFT =
+ new Property<BaseWidgetSheet, Float>(Float.class, "translationShift") {
+
+ @Override
+ public Float get(BaseWidgetSheet view) {
+ return view.mTranslationShift;
+ }
+
+ @Override
+ public void set(BaseWidgetSheet view, Float value) {
+ view.setTranslationShift(value);
+ }
+ };
+ protected static final float TRANSLATION_SHIFT_CLOSED = 1f;
+ protected static final float TRANSLATION_SHIFT_OPENED = 0f;
+
+ /* Touch handling related member variables. */
+ private Toast mWidgetInstructionToast;
+
+ protected final Launcher mLauncher;
+ protected final SwipeDetector mSwipeDetector;
+ protected final ObjectAnimator mOpenCloseAnimator;
+
+ protected View mContent;
+ protected GradientView mGradientView;
+ protected Interpolator mScrollInterpolator;
+
+ // range [0, 1], 0=> completely open, 1=> completely closed
+ protected float mTranslationShift = TRANSLATION_SHIFT_CLOSED;
+
+ protected boolean mNoIntercept;
+
+ public BaseWidgetSheet(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mLauncher = Launcher.getLauncher(context);
+
+ mScrollInterpolator = Interpolators.SCROLL_CUBIC;
+ mSwipeDetector = new SwipeDetector(context, this, SwipeDetector.VERTICAL);
+
+ mOpenCloseAnimator = LauncherAnimUtils.ofPropertyValuesHolder(this);
+ mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mSwipeDetector.finishedScrolling();
+ }
+ });
+ }
+
+ @Override
+ public final void onClick(View v) {
+ // Let the user know that they have to long press to add a widget
+ if (mWidgetInstructionToast != null) {
+ mWidgetInstructionToast.cancel();
+ }
+
+ CharSequence msg = Utilities.wrapForTts(
+ getContext().getText(R.string.long_press_widget_to_add),
+ getContext().getString(R.string.long_accessible_way_to_add));
+ mWidgetInstructionToast = Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT);
+ mWidgetInstructionToast.show();
+ }
+
+ @Override
+ public final boolean onLongClick(View v) {
+ if (!mLauncher.isDraggingEnabled()) return false;
+
+ if (v instanceof WidgetCell) {
+ return beginDraggingWidget((WidgetCell) v);
+ }
+ return true;
+ }
+
+ protected void setTranslationShift(float translationShift) {
+ mTranslationShift = translationShift;
+ mGradientView.setAlpha(1 - mTranslationShift);
+ mContent.setTranslationY(mTranslationShift * mContent.getHeight());
+ }
+
+ private boolean beginDraggingWidget(WidgetCell v) {
+ // Get the widget preview as the drag representation
+ WidgetImageView image = v.getWidgetView();
+
+ // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and
+ // we abort the drag.
+ if (image.getBitmap() == null) {
+ return false;
+ }
+
+ int[] loc = new int[2];
+ mLauncher.getDragLayer().getLocationInDragLayer(image, loc);
+
+ new PendingItemDragHelper(v).startDrag(
+ image.getBitmapBounds(), image.getBitmap().getWidth(), image.getWidth(),
+ new Point(loc[0], loc[1]), this, new DragOptions());
+ close(true);
+ return true;
+ }
+
+ //
+ // Drag related handling methods that implement {@link DragSource} interface.
+ //
+
+ @Override
+ public void onDropCompleted(View target, DragObject d, boolean success) { }
+
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_UP && !mNoIntercept) {
+ // If we got ACTION_UP without ever returning true on intercept,
+ // the user never started dragging the bottom sheet.
+ if (!mLauncher.getDragLayer().isEventOverView(mContent, ev)) {
+ close(true);
+ return false;
+ }
+ }
+
+ if (mNoIntercept) {
+ return false;
+ }
+
+ int directionsToDetectScroll = mSwipeDetector.isIdleState() ?
+ SwipeDetector.DIRECTION_NEGATIVE : 0;
+ mSwipeDetector.setDetectableScrollConditions(
+ directionsToDetectScroll, false);
+ mSwipeDetector.onTouchEvent(ev);
+ return mSwipeDetector.isDraggingOrSettling();
+ }
+
+ @Override
+ public boolean onControllerTouchEvent(MotionEvent ev) {
+ return mSwipeDetector.onTouchEvent(ev);
+ }
+
+ /* SwipeDetector.Listener */
+
+ @Override
+ public void onDragStart(boolean start) { }
+
+ @Override
+ public boolean onDrag(float displacement, float velocity) {
+ float range = mContent.getHeight();
+ displacement = Utilities.boundToRange(displacement, 0, range);
+ setTranslationShift(displacement / range);
+ return true;
+ }
+
+ @Override
+ public void onDragEnd(float velocity, boolean fling) {
+ if ((fling && velocity > 0) || mTranslationShift > 0.5f) {
+ mScrollInterpolator = scrollInterpolatorForVelocity(velocity);
+ mOpenCloseAnimator.setDuration(SwipeDetector.calculateDuration(
+ velocity, TRANSLATION_SHIFT_CLOSED - mTranslationShift));
+ close(true);
+ } else {
+ mOpenCloseAnimator.setValues(PropertyValuesHolder.ofFloat(
+ TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
+ mOpenCloseAnimator.setDuration(
+ SwipeDetector.calculateDuration(velocity, mTranslationShift))
+ .setInterpolator(Interpolators.DEACCEL);
+ mOpenCloseAnimator.start();
+ }
+ }
+
+ protected void handleClose(boolean animate, long defaultDuration) {
+ if (!mIsOpen || mOpenCloseAnimator.isRunning()) {
+ return;
+ }
+ if (animate) {
+ mOpenCloseAnimator.setValues(
+ PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_CLOSED));
+ mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ onCloseComplete();
+ }
+ });
+ if (mSwipeDetector.isIdleState()) {
+ mOpenCloseAnimator
+ .setDuration(defaultDuration)
+ .setInterpolator(Interpolators.ACCEL);
+ } else {
+ mOpenCloseAnimator.setInterpolator(mScrollInterpolator);
+ }
+ mOpenCloseAnimator.start();
+ } else {
+ setTranslationShift(TRANSLATION_SHIFT_CLOSED);
+ onCloseComplete();
+ }
+ }
+
+ protected void onCloseComplete() {
+ mIsOpen = false;
+ mLauncher.getDragLayer().removeView(this);
+ mLauncher.getSystemUiController().updateUiState(
+ SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, 0);
+ }
+
+ protected void setupNavBarColor() {
+ boolean isSheetDark = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark);
+ mLauncher.getSystemUiController().updateUiState(
+ SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET,
+ isSheetDark ? SystemUiController.FLAG_DARK_NAV : SystemUiController.FLAG_LIGHT_NAV);
+ }
+
+ @Override
+ public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
+ targetParent.containerType = ContainerType.WIDGETS;
+ targetParent.cardinality = getElementsRowCount();
+ }
+
+ @Override
+ public final void logActionCommand(int command) {
+ Target target = newContainerTarget(ContainerType.WIDGETS);
+ target.cardinality = getElementsRowCount();
+ mLauncher.getUserEventDispatcher().logActionCommand(command, target);
+ }
+
+ protected abstract int getElementsRowCount();
+
+}
diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
index ad05ce9..bc40484 100644
--- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
@@ -35,13 +35,13 @@
public Bundle bindOptions = null;
public PendingAddWidgetInfo(LauncherAppWidgetProviderInfo i) {
- if (i.isCustomWidget) {
+ if (i.isCustomWidget()) {
itemType = LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
} else {
itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
}
this.info = i;
- user = i.getUser();
+ user = i.getProfile();
componentName = i.provider;
previewImage = i.previewImage;
icon = i.icon;
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index 19be28d..c5cf5e2 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -22,7 +22,6 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.os.Build;
import android.view.View;
import android.widget.RemoteViews;
@@ -32,11 +31,9 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.R;
-import com.android.launcher3.Workspace;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.LivePreviewWidgetCell;
import com.android.launcher3.graphics.DragPreviewProvider;
-import com.android.launcher3.graphics.HolographicOutlineHelper;
import com.android.launcher3.graphics.LauncherIcons;
/**
@@ -48,8 +45,8 @@
private static final float MAX_WIDGET_SCALE = 1.25f;
private final PendingAddItemInfo mAddInfo;
+ private int[] mEstimatedCellSize;
- private Bitmap mPreviewBitmap;
private RemoteViews mPreview;
public PendingItemDragHelper(View view) {
@@ -80,12 +77,12 @@
final Point dragOffset;
final Rect dragRegion;
+ mEstimatedCellSize = launcher.getWorkspace().estimateItemSize(mAddInfo);
if (mAddInfo instanceof PendingAddWidgetInfo) {
PendingAddWidgetInfo createWidgetInfo = (PendingAddWidgetInfo) mAddInfo;
- int[] size = launcher.getWorkspace().estimateItemSize(createWidgetInfo, true, false);
- int maxWidth = Math.min((int) (previewBitmapWidth * MAX_WIDGET_SCALE), size[0]);
+ int maxWidth = Math.min((int) (previewBitmapWidth * MAX_WIDGET_SCALE), mEstimatedCellSize[0]);
int[] previewSizeBeforeScale = new int[1];
@@ -117,14 +114,12 @@
PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) mAddInfo;
Drawable icon = createShortcutInfo.activityInfo.getFullResIcon(app.getIconCache());
preview = LauncherIcons.createScaledBitmapWithoutShadow(icon, launcher, 0);
- mAddInfo.spanX = mAddInfo.spanY = 1;
scale = ((float) launcher.getDeviceProfile().iconSizePx) / preview.getWidth();
dragOffset = new Point(previewPadding / 2, previewPadding / 2);
// Create a preview same as the workspace cell size and draw the icon at the
// appropriate position.
- int[] size = launcher.getWorkspace().estimateItemSize(mAddInfo, false, true);
DeviceProfile dp = launcher.getDeviceProfile();
int iconSize = dp.iconSizePx;
@@ -134,9 +129,10 @@
previewBounds.top += padding;
dragRegion = new Rect();
- dragRegion.left = (size[0] - iconSize) / 2;
+ dragRegion.left = (mEstimatedCellSize[0] - iconSize) / 2;
dragRegion.right = dragRegion.left + iconSize;
- dragRegion.top = (size[1] - iconSize - dp.iconTextSizePx - dp.iconDrawablePaddingPx) / 2;
+ dragRegion.top = (mEstimatedCellSize[1]
+ - iconSize - dp.iconTextSizePx - dp.iconDrawablePaddingPx) / 2;
dragRegion.bottom = dragRegion.top + iconSize;
}
@@ -149,60 +145,31 @@
int dragLayerY = screenPos.y + previewBounds.top
+ (int) ((scale * preview.getHeight() - preview.getHeight()) / 2);
- mPreviewBitmap = preview;
// Start the drag
launcher.getDragController().startDrag(preview, dragLayerX, dragLayerY, source, mAddInfo,
dragOffset, dragRegion, scale, options);
}
-
@Override
- public Bitmap createDragOutline(Canvas canvas) {
- if (mAddInfo instanceof PendingAddShortcutInfo) {
- int width = mPreviewBitmap.getWidth();
- int height = mPreviewBitmap.getHeight();
- Bitmap b = Bitmap.createBitmap(width + blurSizeOutline, height + blurSizeOutline,
- Bitmap.Config.ALPHA_8);
- canvas.setBitmap(b);
-
- Launcher launcher = Launcher.getLauncher(mView.getContext());
- int size = launcher.getDeviceProfile().iconSizePx;
-
- Rect src = new Rect(0, 0, mPreviewBitmap.getWidth(), mPreviewBitmap.getHeight());
- Rect dst = new Rect(0, 0, size, size);
- dst.offset(blurSizeOutline / 2, blurSizeOutline / 2);
- canvas.drawBitmap(mPreviewBitmap, src, dst, new Paint(Paint.FILTER_BITMAP_FLAG));
-
- HolographicOutlineHelper.getInstance(mView.getContext())
- .applyExpensiveOutlineWithBlur(b, canvas);
-
- canvas.setBitmap(null);
- return b;
+ protected Bitmap convertPreviewToAlphaBitmap(Bitmap preview) {
+ if (mAddInfo instanceof PendingAddShortcutInfo || mEstimatedCellSize == null) {
+ return super.convertPreviewToAlphaBitmap(preview);
}
- Workspace workspace = Launcher.getLauncher(mView.getContext()).getWorkspace();
- int[] size = workspace.estimateItemSize(mAddInfo, false, false);
-
- int w = size[0];
- int h = size[1];
+ int w = mEstimatedCellSize[0];
+ int h = mEstimatedCellSize[1];
final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ALPHA_8);
- canvas.setBitmap(b);
+ Rect src = new Rect(0, 0, preview.getWidth(), preview.getHeight());
- Rect src = new Rect(0, 0, mPreviewBitmap.getWidth(), mPreviewBitmap.getHeight());
- float scaleFactor = Math.min((w - blurSizeOutline) / (float) mPreviewBitmap.getWidth(),
- (h - blurSizeOutline) / (float) mPreviewBitmap.getHeight());
- int scaledWidth = (int) (scaleFactor * mPreviewBitmap.getWidth());
- int scaledHeight = (int) (scaleFactor * mPreviewBitmap.getHeight());
+ float scaleFactor = Math.min((w - blurSizeOutline) / (float) preview.getWidth(),
+ (h - blurSizeOutline) / (float) preview.getHeight());
+ int scaledWidth = (int) (scaleFactor * preview.getWidth());
+ int scaledHeight = (int) (scaleFactor * preview.getHeight());
Rect dst = new Rect(0, 0, scaledWidth, scaledHeight);
// center the image
dst.offset((w - scaledWidth) / 2, (h - scaledHeight) / 2);
-
- canvas.drawBitmap(mPreviewBitmap, src, dst, null);
- HolographicOutlineHelper.getInstance(mView.getContext())
- .applyExpensiveOutlineWithBlur(b, canvas);
- canvas.setBitmap(null);
-
+ new Canvas(b).drawBitmap(preview, src, dst, new Paint(Paint.FILTER_BITMAP_FLAG));
return b;
}
}
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 40dbd52..2ba55ab 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -75,6 +75,9 @@
protected CancellationSignal mActiveRequest;
private boolean mAnimatePreview = true;
+ private boolean mApplyBitmapDeferred = false;
+ private Bitmap mDeferredBitmap;
+
protected final BaseActivity mActivity;
public WidgetCell(Context context) {
@@ -150,15 +153,31 @@
return mWidgetImage;
}
+ /**
+ * Sets if applying bitmap preview should be deferred. The UI will still load the bitmap, but
+ * will not cause invalidate, so that when deferring is disabled later, all the bitmaps are
+ * ready.
+ * This prevents invalidates while the animation is running.
+ */
+ public void setApplyBitmapDeferred(boolean isDeferred) {
+ if (mApplyBitmapDeferred != isDeferred) {
+ mApplyBitmapDeferred = isDeferred;
+ if (!mApplyBitmapDeferred && mDeferredBitmap != null) {
+ applyPreview(mDeferredBitmap);
+ mDeferredBitmap = null;
+ }
+ }
+ }
+
public void setAnimatePreview(boolean shouldAnimate) {
mAnimatePreview = shouldAnimate;
}
public void applyPreview(Bitmap bitmap) {
- applyPreview(bitmap, true);
- }
-
- public void applyPreview(Bitmap bitmap, boolean animate) {
+ if (mApplyBitmapDeferred) {
+ mDeferredBitmap = bitmap;
+ return;
+ }
if (bitmap != null) {
mWidgetImage.setBitmap(bitmap,
DrawableFactory.get(getContext()).getBadgeForUser(mItem.user, getContext()));
@@ -173,15 +192,11 @@
}
public void ensurePreview() {
- ensurePreview(true);
- }
-
- public void ensurePreview(boolean animate) {
if (mActiveRequest != null) {
return;
}
mActiveRequest = mWidgetPreviewLoader.getPreview(
- mItem, mPresetPreviewSize, mPresetPreviewSize, this, animate);
+ mItem, mPresetPreviewSize, mPresetPreviewSize, this);
}
@Override
diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
index 5eeea44..8dcdd44 100644
--- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java
+++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
@@ -85,7 +85,7 @@
private boolean preloadWidget() {
final LauncherAppWidgetProviderInfo pInfo = mInfo.info;
- if (pInfo.isCustomWidget) {
+ if (pInfo.isCustomWidget()) {
return false;
}
final Bundle options = getDefaultOptionsForWidget(mLauncher, mInfo);
@@ -129,7 +129,7 @@
mWidgetLoadingId = -1;
hostView.setVisibility(View.INVISIBLE);
- int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(mInfo, false, true);
+ int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(mInfo);
// We want the first widget layout to be the correct size. This will be important
// for width size reporting to the AppWidgetManager.
DragLayer.LayoutParams lp = new DragLayer.LayoutParams(unScaledSize[0],
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index 01101ac..7fa5ff0 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -16,63 +16,36 @@
package com.android.launcher3.widget;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
-import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
import android.widget.TextView;
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.DropTarget;
import com.android.launcher3.Insettable;
import com.android.launcher3.ItemInfo;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.touch.SwipeDetector;
-import com.android.launcher3.anim.PropertyListBuilder;
-import com.android.launcher3.dragndrop.DragController;
-import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.graphics.GradientView;
import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.util.SystemUiController;
-import com.android.launcher3.util.Themes;
-import com.android.launcher3.util.TouchController;
import java.util.List;
/**
* Bottom sheet for the "Widgets" system shortcut in the long-press popup.
*/
-public class WidgetsBottomSheet extends AbstractFloatingView implements Insettable, TouchController,
- SwipeDetector.Listener, View.OnClickListener, View.OnLongClickListener,
- DragController.DragListener {
+public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable {
- private int mTranslationYOpen;
- private int mTranslationYClosed;
- private float mTranslationYRange;
-
- private Launcher mLauncher;
+ private static final int DEFAULT_CLOSE_DURATION = 200;
private ItemInfo mOriginalItemInfo;
- private ObjectAnimator mOpenCloseAnimator;
- private Interpolator mFastOutSlowInInterpolator;
- private SwipeDetector.ScrollInterpolator mScrollInterpolator;
private Rect mInsets;
- private SwipeDetector mSwipeDetector;
- private GradientView mGradientBackground;
public WidgetsBottomSheet(Context context, AttributeSet attrs) {
this(context, attrs, 0);
@@ -81,23 +54,18 @@
public WidgetsBottomSheet(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setWillNotDraw(false);
- mLauncher = Launcher.getLauncher(context);
- mOpenCloseAnimator = LauncherAnimUtils.ofPropertyValuesHolder(this);
- mFastOutSlowInInterpolator =
- AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
- mScrollInterpolator = new SwipeDetector.ScrollInterpolator();
mInsets = new Rect();
- mSwipeDetector = new SwipeDetector(context, this, SwipeDetector.VERTICAL);
- mGradientBackground = (GradientView) mLauncher.getLayoutInflater().inflate(
+
+ mGradientView = (GradientView) mLauncher.getLayoutInflater().inflate(
R.layout.gradient_bg, mLauncher.getDragLayer(), false);
+ mGradientView.setProgress(1, false);
+ mContent = this;
}
@Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- mTranslationYOpen = 0;
- mTranslationYClosed = getMeasuredHeight();
- mTranslationYRange = mTranslationYClosed - mTranslationYOpen;
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ setTranslationShift(mTranslationShift);
}
public void populateAndShow(ItemInfo itemInfo) {
@@ -107,22 +75,21 @@
onWidgetsBound();
- mLauncher.getDragLayer().addView(mGradientBackground);
- mGradientBackground.setVisibility(VISIBLE);
+ mLauncher.getDragLayer().addView(mGradientView);
mLauncher.getDragLayer().addView(this);
- measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- setTranslationY(mTranslationYClosed);
mIsOpen = false;
open(true);
}
@Override
protected void onWidgetsBound() {
- List<WidgetItem> widgets = mLauncher.getWidgetsForPackageUser(new PackageUserKey(
- mOriginalItemInfo.getTargetComponent().getPackageName(), mOriginalItemInfo.user));
+ List<WidgetItem> widgets = mLauncher.getPopupDataProvider().getWidgetsForPackageUser(
+ new PackageUserKey(
+ mOriginalItemInfo.getTargetComponent().getPackageName(),
+ mOriginalItemInfo.user));
- ViewGroup widgetRow = (ViewGroup) findViewById(R.id.widgets);
- ViewGroup widgetCells = (ViewGroup) widgetRow.findViewById(R.id.widgets_cell_list);
+ ViewGroup widgetRow = findViewById(R.id.widgets);
+ ViewGroup widgetCells = widgetRow.findViewById(R.id.widgets_cell_list);
widgetCells.removeAllViews();
@@ -168,72 +135,31 @@
return widget;
}
- @Override
- public void onClick(View view) {
- mLauncher.getWidgetsView().handleClick();
- }
-
- @Override
- public boolean onLongClick(View view) {
- mLauncher.getDragController().addDragListener(this);
- return mLauncher.getWidgetsView().handleLongClick(view);
- }
-
private void open(boolean animate) {
if (mIsOpen || mOpenCloseAnimator.isRunning()) {
return;
}
mIsOpen = true;
- boolean isSheetDark = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark);
- mLauncher.getSystemUiController().updateUiState(
- SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET,
- isSheetDark ? SystemUiController.FLAG_DARK_NAV : SystemUiController.FLAG_LIGHT_NAV);
+ setupNavBarColor();
if (animate) {
- mOpenCloseAnimator.setValues(new PropertyListBuilder()
- .translationY(mTranslationYOpen).build());
- mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mSwipeDetector.finishedScrolling();
- }
- });
- mOpenCloseAnimator.setInterpolator(mFastOutSlowInInterpolator);
+ mOpenCloseAnimator.setValues(
+ PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
+ mOpenCloseAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
mOpenCloseAnimator.start();
} else {
- setTranslationY(mTranslationYOpen);
+ setTranslationShift(TRANSLATION_SHIFT_OPENED);
}
}
@Override
protected void handleClose(boolean animate) {
- if (!mIsOpen || mOpenCloseAnimator.isRunning()) {
- return;
- }
- if (animate) {
- mOpenCloseAnimator.setValues(new PropertyListBuilder()
- .translationY(mTranslationYClosed).build());
- mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mSwipeDetector.finishedScrolling();
- onCloseComplete();
- }
- });
- mOpenCloseAnimator.setInterpolator(mSwipeDetector.isIdleState()
- ? mFastOutSlowInInterpolator : mScrollInterpolator);
- mOpenCloseAnimator.start();
- } else {
- setTranslationY(mTranslationYClosed);
- onCloseComplete();
- }
+ handleClose(animate, DEFAULT_CLOSE_DURATION);
}
- private void onCloseComplete() {
- mIsOpen = false;
- mLauncher.getDragLayer().removeView(mGradientBackground);
- mLauncher.getDragLayer().removeView(WidgetsBottomSheet.this);
- mLauncher.getSystemUiController().updateUiState(
- SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, 0);
+ @Override
+ protected void onCloseComplete() {
+ super.onCloseComplete();
+ mLauncher.getDragLayer().removeView(mGradientView);
}
@Override
@@ -242,18 +168,6 @@
}
@Override
- public int getLogContainerType() {
- return LauncherLogProto.ContainerType.WIDGETS; // TODO: be more specific
- }
-
- /**
- * Returns a {@link WidgetsBottomSheet} which is already open or null
- */
- public static WidgetsBottomSheet getOpen(Launcher launcher) {
- return getOpenView(launcher, TYPE_WIDGETS_BOTTOM_SHEET);
- }
-
- @Override
public void setInsets(Rect insets) {
// Extend behind left, right, and bottom insets.
int leftInset = insets.left - mInsets.left;
@@ -264,67 +178,8 @@
getPaddingRight() + rightInset, getPaddingBottom() + bottomInset);
}
- /* SwipeDetector.Listener */
-
@Override
- public void onDragStart(boolean start) {
- }
-
- @Override
- public boolean onDrag(float displacement, float velocity) {
- setTranslationY(Utilities.boundToRange(displacement, mTranslationYOpen,
- mTranslationYClosed));
- return true;
- }
-
- @Override
- public void setTranslationY(float translationY) {
- super.setTranslationY(translationY);
- if (mGradientBackground == null) return;
- float p = (mTranslationYClosed - translationY) / mTranslationYRange;
- boolean showScrim = p <= 0;
- mGradientBackground.setProgress(p, showScrim);
- }
-
- @Override
- public void onDragEnd(float velocity, boolean fling) {
- if ((fling && velocity > 0) || getTranslationY() > (mTranslationYRange) / 2) {
- mScrollInterpolator.setVelocityAtZero(velocity);
- mOpenCloseAnimator.setDuration(SwipeDetector.calculateDuration(velocity,
- (mTranslationYClosed - getTranslationY()) / mTranslationYRange));
- close(true);
- } else {
- mIsOpen = false;
- mOpenCloseAnimator.setDuration(SwipeDetector.calculateDuration(velocity,
- (getTranslationY() - mTranslationYOpen) / mTranslationYRange));
- open(true);
- }
- }
-
- @Override
- public boolean onControllerTouchEvent(MotionEvent ev) {
- return mSwipeDetector.onTouchEvent(ev);
- }
-
- @Override
- public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- int directionsToDetectScroll = mSwipeDetector.isIdleState() ?
- SwipeDetector.DIRECTION_NEGATIVE : 0;
- mSwipeDetector.setDetectableScrollConditions(
- directionsToDetectScroll, false);
- mSwipeDetector.onTouchEvent(ev);
- return mSwipeDetector.isDraggingOrSettling();
- }
-
- /* DragListener */
-
- @Override
- public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
- // A widget or custom shortcut was dragged.
- close(true);
- }
-
- @Override
- public void onDragEnd() {
+ protected int getElementsRowCount() {
+ return 1;
}
}
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
deleted file mode 100644
index acec3dd..0000000
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright (C) 2015 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.widget;
-
-import android.content.Context;
-import android.content.pm.LauncherApps;
-import android.graphics.Point;
-import android.support.v7.widget.LinearLayoutManager;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Toast;
-
-import com.android.launcher3.BaseContainerView;
-import com.android.launcher3.DeleteDropTarget;
-import com.android.launcher3.DragSource;
-import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.compat.AlphabeticIndexCompat;
-import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.folder.Folder;
-import com.android.launcher3.model.PackageItemInfo;
-import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-import com.android.launcher3.util.MultiHashMap;
-import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.util.Thunk;
-
-import java.util.List;
-
-/**
- * The widgets list view container.
- */
-public class WidgetsContainerView extends BaseContainerView
- implements View.OnLongClickListener, View.OnClickListener, DragSource {
- private static final String TAG = "WidgetsContainerView";
- private static final boolean LOGD = false;
-
- /* Global instances that are used inside this container. */
- @Thunk Launcher mLauncher;
-
- /* Recycler view related member variables */
- private WidgetsRecyclerView mRecyclerView;
- private WidgetsListAdapter mAdapter;
-
- /* Touch handling related member variables. */
- private Toast mWidgetInstructionToast;
-
- public WidgetsContainerView(Context context) {
- this(context, null);
- }
-
- public WidgetsContainerView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- mLauncher = Launcher.getLauncher(context);
- LauncherAppState apps = LauncherAppState.getInstance(context);
- mAdapter = new WidgetsListAdapter(context, LayoutInflater.from(context),
- apps.getWidgetCache(), new AlphabeticIndexCompat(context), this, this,
- new WidgetsDiffReporter(apps.getIconCache()));
- mAdapter.setNotifyListener();
- if (LOGD) {
- Log.d(TAG, "WidgetsContainerView constructor");
- }
- }
-
- @Override
- public View getTouchDelegateTargetView() {
- return mRecyclerView;
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mRecyclerView = (WidgetsRecyclerView) getContentView().findViewById(R.id.widgets_list_view);
- mRecyclerView.setAdapter(mAdapter);
- mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
- }
-
- //
- // Returns views used for launcher transitions.
- //
-
- public void scrollToTop() {
- mRecyclerView.scrollToPosition(0);
- }
-
- //
- // Touch related handling.
- //
-
- @Override
- public void onClick(View v) {
- // When we have exited widget tray or are in transition, disregard clicks
- if (!mLauncher.isWidgetsViewVisible()
- || mLauncher.getWorkspace().isSwitchingState()
- || !(v instanceof WidgetCell)) return;
-
- handleClick();
- }
-
- public void handleClick() {
- // Let the user know that they have to long press to add a widget
- if (mWidgetInstructionToast != null) {
- mWidgetInstructionToast.cancel();
- }
-
- CharSequence msg = Utilities.wrapForTts(
- getContext().getText(R.string.long_press_widget_to_add),
- getContext().getString(R.string.long_accessible_way_to_add));
- mWidgetInstructionToast = Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT);
- mWidgetInstructionToast.show();
- }
-
- @Override
- public boolean onLongClick(View v) {
- // When we have exited the widget tray, disregard long clicks
- if (!mLauncher.isWidgetsViewVisible()) return false;
- return handleLongClick(v);
- }
-
- public boolean handleLongClick(View v) {
- if (LOGD) {
- Log.d(TAG, String.format("onLongClick [v=%s]", v));
- }
- // When we are in transition, disregard long clicks
- if (mLauncher.getWorkspace().isSwitchingState()) return false;
- // Return if global dragging is not enabled
- if (!mLauncher.isDraggingEnabled()) return false;
-
- return beginDragging(v);
- }
-
- private boolean beginDragging(View v) {
- if (v instanceof WidgetCell) {
- if (!beginDraggingWidget((WidgetCell) v)) {
- return false;
- }
- } else {
- Log.e(TAG, "Unexpected dragging view: " + v);
- }
-
- // We don't enter spring-loaded mode if the drag has been cancelled
- if (mLauncher.getDragController().isDragging()) {
- // Go into spring loaded mode (must happen before we startDrag())
- mLauncher.enterSpringLoadedDragMode();
- }
-
- return true;
- }
-
- private boolean beginDraggingWidget(WidgetCell v) {
- // Get the widget preview as the drag representation
- WidgetImageView image = (WidgetImageView) v.findViewById(R.id.widget_preview);
-
- // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and
- // we abort the drag.
- if (image.getBitmap() == null) {
- return false;
- }
-
- int[] loc = new int[2];
- mLauncher.getDragLayer().getLocationInDragLayer(image, loc);
-
- new PendingItemDragHelper(v).startDrag(
- image.getBitmapBounds(), image.getBitmap().getWidth(), image.getWidth(),
- new Point(loc[0], loc[1]), this, new DragOptions());
- return true;
- }
-
- //
- // Drag related handling methods that implement {@link DragSource} interface.
- //
-
- @Override
- public boolean supportsAppInfoDropTarget() {
- return true;
- }
-
- /*
- * Both this method and {@link #supportsFlingToDelete} has to return {@code false} for the
- * {@link DeleteDropTarget} to be invisible.)
- */
- @Override
- public boolean supportsDeleteDropTarget() {
- return false;
- }
-
- @Override
- public float getIntrinsicIconScaleFactor() {
- return 0;
- }
-
- @Override
- public void onDropCompleted(View target, DragObject d, boolean isFlingToDelete,
- boolean success) {
- if (LOGD) {
- Log.d(TAG, "onDropCompleted");
- }
- if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
- !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
- // Exit spring loaded mode if we have not successfully dropped or have not handled the
- // drop in Workspace
- mLauncher.exitSpringLoadedDragModeDelayed(true,
- Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
- }
- mLauncher.unlockScreenOrientation(false);
-
- if (!success) {
- d.deferDragViewCleanupPostAnimation = false;
- }
- }
-
- /**
- * Initialize the widget data model.
- */
- public void setWidgets(MultiHashMap<PackageItemInfo, WidgetItem> model) {
- mAdapter.setWidgets(model);
-
- View loader = getContentView().findViewById(R.id.loader);
- if (loader != null) {
- ((ViewGroup) getContentView()).removeView(loader);
- }
- }
-
- public boolean isEmpty() {
- return mAdapter.getItemCount() == 0;
- }
-
- public List<WidgetItem> getWidgetsForPackageUser(PackageUserKey packageUserKey) {
- return mAdapter.copyWidgetsForPackageUser(packageUserKey);
- }
-
- @Override
- public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
- targetParent.containerType = ContainerType.WIDGETS;
- }
-}
\ No newline at end of file
diff --git a/src/com/android/launcher3/widget/WidgetsDiffReporter.java b/src/com/android/launcher3/widget/WidgetsDiffReporter.java
index 52deec3..d67f403 100644
--- a/src/com/android/launcher3/widget/WidgetsDiffReporter.java
+++ b/src/com/android/launcher3/widget/WidgetsDiffReporter.java
@@ -16,6 +16,7 @@
package com.android.launcher3.widget;
+import android.support.v7.widget.RecyclerView;
import android.util.Log;
import com.android.launcher3.IconCache;
@@ -26,26 +27,18 @@
import java.util.Iterator;
/**
- * Do diff on widget's tray list items and call the {@link NotifyListener} methods accordingly.
+ * Do diff on widget's tray list items and call the {@link RecyclerView.Adapter}
+ * methods accordingly.
*/
public class WidgetsDiffReporter {
- private final boolean DEBUG = false;
- private final String TAG = "WidgetsDiffReporter";
+ private static final boolean DEBUG = false;
+ private static final String TAG = "WidgetsDiffReporter";
+
private final IconCache mIconCache;
- private NotifyListener mListener;
+ private final RecyclerView.Adapter mListener;
- public interface NotifyListener {
- void notifyDataSetChanged();
- void notifyItemChanged(int index);
- void notifyItemInserted(int index);
- void notifyItemRemoved(int index);
- }
-
- public WidgetsDiffReporter(IconCache iconCache) {
+ public WidgetsDiffReporter(IconCache iconCache, RecyclerView.Adapter listener) {
mIconCache = iconCache;
- }
-
- public void setListener(NotifyListener listener) {
mListener = listener;
}
@@ -55,9 +48,17 @@
Log.d(TAG, "process oldEntries#=" + currentEntries.size()
+ " newEntries#=" + newEntries.size());
}
- if (currentEntries.size() == 0 && newEntries.size() > 0) {
- currentEntries.addAll(newEntries);
- mListener.notifyDataSetChanged();
+ // Early exit if either of the list is empty
+ if (currentEntries.isEmpty() || newEntries.isEmpty()) {
+ // Skip if both list are empty.
+ // On rotation, we open the widget tray with empty. Then try to fetch the list again
+ // when the animation completes (which still gives empty). And we get the final result
+ // when the bind actually completes.
+ if (currentEntries.size() != newEntries.size()) {
+ currentEntries.clear();
+ currentEntries.addAll(newEntries);
+ mListener.notifyDataSetChanged();
+ }
return;
}
ArrayList<WidgetListRowEntry> orgEntries =
diff --git a/src/com/android/launcher3/widget/WidgetsFullSheet.java b/src/com/android/launcher3/widget/WidgetsFullSheet.java
new file mode 100644
index 0000000..a40ea1b
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetsFullSheet.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2017 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.widget;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.PropertyValuesHolder;
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+
+import com.android.launcher3.Insettable;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
+import com.android.launcher3.R;
+
+/**
+ * Popup for showing the full list of available widgets
+ */
+public class WidgetsFullSheet extends BaseWidgetSheet
+ implements Insettable, ProviderChangedListener {
+
+ private static final long DEFAULT_OPEN_DURATION = 267;
+ private static final long FADE_IN_DURATION = 150;
+ private static final float VERTICAL_START_POSITION = 0.3f;
+
+ private static final Rect sTempRect = new Rect();
+
+ private final Rect mInsets = new Rect();
+
+ private final WidgetsListAdapter mAdapter;
+
+ private View mNavBarScrim;
+ private WidgetsRecyclerView mRecyclerView;
+
+ public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ LauncherAppState apps = LauncherAppState.getInstance(context);
+ mAdapter = new WidgetsListAdapter(context,
+ LayoutInflater.from(context), apps.getWidgetCache(), apps.getIconCache(),
+ this, this);
+ }
+
+ public WidgetsFullSheet(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mContent = findViewById(R.id.container);
+ mNavBarScrim = findViewById(R.id.nav_bar_bg);
+
+ mRecyclerView = findViewById(R.id.widgets_list_view);
+ mRecyclerView.setAdapter(mAdapter);
+ mAdapter.setApplyBitmapDeferred(true, mRecyclerView);
+
+ mGradientView = findViewById(R.id.gradient_bg);
+ mGradientView.setProgress(1, false);
+
+ onWidgetsBound();
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mLauncher.getAppWidgetHost().addProviderChangeListener(this);
+ notifyWidgetProvidersChanged();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mLauncher.getAppWidgetHost().removeProviderChangeListener(this);
+ }
+
+ @Override
+ public void setInsets(Rect insets) {
+ mInsets.set(insets);
+
+ mNavBarScrim.getLayoutParams().height = insets.bottom;
+ mRecyclerView.setPadding(
+ mRecyclerView.getPaddingLeft(), mRecyclerView.getPaddingTop(),
+ mRecyclerView.getPaddingRight(), insets.bottom);
+ if (insets.bottom > 0) {
+ setupNavBarColor();
+ }
+ requestLayout();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int widthUsed;
+ if (mInsets.bottom > 0) {
+ // If we have bottom insets, we do not show the scrim as it would overlap
+ // with the navbar scrim
+ mGradientView.setVisibility(View.INVISIBLE);
+ widthUsed = 0;
+ } else {
+ mLauncher.getDeviceProfile().getWorkspacePadding(sTempRect);
+ widthUsed = Math.max(sTempRect.left + sTempRect.right,
+ 2 * (mInsets.left + mInsets.right));
+ }
+
+ int heightUsed = mInsets.top + mLauncher.getDeviceProfile().edgeMarginPx;
+ measureChildWithMargins(mContent, widthMeasureSpec,
+ widthUsed, heightMeasureSpec, heightUsed);
+ measureChild(mGradientView, widthMeasureSpec, heightMeasureSpec);
+ setMeasuredDimension(mGradientView.getMeasuredWidth(), mGradientView.getMeasuredHeight());
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ int width = r - l;
+ int height = b - t;
+ mGradientView.layout(0, 0, width, height);
+
+ // Content is laid out as center bottom aligned
+ int contentWidth = mContent.getMeasuredWidth();
+ int contentLeft = (width - contentWidth) / 2;
+ mContent.layout(contentLeft, height - mContent.getMeasuredHeight(),
+ contentLeft + contentWidth, height);
+
+ setTranslationShift(mTranslationShift);
+ }
+
+ @Override
+ public void notifyWidgetProvidersChanged() {
+ mLauncher.refreshAndBindWidgetsForPackageUser(null);
+ }
+
+ @Override
+ protected void onWidgetsBound() {
+ mAdapter.setWidgets(mLauncher.getPopupDataProvider().getAllWidgets());
+ }
+
+ private void open(boolean animate) {
+ if (mIsOpen) {
+ return;
+ }
+ mIsOpen = true;
+ if (animate) {
+ if (mLauncher.getDragLayer().getInsets().bottom > 0) {
+ mContent.setAlpha(0);
+ setTranslationShift(VERTICAL_START_POSITION);
+ }
+ mOpenCloseAnimator.setValues(
+ PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
+ mOpenCloseAnimator
+ .setDuration(DEFAULT_OPEN_DURATION)
+ .setInterpolator(AnimationUtils.loadInterpolator(
+ getContext(), android.R.interpolator.linear_out_slow_in));
+ mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mRecyclerView.setLayoutFrozen(false);
+ mAdapter.setApplyBitmapDeferred(false, mRecyclerView);
+ mOpenCloseAnimator.removeListener(this);
+ }
+ });
+ post(new Runnable() {
+ @Override
+ public void run() {
+ mRecyclerView.setLayoutFrozen(true);
+ mOpenCloseAnimator.start();
+ mContent.animate().alpha(1).setDuration(FADE_IN_DURATION);
+ }
+ });
+ } else {
+ setTranslationShift(TRANSLATION_SHIFT_OPENED);
+ mAdapter.setApplyBitmapDeferred(false, mRecyclerView);
+ }
+ }
+
+ @Override
+ protected void handleClose(boolean animate) {
+ handleClose(animate, DEFAULT_OPEN_DURATION);
+ }
+
+ @Override
+ protected boolean isOfType(int type) {
+ return (type & TYPE_WIDGETS_FULL_SHEET) != 0;
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ // Disable swipe down when recycler view is scrolling
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mNoIntercept = false;
+ if (mLauncher.getDragLayer().isEventOverView(mContent, ev)) {
+ mNoIntercept = !mRecyclerView.shouldContainerScroll(ev, mLauncher.getDragLayer());
+ }
+ }
+ return super.onControllerInterceptTouchEvent(ev);
+ }
+
+ public static WidgetsFullSheet show(Launcher launcher, boolean animate) {
+ WidgetsFullSheet sheet = (WidgetsFullSheet) launcher.getLayoutInflater()
+ .inflate(R.layout.widgets_full_sheet, launcher.getDragLayer(), false);
+ launcher.getDragLayer().addView(sheet);
+ sheet.open(animate);
+ return sheet;
+ }
+
+ @Override
+ protected int getElementsRowCount() {
+ return mAdapter.getItemCount();
+ }
+}
diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java
index 6b1800c..0147ea4 100644
--- a/src/com/android/launcher3/widget/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java
@@ -25,13 +25,11 @@
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
+import com.android.launcher3.IconCache;
import com.android.launcher3.R;
import com.android.launcher3.WidgetPreviewLoader;
-import com.android.launcher3.compat.AlphabeticIndexCompat;
-import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.util.LabelComparator;
-import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageUserKey;
import java.util.ArrayList;
@@ -39,7 +37,6 @@
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
-import java.util.Map;
/**
* List view adapter for the widget tray.
@@ -56,7 +53,6 @@
private final WidgetPreviewLoader mWidgetPreviewLoader;
private final LayoutInflater mLayoutInflater;
- private final AlphabeticIndexCompat mIndexer;
private final OnClickListener mIconClickListener;
private final OnLongClickListener mIconLongClickListener;
@@ -64,56 +60,43 @@
private ArrayList<WidgetListRowEntry> mEntries = new ArrayList<>();
private final WidgetsDiffReporter mDiffReporter;
+ private boolean mApplyBitmapDeferred;
+
public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
- WidgetPreviewLoader widgetPreviewLoader, AlphabeticIndexCompat indexCompat,
- OnClickListener iconClickListener, OnLongClickListener iconLongClickListener,
- WidgetsDiffReporter diffReporter) {
+ WidgetPreviewLoader widgetPreviewLoader, IconCache iconCache,
+ OnClickListener iconClickListener, OnLongClickListener iconLongClickListener) {
mLayoutInflater = layoutInflater;
mWidgetPreviewLoader = widgetPreviewLoader;
- mIndexer = indexCompat;
mIconClickListener = iconClickListener;
mIconLongClickListener = iconLongClickListener;
mIndent = context.getResources().getDimensionPixelSize(R.dimen.widget_section_indent);
- mDiffReporter = diffReporter;
+ mDiffReporter = new WidgetsDiffReporter(iconCache, this);
}
- public void setNotifyListener() {
- mDiffReporter.setListener(new WidgetsDiffReporter.NotifyListener() {
- @Override
- public void notifyDataSetChanged() {
- WidgetsListAdapter.this.notifyDataSetChanged();
- }
+ /**
+ * Defers applying bitmap on all the {@link WidgetCell} in the {@param rv}
+ *
+ * @see WidgetCell#setApplyBitmapDeferred(boolean)
+ */
+ public void setApplyBitmapDeferred(boolean isDeferred, RecyclerView rv) {
+ mApplyBitmapDeferred = isDeferred;
- @Override
- public void notifyItemChanged(int index) {
- WidgetsListAdapter.this.notifyItemChanged(index);
+ for (int i = rv.getChildCount() - 1; i >= 0; i--) {
+ WidgetsRowViewHolder holder = (WidgetsRowViewHolder)
+ rv.getChildViewHolder(rv.getChildAt(i));
+ for (int j = holder.cellContainer.getChildCount() - 1; j >= 0; j--) {
+ View v = holder.cellContainer.getChildAt(j);
+ if (v instanceof WidgetCell) {
+ ((WidgetCell) v).setApplyBitmapDeferred(mApplyBitmapDeferred);
+ }
}
-
- @Override
- public void notifyItemInserted(int index) {
- WidgetsListAdapter.this.notifyItemInserted(index);
- }
-
- @Override
- public void notifyItemRemoved(int index) {
- WidgetsListAdapter.this.notifyItemRemoved(index);
- }
- });
+ }
}
/**
* Update the widget list.
*/
- public void setWidgets(MultiHashMap<PackageItemInfo, WidgetItem> widgets) {
- ArrayList<WidgetListRowEntry> tempEntries = new ArrayList<>();
-
- WidgetItemComparator widgetComparator = new WidgetItemComparator();
- for (Map.Entry<PackageItemInfo, ArrayList<WidgetItem>> entry : widgets.entrySet()) {
- WidgetListRowEntry row = new WidgetListRowEntry(entry.getKey(), entry.getValue());
- row.titleSectionName = mIndexer.computeSectionName(row.pkgItem.title);
- Collections.sort(row.widgets, widgetComparator);
- tempEntries.add(row);
- }
+ public void setWidgets(ArrayList<WidgetListRowEntry> tempEntries) {
WidgetListRowEntryComparator rowComparator = new WidgetListRowEntryComparator();
Collections.sort(tempEntries, rowComparator);
mDiffReporter.process(mEntries, tempEntries, rowComparator);
@@ -128,26 +111,6 @@
return mEntries.get(pos).titleSectionName;
}
- /**
- * Copies and returns the widgets associated with the package and user of the ComponentKey.
- */
- public List<WidgetItem> copyWidgetsForPackageUser(PackageUserKey packageUserKey) {
- for (WidgetListRowEntry entry : mEntries) {
- if (entry.pkgItem.packageName.equals(packageUserKey.mPackageName)) {
- ArrayList<WidgetItem> widgets = new ArrayList<>(entry.widgets);
- // Remove widgets not associated with the correct user.
- Iterator<WidgetItem> iterator = widgets.iterator();
- while (iterator.hasNext()) {
- if (!iterator.next().user.equals(packageUserKey.mUser)) {
- iterator.remove();
- }
- }
- return widgets.isEmpty() ? null : widgets;
- }
- }
- return null;
- }
-
@Override
public void onBindViewHolder(WidgetsRowViewHolder holder, int pos) {
WidgetListRowEntry entry = mEntries.get(pos);
@@ -194,6 +157,7 @@
for (int i=0; i < infoList.size(); i++) {
WidgetCell widget = (WidgetCell) row.getChildAt(2*i);
widget.applyFromCellItem(infoList.get(i), mWidgetPreviewLoader);
+ widget.setApplyBitmapDeferred(mApplyBitmapDeferred);
widget.ensurePreview();
widget.setVisibility(View.VISIBLE);
@@ -253,5 +217,4 @@
return mComparator.compare(a.pkgItem.title.toString(), b.pkgItem.title.toString());
}
}
-
}
diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
index 9730a82..89c88a4 100644
--- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
@@ -17,12 +17,12 @@
package com.android.launcher3.widget;
import android.content.Context;
-import android.graphics.Color;
import android.support.v7.widget.LinearLayoutManager;
import android.util.AttributeSet;
import android.view.View;
import com.android.launcher3.BaseRecyclerView;
+import com.android.launcher3.R;
/**
* The widgets recycler view.
@@ -32,6 +32,8 @@
private static final String TAG = "WidgetsRecyclerView";
private WidgetsListAdapter mAdapter;
+ private final int mScrollbarTop;
+
public WidgetsRecyclerView(Context context) {
this(context, null);
}
@@ -43,6 +45,7 @@
public WidgetsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
// API 21 and below only support 3 parameter ctor.
super(context, attrs, defStyleAttr);
+ mScrollbarTop = getResources().getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
}
public WidgetsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr,
@@ -130,13 +133,16 @@
@Override
protected int getAvailableScrollHeight() {
View child = getChildAt(0);
- int height = child.getMeasuredHeight() * mAdapter.getItemCount();
- int totalHeight = getPaddingTop() + height + getPaddingBottom();
- int availableScrollHeight = totalHeight - getScrollbarTrackHeight();
- return availableScrollHeight;
+ return child.getMeasuredHeight() * mAdapter.getItemCount() - getScrollbarTrackHeight()
+ - mScrollbarTop;
}
private boolean isModelNotReady() {
return mAdapter.getItemCount() == 0;
}
+
+ @Override
+ public int getScrollBarTop() {
+ return mScrollbarTop;
+ }
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfo.java b/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfo.java
new file mode 100644
index 0000000..1086987
--- /dev/null
+++ b/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfo.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2017 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.widget.custom;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.Utilities;
+
+/**
+ * Custom app widget provider info that can be used as a widget, but provide extra functionality
+ * by allowing custom code and views.
+ */
+public class CustomAppWidgetProviderInfo extends LauncherAppWidgetProviderInfo
+ implements Parcelable {
+
+ public final int providerId;
+
+ protected CustomAppWidgetProviderInfo(Parcel parcel, boolean readSelf, int providerId) {
+ super(parcel);
+ if (readSelf) {
+ this.providerId = parcel.readInt();
+
+ provider = new ComponentName(parcel.readString(), CLS_CUSTOM_WIDGET_PREFIX + providerId);
+
+ label = parcel.readString();
+ initialLayout = parcel.readInt();
+ icon = parcel.readInt();
+ previewImage = parcel.readInt();
+
+ resizeMode = parcel.readInt();
+ spanX = parcel.readInt();
+ spanY = parcel.readInt();
+ minSpanX = parcel.readInt();
+ minSpanY = parcel.readInt();
+ } else {
+ this.providerId = providerId;
+ }
+ }
+
+ @Override
+ public void initSpans(Context context) { }
+
+ @Override
+ public String getLabel(PackageManager packageManager) {
+ return Utilities.trim(label);
+ }
+
+ @Override
+ public String toString() {
+ return "WidgetProviderInfo(" + provider + ")";
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeInt(providerId);
+ out.writeString(provider.getPackageName());
+
+ out.writeString(label);
+ out.writeInt(initialLayout);
+ out.writeInt(icon);
+ out.writeInt(previewImage);
+
+ out.writeInt(resizeMode);
+ out.writeInt(spanX);
+ out.writeInt(spanY);
+ out.writeInt(minSpanX);
+ out.writeInt(minSpanY);
+ }
+
+ public static final Parcelable.Creator<CustomAppWidgetProviderInfo> CREATOR
+ = new Parcelable.Creator<CustomAppWidgetProviderInfo>() {
+
+ @Override
+ public CustomAppWidgetProviderInfo createFromParcel(Parcel parcel) {
+ return new CustomAppWidgetProviderInfo(parcel, true, 0);
+ }
+
+ @Override
+ public CustomAppWidgetProviderInfo[] newArray(int size) {
+ return new CustomAppWidgetProviderInfo[size];
+ }
+ };
+}
diff --git a/src/com/android/launcher3/widget/custom/CustomWidgetParser.java b/src/com/android/launcher3/widget/custom/CustomWidgetParser.java
new file mode 100644
index 0000000..00720c4
--- /dev/null
+++ b/src/com/android/launcher3/widget/custom/CustomWidgetParser.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2017 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.widget.custom;
+
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Parcel;
+import android.os.Process;
+import android.util.SparseArray;
+import android.util.Xml;
+
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.android.launcher3.LauncherAppWidgetProviderInfo.CLS_CUSTOM_WIDGET_PREFIX;
+
+/**
+ * Utility class to parse {@ink CustomAppWidgetProviderInfo} definitions from xml
+ */
+public class CustomWidgetParser {
+
+ private static List<LauncherAppWidgetProviderInfo> sCustomWidgets;
+ private static SparseArray<ComponentName> sWidgetsIdMap;
+
+ public static List<LauncherAppWidgetProviderInfo> getCustomWidgets(Context context) {
+ if (sCustomWidgets == null) {
+ // Synchronization not needed as it it safe to load multiple times
+ parseCustomWidgets(context);
+ }
+
+ return sCustomWidgets;
+ }
+
+ public static int getWidgetIdForCustomProvider(Context context, ComponentName provider) {
+ if (sWidgetsIdMap == null) {
+ parseCustomWidgets(context);
+ }
+ int index = sWidgetsIdMap.indexOfValue(provider);
+ if (index >= 0) {
+ return LauncherAppWidgetInfo.CUSTOM_WIDGET_ID - sWidgetsIdMap.keyAt(index);
+ } else {
+ return AppWidgetManager.INVALID_APPWIDGET_ID;
+ }
+ }
+
+ public static LauncherAppWidgetProviderInfo getWidgetProvider(Context context, int widgetId) {
+ if (sWidgetsIdMap == null || sCustomWidgets == null) {
+ parseCustomWidgets(context);
+ }
+ ComponentName cn = sWidgetsIdMap.get(LauncherAppWidgetInfo.CUSTOM_WIDGET_ID - widgetId);
+ for (LauncherAppWidgetProviderInfo info : sCustomWidgets) {
+ if (info.provider.equals(cn)) {
+ return info;
+ }
+ }
+ return null;
+ }
+
+ private static void parseCustomWidgets(Context context) {
+ ArrayList<LauncherAppWidgetProviderInfo> widgets = new ArrayList<>();
+ SparseArray<ComponentName> idMap = new SparseArray<>();
+
+ List<AppWidgetProviderInfo> providers = AppWidgetManager.getInstance(context)
+ .getInstalledProvidersForProfile(Process.myUserHandle());
+ if (providers.isEmpty()) {
+ sCustomWidgets = widgets;
+ sWidgetsIdMap = idMap;
+ return;
+ }
+
+ Parcel parcel = Parcel.obtain();
+ providers.get(0).writeToParcel(parcel, 0);
+
+ try (XmlResourceParser parser = context.getResources().getXml(R.xml.custom_widgets)) {
+ final int depth = parser.getDepth();
+ int type;
+
+ while (((type = parser.next()) != XmlPullParser.END_TAG ||
+ parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
+ if ((type == XmlPullParser.START_TAG) && "widget".equals(parser.getName())) {
+ TypedArray a = context.obtainStyledAttributes(
+ Xml.asAttributeSet(parser), R.styleable.CustomAppWidgetProviderInfo);
+
+ parcel.setDataPosition(0);
+ CustomAppWidgetProviderInfo info = newInfo(a, parcel, context);
+ widgets.add(info);
+ a.recycle();
+
+ idMap.put(info.providerId, info.provider);
+ }
+ }
+ } catch (IOException | XmlPullParserException e) {
+ throw new RuntimeException(e);
+ }
+ parcel.recycle();
+ sCustomWidgets = widgets;
+ sWidgetsIdMap = idMap;
+ }
+
+ private static CustomAppWidgetProviderInfo newInfo(TypedArray a, Parcel parcel, Context context) {
+ int providerId = a.getInt(R.styleable.CustomAppWidgetProviderInfo_providerId, 0);
+ CustomAppWidgetProviderInfo info = new CustomAppWidgetProviderInfo(parcel, false, providerId);
+ info.provider = new ComponentName(context.getPackageName(), CLS_CUSTOM_WIDGET_PREFIX + providerId);
+
+ info.label = a.getString(R.styleable.CustomAppWidgetProviderInfo_android_label);
+ info.initialLayout = a.getResourceId(R.styleable.CustomAppWidgetProviderInfo_android_initialLayout, 0);
+ info.icon = a.getResourceId(R.styleable.CustomAppWidgetProviderInfo_android_icon, 0);
+ info.previewImage = a.getResourceId(R.styleable.CustomAppWidgetProviderInfo_android_previewImage, 0);
+ info.resizeMode = a.getInt(R.styleable.CustomAppWidgetProviderInfo_android_resizeMode, 0);
+
+ info.spanX = a.getInt(R.styleable.CustomAppWidgetProviderInfo_numColumns, 1);
+ info.spanY = a.getInt(R.styleable.CustomAppWidgetProviderInfo_numRows, 1);
+ info.minSpanX = a.getInt(R.styleable.CustomAppWidgetProviderInfo_numMinColumns, 1);
+ info.minSpanY = a.getInt(R.styleable.CustomAppWidgetProviderInfo_numMinRows, 1);
+ return info;
+ }
+}
diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
index 82f34e4..a486ceb 100644
--- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
+++ b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
@@ -55,7 +55,7 @@
for (ItemInfo item : items) {
list.add(Pair.create(item, null));
}
- return new AddWorkspaceItemsTask(Provider.of(list)) {
+ return new AddWorkspaceItemsTask(list) {
@Override
protected void updateScreens(Context context, ArrayList<Long> workspaceScreens) { }
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index d4d517a..87103d7 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -16,6 +16,7 @@
package com.android.launcher3.ui.widget;
import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentValues;
@@ -171,8 +172,9 @@
// Widget has a valid Id now.
assertEquals(0, mCursor.getInt(mCursor.getColumnIndex(LauncherSettings.Favorites.RESTORED))
& LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
- assertNotNull(mWidgetManager.getAppWidgetInfo(mCursor.getInt(mCursor.getColumnIndex(
- LauncherSettings.Favorites.APPWIDGET_ID))));
+ assertNotNull(AppWidgetManager.getInstance(mTargetContext)
+ .getAppWidgetInfo(mCursor.getInt(mCursor.getColumnIndex(
+ LauncherSettings.Favorites.APPWIDGET_ID))));
}
@Test
@@ -297,7 +299,7 @@
item.spanY = info.minSpanY;
item.minSpanX = info.minSpanX;
item.minSpanY = info.minSpanY;
- item.user = info.getUser();
+ item.user = info.getProfile();
item.cellX = 0;
item.cellY = 1;
item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
diff --git a/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java b/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java
index 40b65e4..0185f13 100644
--- a/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java
+++ b/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java
@@ -22,13 +22,13 @@
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import com.android.launcher3.IconCache;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.WidgetPreviewLoader;
-import com.android.launcher3.compat.AlphabeticIndexCompat;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.model.WidgetItem;
@@ -40,8 +40,11 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
+import java.util.ArrayList;
+import java.util.Map;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNull;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -49,15 +52,12 @@
@RunWith(AndroidJUnit4.class)
public class WidgetsListAdapterTest {
- private final String TAG = "WidgetsListAdapterTest";
-
@Mock private LayoutInflater mMockLayoutInflater;
@Mock private WidgetPreviewLoader mMockWidgetCache;
- @Mock private WidgetsDiffReporter.NotifyListener mListener;
+ @Mock private RecyclerView.AdapterDataObserver mListener;
@Mock private IconCache mIconCache;
private WidgetsListAdapter mAdapter;
- private AlphabeticIndexCompat mIndexCompat;
private InvariantDeviceProfile mTestProfile;
private Context mContext;
@@ -68,41 +68,39 @@
mTestProfile = new InvariantDeviceProfile();
mTestProfile.numRows = 5;
mTestProfile.numColumns = 5;
- mIndexCompat = new AlphabeticIndexCompat(mContext);
- WidgetsDiffReporter reporter = new WidgetsDiffReporter(mIconCache);
- reporter.setListener(mListener);
mAdapter = new WidgetsListAdapter(mContext, mMockLayoutInflater, mMockWidgetCache,
- mIndexCompat, null, null, reporter);
+ mIconCache, null, null);
+ mAdapter.registerAdapterDataObserver(mListener);
}
@Test
public void test_notifyDataSetChanged() throws Exception {
mAdapter.setWidgets(generateSampleMap(1));
- verify(mListener, times(1)).notifyDataSetChanged();
+ verify(mListener, times(1)).onChanged();
}
@Test
public void test_notifyItemInserted() throws Exception {
mAdapter.setWidgets(generateSampleMap(1));
mAdapter.setWidgets(generateSampleMap(2));
- verify(mListener, times(1)).notifyDataSetChanged();
- verify(mListener, times(1)).notifyItemInserted(1);
+ verify(mListener, times(1)).onChanged();
+ verify(mListener, times(1)).onItemRangeInserted(eq(1), eq(1));
}
@Test
public void test_notifyItemRemoved() throws Exception {
mAdapter.setWidgets(generateSampleMap(2));
mAdapter.setWidgets(generateSampleMap(1));
- verify(mListener, times(1)).notifyDataSetChanged();
- verify(mListener, times(1)).notifyItemRemoved(1);
+ verify(mListener, times(1)).onChanged();
+ verify(mListener, times(1)).onItemRangeRemoved(eq(1), eq(1));
}
@Test
public void testNotifyItemChanged_PackageIconDiff() throws Exception {
mAdapter.setWidgets(generateSampleMap(1));
mAdapter.setWidgets(generateSampleMap(1));
- verify(mListener, times(1)).notifyDataSetChanged();
- verify(mListener, times(1)).notifyItemChanged(0);
+ verify(mListener, times(1)).onChanged();
+ verify(mListener, times(1)).onItemRangeChanged(eq(0), eq(1), isNull());
}
@Test
@@ -125,10 +123,11 @@
* @param num the number of WidgetItem the map should contain
* @return
*/
- private MultiHashMap<PackageItemInfo, WidgetItem> generateSampleMap(int num) {
- MultiHashMap<PackageItemInfo, WidgetItem> newMap = new MultiHashMap();
- if (num <= 0) return newMap;
+ private ArrayList<WidgetListRowEntry> generateSampleMap(int num) {
+ ArrayList<WidgetListRowEntry> result = new ArrayList<>();
+ if (num <= 0) return result;
+ MultiHashMap<PackageItemInfo, WidgetItem> newMap = new MultiHashMap();
PackageManager pm = mContext.getPackageManager();
AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(mContext);
for (AppWidgetProviderInfo widgetInfo : widgetManager.getAllProviders(null)) {
@@ -144,6 +143,10 @@
break;
}
}
- return newMap;
+ for (Map.Entry<PackageItemInfo, ArrayList<WidgetItem>> entry : newMap.entrySet()) {
+ result.add(new WidgetListRowEntry(entry.getKey(), entry.getValue()));
+ }
+
+ return result;
}
}