Loading core/java/android/app/SystemServiceRegistry.java +13 −0 Original line number Diff line number Diff line Loading @@ -270,6 +270,8 @@ import android.view.contentcapture.ContentCaptureManager; import android.view.contentcapture.IContentCaptureManager; import android.view.displayhash.DisplayHashManager; import android.view.inputmethod.InputMethodManager; import android.view.selectiontoolbar.ISelectionToolbarManager; import android.view.selectiontoolbar.SelectionToolbarManager; import android.view.textclassifier.TextClassificationManager; import android.view.textservice.TextServicesManager; import android.view.translation.ITranslationManager; Loading Loading @@ -422,6 +424,17 @@ public final class SystemServiceRegistry { return new TextClassificationManager(ctx); }}); registerService(Context.SELECTION_TOOLBAR_SERVICE, SelectionToolbarManager.class, new CachedServiceFetcher<SelectionToolbarManager>() { @Override public SelectionToolbarManager createService(ContextImpl ctx) throws ServiceNotFoundException { IBinder b = ServiceManager.getServiceOrThrow( Context.SELECTION_TOOLBAR_SERVICE); return new SelectionToolbarManager( ISelectionToolbarManager.Stub.asInterface(b)); }}); registerService(Context.FONT_SERVICE, FontManager.class, new CachedServiceFetcher<FontManager>() { @Override Loading core/java/android/service/selectiontoolbar/DefaultSelectionToolbarRenderService.java 0 → 100644 +154 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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 android.service.selectiontoolbar; import static android.view.selectiontoolbar.ISelectionToolbarCallback.ERROR_DO_NOT_ALLOW_MULTIPLE_TOOL_BAR; import static android.view.selectiontoolbar.SelectionToolbarManager.NO_TOOLBAR_ID; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.view.selectiontoolbar.ShowInfo; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.UUID; /** * The default implementation of {@link SelectionToolbarRenderService}. * * <p><b>NOTE:</b> The requests are handled on the service main thread. * * @hide */ // TODO(b/214122495): fix class not found then move to system service folder public final class DefaultSelectionToolbarRenderService extends SelectionToolbarRenderService { private static final String TAG = "DefaultSelectionToolbarRenderService"; // TODO(b/215497659): handle remove if the client process dies. // Only show one toolbar, dismiss the old ones and remove from cache // Maps uid -> (widget token, toolbar instance) private final SparseArray<Pair<Long, RemoteSelectionToolbar>> mToolbarCache = new SparseArray<>(); /** * Only allow one package to create one toolbar. */ private boolean isToolbarShown(int uid, ShowInfo showInfo) { if (showInfo.widgetToken != NO_TOOLBAR_ID) { return true; } return mToolbarCache.contains(uid); } @Override public void onShow(int callingUid, ShowInfo showInfo, SelectionToolbarRenderService.RemoteCallbackWrapper callbackWrapper) { if (isToolbarShown(callingUid, showInfo)) { Slog.e(TAG, "Do not allow multiple toolbar for the app."); callbackWrapper.onError(ERROR_DO_NOT_ALLOW_MULTIPLE_TOOL_BAR); return; } long widgetToken = showInfo.widgetToken == NO_TOOLBAR_ID ? UUID.randomUUID().getMostSignificantBits() : showInfo.widgetToken; if (mToolbarCache.indexOfKey(callingUid) < 0) { RemoteSelectionToolbar toolbar = new RemoteSelectionToolbar(this, widgetToken, showInfo, callbackWrapper, this::transferTouch); mToolbarCache.put(callingUid, new Pair<>(widgetToken, toolbar)); } Slog.v(TAG, "onShow() for " + widgetToken); Pair<Long, RemoteSelectionToolbar> toolbarPair = mToolbarCache.get(callingUid); if (toolbarPair.first == widgetToken) { toolbarPair.second.show(showInfo); } else { Slog.w(TAG, "onShow() for unknown " + widgetToken); } } @Override public void onHide(long widgetToken) { RemoteSelectionToolbar toolbar = getRemoteSelectionToolbarByToken(widgetToken); if (toolbar != null) { Slog.v(TAG, "onHide() for " + widgetToken); toolbar.hide(widgetToken); } } @Override public void onDismiss(long widgetToken) { RemoteSelectionToolbar toolbar = getRemoteSelectionToolbarByToken(widgetToken); if (toolbar != null) { Slog.v(TAG, "onDismiss() for " + widgetToken); toolbar.dismiss(widgetToken); removeRemoteSelectionToolbarByTokenLocked(widgetToken); } } @Override public void onToolbarShowTimeout(int callingUid) { Slog.w(TAG, "onToolbarShowTimeout for callingUid = " + callingUid); Pair<Long, RemoteSelectionToolbar> toolbarPair = mToolbarCache.get(callingUid); if (toolbarPair != null) { RemoteSelectionToolbar remoteToolbar = toolbarPair.second; remoteToolbar.dismiss(toolbarPair.first); remoteToolbar.onToolbarShowTimeout(); mToolbarCache.remove(callingUid); } } private RemoteSelectionToolbar getRemoteSelectionToolbarByToken(long widgetToken) { for (int i = 0; i < mToolbarCache.size(); i++) { Pair<Long, RemoteSelectionToolbar> toolbarPair = mToolbarCache.valueAt(i); if (toolbarPair.first == widgetToken) { return toolbarPair.second; } } return null; } private void removeRemoteSelectionToolbarByTokenLocked(long widgetToken) { for (int i = 0; i < mToolbarCache.size(); i++) { Pair<Long, RemoteSelectionToolbar> toolbarPair = mToolbarCache.valueAt(i); if (toolbarPair.first == widgetToken) { mToolbarCache.remove(mToolbarCache.keyAt(i)); return; } } } @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { int size = mToolbarCache.size(); pw.print("number selectionToolbar: "); pw.println(size); String pfx = " "; for (int i = 0; i < size; i++) { pw.print("#"); pw.println(i); int callingUid = mToolbarCache.keyAt(i); pw.print(pfx); pw.print("callingUid: "); pw.println(callingUid); Pair<Long, RemoteSelectionToolbar> toolbarPair = mToolbarCache.valueAt(i); RemoteSelectionToolbar selectionToolbar = toolbarPair.second; pw.print(pfx); pw.print("selectionToolbar: "); selectionToolbar.dump(pfx, pw); pw.println(); } } } core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java 0 → 100644 +89 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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 android.service.selectiontoolbar; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Rect; import android.os.IBinder; import android.util.Log; import android.view.MotionEvent; import android.widget.LinearLayout; import java.io.PrintWriter; /** * This class is the root view for the selection toolbar. It is responsible for * detecting the click on the item and to also transfer input focus to the application. * * @hide */ @SuppressLint("ViewConstructor") public class FloatingToolbarRoot extends LinearLayout { private static final boolean DEBUG = false; private static final String TAG = "FloatingToolbarRoot"; private final IBinder mTargetInputToken; private final SelectionToolbarRenderService.TransferTouchListener mTransferTouchListener; private final Rect mContentRect = new Rect(); private int mLastDownX = -1; private int mLastDownY = -1; public FloatingToolbarRoot(Context context, IBinder targetInputToken, SelectionToolbarRenderService.TransferTouchListener transferTouchListener) { super(context); mTargetInputToken = targetInputToken; mTransferTouchListener = transferTouchListener; setFocusable(false); } /** * Sets the Rect that shows the selection toolbar content. */ public void setContentRect(Rect contentRect) { mContentRect.set(contentRect); } @Override @SuppressLint("ClickableViewAccessibility") public boolean dispatchTouchEvent(MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { mLastDownX = (int) event.getX(); mLastDownY = (int) event.getY(); if (DEBUG) { Log.d(TAG, "downX=" + mLastDownX + " downY=" + mLastDownY); } // TODO(b/215497659): Check FLAG_WINDOW_IS_PARTIALLY_OBSCURED if (!mContentRect.contains(mLastDownX, mLastDownY)) { if (DEBUG) { Log.d(TAG, "Transfer touch focus to application."); } mTransferTouchListener.onTransferTouch(getViewRootImpl().getInputToken(), mTargetInputToken); } } return super.dispatchTouchEvent(event); } void dump(String prefix, PrintWriter pw) { pw.print(prefix); pw.println("FloatingToolbarRoot:"); pw.print(prefix + " "); pw.print("last down X: "); pw.println(mLastDownX); pw.print(prefix + " "); pw.print("last down Y: "); pw.println(mLastDownY); } } core/java/android/service/selectiontoolbar/ISelectionToolbarRenderService.aidl 0 → 100644 +32 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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 android.service.selectiontoolbar; import android.view.selectiontoolbar.ISelectionToolbarCallback; import android.view.selectiontoolbar.ShowInfo; /** * The service to render the selection toolbar menus. * * @hide */ oneway interface ISelectionToolbarRenderService { void onConnected(in IBinder callback); void onShow(int callingUid, in ShowInfo showInfo, in ISelectionToolbarCallback callback); void onHide(long widgetToken); void onDismiss(int callingUid, long widgetToken); } core/java/android/service/selectiontoolbar/ISelectionToolbarRenderServiceCallback.aidl 0 → 100644 +28 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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 android.service.selectiontoolbar; import android.os.IBinder; /** * The interface from the SelectionToolbarRenderService to the system. * * @hide */ oneway interface ISelectionToolbarRenderServiceCallback { void transferTouch(in IBinder source, in IBinder target); } Loading
core/java/android/app/SystemServiceRegistry.java +13 −0 Original line number Diff line number Diff line Loading @@ -270,6 +270,8 @@ import android.view.contentcapture.ContentCaptureManager; import android.view.contentcapture.IContentCaptureManager; import android.view.displayhash.DisplayHashManager; import android.view.inputmethod.InputMethodManager; import android.view.selectiontoolbar.ISelectionToolbarManager; import android.view.selectiontoolbar.SelectionToolbarManager; import android.view.textclassifier.TextClassificationManager; import android.view.textservice.TextServicesManager; import android.view.translation.ITranslationManager; Loading Loading @@ -422,6 +424,17 @@ public final class SystemServiceRegistry { return new TextClassificationManager(ctx); }}); registerService(Context.SELECTION_TOOLBAR_SERVICE, SelectionToolbarManager.class, new CachedServiceFetcher<SelectionToolbarManager>() { @Override public SelectionToolbarManager createService(ContextImpl ctx) throws ServiceNotFoundException { IBinder b = ServiceManager.getServiceOrThrow( Context.SELECTION_TOOLBAR_SERVICE); return new SelectionToolbarManager( ISelectionToolbarManager.Stub.asInterface(b)); }}); registerService(Context.FONT_SERVICE, FontManager.class, new CachedServiceFetcher<FontManager>() { @Override Loading
core/java/android/service/selectiontoolbar/DefaultSelectionToolbarRenderService.java 0 → 100644 +154 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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 android.service.selectiontoolbar; import static android.view.selectiontoolbar.ISelectionToolbarCallback.ERROR_DO_NOT_ALLOW_MULTIPLE_TOOL_BAR; import static android.view.selectiontoolbar.SelectionToolbarManager.NO_TOOLBAR_ID; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.view.selectiontoolbar.ShowInfo; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.UUID; /** * The default implementation of {@link SelectionToolbarRenderService}. * * <p><b>NOTE:</b> The requests are handled on the service main thread. * * @hide */ // TODO(b/214122495): fix class not found then move to system service folder public final class DefaultSelectionToolbarRenderService extends SelectionToolbarRenderService { private static final String TAG = "DefaultSelectionToolbarRenderService"; // TODO(b/215497659): handle remove if the client process dies. // Only show one toolbar, dismiss the old ones and remove from cache // Maps uid -> (widget token, toolbar instance) private final SparseArray<Pair<Long, RemoteSelectionToolbar>> mToolbarCache = new SparseArray<>(); /** * Only allow one package to create one toolbar. */ private boolean isToolbarShown(int uid, ShowInfo showInfo) { if (showInfo.widgetToken != NO_TOOLBAR_ID) { return true; } return mToolbarCache.contains(uid); } @Override public void onShow(int callingUid, ShowInfo showInfo, SelectionToolbarRenderService.RemoteCallbackWrapper callbackWrapper) { if (isToolbarShown(callingUid, showInfo)) { Slog.e(TAG, "Do not allow multiple toolbar for the app."); callbackWrapper.onError(ERROR_DO_NOT_ALLOW_MULTIPLE_TOOL_BAR); return; } long widgetToken = showInfo.widgetToken == NO_TOOLBAR_ID ? UUID.randomUUID().getMostSignificantBits() : showInfo.widgetToken; if (mToolbarCache.indexOfKey(callingUid) < 0) { RemoteSelectionToolbar toolbar = new RemoteSelectionToolbar(this, widgetToken, showInfo, callbackWrapper, this::transferTouch); mToolbarCache.put(callingUid, new Pair<>(widgetToken, toolbar)); } Slog.v(TAG, "onShow() for " + widgetToken); Pair<Long, RemoteSelectionToolbar> toolbarPair = mToolbarCache.get(callingUid); if (toolbarPair.first == widgetToken) { toolbarPair.second.show(showInfo); } else { Slog.w(TAG, "onShow() for unknown " + widgetToken); } } @Override public void onHide(long widgetToken) { RemoteSelectionToolbar toolbar = getRemoteSelectionToolbarByToken(widgetToken); if (toolbar != null) { Slog.v(TAG, "onHide() for " + widgetToken); toolbar.hide(widgetToken); } } @Override public void onDismiss(long widgetToken) { RemoteSelectionToolbar toolbar = getRemoteSelectionToolbarByToken(widgetToken); if (toolbar != null) { Slog.v(TAG, "onDismiss() for " + widgetToken); toolbar.dismiss(widgetToken); removeRemoteSelectionToolbarByTokenLocked(widgetToken); } } @Override public void onToolbarShowTimeout(int callingUid) { Slog.w(TAG, "onToolbarShowTimeout for callingUid = " + callingUid); Pair<Long, RemoteSelectionToolbar> toolbarPair = mToolbarCache.get(callingUid); if (toolbarPair != null) { RemoteSelectionToolbar remoteToolbar = toolbarPair.second; remoteToolbar.dismiss(toolbarPair.first); remoteToolbar.onToolbarShowTimeout(); mToolbarCache.remove(callingUid); } } private RemoteSelectionToolbar getRemoteSelectionToolbarByToken(long widgetToken) { for (int i = 0; i < mToolbarCache.size(); i++) { Pair<Long, RemoteSelectionToolbar> toolbarPair = mToolbarCache.valueAt(i); if (toolbarPair.first == widgetToken) { return toolbarPair.second; } } return null; } private void removeRemoteSelectionToolbarByTokenLocked(long widgetToken) { for (int i = 0; i < mToolbarCache.size(); i++) { Pair<Long, RemoteSelectionToolbar> toolbarPair = mToolbarCache.valueAt(i); if (toolbarPair.first == widgetToken) { mToolbarCache.remove(mToolbarCache.keyAt(i)); return; } } } @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { int size = mToolbarCache.size(); pw.print("number selectionToolbar: "); pw.println(size); String pfx = " "; for (int i = 0; i < size; i++) { pw.print("#"); pw.println(i); int callingUid = mToolbarCache.keyAt(i); pw.print(pfx); pw.print("callingUid: "); pw.println(callingUid); Pair<Long, RemoteSelectionToolbar> toolbarPair = mToolbarCache.valueAt(i); RemoteSelectionToolbar selectionToolbar = toolbarPair.second; pw.print(pfx); pw.print("selectionToolbar: "); selectionToolbar.dump(pfx, pw); pw.println(); } } }
core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java 0 → 100644 +89 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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 android.service.selectiontoolbar; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Rect; import android.os.IBinder; import android.util.Log; import android.view.MotionEvent; import android.widget.LinearLayout; import java.io.PrintWriter; /** * This class is the root view for the selection toolbar. It is responsible for * detecting the click on the item and to also transfer input focus to the application. * * @hide */ @SuppressLint("ViewConstructor") public class FloatingToolbarRoot extends LinearLayout { private static final boolean DEBUG = false; private static final String TAG = "FloatingToolbarRoot"; private final IBinder mTargetInputToken; private final SelectionToolbarRenderService.TransferTouchListener mTransferTouchListener; private final Rect mContentRect = new Rect(); private int mLastDownX = -1; private int mLastDownY = -1; public FloatingToolbarRoot(Context context, IBinder targetInputToken, SelectionToolbarRenderService.TransferTouchListener transferTouchListener) { super(context); mTargetInputToken = targetInputToken; mTransferTouchListener = transferTouchListener; setFocusable(false); } /** * Sets the Rect that shows the selection toolbar content. */ public void setContentRect(Rect contentRect) { mContentRect.set(contentRect); } @Override @SuppressLint("ClickableViewAccessibility") public boolean dispatchTouchEvent(MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { mLastDownX = (int) event.getX(); mLastDownY = (int) event.getY(); if (DEBUG) { Log.d(TAG, "downX=" + mLastDownX + " downY=" + mLastDownY); } // TODO(b/215497659): Check FLAG_WINDOW_IS_PARTIALLY_OBSCURED if (!mContentRect.contains(mLastDownX, mLastDownY)) { if (DEBUG) { Log.d(TAG, "Transfer touch focus to application."); } mTransferTouchListener.onTransferTouch(getViewRootImpl().getInputToken(), mTargetInputToken); } } return super.dispatchTouchEvent(event); } void dump(String prefix, PrintWriter pw) { pw.print(prefix); pw.println("FloatingToolbarRoot:"); pw.print(prefix + " "); pw.print("last down X: "); pw.println(mLastDownX); pw.print(prefix + " "); pw.print("last down Y: "); pw.println(mLastDownY); } }
core/java/android/service/selectiontoolbar/ISelectionToolbarRenderService.aidl 0 → 100644 +32 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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 android.service.selectiontoolbar; import android.view.selectiontoolbar.ISelectionToolbarCallback; import android.view.selectiontoolbar.ShowInfo; /** * The service to render the selection toolbar menus. * * @hide */ oneway interface ISelectionToolbarRenderService { void onConnected(in IBinder callback); void onShow(int callingUid, in ShowInfo showInfo, in ISelectionToolbarCallback callback); void onHide(long widgetToken); void onDismiss(int callingUid, long widgetToken); }
core/java/android/service/selectiontoolbar/ISelectionToolbarRenderServiceCallback.aidl 0 → 100644 +28 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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 android.service.selectiontoolbar; import android.os.IBinder; /** * The interface from the SelectionToolbarRenderService to the system. * * @hide */ oneway interface ISelectionToolbarRenderServiceCallback { void transferTouch(in IBinder source, in IBinder target); }