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.content.ComponentName; 24 import android.content.Intent; 25 import android.graphics.RectF; 26 import android.os.Bundle; 27 import android.os.Handler; 28 import android.os.Looper; 29 import android.os.Message; 30 import android.os.Messenger; 31 import android.os.RemoteException; 32 import android.os.UserHandle; 33 import android.util.Log; 34 import android.view.SurfaceControl; 35 36 import androidx.annotation.NonNull; 37 import androidx.annotation.Nullable; 38 39 import com.android.launcher3.views.ActivityContext; 40 41 import java.lang.ref.WeakReference; 42 43 /** 44 * Class to encapsulate the handshake protocol between Launcher and gestureNav. 45 */ 46 public class GestureNavContract { 47 48 private static final String TAG = "GestureNavContract"; 49 50 public static final String EXTRA_GESTURE_CONTRACT = "gesture_nav_contract_v1"; 51 public static final String EXTRA_ICON_POSITION = "gesture_nav_contract_icon_position"; 52 public static final String EXTRA_ICON_SURFACE = "gesture_nav_contract_surface_control"; 53 public static final String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK"; 54 public static final String EXTRA_ON_FINISH_CALLBACK = "gesture_nav_contract_finish_callback"; 55 56 public final ComponentName componentName; 57 public final UserHandle user; 58 59 private final Message mCallback; 60 GestureNavContract(ComponentName componentName, UserHandle user, Message callback)61 public GestureNavContract(ComponentName componentName, UserHandle user, Message callback) { 62 this.componentName = componentName; 63 this.user = user; 64 this.mCallback = callback; 65 } 66 67 /** 68 * Sends the position information to the receiver 69 */ sendEndPosition(RectF position, ActivityContext context, @Nullable SurfaceControl surfaceControl)70 public void sendEndPosition(RectF position, ActivityContext context, 71 @Nullable SurfaceControl surfaceControl) { 72 Bundle result = new Bundle(); 73 result.putParcelable(EXTRA_ICON_POSITION, position); 74 result.putParcelable(EXTRA_ICON_SURFACE, surfaceControl); 75 if (sMessageReceiver == null) { 76 sMessageReceiver = new StaticMessageReceiver(); 77 } 78 result.putParcelable(EXTRA_ON_FINISH_CALLBACK, sMessageReceiver.setCurrentContext(context)); 79 80 Message callback = Message.obtain(); 81 callback.copyFrom(mCallback); 82 callback.setData(result); 83 84 try { 85 callback.replyTo.send(callback); 86 } catch (RemoteException e) { 87 Log.e(TAG, "Error sending icon position", e); 88 } 89 } 90 91 /** 92 * Clears and returns the GestureNavContract if it was present in the intent. 93 */ fromIntent(Intent intent)94 public static GestureNavContract fromIntent(Intent intent) { 95 Bundle extras = intent.getBundleExtra(EXTRA_GESTURE_CONTRACT); 96 if (extras == null) { 97 return null; 98 } 99 intent.removeExtra(EXTRA_GESTURE_CONTRACT); 100 101 ComponentName componentName = extras.getParcelable(EXTRA_COMPONENT_NAME); 102 UserHandle userHandle = extras.getParcelable(EXTRA_USER); 103 Message callback = extras.getParcelable(EXTRA_REMOTE_CALLBACK); 104 105 if (componentName != null && userHandle != null && callback != null 106 && callback.replyTo != null) { 107 return new GestureNavContract(componentName, userHandle, callback); 108 } 109 return null; 110 } 111 112 /** 113 * Message used for receiving gesture nav contract information. We use a static messenger to 114 * avoid leaking too make binders in case the receiving launcher does not handle the contract 115 * properly. 116 */ 117 private static StaticMessageReceiver sMessageReceiver = null; 118 119 private static class StaticMessageReceiver implements Handler.Callback { 120 121 private static final int MSG_CLOSE_LAST_TARGET = 0; 122 123 private final Messenger mMessenger = 124 new Messenger(new Handler(Looper.getMainLooper(), this)); 125 126 private WeakReference<ActivityContext> mLastTarget = new WeakReference<>(null); 127 setCurrentContext(ActivityContext context)128 public Message setCurrentContext(ActivityContext context) { 129 mLastTarget = new WeakReference<>(context); 130 131 Message msg = Message.obtain(); 132 msg.replyTo = mMessenger; 133 msg.what = MSG_CLOSE_LAST_TARGET; 134 return msg; 135 } 136 137 @Override handleMessage(@onNull Message message)138 public boolean handleMessage(@NonNull Message message) { 139 if (message.what == MSG_CLOSE_LAST_TARGET) { 140 ActivityContext lastContext = mLastTarget.get(); 141 if (lastContext != null) { 142 AbstractFloatingView.closeOpenViews(lastContext, false, TYPE_ICON_SURFACE); 143 } 144 return true; 145 } 146 return false; 147 } 148 } 149 } 150