diff --git a/Android.bp b/Android.bp index ae8e99bb84..72b0f925c3 100644 --- a/Android.bp +++ b/Android.bp @@ -153,7 +153,8 @@ android_library { "androidx.cardview_cardview", "com.google.android.material_material", "iconloader_base", - "view_capture" + "view_capture", + "animationlib" ], manifest: "AndroidManifest-common.xml", sdk_version: "current", @@ -172,6 +173,7 @@ android_library { static_libs: [ "Launcher3ResLib", "launcher-testing-shared", + "animationlib" ], sdk_version: "current", min_sdk_version: min_launcher3_sdk_version, diff --git a/OWNERS b/OWNERS index b684460327..353ac8ed34 100644 --- a/OWNERS +++ b/OWNERS @@ -13,6 +13,9 @@ vadimt@google.com winsonc@google.com jonmiranda@google.com alexchau@google.com +patmanning@google.com +tsuharesu@google.com +awickham@google.com per-file FeatureFlags.java, globs = set noparent per-file FeatureFlags.java = sunnygoyal@google.com, winsonc@google.com, adamcohen@google.com, hyunyoungs@google.com, captaincole@google.com diff --git a/build.gradle b/build.gradle index b3d4304d56..b6065f04c0 100644 --- a/build.gradle +++ b/build.gradle @@ -118,7 +118,7 @@ final def releaseName = "Alpha 1" final def versionDisplayName = "${version} ${isReleaseBuild ? releaseName : devReleaseName}" final def majorVersion = versionDisplayName.split("\\.")[0] -final def quickstepMinSdk = "32" +final def quickstepMinSdk = "34" final def quickstepMaxSdk = "34" android { diff --git a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java index e5b2c678ad..29b24b797c 100644 --- a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java +++ b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java @@ -25,14 +25,13 @@ import android.content.Context; import android.os.Binder; import android.os.Bundle; import android.system.Os; -import android.util.Log; import androidx.annotation.Keep; import androidx.annotation.Nullable; import com.android.launcher3.BubbleTextView; import com.android.launcher3.LauncherAppState; -import com.android.launcher3.LauncherSettings; +import com.android.launcher3.LauncherModel; import com.android.launcher3.ShortcutAndWidgetContainer; import com.android.launcher3.testing.shared.TestProtocol; @@ -62,7 +61,6 @@ public class DebugTestInformationHandler extends TestInformationHandler { public void onActivityCreated(Activity activity, Bundle bundle) { sActivities.put(activity, true); ++sActivitiesCreatedCount; - Log.d(TestProtocol.FLAKY_ACTIVITY_COUNT, "onActivityCreated", new Exception()); } @Override @@ -188,13 +186,29 @@ public class DebugTestInformationHandler extends TestInformationHandler { return response; } + case TestProtocol.REQUEST_REINITIALIZE_DATA: { + final long identity = Binder.clearCallingIdentity(); + try { + MODEL_EXECUTOR.execute(() -> { + LauncherModel model = LauncherAppState.getInstance(mContext).getModel(); + model.getModelDbController().createEmptyDB(); + MAIN_EXECUTOR.execute(model::forceReload); + }); + return response; + } finally { + Binder.restoreCallingIdentity(identity); + } + } + case TestProtocol.REQUEST_CLEAR_DATA: { final long identity = Binder.clearCallingIdentity(); try { - LauncherSettings.Settings.call(mContext.getContentResolver(), - LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); - MAIN_EXECUTOR.submit(() -> - LauncherAppState.getInstance(mContext).getModel().forceReload()); + MODEL_EXECUTOR.execute(() -> { + LauncherModel model = LauncherAppState.getInstance(mContext).getModel(); + model.getModelDbController().createEmptyDB(); + model.getModelDbController().clearEmptyDbFlag(); + MAIN_EXECUTOR.execute(model::forceReload); + }); return response; } finally { Binder.restoreCallingIdentity(identity); diff --git a/go/quickstep/res/values-el/strings.xml b/go/quickstep/res/values-el/strings.xml index 9a67420f30..7038c11e57 100644 --- a/go/quickstep/res/values-el/strings.xml +++ b/go/quickstep/res/values-el/strings.xml @@ -14,7 +14,7 @@ "Για να ακούσετε ή να μεταφράσετε κείμενο στην οθόνη σας, επιλέξτε μια εφαρμογή ψηφιακού βοηθού στις Ρυθμίσεις." "Αλλάξτε τον βοηθό σας για να χρησιμοποιήσετε αυτήν τη λειτουργία" "Για να ακούσετε ή να μεταφράσετε κείμενο στην οθόνη σας, αλλάξτε την εφαρμογή ψηφιακού βοηθού στις Ρυθμίσεις." - "Πατήστε εδώ για να ακούσετε το κείμενο σε αυτήν την οθόνη" - "Πατήστε εδώ για να μεταφράσετε το κείμενο σε αυτήν την οθόνη" + "Πατήστε εδώ για να ακούσετε το κείμενο σε αυτή την οθόνη" + "Πατήστε εδώ για να μεταφράσετε το κείμενο σε αυτή την οθόνη" "Δεν είναι δυνατή η κοινή χρήση της εφαρμογής" diff --git a/lawnchair/res/color/hotseat_qsb_bg_color.xml b/lawnchair/res/color/hotseat_qsb_bg_color.xml index ec3edc0eb8..fabd17f0d2 100644 --- a/lawnchair/res/color/hotseat_qsb_bg_color.xml +++ b/lawnchair/res/color/hotseat_qsb_bg_color.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/lawnchair/res/values/attrs.xml b/lawnchair/res/values/attrs.xml index 3a34f60ae6..19c0127fb1 100644 --- a/lawnchair/res/values/attrs.xml +++ b/lawnchair/res/values/attrs.xml @@ -29,6 +29,7 @@ + diff --git a/lawnchair/res/values/config.xml b/lawnchair/res/values/config.xml index 78e46ee0fc..3ef0893b5a 100644 --- a/lawnchair/res/values/config.xml +++ b/lawnchair/res/values/config.xml @@ -104,7 +104,7 @@ true true false - false + true true false false diff --git a/lawnchair/src/app/lawnchair/LauncherStateExtensions.kt b/lawnchair/src/app/lawnchair/LauncherStateExtensions.kt index 2959685da9..4e098d5802 100644 --- a/lawnchair/src/app/lawnchair/LauncherStateExtensions.kt +++ b/lawnchair/src/app/lawnchair/LauncherStateExtensions.kt @@ -1,9 +1,9 @@ package app.lawnchair +import com.android.app.animation.Interpolators import com.android.launcher3.Launcher import com.android.launcher3.LauncherState import com.android.launcher3.anim.AnimatorListeners.forEndCallback -import com.android.launcher3.anim.Interpolators import com.android.launcher3.states.StateAnimationConfig import com.android.launcher3.touch.AllAppsSwipeController import kotlin.coroutines.resume @@ -21,7 +21,7 @@ suspend fun Launcher.animateToAllApps() { val anim = animation.animationPlayer anim.setFloatValues(0f, 1f) anim.duration = duration - anim.interpolator = Interpolators.DEACCEL + anim.interpolator = Interpolators.DECELERATE anim.addListener(forEndCallback(Runnable { cont.resume(Unit) })) animation.dispatchOnStart() anim.start() diff --git a/lawnchair/src/app/lawnchair/LawnchairLauncher.kt b/lawnchair/src/app/lawnchair/LawnchairLauncher.kt index 90019ff71f..77ed917429 100644 --- a/lawnchair/src/app/lawnchair/LawnchairLauncher.kt +++ b/lawnchair/src/app/lawnchair/LawnchairLauncher.kt @@ -16,9 +16,11 @@ package app.lawnchair +import android.app.ActivityOptions import android.content.Context import android.content.Intent import android.content.IntentSender +import android.graphics.Color import android.os.Bundle import android.os.Handler import android.os.Looper @@ -40,11 +42,9 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleRegistry import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.setViewTreeLifecycleOwner import androidx.savedstate.SavedStateRegistry import androidx.savedstate.SavedStateRegistryController import androidx.savedstate.SavedStateRegistryOwner -import androidx.savedstate.setViewTreeSavedStateRegistryOwner import app.lawnchair.LawnchairApp.Companion.showQuickstepWarningIfNecessary import app.lawnchair.factory.LawnchairWidgetHolder import app.lawnchair.gestures.GestureController @@ -64,8 +64,8 @@ import com.android.launcher3.AbstractFloatingView import com.android.launcher3.BaseActivity import com.android.launcher3.GestureNavContract import com.android.launcher3.LauncherAppState -import com.android.launcher3.LauncherRootView import com.android.launcher3.LauncherState +import com.android.launcher3.QuickstepTransitionManager import com.android.launcher3.R import com.android.launcher3.Utilities import com.android.launcher3.allapps.ActivityAllAppsContainerView @@ -74,9 +74,13 @@ import com.android.launcher3.popup.SystemShortcut import com.android.launcher3.statemanager.StateManager import com.android.launcher3.uioverrides.QuickstepLauncher import com.android.launcher3.uioverrides.states.OverviewState +import com.android.launcher3.util.ActivityOptionsWrapper +import com.android.launcher3.util.Executors +import com.android.launcher3.util.RunnableList import com.android.launcher3.util.SystemUiController.UI_STATE_BASE_WINDOW import com.android.launcher3.util.Themes import com.android.launcher3.util.TouchController +import com.android.launcher3.views.ComposeInitializer import com.android.launcher3.views.FloatingSurfaceView import com.android.launcher3.widget.LauncherWidgetHolder import com.android.launcher3.widget.RoundedCornerEnforcement @@ -86,11 +90,11 @@ import com.kieronquinn.app.smartspacer.sdk.client.SmartspacerClient import com.patrykmichalik.opto.core.firstBlocking import com.patrykmichalik.opto.core.onEach import dev.kdrag0n.monet.theme.ColorScheme -import java.util.stream.Stream import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import java.util.stream.Stream class LawnchairLauncher : QuickstepLauncher(), @@ -271,10 +275,7 @@ class LawnchairLauncher : override fun setupViews() { super.setupViews() - findViewById(R.id.launcher).also { - it.setViewTreeLifecycleOwner(this) - it.setViewTreeSavedStateRegistryOwner(this) - } + ComposeInitializer.initCompose(this) } override fun collectStateHandlers(out: MutableList>) { @@ -342,6 +343,19 @@ class LawnchairLauncher : } } + override fun makeDefaultActivityOptions(splashScreenStyle: Int): ActivityOptionsWrapper { + val callbacks = RunnableList() + val options = ActivityOptions.makeCustomAnimation( + this, 0, 0, Color.TRANSPARENT, + Executors.MAIN_EXECUTOR.handler, null + ) { elapsedRealTime -> callbacks.executeAllAndDestroy() } + if (Utilities.ATLEAST_T) { + options.setSplashScreenStyle(splashScreenStyle) + } + Utilities.allowBGLaunch(options) + return ActivityOptionsWrapper(options, callbacks) + } + override fun onStart() { super.onStart() lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_START) diff --git a/lawnchair/src/app/lawnchair/SearchBarStateHandler.kt b/lawnchair/src/app/lawnchair/SearchBarStateHandler.kt index 5ca6d5195a..c15b641795 100644 --- a/lawnchair/src/app/lawnchair/SearchBarStateHandler.kt +++ b/lawnchair/src/app/lawnchair/SearchBarStateHandler.kt @@ -4,12 +4,12 @@ import android.os.CancellationSignal import android.view.WindowInsets import androidx.core.view.WindowInsetsCompat import app.lawnchair.preferences2.PreferenceManager2 +import com.android.app.animation.Interpolators import com.android.launcher3.LauncherState import com.android.launcher3.Utilities import com.android.launcher3.anim.AnimatedFloat import com.android.launcher3.anim.AnimatorListeners.forEndCallback import com.android.launcher3.anim.AnimatorListeners.forSuccessCallback -import com.android.launcher3.anim.Interpolators import com.android.launcher3.anim.PendingAnimation import com.android.launcher3.statemanager.StateManager import com.android.launcher3.states.StateAnimationConfig @@ -57,7 +57,7 @@ class SearchBarStateHandler(private val launcher: LawnchairLauncher) : handler.progress, AnimatedFloat.VALUE, 1f, - Interpolators.DEACCEL_1_7, + Interpolators.DECELERATE_1_7, ) animation.addListener( forEndCallback( @@ -99,6 +99,6 @@ class SearchBarStateHandler(private val launcher: LawnchairLauncher) : private fun showKeyboard() { val editText = launcher.appsView.searchUiManager.editText ?: return - editText.showKeyboard() + editText.showKeyboard(true) } } diff --git a/lawnchair/src/app/lawnchair/allapps/LawnchairAlphabeticalAppsList.kt b/lawnchair/src/app/lawnchair/allapps/LawnchairAlphabeticalAppsList.kt index d522fb07e9..3abe3fb680 100644 --- a/lawnchair/src/app/lawnchair/allapps/LawnchairAlphabeticalAppsList.kt +++ b/lawnchair/src/app/lawnchair/allapps/LawnchairAlphabeticalAppsList.kt @@ -15,7 +15,7 @@ import java.util.function.Predicate class LawnchairAlphabeticalAppsList( context: T, - appsStore: AllAppsStore?, + appsStore: AllAppsStore, workProfileManager: WorkProfileManager?, ) : AlphabeticalAppsList(context, appsStore, workProfileManager) where T : Context, T : ActivityContext { diff --git a/lawnchair/src/app/lawnchair/bubbles/DismissView.kt b/lawnchair/src/app/lawnchair/bubbles/DismissView.kt new file mode 100644 index 0000000000..a06ccf2b37 --- /dev/null +++ b/lawnchair/src/app/lawnchair/bubbles/DismissView.kt @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package app.lawnchair.bubbles + +import android.animation.ObjectAnimator +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.GradientDrawable +import android.util.IntProperty +import android.util.Log +import android.view.Gravity +import android.view.View +import android.view.ViewGroup +import android.view.WindowInsets +import android.view.WindowManager +import android.widget.FrameLayout +import androidx.annotation.ColorRes +import androidx.annotation.DimenRes +import androidx.annotation.DrawableRes +import androidx.core.content.ContextCompat +import androidx.dynamicanimation.animation.DynamicAnimation +import androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_LOW_BOUNCY +import androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW +import app.lawnchair.animation.PhysicsAnimator +import com.android.launcher3.Utilities +import com.android.wm.shell.common.bubbles.DismissCircleView + +/** + * View that handles interactions between DismissCircleView and BubbleStackView. + * + * @note [setup] method should be called after initialisation + */ +class DismissView(context: Context) : FrameLayout(context) { + /** + * The configuration is used to provide module specific resource ids + * + * @see [setup] method + */ + data class Config( + /** dimen resource id of the dismiss target circle view size */ + @DimenRes val targetSizeResId: Int, + /** dimen resource id of the icon size in the dismiss target */ + @DimenRes val iconSizeResId: Int, + /** dimen resource id of the bottom margin for the dismiss target */ + @DimenRes var bottomMarginResId: Int, + /** dimen resource id of the height for dismiss area gradient */ + @DimenRes val floatingGradientHeightResId: Int, + /** color resource id of the dismiss area gradient color */ + @ColorRes val floatingGradientColorResId: Int, + /** drawable resource id of the dismiss target background */ + @DrawableRes val backgroundResId: Int, + /** drawable resource id of the icon for the dismiss target */ + @DrawableRes val iconResId: Int + ) + + companion object { + private const val SHOULD_SETUP = + "The view isn't ready. Should be called after `setup`" + private val TAG = DismissView::class.simpleName + } + + var circle = DismissCircleView(context) + var isShowing = false + var config: Config? = null + + private val animator = PhysicsAnimator.getInstance(circle) + private val spring = PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_LOW_BOUNCY) + private val DISMISS_SCRIM_FADE_MS = 200L + private var wm: WindowManager = + context.getSystemService(Context.WINDOW_SERVICE) as WindowManager + private var gradientDrawable: GradientDrawable? = null + + private val GRADIENT_ALPHA: IntProperty = + object : IntProperty("alpha") { + override fun setValue(d: GradientDrawable, percent: Int) { + d.alpha = percent + } + override fun get(d: GradientDrawable): Int { + return d.alpha + } + } + + init { + clipToPadding = false + clipChildren = false + visibility = View.INVISIBLE + addView(circle) + } + + /** + * Sets up view with the provided resource ids. + * + * Decouples resource dependency in order to be used externally (e.g. Launcher). Usually called + * with default params in module specific extension: + * @see [DismissView.setup] in DismissViewExt.kt + */ + fun setup(config: Config) { + this.config = config + + // Setup layout + layoutParams = LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + resources.getDimensionPixelSize(config.floatingGradientHeightResId), + Gravity.BOTTOM) + updatePadding() + + // Setup gradient + gradientDrawable = createGradient(color = config.floatingGradientColorResId) + setBackgroundDrawable(gradientDrawable) + + // Setup DismissCircleView + circle.setup(config.backgroundResId, config.iconResId, config.iconSizeResId) + val targetSize: Int = resources.getDimensionPixelSize(config.targetSizeResId) + circle.layoutParams = LayoutParams(targetSize, targetSize, + Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL) + // Initial position with circle offscreen so it's animated up + circle.translationY = resources.getDimensionPixelSize(config.floatingGradientHeightResId) + .toFloat() + } + + /** + * Animates this view in. + */ + fun show() { + if (isShowing) return + val gradientDrawable = checkExists(gradientDrawable) ?: return + isShowing = true + setVisibility(View.VISIBLE) + val alphaAnim = ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA, + gradientDrawable.alpha, 255) + alphaAnim.setDuration(DISMISS_SCRIM_FADE_MS) + alphaAnim.start() + + animator.cancel() + animator + .spring(DynamicAnimation.TRANSLATION_Y, 0f, spring) + .start() + } + + /** + * Animates this view out, as well as the circle that encircles the bubbles, if they + * were dragged into the target and encircled. + */ + fun hide() { + if (!isShowing) return + val gradientDrawable = checkExists(gradientDrawable) ?: return + isShowing = false + val alphaAnim = ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA, + gradientDrawable.alpha, 0) + alphaAnim.setDuration(DISMISS_SCRIM_FADE_MS) + alphaAnim.start() + animator + .spring(DynamicAnimation.TRANSLATION_Y, height.toFloat(), + spring) + .withEndActions({ setVisibility(View.INVISIBLE) }) + .start() + } + + /** + * Cancels the animator for the dismiss target. + */ + fun cancelAnimators() { + animator.cancel() + } + + fun updateResources() { + val config = checkExists(config) ?: return + updatePadding() + layoutParams.height = resources.getDimensionPixelSize(config.floatingGradientHeightResId) + val targetSize = resources.getDimensionPixelSize(config.targetSizeResId) + circle.layoutParams.width = targetSize + circle.layoutParams.height = targetSize + circle.requestLayout() + } + + private fun createGradient(@ColorRes color: Int): GradientDrawable { + val gradientColor = ContextCompat.getColor(context, color) + val alpha = 0.7f * 255 + val gradientColorWithAlpha = Color.argb(alpha.toInt(), + Color.red(gradientColor), + Color.green(gradientColor), + Color.blue(gradientColor)) + val gd = GradientDrawable( + GradientDrawable.Orientation.BOTTOM_TOP, + intArrayOf(gradientColorWithAlpha, Color.TRANSPARENT)) + gd.setDither(true) + gd.setAlpha(0) + return gd + } + + private fun updatePadding() { + if (!Utilities.ATLEAST_R) return + val config = checkExists(config) ?: return + val insets: WindowInsets = wm.getCurrentWindowMetrics().getWindowInsets() + val navInset = insets.getInsetsIgnoringVisibility( + WindowInsets.Type.navigationBars()) + setPadding(0, 0, 0, navInset.bottom + + resources.getDimensionPixelSize(config.bottomMarginResId)) + } + + /** + * Checks if the value is set up and exists, if not logs an exception. + * Used for convenient logging in case `setup` wasn't called before + * + * @return value provided as argument + */ + private fun checkExists(value: T?): T? { + if (value == null) Log.e(TAG, SHOULD_SETUP) + return value + } +} diff --git a/lawnchair/src/app/lawnchair/factory/LawnchairWidgetHolder.kt b/lawnchair/src/app/lawnchair/factory/LawnchairWidgetHolder.kt index 8fef8f13c4..005238a5ab 100644 --- a/lawnchair/src/app/lawnchair/factory/LawnchairWidgetHolder.kt +++ b/lawnchair/src/app/lawnchair/factory/LawnchairWidgetHolder.kt @@ -13,13 +13,17 @@ class LawnchairWidgetHolder(context: Context, intConsumer: IntConsumer?) : Launc @Keep class LawnchairHolderFactory @Suppress("unused") - constructor(context: Context?) : + constructor(context: Context) : HolderFactory() { override fun newInstance( context: Context, appWidgetRemovedCallback: IntConsumer?, ): LauncherWidgetHolder { - return newInstance(context, appWidgetRemovedCallback, null) + return try { + newInstance(context, appWidgetRemovedCallback, null) + } catch (t: Throwable) { + super.newInstance(context, appWidgetRemovedCallback) + } } /** @@ -51,4 +55,4 @@ class LawnchairWidgetHolder(context: Context, intConsumer: IntConsumer?) : Launc } } } -} +} \ No newline at end of file diff --git a/lawnchair/src/app/lawnchair/gestures/handlers/OpenAppSearchGestureHandler.kt b/lawnchair/src/app/lawnchair/gestures/handlers/OpenAppSearchGestureHandler.kt index c3c2f148bb..60853b8b22 100644 --- a/lawnchair/src/app/lawnchair/gestures/handlers/OpenAppSearchGestureHandler.kt +++ b/lawnchair/src/app/lawnchair/gestures/handlers/OpenAppSearchGestureHandler.kt @@ -7,6 +7,6 @@ class OpenAppSearchGestureHandler(context: Context) : OpenAppDrawerGestureHandle override suspend fun onTrigger(launcher: LawnchairLauncher) { super.onTrigger(launcher) - launcher.appsView.searchUiManager.editText?.showKeyboard() + launcher.appsView.searchUiManager.editText?.showKeyboard(true) } } diff --git a/lawnchair/src/app/lawnchair/icons/shape/IconCornerShape.kt b/lawnchair/src/app/lawnchair/icons/shape/IconCornerShape.kt index a3b1310202..519685b549 100644 --- a/lawnchair/src/app/lawnchair/icons/shape/IconCornerShape.kt +++ b/lawnchair/src/app/lawnchair/icons/shape/IconCornerShape.kt @@ -21,8 +21,8 @@ package app.lawnchair.icons.shape import android.graphics.Path import android.graphics.PointF +import com.android.app.animation.Interpolators.LINEAR import com.android.launcher3.Utilities -import com.android.launcher3.anim.Interpolators.LINEAR sealed class IconCornerShape { diff --git a/lawnchair/src/app/lawnchair/nexuslauncher/SmartSpaceHostView.kt b/lawnchair/src/app/lawnchair/nexuslauncher/SmartSpaceHostView.kt index cd379ecca1..c7fc5459a0 100644 --- a/lawnchair/src/app/lawnchair/nexuslauncher/SmartSpaceHostView.kt +++ b/lawnchair/src/app/lawnchair/nexuslauncher/SmartSpaceHostView.kt @@ -10,6 +10,7 @@ import android.view.MotionEvent import android.view.View import android.view.View.OnLongClickListener import android.view.ViewGroup +import app.lawnchair.LawnchairLauncher import app.lawnchair.util.unsafeLazy import com.android.launcher3.CheckLongPressHelper import com.android.launcher3.Launcher @@ -49,7 +50,7 @@ sealed class SmartSpaceHostView(context: Context) : QsbWidgetHostView(context), R.drawable.ic_smartspace_preferences, NexusLauncherEnum.SMARTSPACE_TAP_OR_LONGPRESS, ) { v: View -> openSettings(v) } - OptionsPopupView.show(mLauncher, centerPos, listOf(item), true) + OptionsPopupView.show(mLauncher, centerPos, listOf(item), true) return true } diff --git a/lawnchair/src/app/lawnchair/qsb/providers/AppSearch.kt b/lawnchair/src/app/lawnchair/qsb/providers/AppSearch.kt index 7ca0685472..f79d810880 100644 --- a/lawnchair/src/app/lawnchair/qsb/providers/AppSearch.kt +++ b/lawnchair/src/app/lawnchair/qsb/providers/AppSearch.kt @@ -16,6 +16,6 @@ data object AppSearch : QsbSearchProvider( ) { override suspend fun launch(launcher: Launcher, forceWebsite: Boolean) { launcher.animateToAllApps() - launcher.appsView.searchUiManager.editText?.showKeyboard() + launcher.appsView.searchUiManager.editText?.showKeyboard(true) } } diff --git a/lawnchair/src/app/lawnchair/search/LawnchairSearchAdapterProvider.kt b/lawnchair/src/app/lawnchair/search/LawnchairSearchAdapterProvider.kt index b4a069ec39..d5de146c3d 100644 --- a/lawnchair/src/app/lawnchair/search/LawnchairSearchAdapterProvider.kt +++ b/lawnchair/src/app/lawnchair/search/LawnchairSearchAdapterProvider.kt @@ -38,7 +38,7 @@ class LawnchairSearchAdapterProvider( private var quickLaunchItem: SearchResultView? = null set(value) { field = value - appsView.searchUiManager.setFocusedResultTitle(field?.titleText, field?.titleText, false) + appsView.searchUiManager.setFocusedResultTitle(field?.titleText, field?.titleText, true) } override fun isViewSupported(viewType: Int): Boolean = layoutIdMap.contains(viewType) diff --git a/lawnchair/src/app/lawnchair/smartspace/SmartspaceViewContainer.kt b/lawnchair/src/app/lawnchair/smartspace/SmartspaceViewContainer.kt index 56dca78715..aa014ea5f3 100644 --- a/lawnchair/src/app/lawnchair/smartspace/SmartspaceViewContainer.kt +++ b/lawnchair/src/app/lawnchair/smartspace/SmartspaceViewContainer.kt @@ -8,6 +8,7 @@ import android.view.LayoutInflater import android.view.MotionEvent import android.view.View import android.widget.FrameLayout +import app.lawnchair.LawnchairLauncher import app.lawnchair.launcher import app.lawnchair.ui.preferences.PreferenceActivity import app.lawnchair.ui.preferences.Routes @@ -42,7 +43,7 @@ class SmartspaceViewContainer @JvmOverloads constructor( val launcher = context.launcher val pos = Rect() launcher.dragLayer.getDescendantRectRelativeToSelf(smartspaceView, pos) - OptionsPopupView.show(launcher, RectF(pos), listOf(getCustomizeOption()), true) + OptionsPopupView.show(launcher, RectF(pos), listOf(getCustomizeOption()), true) } private fun getCustomizeOption() = OptionsPopupView.OptionItem( diff --git a/lawnchair/src/app/lawnchair/smartspace/SmartspacerView.kt b/lawnchair/src/app/lawnchair/smartspace/SmartspacerView.kt index 7bd8408aa2..571ad6696d 100644 --- a/lawnchair/src/app/lawnchair/smartspace/SmartspacerView.kt +++ b/lawnchair/src/app/lawnchair/smartspace/SmartspacerView.kt @@ -6,6 +6,7 @@ import android.graphics.Rect import android.graphics.RectF import android.util.AttributeSet import android.view.View +import app.lawnchair.LawnchairLauncher import app.lawnchair.launcher import app.lawnchair.ui.preferences.PreferenceActivity import app.lawnchair.ui.preferences.Routes @@ -43,7 +44,7 @@ class SmartspacerView(context: Context, attrs: AttributeSet?) : BcSmartspaceView getDismissOption(target, dismissAction), ).ifEmpty { listOf(getCustomizeOptionFallback()) } val popup = OptionsPopupView - .show(launcher, RectF(pos), options, true) + .show(launcher, RectF(pos), options, true) return object : Popup { override fun dismiss() { popup.close(true) diff --git a/lawnchair/src/app/lawnchair/util/LawnchairWindowManagerProxy.kt b/lawnchair/src/app/lawnchair/util/LawnchairWindowManagerProxy.kt index 126e683162..81cf156865 100644 --- a/lawnchair/src/app/lawnchair/util/LawnchairWindowManagerProxy.kt +++ b/lawnchair/src/app/lawnchair/util/LawnchairWindowManagerProxy.kt @@ -29,9 +29,9 @@ class LawnchairWindowManagerProxy(context: Context) : WindowManagerProxy(true) { return super.getStatusBarHeight(context, isPortrait, statusBarInset) } - override fun estimateInternalDisplayBounds(displayInfoContext: Context): ArrayMap> { + override fun estimateInternalDisplayBounds(displayInfoContext: Context): ArrayMap> { if (LawnchairApp.isAtleastT) { - val result = ArrayMap>() + val result = ArrayMap>() val windowManager = displayInfoContext.getSystemService(WindowManager::class.java) val possibleMaximumWindowMetrics = windowManager.getPossibleMaximumWindowMetrics(DEFAULT_DISPLAY) diff --git a/lawnchair/src/app/lawnchair/views/ComposeBottomSheet.kt b/lawnchair/src/app/lawnchair/views/ComposeBottomSheet.kt index c8b9d2c3c7..b4802c6f52 100644 --- a/lawnchair/src/app/lawnchair/views/ComposeBottomSheet.kt +++ b/lawnchair/src/app/lawnchair/views/ComposeBottomSheet.kt @@ -1,6 +1,5 @@ package app.lawnchair.views -import android.animation.PropertyValuesHolder import android.content.Context import android.util.FloatProperty import android.view.Gravity @@ -37,7 +36,6 @@ import app.lawnchair.ui.theme.LawnchairTheme import app.lawnchair.util.ProvideLifecycleState import app.lawnchair.util.minus import com.android.launcher3.Launcher -import com.android.launcher3.anim.Interpolators import com.android.launcher3.anim.PendingAnimation import com.android.launcher3.util.SystemUiController import com.android.launcher3.views.AbstractSlideInView @@ -92,15 +90,11 @@ class ComposeBottomSheet(context: Context) : } private fun animateOpen() { - if (mIsOpen || mOpenCloseAnimator.isRunning) { + if (mIsOpen || mOpenCloseAnimation.animationPlayer.isRunning) { return } mIsOpen = true - mOpenCloseAnimator.setValues( - PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED), - ) - mOpenCloseAnimator.interpolator = Interpolators.FAST_OUT_SLOW_IN - mOpenCloseAnimator.start() + setUpDefaultOpenAnimation().start() } override fun handleClose(animate: Boolean) { @@ -251,6 +245,7 @@ class ComposeBottomSheet(context: Context) : contentPaddings: PaddingValues = PaddingValues(all = 0.dp), content: @Composable ComposeBottomSheet.() -> Unit, ) where T : Context, T : ActivityContext { + closeAllOpenViews(context) val view = ComposeBottomSheet(context) view.setContent(contentPaddings, content) view.show() diff --git a/lawnchair/src/com/android/systemui/plugins/BcSmartspaceConfigPlugin.kt b/lawnchair/src/com/android/systemui/plugins/BcSmartspaceConfigPlugin.kt new file mode 100644 index 0000000000..509f022310 --- /dev/null +++ b/lawnchair/src/com/android/systemui/plugins/BcSmartspaceConfigPlugin.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 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.systemui.plugins + +// TODO(b/265360975): Evaluate this plugin approach. +/** Plugin to provide BC smartspace configuration */ +interface BcSmartspaceConfigPlugin { + /** Gets default date/weather disabled status. */ + val isDefaultDateWeatherDisabled: Boolean +} diff --git a/lawnchair/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/lawnchair/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java index eadaa68a38..64c0f99f4b 100644 --- a/lawnchair/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java +++ b/lawnchair/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java @@ -20,6 +20,7 @@ import android.app.PendingIntent; import android.app.smartspace.SmartspaceAction; import android.app.smartspace.SmartspaceTarget; import android.app.smartspace.SmartspaceTargetEvent; +import android.app.smartspace.uitemplatedata.TapAction; import android.content.ActivityNotFoundException; import android.content.Intent; import android.graphics.drawable.Drawable; @@ -39,21 +40,34 @@ import java.util.List; */ @ProvidesInterface(action = BcSmartspaceDataPlugin.ACTION, version = BcSmartspaceDataPlugin.VERSION) public interface BcSmartspaceDataPlugin extends Plugin { + String UI_SURFACE_LOCK_SCREEN_AOD = "lockscreen"; + String UI_SURFACE_HOME_SCREEN = "home"; + String UI_SURFACE_MEDIA = "media_data_manager"; + String UI_SURFACE_DREAM = "dream"; + String ACTION = "com.android.systemui.action.PLUGIN_BC_SMARTSPACE_DATA"; int VERSION = 1; String TAG = "BcSmartspaceDataPlugin"; /** Register a listener to get Smartspace data. */ - void registerListener(SmartspaceTargetListener listener); + default void registerListener(SmartspaceTargetListener listener) { + throw new UnsupportedOperationException("Not implemented by " + getClass()); + } /** Unregister a listener. */ - void unregisterListener(SmartspaceTargetListener listener); + default void unregisterListener(SmartspaceTargetListener listener) { + throw new UnsupportedOperationException("Not implemented by " + getClass()); + } /** Register a SmartspaceEventNotifier. */ - default void registerSmartspaceEventNotifier(SmartspaceEventNotifier notifier) {} + default void registerSmartspaceEventNotifier(SmartspaceEventNotifier notifier) { + throw new UnsupportedOperationException("Not implemented by " + getClass()); + } /** Push a SmartspaceTargetEvent to the SmartspaceEventNotifier. */ - default void notifySmartspaceEvent(SmartspaceTargetEvent event) {} + default void notifySmartspaceEvent(SmartspaceTargetEvent event) { + throw new UnsupportedOperationException("Not implemented by " + getClass()); + } /** Allows for notifying the SmartspaceSession of SmartspaceTargetEvents. */ interface SmartspaceEventNotifier { @@ -66,16 +80,20 @@ public interface BcSmartspaceDataPlugin extends Plugin { * will be responsible for correctly setting the LayoutParams */ default SmartspaceView getView(ViewGroup parent) { - return null; + throw new UnsupportedOperationException("Not implemented by " + getClass()); } /** * As the smartspace view becomes available, allow listeners to receive an event. */ - default void addOnAttachStateChangeListener(View.OnAttachStateChangeListener listener) { } + default void addOnAttachStateChangeListener(View.OnAttachStateChangeListener listener) { + throw new UnsupportedOperationException("Not implemented by " + getClass()); + } /** Updates Smartspace data and propagates it to any listeners. */ - void onTargetsAvailable(List targets); + default void onTargetsAvailable(List targets) { + throw new UnsupportedOperationException("Not implemented by " + getClass()); + } /** Provides Smartspace data to registered listeners. */ interface SmartspaceTargetListener { @@ -87,36 +105,89 @@ public interface BcSmartspaceDataPlugin extends Plugin { interface SmartspaceView { void registerDataProvider(BcSmartspaceDataPlugin plugin); + /** + * Sets {@link BcSmartspaceConfigPlugin}. + */ + default void registerConfigProvider(BcSmartspaceConfigPlugin configProvider) { + throw new UnsupportedOperationException("Not implemented by " + getClass()); + } + /** * Primary color for unprotected text */ void setPrimaryTextColor(int color); + /** + * Set the UI surface for the cards. Should be called immediately after the view is created. + */ + void setUiSurface(String uiSurface); + /** * Range [0.0 - 1.0] when transitioning from Lockscreen to/from AOD */ void setDozeAmount(float amount); + /** + * Set if dozing is true or false + */ + default void setDozing(boolean dozing) {} + + /** + * Set if split shade enabled + */ + default void setSplitShadeEnabled(boolean enabled) {} + + /** + * Set the current keyguard bypass enabled status. + */ + default void setKeyguardBypassEnabled(boolean enabled) {} + /** * Overrides how Intents/PendingIntents gets launched. Mostly to support auth from * the lockscreen. */ void setIntentStarter(IntentStarter intentStarter); + /** + * When on the lockscreen, use the FalsingManager to help detect errant touches + */ + void setFalsingManager(com.android.systemui.plugins.FalsingManager falsingManager); + /** * Set or clear Do Not Disturb information. */ - void setDnd(@Nullable Drawable image, @Nullable String description); + default void setDnd(@Nullable Drawable image, @Nullable String description) { + throw new UnsupportedOperationException("Not implemented by " + getClass()); + } /** * Set or clear next alarm information */ - void setNextAlarm(@Nullable Drawable image, @Nullable String description); + default void setNextAlarm(@Nullable Drawable image, @Nullable String description) { + throw new UnsupportedOperationException("Not implemented by " + getClass()); + } /** * Set or clear device media playing */ - void setMediaTarget(@Nullable SmartspaceTarget target); + default void setMediaTarget(@Nullable SmartspaceTarget target) { + throw new UnsupportedOperationException("Not implemented by " + getClass()); + } + + /** + * Get the index of the currently selected page. + */ + default int getSelectedPage() { + throw new UnsupportedOperationException("Not implemented by " + getClass()); + } + + /** + * Return the top padding value from the currently visible card, or 0 if there is no current + * card. + */ + default int getCurrentCardTopPadding() { + throw new UnsupportedOperationException("Not implemented by " + getClass()); + } } /** Interface for launching Intents, which can differ on the lockscreen */ @@ -126,7 +197,19 @@ public interface BcSmartspaceDataPlugin extends Plugin { if (action.getIntent() != null) { startIntent(v, action.getIntent(), showOnLockscreen); } else if (action.getPendingIntent() != null) { - startPendingIntent(action.getPendingIntent(), showOnLockscreen); + startPendingIntent(v, action.getPendingIntent(), showOnLockscreen); + } + } catch (ActivityNotFoundException e) { + Log.w(TAG, "Could not launch intent for action: " + action, e); + } + } + + default void startFromAction(TapAction action, View v, boolean showOnLockscreen) { + try { + if (action.getIntent() != null) { + startIntent(v, action.getIntent(), showOnLockscreen); + } else if (action.getPendingIntent() != null) { + startPendingIntent(v, action.getPendingIntent(), showOnLockscreen); } } catch (ActivityNotFoundException e) { Log.w(TAG, "Could not launch intent for action: " + action, e); @@ -137,6 +220,6 @@ public interface BcSmartspaceDataPlugin extends Plugin { void startIntent(View v, Intent i, boolean showOnLockscreen); /** Start the PendingIntent */ - void startPendingIntent(PendingIntent pi, boolean showOnLockscreen); + void startPendingIntent(View v, PendingIntent pi, boolean showOnLockscreen); } } diff --git a/platform_frameworks_libs_systemui b/platform_frameworks_libs_systemui index 8c0d7f78da..02aee0e36d 160000 --- a/platform_frameworks_libs_systemui +++ b/platform_frameworks_libs_systemui @@ -1 +1 @@ -Subproject commit 8c0d7f78da9160c700d1c098fefceb8961c4900a +Subproject commit 02aee0e36d501413267d1e5d0564e816bfe53686 diff --git a/prebuilts/libs/WindowManager-Shell-14.jar b/prebuilts/libs/WindowManager-Shell-14.jar index 9c471eccaa..7851c925b6 100644 Binary files a/prebuilts/libs/WindowManager-Shell-14.jar and b/prebuilts/libs/WindowManager-Shell-14.jar differ diff --git a/proguard.pro b/proguard.pro index 170ba86e72..3afde6be4f 100644 --- a/proguard.pro +++ b/proguard.pro @@ -21,6 +21,7 @@ -dontwarn com.skydoves.balloon.** -dontwarn dalvik.system.CloseGuard -dontwarn lineageos.providers.LineageSettings$System +-dontwarn androidx.compose.runtime.PrimitiveSnapshotStateKt # Common rules. @@ -44,3 +45,10 @@ -keep class com.google.protobuf.Timestamp { *; } # TODO: Remove this after the change in https://github.com/ChickenHook/RestrictionBypass/pull/9 has been released. -keep class org.chickenhook.restrictionbypass.** { *; } +-keep class androidx.compose.runtime.** { *; } + +# systemUIPluginCore +-keepattributes RuntimeVisible*Annotation*,AnnotationDefault +-keep interface com.android.systemui.plugins.annotations.** { *; } +-keep class com.android.systemui.plugins.BcSmartspaceDataPlugin$** { *; } +-keep,allowshrinking,allowoptimization,allowobfuscation,allowaccessmodification @com.android.systemui.plugins.annotations.** class * diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto index 63ea20c73d..f8b08f86c0 100644 --- a/protos/launcher_atom.proto +++ b/protos/launcher_atom.proto @@ -135,7 +135,7 @@ message TaskBarContainer { } } -// Next value 51 +// Next value 52 enum Attribute { option allow_alias = true; @@ -187,6 +187,7 @@ enum Attribute { ALL_APPS_SEARCH_RESULT_SYSTEM_POINTER = 42; ALL_APPS_SEARCH_RESULT_EDUCARD = 43; ALL_APPS_SEARCH_RESULT_LOCATION = 50; + ALL_APPS_SEARCH_RESULT_TEXT_HEADER = 51; // Result sources DATA_SOURCE_APPSEARCH_APP_PREVIEW = 45; diff --git a/quickstep/Android.bp b/quickstep/Android.bp index f5a8253563..638ce27800 100644 --- a/quickstep/Android.bp +++ b/quickstep/Android.bp @@ -42,5 +42,6 @@ filegroup { "tests/src/com/android/quickstep/NavigationModeSwitchRule.java", "tests/src/com/android/quickstep/AbstractQuickStepTest.java", "tests/src/com/android/quickstep/TaplTestsQuickstep.java", + "tests/src/com/android/quickstep/TaplTestsSplitscreen.java", ] } diff --git a/quickstep/protos_overrides/launcher_atom_extension.proto b/quickstep/protos_overrides/launcher_atom_extension.proto index f5a277bb95..b3df353913 100644 --- a/quickstep/protos_overrides/launcher_atom_extension.proto +++ b/quickstep/protos_overrides/launcher_atom_extension.proto @@ -61,6 +61,9 @@ message DeviceSearchResultContainer{ // User entered by tapping on QSB bar on homescreen. QSB = 2; + + // User entered by swiping up from overview (using Rocket Gesture). + OVERVIEW = 3; } } } diff --git a/res/interpolator/standard_accelerate.xml b/quickstep/res/drawable/bg_bubble_dismiss_circle.xml similarity index 65% rename from res/interpolator/standard_accelerate.xml rename to quickstep/res/drawable/bg_bubble_dismiss_circle.xml index 394393dc36..b793eecdf9 100644 --- a/res/interpolator/standard_accelerate.xml +++ b/quickstep/res/drawable/bg_bubble_dismiss_circle.xml @@ -1,6 +1,6 @@ - \ No newline at end of file + + + + + + \ No newline at end of file diff --git a/quickstep/res/drawable/bg_floating_desktop_select.xml b/quickstep/res/drawable/bg_floating_desktop_select.xml new file mode 100644 index 0000000000..6481be40c8 --- /dev/null +++ b/quickstep/res/drawable/bg_floating_desktop_select.xml @@ -0,0 +1,23 @@ + + + + + + \ No newline at end of file diff --git a/quickstep/res/drawable/ic_bubble_dismiss_white.xml b/quickstep/res/drawable/ic_bubble_dismiss_white.xml new file mode 100644 index 0000000000..b15111b821 --- /dev/null +++ b/quickstep/res/drawable/ic_bubble_dismiss_white.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/quickstep/res/drawable/taskbar_divider_button.xml b/quickstep/res/drawable/taskbar_divider_button.xml new file mode 100644 index 0000000000..cb116cfece --- /dev/null +++ b/quickstep/res/drawable/taskbar_divider_button.xml @@ -0,0 +1,30 @@ + + + + + + + + \ No newline at end of file diff --git a/quickstep/res/interpolator/three_point_fast_out_extra_slow_in.xml b/quickstep/res/interpolator/three_point_fast_out_extra_slow_in.xml deleted file mode 100644 index 70c4231140..0000000000 --- a/quickstep/res/interpolator/three_point_fast_out_extra_slow_in.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - diff --git a/quickstep/res/layout/activity_allset.xml b/quickstep/res/layout/activity_allset.xml index 2c312a7e33..685a15180b 100644 --- a/quickstep/res/layout/activity_allset.xml +++ b/quickstep/res/layout/activity_allset.xml @@ -41,6 +41,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" + android:fitsSystemWindows="true" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" @@ -95,7 +96,6 @@ style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginBottom="24dp" android:text="@string/allset_hint" android:textSize="@dimen/allset_page_swipe_up_text_size" android:gravity="center_horizontal" diff --git a/quickstep/res/layout/digital_wellbeing_toast.xml b/quickstep/res/layout/digital_wellbeing_toast.xml index a9ea85fafc..43a38ad85a 100644 --- a/quickstep/res/layout/digital_wellbeing_toast.xml +++ b/quickstep/res/layout/digital_wellbeing_toast.xml @@ -27,4 +27,4 @@ android:textColor="?attr/materialColorOnSecondaryFixed" android:textSize="14sp" android:autoSizeTextType="uniform" - android:autoSizeMaxTextSize="14sp"/> + android:autoSizeMaxTextSize="14sp"/> \ No newline at end of file diff --git a/quickstep/res/layout/fallback_recents_activity.xml b/quickstep/res/layout/fallback_recents_activity.xml index 32183610fa..341efaee42 100644 --- a/quickstep/res/layout/fallback_recents_activity.xml +++ b/quickstep/res/layout/fallback_recents_activity.xml @@ -15,6 +15,7 @@ --> diff --git a/quickstep/res/layout/floating_desktop_app_select.xml b/quickstep/res/layout/floating_desktop_app_select.xml new file mode 100644 index 0000000000..5beb4afed0 --- /dev/null +++ b/quickstep/res/layout/floating_desktop_app_select.xml @@ -0,0 +1,56 @@ + + + + + + +