diff --git a/quickstep/src/com/android/launcher3/uioverrides/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/BackgroundAppState.java new file mode 100644 index 0000000000..53dcc74808 --- /dev/null +++ b/quickstep/src/com/android/launcher3/uioverrides/BackgroundAppState.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.uioverrides; + +import com.android.launcher3.Launcher; +import com.android.launcher3.allapps.AllAppsTransitionController; +import com.android.quickstep.QuickScrubController; +import com.android.quickstep.util.LayoutUtils; + +/** + * State indicating that the Launcher is behind an app + */ +public class BackgroundAppState extends OverviewState { + + private static final int STATE_FLAGS = + FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_DISABLE_ACCESSIBILITY; + + public BackgroundAppState(int id) { + super(id, QuickScrubController.QUICK_SCRUB_FROM_HOME_START_DURATION, STATE_FLAGS); + } + + @Override + public float getVerticalProgress(Launcher launcher) { + if (launcher.getDeviceProfile().isVerticalBarLayout()) { + return super.getVerticalProgress(launcher); + } + int transitionLength = LayoutUtils.getShelfTrackingDistance(launcher.getDeviceProfile()); + AllAppsTransitionController controller = launcher.getAllAppsController(); + float scrollRange = Math.max(controller.getShiftRange(), 1); + float progressDelta = (transitionLength / scrollRange); + return super.getVerticalProgress(launcher) + progressDelta; + } +} diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java index 25b5f5711c..0d77bca58b 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java @@ -19,6 +19,7 @@ import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; import static com.android.launcher3.anim.Interpolators.DEACCEL_2; import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE; +import android.graphics.Rect; import android.view.View; import com.android.launcher3.AbstractFloatingView; @@ -38,6 +39,8 @@ import com.android.quickstep.views.TaskView; */ public class OverviewState extends LauncherState { + protected static final Rect sTempRect = new Rect(); + private static final int STATE_FLAGS = FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_DISABLE_ACCESSIBILITY; diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java index 77af211f5e..63fc6cdb3b 100644 --- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java +++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java @@ -16,8 +16,10 @@ package com.android.quickstep; import static android.view.View.TRANSLATION_Y; + import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; +import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.FAST_OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS; @@ -37,7 +39,6 @@ import android.app.ActivityManager.RunningTaskInfo; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.graphics.PointF; import android.graphics.Rect; import android.os.Build; import android.os.Handler; @@ -54,7 +55,6 @@ import com.android.launcher3.R; import com.android.launcher3.TestProtocol; import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.allapps.DiscoveryBounce; -import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.dragndrop.DragLayer; @@ -100,13 +100,8 @@ public interface ActivityControlHelper { void onTransitionCancelled(T activity, boolean activityVisible); - default int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, - @InteractionType int interactionType, TransformedRect outRect) { - return getSwipeUpDestinationAndLength(dp, context, interactionType, outRect, null); - } - int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, - @InteractionType int interactionType, TransformedRect outRect, PointF touchTown); + @InteractionType int interactionType, TransformedRect outRect); void onSwipeUpComplete(T activity); @@ -190,7 +185,7 @@ public interface ActivityControlHelper { @Override public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, - @InteractionType int interactionType, TransformedRect outRect, PointF touchDown) { + @InteractionType int interactionType, TransformedRect outRect) { LayoutUtils.calculateLauncherTaskSize(context, dp, outRect.rect); if (interactionType == INTERACTION_QUICK_SCRUB) { outRect.scale = FastOverviewState.getOverviewScale(dp, outRect.rect, context); @@ -200,12 +195,7 @@ public interface ActivityControlHelper { int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right; return dp.hotseatBarSizePx + hotseatInset; } else { - int swipeLength = LayoutUtils.getShelfTrackingDistance(dp); - if (touchDown != null) { - // We are already partway through based on where we touched the nav bar. - swipeLength -= dp.heightPx - touchDown.y; - } - return swipeLength; + return LayoutUtils.getShelfTrackingDistance(dp); } } @@ -233,24 +223,28 @@ public interface ActivityControlHelper { } activity.getStateManager().setRestState(resetState); + final LauncherState fromState; if (!activityVisible) { // Since the launcher is not visible, we can safely reset the scroll position. // This ensures then the next swipe up to all-apps starts from scroll 0. activity.getAppsView().reset(false /* animate */); - activity.getStateManager().goToState(OVERVIEW, false); + fromState = BACKGROUND_APP; + activity.getStateManager().goToState(BACKGROUND_APP, false); // Optimization, hide the all apps view to prevent layout while initializing activity.getAppsView().getContentView().setVisibility(View.GONE); AccessibilityManagerCompat.sendEventToTest( activity, TestProtocol.SWITCHED_TO_STATE_MESSAGE); + } else { + fromState = startState; } return new AnimationFactory() { @Override public void createActivityController(long transitionLength, @InteractionType int interactionType) { - createActivityControllerInternal(activity, activityVisible, startState, + createActivityControllerInternal(activity, activityVisible, fromState, transitionLength, interactionType, callback); } @@ -262,7 +256,7 @@ public interface ActivityControlHelper { } private void createActivityControllerInternal(Launcher activity, boolean wasVisible, - LauncherState startState, long transitionLength, + LauncherState fromState, long transitionLength, @InteractionType int interactionType, Consumer callback) { LauncherState endState = interactionType == INTERACTION_QUICK_SCRUB @@ -271,31 +265,18 @@ public interface ActivityControlHelper { DeviceProfile dp = activity.getDeviceProfile(); long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx); callback.accept(activity.getStateManager() - .createAnimationToNewWorkspace(startState, endState, accuracy)); + .createAnimationToNewWorkspace(fromState, endState, accuracy)); return; } AnimatorSet anim = new AnimatorSet(); - if (!activity.getDeviceProfile().isVerticalBarLayout()) { AllAppsTransitionController controller = activity.getAllAppsController(); - float scrollRange = Math.max(controller.getShiftRange(), 1); - float progressDelta = (transitionLength / scrollRange); - - float endProgress = endState.getVerticalProgress(activity); - float startProgress = endProgress + progressDelta; - ObjectAnimator shiftAnim = ObjectAnimator.ofFloat( - controller, ALL_APPS_PROGRESS, startProgress, endProgress); + ObjectAnimator shiftAnim = ObjectAnimator.ofFloat(controller, ALL_APPS_PROGRESS, + fromState.getVerticalProgress(activity), + endState.getVerticalProgress(activity)); shiftAnim.setInterpolator(LINEAR); anim.play(shiftAnim); - - // Since we are changing the start position of the UI, reapply the state, at the end - anim.addListener(new AnimationSuccessListener() { - @Override - public void onAnimationSuccess(Animator animator) { - activity.getStateManager().reapplyState(); - } - }); } if (interactionType == INTERACTION_NORMAL) { @@ -304,7 +285,14 @@ public interface ActivityControlHelper { anim.setDuration(transitionLength * 2); activity.getStateManager().setCurrentAnimation(anim); - callback.accept(AnimatorPlaybackController.wrap(anim, transitionLength * 2)); + AnimatorPlaybackController controller = + AnimatorPlaybackController.wrap(anim, transitionLength * 2); + + // Since we are changing the start position of the UI, reapply the state, at the end + controller.setEndAction(() -> + activity.getStateManager().goToState( + controller.getProgressFraction() > 0.5 ? endState : fromState, false)); + callback.accept(controller); } /** @@ -464,7 +452,7 @@ public interface ActivityControlHelper { @Override public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, - @InteractionType int interactionType, TransformedRect outRect, PointF touchDown) { + @InteractionType int interactionType, TransformedRect outRect) { LayoutUtils.calculateFallbackTaskSize(context, dp, outRect.rect); if (dp.isVerticalBarLayout()) { Rect targetInsets = dp.getInsets(); diff --git a/quickstep/src/com/android/quickstep/MultiStateCallback.java b/quickstep/src/com/android/quickstep/MultiStateCallback.java index bda3d06aac..98d723fbb8 100644 --- a/quickstep/src/com/android/quickstep/MultiStateCallback.java +++ b/quickstep/src/com/android/quickstep/MultiStateCallback.java @@ -17,20 +17,23 @@ package com.android.quickstep; import android.util.SparseArray; +import com.android.launcher3.Utilities.Consumer; + /** * Utility class to help manage multiple callbacks based on different states. */ public class MultiStateCallback { private final SparseArray mCallbacks = new SparseArray<>(); + private final SparseArray> mStateChangeHandlers = new SparseArray<>(); private int mState = 0; /** * Adds the provided state flags to the global state and executes any callbacks as a result. - * @param stateFlag */ public void setState(int stateFlag) { + int oldState = mState; mState = mState | stateFlag; int count = mCallbacks.size(); @@ -46,6 +49,30 @@ public class MultiStateCallback { } } } + notifyStateChangeHandlers(oldState); + } + + /** + * Adds the provided state flags to the global state and executes any change handlers + * as a result. + */ + public void clearState(int stateFlag) { + int oldState = mState; + mState = mState & ~stateFlag; + notifyStateChangeHandlers(oldState); + } + + private void notifyStateChangeHandlers(int oldState) { + int count = mStateChangeHandlers.size(); + for (int i = 0; i < count; i++) { + int state = mStateChangeHandlers.keyAt(i); + boolean wasOn = (state & oldState) == state; + boolean isOn = (state & mState) == state; + + if (wasOn != isOn) { + mStateChangeHandlers.valueAt(i).accept(isOn); + } + } } /** @@ -56,6 +83,13 @@ public class MultiStateCallback { mCallbacks.put(stateMask, callback); } + /** + * Sets the handler to be called when the provided states are enabled or disabled. + */ + public void addChangeHandler(int stateMask, Consumer handler) { + mStateChangeHandlers.put(stateMask, handler); + } + public int getState() { return mState; } diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java index c3e056812a..0e811f771f 100644 --- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java +++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java @@ -444,7 +444,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC } else { TraceHelper.partitionSection("RecentsController", "Received"); mInteractionHandler.onRecentsAnimationStart(mController, mTargets, - mHomeContentInsets, mMinimizedHomeBounds, mDownPos); + mHomeContentInsets, mMinimizedHomeBounds); } } } diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java index 18fbfbbabc..fa36f6b7ba 100644 --- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -36,7 +36,6 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Point; -import android.graphics.PointF; import android.graphics.Rect; import android.os.Build; import android.os.Bundle; @@ -123,9 +122,11 @@ public class WindowTransformSwipeHandler { private static final int STATE_CAPTURE_SCREENSHOT = 1 << 15; private static final int STATE_SCREENSHOT_CAPTURED = 1 << 16; + private static final int STATE_SCREENSHOT_VIEW_SHOWN = 1 << 17; + + private static final int STATE_RESUME_LAST_TASK = 1 << 18; + private static final int STATE_ASSIST_DATA_RECEIVED = 1 << 19; - private static final int STATE_RESUME_LAST_TASK = 1 << 17; - private static final int STATE_ASSIST_DATA_RECEIVED = 1 << 18; private static final int LAUNCHER_UI_STATES = STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_ACTIVITY_MULTIPLIER_COMPLETE @@ -158,6 +159,7 @@ public class WindowTransformSwipeHandler { "STATE_QUICK_SCRUB_END", "STATE_CAPTURE_SCREENSHOT", "STATE_SCREENSHOT_CAPTURED", + "STATE_SCREENSHOT_VIEW_SHOWN", "STATE_RESUME_LAST_TASK", "STATE_ASSIST_DATA_RECEIVED", }; @@ -176,13 +178,14 @@ public class WindowTransformSwipeHandler { protected boolean mIsGoingToHome; private DeviceProfile mDp; private int mTransitionDragLength; - private PointF mTouchDown; // Shift in the range of [0, 1]. // 0 => preview snapShot is completely visible, and hotseat is completely translated down // 1 => preview snapShot is completely aligned with the recents view and hotseat is completely // visible. private final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift); + // To avoid UI jump when gesture is started, we offset the animation by the threshold. + private float mShiftAtGestureStart = 0; private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper()); @@ -324,6 +327,10 @@ public class WindowTransformSwipeHandler { mStateCallback.addCallback(LONG_SWIPE_ENTER_STATE, this::checkLongSwipeCanEnter); mStateCallback.addCallback(LONG_SWIPE_START_STATE, this::checkLongSwipeCanStart); + + mStateCallback.addChangeHandler(STATE_APP_CONTROLLER_RECEIVED | STATE_LAUNCHER_PRESENT + | STATE_SCREENSHOT_VIEW_SHOWN | STATE_CAPTURE_SCREENSHOT, + (b) -> mRecentsView.setRunningTaskHidden(!b)); } private void executeOnUiThread(Runnable action) { @@ -342,12 +349,12 @@ public class WindowTransformSwipeHandler { } } - private void initTransitionEndpoints(DeviceProfile dp, PointF touchDown) { + private void initTransitionEndpoints(DeviceProfile dp) { mDp = dp; TransformedRect tempRect = new TransformedRect(); mTransitionDragLength = mActivityControlHelper.getSwipeUpDestinationAndLength( - dp, mContext, mInteractionType, tempRect, touchDown); + dp, mContext, mInteractionType, tempRect); mClipAnimationHelper.updateTargetRect(tempRect); } @@ -571,14 +578,14 @@ public class WindowTransformSwipeHandler { * Called by {@link #mLayoutListener} when launcher layout changes */ public void buildAnimationController() { - initTransitionEndpoints(mActivity.getDeviceProfile(), mTouchDown); + initTransitionEndpoints(mActivity.getDeviceProfile()); mAnimationFactory.createActivityController(mTransitionDragLength, mInteractionType); } private void onAnimatorPlaybackControllerCreated(AnimatorPlaybackController anim) { mLauncherTransitionController = anim; mLauncherTransitionController.dispatchOnStart(); - mLauncherTransitionController.setPlayFraction(mCurrentShift.value); + updateLauncherTransitionProgress(); } @WorkerThread @@ -617,12 +624,18 @@ public class WindowTransformSwipeHandler { .getAnimationPlayer().isStarted()) { return; } - mLauncherTransitionController.setPlayFraction(mCurrentShift.value); + updateLauncherTransitionProgress(); + } + + private void updateLauncherTransitionProgress() { + float progress = mCurrentShift.value; + mLauncherTransitionController.setPlayFraction( + progress <= mShiftAtGestureStart || mShiftAtGestureStart >= 1 + ? 0 : (progress - mShiftAtGestureStart) / (1 - mShiftAtGestureStart)); } public void onRecentsAnimationStart(RecentsAnimationControllerCompat controller, - RemoteAnimationTargetSet targets, Rect homeContentInsets, Rect minimizedHomeBounds, - PointF touchDown) { + RemoteAnimationTargetSet targets, Rect homeContentInsets, Rect minimizedHomeBounds) { DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext); final Rect overviewStackBounds; RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(mRunningTaskId); @@ -653,8 +666,7 @@ public class WindowTransformSwipeHandler { mClipAnimationHelper.updateSource(overviewStackBounds, runningTaskTarget); } mClipAnimationHelper.prepareAnimation(false /* isOpening */); - mTouchDown = touchDown; - initTransitionEndpoints(dp, mTouchDown); + initTransitionEndpoints(dp); mRecentsAnimationWrapper.setController(controller, targets); setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED); @@ -670,6 +682,7 @@ public class WindowTransformSwipeHandler { public void onGestureStarted() { notifyGestureStartedAsync(); + mShiftAtGestureStart = mCurrentShift.value; setStateOnUiThread(mInteractionType == INTERACTION_NORMAL ? STATE_GESTURE_STARTED_QUICKSTEP : STATE_GESTURE_STARTED_QUICKSCRUB); mGestureStarted = true; @@ -805,8 +818,8 @@ public class WindowTransformSwipeHandler { @Override public void onAnimationSuccess(Animator animator) { setStateOnUiThread(mIsGoingToHome - ? (STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT) - : STATE_SCALED_CONTROLLER_APP); + ? (STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT + | STATE_SCREENSHOT_VIEW_SHOWN) : STATE_SCALED_CONTROLLER_APP); } }); anim.start(); @@ -900,7 +913,6 @@ public class WindowTransformSwipeHandler { mTaskSnapshot = controller.screenshotTask(mRunningTaskId); } TaskView taskView = mRecentsView.updateThumbnail(mRunningTaskId, mTaskSnapshot); - mRecentsView.setRunningTaskHidden(false); if (taskView != null) { // Defer finishing the animation until the next launcher frame with the // new thumbnail @@ -1042,6 +1054,7 @@ public class WindowTransformSwipeHandler { private void onLongSwipeDisabledUi() { mUiLongSwipeMode = false; + mStateCallback.clearState(STATE_SCREENSHOT_VIEW_SHOWN); if (mLongSwipeController != null) { mLongSwipeController.destroy(); @@ -1067,7 +1080,7 @@ public class WindowTransformSwipeHandler { } // We are entering long swipe mode, make sure the screen shot is captured. - mStateCallback.setState(STATE_CAPTURE_SCREENSHOT); + mStateCallback.setState(STATE_CAPTURE_SCREENSHOT | STATE_SCREENSHOT_VIEW_SHOWN); } diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java index 253e56fab1..6ca0dcef23 100644 --- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java +++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java @@ -113,8 +113,7 @@ public class LayoutUtils { } public static int getShelfTrackingDistance(DeviceProfile dp) { - int shelfHeight = dp.hotseatBarSizePx + dp.getInsets().bottom; - // Track slightly below the top of the shelf (between top and content). - return shelfHeight - dp.edgeMarginPx * 2; + // Start from a third of bottom inset to provide some shelf overlap. + return dp.hotseatBarSizePx + dp.getInsets().bottom / 3 - dp.edgeMarginPx * 2; } } diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java index bbe44c005e..beef97e4ff 100644 --- a/src/com/android/launcher3/LauncherState.java +++ b/src/com/android/launcher3/LauncherState.java @@ -22,11 +22,11 @@ import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CH import static com.android.launcher3.anim.Interpolators.ACCEL_2; import static com.android.launcher3.states.RotationHelper.REQUEST_NONE; -import android.graphics.Rect; import android.view.animation.Interpolator; import com.android.launcher3.states.SpringLoadedState; import com.android.launcher3.uioverrides.AllAppsState; +import com.android.launcher3.uioverrides.BackgroundAppState; import com.android.launcher3.uioverrides.FastOverviewState; import com.android.launcher3.uioverrides.OverviewState; import com.android.launcher3.uioverrides.UiFactory; @@ -73,7 +73,7 @@ public class LauncherState { } }; - private static final LauncherState[] sAllStates = new LauncherState[5]; + private static final LauncherState[] sAllStates = new LauncherState[6]; /** * TODO: Create a separate class for NORMAL state. @@ -89,8 +89,7 @@ public class LauncherState { public static final LauncherState OVERVIEW = new OverviewState(2); public static final LauncherState FAST_OVERVIEW = new FastOverviewState(3); public static final LauncherState ALL_APPS = new AllAppsState(4); - - protected static final Rect sTempRect = new Rect(); + public static final LauncherState BACKGROUND_APP = new BackgroundAppState(5); public final int ordinal; diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/BackgroundAppState.java b/src_ui_overrides/com/android/launcher3/uioverrides/BackgroundAppState.java new file mode 100644 index 0000000000..9133b07eca --- /dev/null +++ b/src_ui_overrides/com/android/launcher3/uioverrides/BackgroundAppState.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.uioverrides; + +/** + * A dummy background app state + */ +public class BackgroundAppState extends OverviewState { + + public BackgroundAppState(int id) { + super(id); + } +}