Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit db78cc2c authored by Joanne Chung's avatar Joanne Chung
Browse files

The service implementatoin of selection toolbar

We clone most the render logic from LocalFloatingToolbarPopup to the
DefaultSelectionToolbarRenderService to render the menu items in the
system process. When the rendering is done, the system will notify
the application process events to let the application to position
the rendered content.

The changes also removes unused file SelctionContext. We punt the TC
part to the next release, the file is unnecessary now.

This change is only for the service implementation, the client side
implementation will be done on the next change.

Bug: 190030331
Bug: 205823018
Test: manual.

Merged-In: I3915e8165b4d371700cb5fb9e7eb23d46f39bd5c
Change-Id: I2e704432b1e149ce8dd6853fcad6850cf02b60bb
parent 7fd4ea6c
Loading
Loading
Loading
Loading
+92 −8
Original line number Original line Diff line number Diff line
@@ -16,12 +16,21 @@


package android.service.selectiontoolbar;
package android.service.selectiontoolbar;


import android.util.Log;
import static android.view.selectiontoolbar.SelectionToolbarManager.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 android.view.selectiontoolbar.ShowInfo;


import java.util.UUID;

/**
/**
 * The default implementation of {@link SelectionToolbarRenderService}.
 * The default implementation of {@link SelectionToolbarRenderService}.
 *
 *
 * <p><b>NOTE:<b/> The requests are handled on the service main thread.
 *
 *  @hide
 *  @hide
 */
 */
// TODO(b/214122495): fix class not found then move to system service folder
// TODO(b/214122495): fix class not found then move to system service folder
@@ -29,22 +38,97 @@ public final class DefaultSelectionToolbarRenderService extends SelectionToolbar


    private static final String TAG = "DefaultSelectionToolbarRenderService";
    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
    private final SparseArray<Pair<Long, RemoteSelectionToolbar>> mToolbarCache =
            new SparseArray<>();

    /**
     * Only allow one package to create one toolbar.
     */
    private boolean canShowToolbar(int uid, ShowInfo showInfo) {
        if (showInfo.getWidgetToken() != NO_TOOLBAR_ID) {
            return true;
        }
        return mToolbarCache.indexOfKey(uid) < 0;
    }

    @Override
    @Override
    public void onShow(ShowInfo showInfo,
    public void onShow(int callingUid, ShowInfo showInfo,
            SelectionToolbarRenderService.RemoteCallbackWrapper callbackWrapper) {
            SelectionToolbarRenderService.RemoteCallbackWrapper callbackWrapper) {
        // TODO: Add implementation
        if (!canShowToolbar(callingUid, showInfo)) {
        Log.w(TAG, "onShow()");
            Slog.e(TAG, "Do not allow multiple toolbar for the app.");
            callbackWrapper.onError(ERROR_DO_NOT_ALLOW_MULTIPLE_TOOL_BAR);
            return;
        }
        long widgetToken = showInfo.getWidgetToken() == NO_TOOLBAR_ID
                ? UUID.randomUUID().getMostSignificantBits()
                : showInfo.getWidgetToken();

        if (mToolbarCache.indexOfKey(callingUid) < 0) {
            RemoteSelectionToolbar toolbar = new RemoteSelectionToolbar(this,
                    widgetToken, showInfo.getHostInputToken(),
                    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
    @Override
    public void onHide(long widgetToken) {
    public void onHide(long widgetToken) {
        // TODO: Add implementation
        RemoteSelectionToolbar toolbar = getRemoteSelectionToolbarByTokenLocked(widgetToken);
        Log.w(TAG, "onHide()");
        if (toolbar != null) {
            Slog.v(TAG, "onHide() for " + widgetToken);
            toolbar.hide(widgetToken);
        }
    }
    }


    @Override
    @Override
    public void onDismiss(long widgetToken) {
    public void onDismiss(long widgetToken) {
        // TODO: Add implementation
        RemoteSelectionToolbar toolbar = getRemoteSelectionToolbarByTokenLocked(widgetToken);
        Log.w(TAG, "onDismiss()");
        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 getRemoteSelectionToolbarByTokenLocked(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;
            }
        }
    }
}
+61 −0
Original line number Original line 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.os.IBinder;
import android.view.MotionEvent;
import android.widget.LinearLayout;

/**
 * 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 final IBinder mTargetInputToken;
    private final SelectionToolbarRenderService.TransferTouchListener mTransferTouchListener;
    private float mDownX;
    private float mDownY;

    public FloatingToolbarRoot(Context context, IBinder targetInputToken,
            SelectionToolbarRenderService.TransferTouchListener transferTouchListener) {
        super(context);
        mTargetInputToken = targetInputToken;
        mTransferTouchListener = transferTouchListener;
        setFocusable(false);
    }

    @Override
    @SuppressLint("ClickableViewAccessibility")
    public boolean dispatchTouchEvent(MotionEvent event) {
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN: {
                mDownX = event.getX();
                mDownY = event.getY();
                // TODO: Check x, y if we need to transfer touch focus to application
                //mTransferTouchListener.onTransferTouch(getViewRootImpl().getInputToken(),
                //        mTargetInputToken);
            }
        }
        return super.dispatchTouchEvent(event);
    }
}
+3 −2
Original line number Original line Diff line number Diff line
@@ -25,7 +25,8 @@ import android.view.selectiontoolbar.ShowInfo;
 * @hide
 * @hide
 */
 */
oneway interface ISelectionToolbarRenderService {
oneway interface ISelectionToolbarRenderService {
    void onShow(in ShowInfo showInfo, in ISelectionToolbarCallback callback);
    void onConnected(in IBinder callback);
    void onShow(int callingUid, in ShowInfo showInfo, in ISelectionToolbarCallback callback);
    void onHide(long widgetToken);
    void onHide(long widgetToken);
    void onDismiss(long widgetToken);
    void onDismiss(int callingUid, long widgetToken);
}
}
+9 −3
Original line number Original line Diff line number Diff line
/*
/*
 * Copyright (C) 2021 The Android Open Source Project
 * Copyright (C) 2022 The Android Open Source Project
 *
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * you may not use this file except in compliance with the License.
@@ -14,9 +14,15 @@
 * limitations under the License.
 * limitations under the License.
 */
 */


package android.view.selectiontoolbar;
package android.service.selectiontoolbar;

import android.os.IBinder;


/**
/**
 * The interface from the SelectionToolbarRenderService to the system.
 *
 * @hide
 * @hide
 */
 */
parcelable SelectionContext;
oneway interface ISelectionToolbarRenderServiceCallback {
    void transferTouch(in IBinder source, in IBinder target);
}
+181 −451

File changed.

Preview size limit exceeded, changes collapsed.

Loading