1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.launcher3; 17 18 import static android.content.Intent.EXTRA_COMPONENT_NAME; 19 import static android.content.Intent.EXTRA_USER; 20 21 import static com.android.launcher3.AbstractFloatingView.TYPE_ICON_SURFACE; 22 23 import android.annotation.TargetApi; 24 import android.content.ComponentName; 25 import android.content.Intent; 26 import android.graphics.RectF; 27 import android.os.Build; 28 import android.os.Bundle; 29 import android.os.Handler; 30 import android.os.Looper; 31 import android.os.Message; 32 import android.os.Messenger; 33 import android.os.RemoteException; 34 import android.os.UserHandle; 35 import android.util.Log; 36 import android.view.SurfaceControl; 37 38 import androidx.annotation.NonNull; 39 import androidx.annotation.Nullable; 40 41 import com.android.launcher3.views.ActivityContext; 42 43 import java.lang.ref.WeakReference; 44 45 /** 46 * Class to encapsulate the handshake protocol between Launcher and gestureNav. 47 */ 48 public class GestureNavContract { 49 50 private static final String TAG = "GestureNavContract"; 51 52 public static final String EXTRA_GESTURE_CONTRACT = "gesture_nav_contract_v1"; 53 public static final String EXTRA_ICON_POSITION = "gesture_nav_contract_icon_position"; 54 public static final String EXTRA_ICON_SURFACE = "gesture_nav_contract_surface_control"; 55 public static final String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK"; 56 public static final String EXTRA_ON_FINISH_CALLBACK = "gesture_nav_contract_finish_callback"; 57 58 public final ComponentName componentName; 59 public final UserHandle user; 60 61 private final Message mCallback; 62 GestureNavContract(ComponentName componentName, UserHandle user, Message callback)63 public GestureNavContract(ComponentName componentName, UserHandle user, Message callback) { 64 this.componentName = componentName; 65 this.user = user; 66 this.mCallback = callback; 67 } 68 69 /** 70 * Sends the position information to the receiver 71 */ 72 @TargetApi(Build.VERSION_CODES.R) sendEndPosition(RectF position, ActivityContext context, @Nullable SurfaceControl surfaceControl)73 public void sendEndPosition(RectF position, ActivityContext context, 74 @Nullable SurfaceControl surfaceControl) { 75 Bundle result = new Bundle(); 76 result.putParcelable(EXTRA_ICON_POSITION, position); 77 result.putParcelable(EXTRA_ICON_SURFACE, surfaceControl); 78 if (sMessageReceiver == null) { 79 sMessageReceiver = new StaticMessageReceiver(); 80 } 81 result.putParcelable(EXTRA_ON_FINISH_CALLBACK, sMessageReceiver.setCurrentContext(context)); 82 83 Message callback = Message.obtain(); 84 callback.copyFrom(mCallback); 85 callback.setData(result); 86 87 try { 88 callback.replyTo.send(callback); 89 } catch (RemoteException e) { 90 Log.e(TAG, "Error sending icon position", e); 91 } 92 } 93 94 /** 95 * Clears and returns the GestureNavContract if it was present in the intent. 96 */ fromIntent(Intent intent)97 public static GestureNavContract fromIntent(Intent intent) { 98 if (!Utilities.ATLEAST_R) { 99 return null; 100 } 101 Bundle extras = intent.getBundleExtra(EXTRA_GESTURE_CONTRACT); 102 if (extras == null) { 103 return null; 104 } 105 intent.removeExtra(EXTRA_GESTURE_CONTRACT); 106 107 ComponentName componentName = extras.getParcelable(EXTRA_COMPONENT_NAME); 108 UserHandle userHandle = extras.getParcelable(EXTRA_USER); 109 Message callback = extras.getParcelable(EXTRA_REMOTE_CALLBACK); 110 111 if (componentName != null && userHandle != null && callback != null 112 && callback.replyTo != null) { 113 return new GestureNavContract(componentName, userHandle, callback); 114 } 115 return null; 116 } 117 118 /** 119 * Message used for receiving gesture nav contract information. We use a static messenger to 120 * avoid leaking too make binders in case the receiving launcher does not handle the contract 121 * properly. 122 */ 123 private static StaticMessageReceiver sMessageReceiver = null; 124 125 private static class StaticMessageReceiver implements Handler.Callback { 126 127 private static final int MSG_CLOSE_LAST_TARGET = 0; 128 129 private final Messenger mMessenger = 130 new Messenger(new Handler(Looper.getMainLooper(), this)); 131 132 private WeakReference<ActivityContext> mLastTarget = new WeakReference<>(null); 133 setCurrentContext(ActivityContext context)134 public Message setCurrentContext(ActivityContext context) { 135 mLastTarget = new WeakReference<>(context); 136 137 Message msg = Message.obtain(); 138 msg.replyTo = mMessenger; 139 msg.what = MSG_CLOSE_LAST_TARGET; 140 return msg; 141 } 142 143 @Override handleMessage(@onNull Message message)144 public boolean handleMessage(@NonNull Message message) { 145 if (message.what == MSG_CLOSE_LAST_TARGET) { 146 ActivityContext lastContext = mLastTarget.get(); 147 if (lastContext != null) { 148 AbstractFloatingView.closeOpenViews(lastContext, false, TYPE_ICON_SURFACE); 149 } 150 return true; 151 } 152 return false; 153 } 154 } 155 } 156