Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2019 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 | |
| 17 | package android.view.inputmethod; |
| 18 | |
Feng Cao | deec8f0 | 2020-03-10 18:14:13 -0700 | [diff] [blame] | 19 | import android.annotation.BinderThread; |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 20 | import android.annotation.CallbackExecutor; |
Feng Cao | b46851c | 2020-04-29 18:22:49 -0700 | [diff] [blame] | 21 | import android.annotation.MainThread; |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 22 | import android.annotation.NonNull; |
| 23 | import android.annotation.Nullable; |
Joanne Chung | 2e70760 | 2020-01-09 18:14:49 +0800 | [diff] [blame] | 24 | import android.annotation.TestApi; |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 25 | import android.content.Context; |
Feng Cao | b46851c | 2020-04-29 18:22:49 -0700 | [diff] [blame] | 26 | import android.os.Handler; |
| 27 | import android.os.Looper; |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 28 | import android.os.Parcel; |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 29 | import android.os.Parcelable; |
| 30 | import android.os.RemoteException; |
| 31 | import android.util.Size; |
| 32 | import android.util.Slog; |
TYM Tsai | b1f42dd | 2020-02-13 16:57:47 +0800 | [diff] [blame] | 33 | import android.view.SurfaceControlViewHost; |
Feng Cao | bd26abb | 2020-04-24 11:35:58 -0700 | [diff] [blame] | 34 | import android.view.ViewGroup; |
Adam He | f27433a | 2020-03-25 15:11:18 -0700 | [diff] [blame] | 35 | import android.widget.inline.InlineContentView; |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 36 | |
| 37 | import com.android.internal.util.DataClass; |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 38 | import com.android.internal.util.Parcelling; |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 39 | import com.android.internal.view.inline.IInlineContentCallback; |
| 40 | import com.android.internal.view.inline.IInlineContentProvider; |
| 41 | |
| 42 | import java.lang.ref.WeakReference; |
| 43 | import java.util.concurrent.Executor; |
| 44 | import java.util.function.Consumer; |
| 45 | |
| 46 | /** |
Feng Cao | b46851c | 2020-04-29 18:22:49 -0700 | [diff] [blame] | 47 | * This class represents an inline suggestion which is made by one app and can be embedded into the |
| 48 | * UI of another. Suggestions may contain sensitive information not known to the host app which |
| 49 | * needs to be protected from spoofing. To address that the suggestion view inflated on demand for |
| 50 | * embedding is created in such a way that the hosting app cannot introspect its content and cannot |
| 51 | * interact with it. |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 52 | */ |
Feng Cao | b46851c | 2020-04-29 18:22:49 -0700 | [diff] [blame] | 53 | @DataClass(genEqualsHashCode = true, genToString = true, genHiddenConstDefs = true, |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 54 | genHiddenConstructor = true) |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 55 | public final class InlineSuggestion implements Parcelable { |
| 56 | |
| 57 | private static final String TAG = "InlineSuggestion"; |
| 58 | |
Feng Cao | b46851c | 2020-04-29 18:22:49 -0700 | [diff] [blame] | 59 | @NonNull |
| 60 | private final InlineSuggestionInfo mInfo; |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 61 | |
Feng Cao | b46851c | 2020-04-29 18:22:49 -0700 | [diff] [blame] | 62 | /** |
| 63 | * @hide |
| 64 | */ |
| 65 | @Nullable |
| 66 | private final IInlineContentProvider mContentProvider; |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 67 | |
| 68 | /** |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 69 | * Used to keep a strong reference to the callback so it doesn't get garbage collected. |
| 70 | * |
| 71 | * @hide |
| 72 | */ |
| 73 | @DataClass.ParcelWith(InlineContentCallbackImplParceling.class) |
Feng Cao | b46851c | 2020-04-29 18:22:49 -0700 | [diff] [blame] | 74 | @Nullable |
| 75 | private InlineContentCallbackImpl mInlineContentCallback; |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 76 | |
| 77 | /** |
Joanne Chung | 2e70760 | 2020-01-09 18:14:49 +0800 | [diff] [blame] | 78 | * Creates a new {@link InlineSuggestion}, for testing purpose. |
| 79 | * |
| 80 | * @hide |
| 81 | */ |
| 82 | @TestApi |
| 83 | @NonNull |
| 84 | public static InlineSuggestion newInlineSuggestion(@NonNull InlineSuggestionInfo info) { |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 85 | return new InlineSuggestion(info, null, /* inlineContentCallback */ null); |
Joanne Chung | 2e70760 | 2020-01-09 18:14:49 +0800 | [diff] [blame] | 86 | } |
| 87 | |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 88 | /** |
| 89 | * Creates a new {@link InlineSuggestion}. |
| 90 | * |
| 91 | * @hide |
| 92 | */ |
Feng Cao | b46851c | 2020-04-29 18:22:49 -0700 | [diff] [blame] | 93 | public InlineSuggestion(@NonNull InlineSuggestionInfo info, |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 94 | @Nullable IInlineContentProvider contentProvider) { |
| 95 | this(info, contentProvider, /* inlineContentCallback */ null); |
| 96 | } |
Joanne Chung | 2e70760 | 2020-01-09 18:14:49 +0800 | [diff] [blame] | 97 | |
Joanne Chung | 2e70760 | 2020-01-09 18:14:49 +0800 | [diff] [blame] | 98 | /** |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 99 | * Inflates a view with the content of this suggestion at a specific size. |
Feng Cao | bd26abb | 2020-04-24 11:35:58 -0700 | [diff] [blame] | 100 | * |
Feng Cao | b46851c | 2020-04-29 18:22:49 -0700 | [diff] [blame] | 101 | * <p> Each dimension of the size must satisfy one of the following conditions: |
| 102 | * |
| 103 | * <ol> |
| 104 | * <li>between {@link android.widget.inline.InlinePresentationSpec#getMinSize()} and |
| 105 | * {@link android.widget.inline.InlinePresentationSpec#getMaxSize()} of the presentation spec |
| 106 | * from {@code mInfo} |
| 107 | * <li>{@link ViewGroup.LayoutParams#WRAP_CONTENT} |
| 108 | * </ol> |
| 109 | * |
| 110 | * If the size is set to {@link |
| 111 | * ViewGroup.LayoutParams#WRAP_CONTENT}, then the size of the inflated view will be just large |
| 112 | * enough to fit the content, while still conforming to the min / max size specified by the |
| 113 | * {@link android.widget.inline.InlinePresentationSpec}. |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 114 | * |
Svet Ganov | a57dadd | 2020-03-18 23:30:16 -0700 | [diff] [blame] | 115 | * <p> The caller can attach an {@link android.view.View.OnClickListener} and/or an |
Feng Cao | b46851c | 2020-04-29 18:22:49 -0700 | [diff] [blame] | 116 | * {@link android.view.View.OnLongClickListener} to the view in the {@code callback} to receive |
| 117 | * click and long click events on the view. |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 118 | * |
Feng Cao | deec8f0 | 2020-03-10 18:14:13 -0700 | [diff] [blame] | 119 | * @param context Context in which to inflate the view. |
Feng Cao | b46851c | 2020-04-29 18:22:49 -0700 | [diff] [blame] | 120 | * @param size The size at which to inflate the suggestion. For each dimension, it maybe an |
| 121 | * exact value or {@link ViewGroup.LayoutParams#WRAP_CONTENT}. |
| 122 | * @param callback Callback for receiving the inflated view, where the {@link |
| 123 | * ViewGroup.LayoutParams} of the view is set as the actual size of the |
| 124 | * underlying remote view. |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 125 | * @throws IllegalArgumentException If an invalid argument is passed. |
Feng Cao | deec8f0 | 2020-03-10 18:14:13 -0700 | [diff] [blame] | 126 | * @throws IllegalStateException If this method is already called. |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 127 | */ |
| 128 | public void inflate(@NonNull Context context, @NonNull Size size, |
| 129 | @NonNull @CallbackExecutor Executor callbackExecutor, |
Svet Ganov | a57dadd | 2020-03-18 23:30:16 -0700 | [diff] [blame] | 130 | @NonNull Consumer<InlineContentView> callback) { |
Adam He | f27433a | 2020-03-25 15:11:18 -0700 | [diff] [blame] | 131 | final Size minSize = mInfo.getInlinePresentationSpec().getMinSize(); |
| 132 | final Size maxSize = mInfo.getInlinePresentationSpec().getMaxSize(); |
Feng Cao | bd26abb | 2020-04-24 11:35:58 -0700 | [diff] [blame] | 133 | if (!isValid(size.getWidth(), minSize.getWidth(), maxSize.getWidth()) |
| 134 | || !isValid(size.getHeight(), minSize.getHeight(), maxSize.getHeight())) { |
| 135 | throw new IllegalArgumentException( |
| 136 | "size is neither between min:" + minSize + " and max:" + maxSize |
| 137 | + ", nor wrap_content"); |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 138 | } |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 139 | mInlineContentCallback = getInlineContentCallback(context, callbackExecutor, callback); |
Feng Cao | b46851c | 2020-04-29 18:22:49 -0700 | [diff] [blame] | 140 | if (mContentProvider == null) { |
| 141 | callbackExecutor.execute(() -> callback.accept(/* view */ null)); |
| 142 | return; |
| 143 | } |
| 144 | try { |
| 145 | mContentProvider.provideContent(size.getWidth(), size.getHeight(), |
| 146 | new InlineContentCallbackWrapper(mInlineContentCallback)); |
| 147 | } catch (RemoteException e) { |
| 148 | Slog.w(TAG, "Error creating suggestion content surface: " + e); |
| 149 | callbackExecutor.execute(() -> callback.accept(/* view */ null)); |
| 150 | } |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 151 | } |
| 152 | |
Feng Cao | bd26abb | 2020-04-24 11:35:58 -0700 | [diff] [blame] | 153 | /** |
| 154 | * Returns true if the {@code actual} length is within [min, max] or is {@link |
| 155 | * ViewGroup.LayoutParams#WRAP_CONTENT}. |
| 156 | */ |
| 157 | private static boolean isValid(int actual, int min, int max) { |
| 158 | if (actual == ViewGroup.LayoutParams.WRAP_CONTENT) { |
| 159 | return true; |
| 160 | } |
| 161 | return actual >= min && actual <= max; |
| 162 | } |
| 163 | |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 164 | private synchronized InlineContentCallbackImpl getInlineContentCallback(Context context, |
Svet Ganov | a57dadd | 2020-03-18 23:30:16 -0700 | [diff] [blame] | 165 | Executor callbackExecutor, Consumer<InlineContentView> callback) { |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 166 | if (mInlineContentCallback != null) { |
| 167 | throw new IllegalStateException("Already called #inflate()"); |
| 168 | } |
Feng Cao | b46851c | 2020-04-29 18:22:49 -0700 | [diff] [blame] | 169 | return new InlineContentCallbackImpl(context, mContentProvider, callbackExecutor, |
| 170 | callback); |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 171 | } |
| 172 | |
Feng Cao | b46851c | 2020-04-29 18:22:49 -0700 | [diff] [blame] | 173 | /** |
| 174 | * A wrapper class around the {@link InlineContentCallbackImpl} to ensure it's not strongly |
| 175 | * reference by the remote system server process. |
| 176 | */ |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 177 | private static final class InlineContentCallbackWrapper extends IInlineContentCallback.Stub { |
| 178 | |
| 179 | private final WeakReference<InlineContentCallbackImpl> mCallbackImpl; |
| 180 | |
| 181 | InlineContentCallbackWrapper(InlineContentCallbackImpl callbackImpl) { |
| 182 | mCallbackImpl = new WeakReference<>(callbackImpl); |
| 183 | } |
| 184 | |
| 185 | @Override |
Feng Cao | deec8f0 | 2020-03-10 18:14:13 -0700 | [diff] [blame] | 186 | @BinderThread |
Feng Cao | bd26abb | 2020-04-24 11:35:58 -0700 | [diff] [blame] | 187 | public void onContent(SurfaceControlViewHost.SurfacePackage content, int width, |
| 188 | int height) { |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 189 | final InlineContentCallbackImpl callbackImpl = mCallbackImpl.get(); |
| 190 | if (callbackImpl != null) { |
Feng Cao | bd26abb | 2020-04-24 11:35:58 -0700 | [diff] [blame] | 191 | callbackImpl.onContent(content, width, height); |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 192 | } |
| 193 | } |
Feng Cao | deec8f0 | 2020-03-10 18:14:13 -0700 | [diff] [blame] | 194 | |
| 195 | @Override |
| 196 | @BinderThread |
| 197 | public void onClick() { |
| 198 | final InlineContentCallbackImpl callbackImpl = mCallbackImpl.get(); |
| 199 | if (callbackImpl != null) { |
| 200 | callbackImpl.onClick(); |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | @Override |
| 205 | @BinderThread |
| 206 | public void onLongClick() { |
| 207 | final InlineContentCallbackImpl callbackImpl = mCallbackImpl.get(); |
| 208 | if (callbackImpl != null) { |
| 209 | callbackImpl.onLongClick(); |
| 210 | } |
| 211 | } |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 212 | } |
| 213 | |
Feng Cao | b46851c | 2020-04-29 18:22:49 -0700 | [diff] [blame] | 214 | /** |
| 215 | * Handles the communication between the inline suggestion view in current (IME) process and |
| 216 | * the remote view provided from the system server. |
| 217 | * |
| 218 | * <p>This class is thread safe, because all the outside calls are piped into a single |
| 219 | * handler thread to be processed. |
| 220 | */ |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 221 | private static final class InlineContentCallbackImpl { |
| 222 | |
Feng Cao | b46851c | 2020-04-29 18:22:49 -0700 | [diff] [blame] | 223 | @NonNull |
| 224 | private final Handler mMainHandler = new Handler(Looper.getMainLooper()); |
| 225 | |
| 226 | @NonNull |
| 227 | private final Context mContext; |
| 228 | @Nullable |
| 229 | private final IInlineContentProvider mInlineContentProvider; |
| 230 | @NonNull |
| 231 | private final Executor mCallbackExecutor; |
| 232 | |
| 233 | /** |
| 234 | * Callback from the client (IME) that will receive the inflated suggestion view. It'll |
| 235 | * only be called once when the view SurfacePackage is first sent back to the client. Any |
| 236 | * updates to the view due to attach to window and detach from window events will be |
| 237 | * handled under the hood, transparent from the client. |
| 238 | */ |
| 239 | @NonNull |
| 240 | private final Consumer<InlineContentView> mCallback; |
| 241 | |
| 242 | /** |
| 243 | * Indicates whether the first content has been received or not. |
| 244 | */ |
| 245 | private boolean mFirstContentReceived = false; |
| 246 | |
| 247 | /** |
| 248 | * The client (IME) side view which internally wraps a remote view. It'll be set when |
| 249 | * {@link #onContent(SurfaceControlViewHost.SurfacePackage, int, int)} is called, which |
| 250 | * should only happen once in the lifecycle of this inline suggestion instance. |
| 251 | */ |
| 252 | @Nullable |
| 253 | private InlineContentView mView; |
| 254 | |
| 255 | /** |
| 256 | * The SurfacePackage pointing to the remote view. It's cached here to be sent to the next |
| 257 | * available consumer. |
| 258 | */ |
| 259 | @Nullable |
| 260 | private SurfaceControlViewHost.SurfacePackage mSurfacePackage; |
| 261 | |
| 262 | /** |
| 263 | * The callback (from the {@link InlineContentView}) which consumes the surface package. |
| 264 | * It's cached here to be called when the SurfacePackage is returned from the remote |
| 265 | * view owning process. |
| 266 | */ |
| 267 | @Nullable |
| 268 | private Consumer<SurfaceControlViewHost.SurfacePackage> mSurfacePackageConsumer; |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 269 | |
| 270 | InlineContentCallbackImpl(@NonNull Context context, |
Feng Cao | b46851c | 2020-04-29 18:22:49 -0700 | [diff] [blame] | 271 | @Nullable IInlineContentProvider inlineContentProvider, |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 272 | @NonNull @CallbackExecutor Executor callbackExecutor, |
Svet Ganov | a57dadd | 2020-03-18 23:30:16 -0700 | [diff] [blame] | 273 | @NonNull Consumer<InlineContentView> callback) { |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 274 | mContext = context; |
Feng Cao | b46851c | 2020-04-29 18:22:49 -0700 | [diff] [blame] | 275 | mInlineContentProvider = inlineContentProvider; |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 276 | mCallbackExecutor = callbackExecutor; |
| 277 | mCallback = callback; |
| 278 | } |
| 279 | |
Feng Cao | deec8f0 | 2020-03-10 18:14:13 -0700 | [diff] [blame] | 280 | @BinderThread |
Feng Cao | bd26abb | 2020-04-24 11:35:58 -0700 | [diff] [blame] | 281 | public void onContent(SurfaceControlViewHost.SurfacePackage content, int width, |
| 282 | int height) { |
Feng Cao | b46851c | 2020-04-29 18:22:49 -0700 | [diff] [blame] | 283 | mMainHandler.post(() -> handleOnContent(content, width, height)); |
| 284 | } |
| 285 | |
| 286 | @MainThread |
| 287 | private void handleOnContent(SurfaceControlViewHost.SurfacePackage content, int width, |
| 288 | int height) { |
| 289 | if (!mFirstContentReceived) { |
| 290 | handleOnFirstContentReceived(content, width, height); |
| 291 | mFirstContentReceived = true; |
| 292 | } else { |
| 293 | handleOnSurfacePackage(content); |
| 294 | } |
| 295 | } |
| 296 | |
| 297 | /** |
| 298 | * Called when the view content is returned for the first time. |
| 299 | */ |
| 300 | @MainThread |
| 301 | private void handleOnFirstContentReceived(SurfaceControlViewHost.SurfacePackage content, |
| 302 | int width, int height) { |
| 303 | mSurfacePackage = content; |
| 304 | if (mSurfacePackage == null) { |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 305 | mCallbackExecutor.execute(() -> mCallback.accept(/* view */null)); |
| 306 | } else { |
Svet Ganov | a57dadd | 2020-03-18 23:30:16 -0700 | [diff] [blame] | 307 | mView = new InlineContentView(mContext); |
Feng Cao | bd26abb | 2020-04-24 11:35:58 -0700 | [diff] [blame] | 308 | mView.setLayoutParams(new ViewGroup.LayoutParams(width, height)); |
Feng Cao | b46851c | 2020-04-29 18:22:49 -0700 | [diff] [blame] | 309 | mView.setChildSurfacePackageUpdater(getSurfacePackageUpdater()); |
Feng Cao | deec8f0 | 2020-03-10 18:14:13 -0700 | [diff] [blame] | 310 | mCallbackExecutor.execute(() -> mCallback.accept(mView)); |
| 311 | } |
| 312 | } |
| 313 | |
Feng Cao | b46851c | 2020-04-29 18:22:49 -0700 | [diff] [blame] | 314 | /** |
| 315 | * Called when any subsequent SurfacePackage is returned from the remote view owning |
| 316 | * process. |
| 317 | */ |
| 318 | @MainThread |
| 319 | private void handleOnSurfacePackage(SurfaceControlViewHost.SurfacePackage surfacePackage) { |
| 320 | mSurfacePackage = surfacePackage; |
| 321 | if (mSurfacePackage != null && mSurfacePackageConsumer != null) { |
| 322 | mSurfacePackageConsumer.accept(mSurfacePackage); |
| 323 | mSurfacePackageConsumer = null; |
| 324 | } |
| 325 | } |
| 326 | |
| 327 | @MainThread |
| 328 | private void handleOnSurfacePackageReleased() { |
| 329 | mSurfacePackage = null; |
| 330 | try { |
| 331 | mInlineContentProvider.onSurfacePackageReleased(); |
| 332 | } catch (RemoteException e) { |
| 333 | Slog.w(TAG, "Error calling onSurfacePackageReleased(): " + e); |
| 334 | } |
| 335 | } |
| 336 | |
| 337 | @MainThread |
| 338 | private void handleGetSurfacePackage( |
| 339 | Consumer<SurfaceControlViewHost.SurfacePackage> consumer) { |
| 340 | if (mSurfacePackage != null) { |
| 341 | consumer.accept(mSurfacePackage); |
| 342 | } else { |
| 343 | mSurfacePackageConsumer = consumer; |
| 344 | try { |
| 345 | mInlineContentProvider.requestSurfacePackage(); |
| 346 | } catch (RemoteException e) { |
| 347 | Slog.w(TAG, "Error calling getSurfacePackage(): " + e); |
| 348 | consumer.accept(null); |
| 349 | mSurfacePackageConsumer = null; |
| 350 | } |
| 351 | } |
| 352 | } |
| 353 | |
| 354 | private InlineContentView.SurfacePackageUpdater getSurfacePackageUpdater() { |
| 355 | return new InlineContentView.SurfacePackageUpdater() { |
| 356 | @Override |
| 357 | public void onSurfacePackageReleased() { |
| 358 | mMainHandler.post( |
| 359 | () -> InlineContentCallbackImpl.this.handleOnSurfacePackageReleased()); |
| 360 | } |
| 361 | |
| 362 | @Override |
| 363 | public void getSurfacePackage( |
| 364 | Consumer<SurfaceControlViewHost.SurfacePackage> consumer) { |
| 365 | mMainHandler.post( |
| 366 | () -> InlineContentCallbackImpl.this.handleGetSurfacePackage(consumer)); |
| 367 | } |
| 368 | }; |
| 369 | } |
| 370 | |
Feng Cao | deec8f0 | 2020-03-10 18:14:13 -0700 | [diff] [blame] | 371 | @BinderThread |
| 372 | public void onClick() { |
Feng Cao | b46851c | 2020-04-29 18:22:49 -0700 | [diff] [blame] | 373 | mMainHandler.post(() -> { |
| 374 | if (mView != null && mView.hasOnClickListeners()) { |
| 375 | mView.callOnClick(); |
| 376 | } |
| 377 | }); |
Feng Cao | deec8f0 | 2020-03-10 18:14:13 -0700 | [diff] [blame] | 378 | } |
| 379 | |
| 380 | @BinderThread |
| 381 | public void onLongClick() { |
Feng Cao | b46851c | 2020-04-29 18:22:49 -0700 | [diff] [blame] | 382 | mMainHandler.post(() -> { |
| 383 | if (mView != null && mView.hasOnLongClickListeners()) { |
| 384 | mView.performLongClick(); |
| 385 | } |
| 386 | }); |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 387 | } |
| 388 | } |
| 389 | |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 390 | /** |
| 391 | * This class used to provide parcelling logic for InlineContentCallbackImpl. It's intended to |
| 392 | * make this parcelling a no-op, since it can't be parceled and we don't need to parcel it. |
| 393 | */ |
| 394 | private static class InlineContentCallbackImplParceling implements |
| 395 | Parcelling<InlineContentCallbackImpl> { |
| 396 | @Override |
| 397 | public void parcel(InlineContentCallbackImpl item, Parcel dest, int parcelFlags) { |
| 398 | } |
| 399 | |
| 400 | @Override |
| 401 | public InlineContentCallbackImpl unparcel(Parcel source) { |
| 402 | return null; |
| 403 | } |
| 404 | } |
| 405 | |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 406 | |
| 407 | |
Feng Cao | b46851c | 2020-04-29 18:22:49 -0700 | [diff] [blame] | 408 | |
Feng Cao | deec8f0 | 2020-03-10 18:14:13 -0700 | [diff] [blame] | 409 | // Code below generated by codegen v1.0.15. |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 410 | // |
| 411 | // DO NOT MODIFY! |
| 412 | // CHECKSTYLE:OFF Generated code |
| 413 | // |
| 414 | // To regenerate run: |
| 415 | // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/inputmethod/InlineSuggestion.java |
| 416 | // |
| 417 | // To exclude the generated code from IntelliJ auto-formatting enable (one-time): |
| 418 | // Settings > Editor > Code Style > Formatter Control |
| 419 | //@formatter:off |
| 420 | |
| 421 | |
| 422 | /** |
| 423 | * Creates a new InlineSuggestion. |
| 424 | * |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 425 | * @param inlineContentCallback |
| 426 | * Used to keep a strong reference to the callback so it doesn't get garbage collected. |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 427 | * @hide |
| 428 | */ |
| 429 | @DataClass.Generated.Member |
| 430 | public InlineSuggestion( |
| 431 | @NonNull InlineSuggestionInfo info, |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 432 | @Nullable IInlineContentProvider contentProvider, |
| 433 | @Nullable InlineContentCallbackImpl inlineContentCallback) { |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 434 | this.mInfo = info; |
| 435 | com.android.internal.util.AnnotationValidations.validate( |
| 436 | NonNull.class, null, mInfo); |
| 437 | this.mContentProvider = contentProvider; |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 438 | this.mInlineContentCallback = inlineContentCallback; |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 439 | |
| 440 | // onConstructed(); // You can define this method to get a callback |
| 441 | } |
| 442 | |
| 443 | @DataClass.Generated.Member |
| 444 | public @NonNull InlineSuggestionInfo getInfo() { |
| 445 | return mInfo; |
| 446 | } |
| 447 | |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 448 | /** |
Feng Cao | b46851c | 2020-04-29 18:22:49 -0700 | [diff] [blame] | 449 | * @hide |
| 450 | */ |
| 451 | @DataClass.Generated.Member |
| 452 | public @Nullable IInlineContentProvider getContentProvider() { |
| 453 | return mContentProvider; |
| 454 | } |
| 455 | |
| 456 | /** |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 457 | * Used to keep a strong reference to the callback so it doesn't get garbage collected. |
| 458 | * |
| 459 | * @hide |
| 460 | */ |
| 461 | @DataClass.Generated.Member |
| 462 | public @Nullable InlineContentCallbackImpl getInlineContentCallback() { |
| 463 | return mInlineContentCallback; |
| 464 | } |
| 465 | |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 466 | @Override |
| 467 | @DataClass.Generated.Member |
| 468 | public String toString() { |
| 469 | // You can override field toString logic by defining methods like: |
| 470 | // String fieldNameToString() { ... } |
| 471 | |
| 472 | return "InlineSuggestion { " + |
| 473 | "info = " + mInfo + ", " + |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 474 | "contentProvider = " + mContentProvider + ", " + |
| 475 | "inlineContentCallback = " + mInlineContentCallback + |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 476 | " }"; |
| 477 | } |
| 478 | |
| 479 | @Override |
| 480 | @DataClass.Generated.Member |
| 481 | public boolean equals(@Nullable Object o) { |
| 482 | // You can override field equality logic by defining either of the methods like: |
| 483 | // boolean fieldNameEquals(InlineSuggestion other) { ... } |
| 484 | // boolean fieldNameEquals(FieldType otherValue) { ... } |
| 485 | |
| 486 | if (this == o) return true; |
| 487 | if (o == null || getClass() != o.getClass()) return false; |
| 488 | @SuppressWarnings("unchecked") |
| 489 | InlineSuggestion that = (InlineSuggestion) o; |
| 490 | //noinspection PointlessBooleanExpression |
| 491 | return true |
| 492 | && java.util.Objects.equals(mInfo, that.mInfo) |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 493 | && java.util.Objects.equals(mContentProvider, that.mContentProvider) |
| 494 | && java.util.Objects.equals(mInlineContentCallback, that.mInlineContentCallback); |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 495 | } |
| 496 | |
| 497 | @Override |
| 498 | @DataClass.Generated.Member |
| 499 | public int hashCode() { |
| 500 | // You can override field hashCode logic by defining methods like: |
| 501 | // int fieldNameHashCode() { ... } |
| 502 | |
| 503 | int _hash = 1; |
| 504 | _hash = 31 * _hash + java.util.Objects.hashCode(mInfo); |
| 505 | _hash = 31 * _hash + java.util.Objects.hashCode(mContentProvider); |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 506 | _hash = 31 * _hash + java.util.Objects.hashCode(mInlineContentCallback); |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 507 | return _hash; |
| 508 | } |
| 509 | |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 510 | @DataClass.Generated.Member |
| 511 | static Parcelling<InlineContentCallbackImpl> sParcellingForInlineContentCallback = |
| 512 | Parcelling.Cache.get( |
| 513 | InlineContentCallbackImplParceling.class); |
| 514 | static { |
| 515 | if (sParcellingForInlineContentCallback == null) { |
| 516 | sParcellingForInlineContentCallback = Parcelling.Cache.put( |
| 517 | new InlineContentCallbackImplParceling()); |
| 518 | } |
| 519 | } |
| 520 | |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 521 | @Override |
| 522 | @DataClass.Generated.Member |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 523 | public void writeToParcel(@NonNull Parcel dest, int flags) { |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 524 | // You can override field parcelling by defining methods like: |
| 525 | // void parcelFieldName(Parcel dest, int flags) { ... } |
| 526 | |
| 527 | byte flg = 0; |
| 528 | if (mContentProvider != null) flg |= 0x2; |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 529 | if (mInlineContentCallback != null) flg |= 0x4; |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 530 | dest.writeByte(flg); |
| 531 | dest.writeTypedObject(mInfo, flags); |
| 532 | if (mContentProvider != null) dest.writeStrongInterface(mContentProvider); |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 533 | sParcellingForInlineContentCallback.parcel(mInlineContentCallback, dest, flags); |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 534 | } |
| 535 | |
| 536 | @Override |
| 537 | @DataClass.Generated.Member |
| 538 | public int describeContents() { return 0; } |
| 539 | |
| 540 | /** @hide */ |
| 541 | @SuppressWarnings({"unchecked", "RedundantCast"}) |
| 542 | @DataClass.Generated.Member |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 543 | /* package-private */ InlineSuggestion(@NonNull Parcel in) { |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 544 | // You can override field unparcelling by defining methods like: |
| 545 | // static FieldType unparcelFieldName(Parcel in) { ... } |
| 546 | |
| 547 | byte flg = in.readByte(); |
| 548 | InlineSuggestionInfo info = (InlineSuggestionInfo) in.readTypedObject(InlineSuggestionInfo.CREATOR); |
| 549 | IInlineContentProvider contentProvider = (flg & 0x2) == 0 ? null : IInlineContentProvider.Stub.asInterface(in.readStrongBinder()); |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 550 | InlineContentCallbackImpl inlineContentCallback = sParcellingForInlineContentCallback.unparcel(in); |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 551 | |
| 552 | this.mInfo = info; |
| 553 | com.android.internal.util.AnnotationValidations.validate( |
| 554 | NonNull.class, null, mInfo); |
| 555 | this.mContentProvider = contentProvider; |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 556 | this.mInlineContentCallback = inlineContentCallback; |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 557 | |
| 558 | // onConstructed(); // You can define this method to get a callback |
| 559 | } |
| 560 | |
| 561 | @DataClass.Generated.Member |
| 562 | public static final @NonNull Parcelable.Creator<InlineSuggestion> CREATOR |
| 563 | = new Parcelable.Creator<InlineSuggestion>() { |
| 564 | @Override |
| 565 | public InlineSuggestion[] newArray(int size) { |
| 566 | return new InlineSuggestion[size]; |
| 567 | } |
| 568 | |
| 569 | @Override |
Joanne Chung | 332c6a9 | 2020-02-07 17:10:23 +0800 | [diff] [blame] | 570 | public InlineSuggestion createFromParcel(@NonNull Parcel in) { |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 571 | return new InlineSuggestion(in); |
| 572 | } |
| 573 | }; |
| 574 | |
| 575 | @DataClass.Generated( |
Feng Cao | b46851c | 2020-04-29 18:22:49 -0700 | [diff] [blame] | 576 | time = 1588308946517L, |
Feng Cao | deec8f0 | 2020-03-10 18:14:13 -0700 | [diff] [blame] | 577 | codegenVersion = "1.0.15", |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 578 | sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestion.java", |
Feng Cao | bd26abb | 2020-04-24 11:35:58 -0700 | [diff] [blame] | 579 | inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo mInfo\nprivate final @android.annotation.Nullable com.android.internal.view.inline.IInlineContentProvider mContentProvider\nprivate @com.android.internal.util.DataClass.ParcelWith(android.view.inputmethod.InlineSuggestion.InlineContentCallbackImplParceling.class) @android.annotation.Nullable android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl mInlineContentCallback\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestion newInlineSuggestion(android.view.inputmethod.InlineSuggestionInfo)\npublic void inflate(android.content.Context,android.util.Size,java.util.concurrent.Executor,java.util.function.Consumer<android.widget.inline.InlineContentView>)\nprivate static boolean isValid(int,int,int)\nprivate synchronized android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl getInlineContentCallback(android.content.Context,java.util.concurrent.Executor,java.util.function.Consumer<android.widget.inline.InlineContentView>)\nclass InlineSuggestion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)") |
Feng Cao | 04830a3 | 2019-11-13 00:45:19 -0800 | [diff] [blame] | 580 | @Deprecated |
| 581 | private void __metadata() {} |
| 582 | |
| 583 | |
| 584 | //@formatter:on |
| 585 | // End of generated code |
| 586 | |
| 587 | } |