diff --git a/quickstep/res/layout/overview_panel.xml b/quickstep/res/layout/overview_panel.xml index 466470f3b4..521551c217 100644 --- a/quickstep/res/layout/overview_panel.xml +++ b/quickstep/res/layout/overview_panel.xml @@ -19,28 +19,33 @@ android:theme="@style/HomeScreenElementTheme" android:layout_width="match_parent" android:layout_height="match_parent" + android:paddingTop="20dp" + android:paddingBottom="20dp" + android:clipToPadding="false" android:layout_gravity="center_horizontal|bottom" android:gravity="top"> - - - diff --git a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java new file mode 100644 index 0000000000..caeef50778 --- /dev/null +++ b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java @@ -0,0 +1,237 @@ +/* + * 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.animation.Animator; +import android.animation.ObjectAnimator; +import android.animation.RectEvaluator; +import android.annotation.TargetApi; +import android.app.ActivityManager.RunningTaskInfo; +import android.graphics.Bitmap; +import android.graphics.Rect; +import android.os.Build; +import android.os.UserHandle; +import android.support.annotation.BinderThread; +import android.support.annotation.UiThread; +import android.util.FloatProperty; +import android.view.Choreographer; +import android.view.Choreographer.FrameCallback; +import android.view.View; +import android.view.ViewGroup; + +import com.android.launcher3.Hotseat; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.Utilities; +import com.android.launcher3.anim.AnimationSuccessListener; +import com.android.launcher3.anim.Interpolators; +import com.android.launcher3.dragndrop.DragLayer; +import com.android.launcher3.states.InternalStateHandler; +import com.android.systemui.shared.recents.model.Task.TaskKey; +import com.android.systemui.shared.system.ActivityManagerWrapper; + +@TargetApi(Build.VERSION_CODES.O) +public class NavBarSwipeInteractionHandler extends InternalStateHandler implements FrameCallback { + + private static FloatProperty SHIFT = + new FloatProperty("currentShift") { + @Override + public void setValue(NavBarSwipeInteractionHandler handler, float v) { + handler.setShift(v); + } + + @Override + public Float get(NavBarSwipeInteractionHandler handler) { + return handler.mCurrentShift; + } + }; + + // The following constants need to be scaled based on density. The scaled versions will be + // assigned to the corresponding member variables below. + private static final int FLING_THRESHOLD_VELOCITY = 500; + private static final int MIN_FLING_VELOCITY = 250; + + private static final float MIN_PROGRESS_FOR_OVERVIEW = 0.5f; + + private final Rect mSourceRect = new Rect(); + private final Rect mTargetRect = new Rect(); + private final Rect mCurrentRect = new Rect(); + private final RectEvaluator mRectEvaluator = new RectEvaluator(mCurrentRect); + + private final Bitmap mTaskSnapshot; + private final RunningTaskInfo mTaskInfo; + + private Launcher mLauncher; + private Choreographer mChoreographer; + private SnapshotDragView mDragView; + private RecentsView mRecentsView; + private Hotseat mHotseat; + + private float mStartDelta; + private float mLastDelta; + + // 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 float mCurrentShift; + + // These are updated on the binder thread, and eventually picked up on doFrame + private float mCurrentDisplacement; + private boolean mTouchEnded = false; + private float mEndVelocity; + + NavBarSwipeInteractionHandler(Bitmap taskSnapShot, RunningTaskInfo taskInfo) { + mTaskSnapshot = taskSnapShot; + mTaskInfo = taskInfo; + } + + @Override + public void onLauncherResume() { + mStartDelta = mCurrentDisplacement; + mLastDelta = mStartDelta; + mChoreographer = Choreographer.getInstance(); + + scheduleNextFrame(); + } + + @Override + public void onNewIntent(Launcher launcher) { + mLauncher = launcher; + + // Go immediately + launcher.getStateManager().goToState(LauncherState.OVERVIEW, false); + + // Optimization + launcher.getAppsView().setVisibility(View.GONE); + + mDragView = new SnapshotDragView(launcher, mTaskSnapshot); + launcher.getDragLayer().addView(mDragView); + mDragView.setPivotX(0); + mDragView.setPivotY(0); + mRecentsView = launcher.getOverviewPanel(); + mRecentsView.scrollTo(0, 0); + mHotseat = launcher.getHotseat(); + } + + @BinderThread + public void updateDisplacement(float displacement) { + mCurrentDisplacement = displacement; + } + + @BinderThread + public void endTouch(float endVelocity) { + mTouchEnded = true; + mEndVelocity = endVelocity; + } + + @UiThread + private void scheduleNextFrame() { + if (!mTouchEnded) { + mChoreographer.postFrameCallback(this); + } else { + animateToFinalShift(); + } + } + + @Override + public void doFrame(long l) { + mLastDelta = mCurrentDisplacement; + + float translation = Utilities.boundToRange(mStartDelta - mLastDelta, 0, + mHotseat.getHeight()); + int hotseatHeight = mHotseat.getHeight(); + float shift = hotseatHeight == 0 ? 0 : translation / hotseatHeight; + setShift(shift); + scheduleNextFrame(); + } + + @UiThread + private void setShift(float shift) { + if (mTargetRect.isEmpty()) { + DragLayer dl = mLauncher.getDragLayer(); + + // Init target rect. + View targetView = ((ViewGroup) mRecentsView.getChildAt(0)).getChildAt(0); + dl.getViewRectRelativeToSelf(targetView, mTargetRect); + mSourceRect.set(0, 0, dl.getWidth(), dl.getHeight()); + } + + mCurrentShift = shift; + int hotseatHeight = mHotseat.getHeight(); + mHotseat.setTranslationY((1 - shift) * hotseatHeight); + + mRectEvaluator.evaluate(shift, mSourceRect, mTargetRect); + + mDragView.setTranslationX(mCurrentRect.left); + mDragView.setTranslationY(mCurrentRect.top); + mDragView.setScaleX((float) mCurrentRect.width() / mSourceRect.width()); + mDragView.setScaleY((float) mCurrentRect.width() / mSourceRect.width()); + } + + @UiThread + private void animateToFinalShift() { + float flingThreshold = Utilities.pxFromDp(FLING_THRESHOLD_VELOCITY, + mLauncher.getResources().getDisplayMetrics()); + boolean isFling = Math.abs(mEndVelocity) > flingThreshold; + + long duration = 200; + final float endShift; + if (!isFling) { + endShift = mCurrentShift >= MIN_PROGRESS_FOR_OVERVIEW ? 1 : 0; + } else { + endShift = mEndVelocity < 0 ? 1 : 0; + float minFlingVelocity = Utilities.pxFromDp(MIN_FLING_VELOCITY, + mLauncher.getResources().getDisplayMetrics()); + if (Math.abs(mEndVelocity) > minFlingVelocity) { + float distanceToTravel = (endShift - mCurrentShift) * mHotseat.getHeight(); + + // we want the page's snap velocity to approximately match the velocity at + // which the user flings, so we scale the duration by a value near to the + // derivative of the scroll interpolator at zero, ie. 5. We use 4 to make + // it a little slower. + duration = 4 * Math.round(1000 * Math.abs(distanceToTravel / mEndVelocity)); + } + } + + ObjectAnimator anim = ObjectAnimator.ofFloat(this, SHIFT, endShift) + .setDuration(duration); + anim.setInterpolator(Interpolators.SCROLL); + anim.addListener(new AnimationSuccessListener() { + @Override + public void onAnimationSuccess(Animator animator) { + if (Float.compare(mCurrentShift, 0) == 0) { + resumeLastTask(); + } else { + mDragView.close(false); + } + } + }); + anim.start(); + } + + @UiThread + private void resumeLastTask() { + // TODO: These should be done as part of ActivityOptions#OnAnimationStarted + mHotseat.setTranslationY(0); + mLauncher.setOnResumeCallback(() -> mDragView.close(false)); + + // TODO: Task key should be received from Recents model + TaskKey taskKey = new TaskKey(mTaskInfo.id, 0, null, UserHandle.myUserId(), 0); + ActivityManagerWrapper.getInstance() + .startActivityFromRecentsAsync(taskKey, null, null, null); + } +} diff --git a/quickstep/src/com/android/quickstep/RecentsView.java b/quickstep/src/com/android/quickstep/RecentsView.java index d85de8f405..d7559daad9 100644 --- a/quickstep/src/com/android/quickstep/RecentsView.java +++ b/quickstep/src/com/android/quickstep/RecentsView.java @@ -30,6 +30,7 @@ import com.android.launcher3.R; * A placeholder view for recents */ public class RecentsView extends HorizontalScrollView implements Insettable { + public RecentsView(Context context) { this(context, null); } diff --git a/quickstep/src/com/android/quickstep/SimpleTaskView.java b/quickstep/src/com/android/quickstep/SimpleTaskView.java new file mode 100644 index 0000000000..8425fa3ba4 --- /dev/null +++ b/quickstep/src/com/android/quickstep/SimpleTaskView.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.quickstep; + +import android.content.Context; +import android.graphics.Point; +import android.util.AttributeSet; +import android.view.View; +import android.view.WindowManager; + +/** + * A simple view which keeps its size proportional to the display size + */ +public class SimpleTaskView extends View { + + private static final Point sTempPoint = new Point(); + + public SimpleTaskView(Context context) { + super(context); + } + + public SimpleTaskView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public SimpleTaskView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int height = MeasureSpec.getSize(heightMeasureSpec); + getContext().getSystemService(WindowManager.class) + .getDefaultDisplay().getRealSize(sTempPoint); + + int width = (int) ((float) height * sTempPoint.x / sTempPoint.y); + setMeasuredDimension(width, height); + } +} diff --git a/quickstep/src/com/android/quickstep/SnapshotDragView.java b/quickstep/src/com/android/quickstep/SnapshotDragView.java new file mode 100644 index 0000000000..791fe9ff17 --- /dev/null +++ b/quickstep/src/com/android/quickstep/SnapshotDragView.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.quickstep; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.view.MotionEvent; + +import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.Insettable; +import com.android.launcher3.Launcher; + +/** + * Floating view which shows the task snapshot allowing it to be dragged and placed. + */ +public class SnapshotDragView extends AbstractFloatingView implements Insettable { + + private final Launcher mLauncher; + private final Bitmap mSnapshot; + + public SnapshotDragView(Launcher launcher, Bitmap snapshot) { + super(launcher, null); + mLauncher = launcher; + mSnapshot = snapshot; + setWillNotDraw(false); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (mSnapshot != null) { + setMeasuredDimension(mSnapshot.getWidth(), mSnapshot.getHeight()); + } else { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + } + + @Override + public void setInsets(Rect insets) { + + } + + @Override + protected void onDraw(Canvas canvas) { + if (mSnapshot != null) { + canvas.drawBitmap(mSnapshot, 0, 0, null); + } + } + + @Override + public boolean onControllerInterceptTouchEvent(MotionEvent ev) { + return false; + } + + @Override + protected void handleClose(boolean animate) { + // We dont suupport animate. + mLauncher.getDragLayer().removeView(this); + } + + @Override + public void logActionCommand(int command) { + // We should probably log the weather + } + + @Override + protected boolean isOfType(int type) { + return (type & TYPE_QUICKSTEP_PREVIEW) != 0; + } +} diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index 091ab54989..dcfa53b41e 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -15,17 +15,201 @@ */ package com.android.quickstep; +import static android.view.MotionEvent.INVALID_POINTER_ID; + +import static com.android.launcher3.states.InternalStateHandler.EXTRA_STATE_HANDLER; + +import android.app.ActivityManager.RunningTaskInfo; +import android.app.ActivityOptions; import android.app.Service; +import android.content.ComponentName; import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.graphics.Bitmap; +import android.graphics.Point; +import android.graphics.PointF; +import android.graphics.Rect; +import android.os.Bundle; import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; +import android.view.Display; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.ViewConfiguration; +import android.view.WindowManager; + +import com.android.systemui.shared.recents.IOverviewProxy; +import com.android.systemui.shared.recents.ISystemUiProxy; +import com.android.systemui.shared.system.ActivityManagerWrapper; /** * Service connected by system-UI for handling touch interaction. */ public class TouchInteractionService extends Service { + private static final String TAG = "TouchInteractionService"; + + private final IBinder mMyBinder = new IOverviewProxy.Stub() { + + @Override + public void onMotionEvent(MotionEvent ev) { + handleMotionEvent(ev); + } + + @Override + public void onBind(ISystemUiProxy iSystemUiProxy) throws RemoteException { + mISystemUiProxy = iSystemUiProxy; + } + }; + + private ActivityManagerWrapper mAM; + private RunningTaskInfo mRunningTask; + private Intent mHomeIntent; + private ComponentName mLauncher; + + private final PointF mDownPos = new PointF(); + private final PointF mLastPos = new PointF(); + private int mActivePointerId = INVALID_POINTER_ID; + private VelocityTracker mVelocityTracker; + private int mTouchSlop; + private NavBarSwipeInteractionHandler mInteractionHandler; + + private ISystemUiProxy mISystemUiProxy; + + @Override + public void onCreate() { + super.onCreate(); + mAM = ActivityManagerWrapper.getInstance(); + + mHomeIntent = new Intent(Intent.ACTION_MAIN) + .addCategory(Intent.CATEGORY_HOME) + .setPackage(getPackageName()) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + ResolveInfo info = getPackageManager().resolveActivity(mHomeIntent, 0); + mLauncher = new ComponentName(getPackageName(), info.activityInfo.name); + mHomeIntent.setComponent(mLauncher); + } + @Override public IBinder onBind(Intent intent) { - return null; + Log.d(TAG, "Touch service connected"); + return mMyBinder; + } + + private void handleMotionEvent(MotionEvent ev) { + if (ev.getActionMasked() != MotionEvent.ACTION_DOWN && mVelocityTracker == null) { + return; + } + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_DOWN: { + mActivePointerId = ev.getPointerId(0); + mDownPos.set(ev.getX(), ev.getY()); + mLastPos.set(mDownPos); + mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop(); + + mRunningTask = mAM.getRunningTask(); + if (mRunningTask == null || mRunningTask.topActivity.equals(mLauncher)) { + // TODO: We could drive all-apps in this case. For now just ignore swipe. + break; + } + + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } else { + mVelocityTracker.clear(); + } + mVelocityTracker.addMovement(ev); + if (mInteractionHandler != null) { + mInteractionHandler.endTouch(0); + mInteractionHandler = null; + } + break; + } + case MotionEvent.ACTION_POINTER_UP: { + int ptrIdx = ev.getActionIndex(); + int ptrId = ev.getPointerId(ptrIdx); + if (ptrId == mActivePointerId) { + final int newPointerIdx = ptrIdx == 0 ? 1 : 0; + mDownPos.set( + ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x), + ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y)); + mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx)); + mActivePointerId = ev.getPointerId(newPointerIdx); + mVelocityTracker.clear(); + } + break; + } + case MotionEvent.ACTION_MOVE: { + int pointerIndex = ev.findPointerIndex(mActivePointerId); + if (pointerIndex == INVALID_POINTER_ID) { + break; + } + mVelocityTracker.addMovement(ev); + mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex)); + + float displacement = ev.getY(pointerIndex) - mDownPos.y; + if (mInteractionHandler == null) { + if (Math.abs(displacement) >= mTouchSlop) { + startTouchTracking(); + } + } else { + // Move + mInteractionHandler.updateDisplacement(displacement); + } + break; + } + case MotionEvent.ACTION_CANCEL: + // TODO: Should be different than ACTION_UP + case MotionEvent.ACTION_UP: { + + endInteraction(); + break; + } + } + } + + private void startTouchTracking() { + mInteractionHandler = new NavBarSwipeInteractionHandler(getCurrentTaskSnapshot(), mRunningTask); + + Bundle extras = new Bundle(); + extras.putBinder(EXTRA_STATE_HANDLER, mInteractionHandler); + Intent homeIntent = new Intent(mHomeIntent).putExtras(extras); + + // TODO: Call ActivityManager#startRecentsActivity instead, so that the system knows that + // recents was started and not Home. + startActivity(homeIntent, + ActivityOptions.makeCustomAnimation(this, 0, 0).toBundle()); + } + + private void endInteraction() { + if (mInteractionHandler != null) { + mVelocityTracker.computeCurrentVelocity(1000, + ViewConfiguration.get(this).getScaledMaximumFlingVelocity()); + + mInteractionHandler.endTouch(mVelocityTracker.getXVelocity(mActivePointerId)); + mInteractionHandler = null; + } + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + + private Bitmap getCurrentTaskSnapshot() { + if (mISystemUiProxy == null) { + Log.e(TAG, "Never received systemUIProxy"); + return null; + } + Display display = getSystemService(WindowManager.class).getDefaultDisplay(); + Point size = new Point(); + display.getRealSize(size); + + // TODO: We are using some hardcoded layers for now, to best approximate the activity layers + try { + return mISystemUiProxy.screenshot(new Rect(), size.x, size.y, 0, 100000, false, + display.getRotation()); + } catch (RemoteException e) { + Log.e(TAG, "Error capturing snapshot", e); + return null; + } } } diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java index 62e0fb1bcd..26024e5310 100644 --- a/src/com/android/launcher3/AbstractFloatingView.java +++ b/src/com/android/launcher3/AbstractFloatingView.java @@ -20,7 +20,6 @@ import android.annotation.SuppressLint; 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; @@ -42,7 +41,8 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch TYPE_ACTION_POPUP, TYPE_WIDGETS_BOTTOM_SHEET, TYPE_WIDGET_RESIZE_FRAME, - TYPE_WIDGETS_FULL_SHEET + TYPE_WIDGETS_FULL_SHEET, + TYPE_QUICKSTEP_PREVIEW }) @Retention(RetentionPolicy.SOURCE) public @interface FloatingViewType {} @@ -51,9 +51,11 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch 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_QUICKSTEP_PREVIEW = 1 << 5; public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP - | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET; + | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET + | TYPE_QUICKSTEP_PREVIEW; protected boolean mIsOpen; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index baed44d75d..b1b3452e4b 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -63,6 +63,7 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.AsyncTask; +import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Parcelable; @@ -123,6 +124,7 @@ 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.states.InternalStateHandler; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; @@ -1405,6 +1407,7 @@ public class Launcher extends BaseActivity }); } } + InternalStateHandler.handleIntent(this, intent); TraceHelper.endSection("NEW_INTENT"); } diff --git a/src/com/android/launcher3/states/InternalStateHandler.java b/src/com/android/launcher3/states/InternalStateHandler.java new file mode 100644 index 0000000000..a90ed36aa9 --- /dev/null +++ b/src/com/android/launcher3/states/InternalStateHandler.java @@ -0,0 +1,43 @@ +/* + * 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 android.content.Intent; +import android.os.Binder; +import android.os.IBinder; + +import com.android.launcher3.Launcher; +import com.android.launcher3.Launcher.OnResumeCallback; + +/** + * Utility class to sending state handling logic to Launcher from within the same process + */ +public abstract class InternalStateHandler extends Binder implements OnResumeCallback { + + public static final String EXTRA_STATE_HANDLER = "launcher.state_handler"; + + public abstract void onNewIntent(Launcher launcher); + + public static void handleIntent(Launcher launcher, Intent intent) { + IBinder stateBinder = intent.getExtras().getBinder(EXTRA_STATE_HANDLER); + if (stateBinder instanceof InternalStateHandler) { + InternalStateHandler handler = (InternalStateHandler) stateBinder; + launcher.setOnResumeCallback(handler); + handler.onNewIntent(launcher); + } + intent.getExtras().remove(EXTRA_STATE_HANDLER); + } +}