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

Commit 4b57c5ea authored by Erfan Norozi's avatar Erfan Norozi
Browse files

Allow customization of ResolverActivity via RRO

* Add DismissableView to somewhat decouple ResolverActivity and ResolverDrawerLayout
* Add config_resolverActivityFilterLastUsed so filterLastUsed (and in turn layout with default) can be disabled.
* Add null checks and instanceof checks in ResolverActivity to break the assumption that the root container is always ResolverDrawerLayout
* Replace swiping up on contentPanel with swiping up in title_container. This is necessary to prevent a dialog container being dismissed.

Bug: 417351080
Test: atest com.android.internal.app.ResolverActivityTest
Flag: EXEMPT mechanical refactor

Change-Id: Ib5c2210717c0368464082e5615de000745259ee7
parent fc762204
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -54,7 +54,7 @@ public class AccessibilityButtonChooserActivity extends Activity {

        final ResolverDrawerLayout rdl = findViewById(R.id.contentPanel);
        if (rdl != null) {
            rdl.setOnDismissedListener(this::finish);
            rdl.setOnDismissListener(this::finish);
        }

        final AccessibilityManager accessibilityManager =
+47 −37
Original line number Diff line number Diff line
@@ -125,6 +125,7 @@ import com.android.internal.content.PackageMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.DismissableView;
import com.android.internal.widget.ResolverDrawerLayout;
import com.android.internal.widget.ViewPager;

@@ -169,6 +170,7 @@ public class ResolverActivity extends Activity implements
    private int mDefaultTitleResId;
    // Expected to be true if this object is ResolverActivity or is ResolverWrapperActivity.
    private final boolean mIsIntentPicker;
    private View mContainer;

    // Whether this activity was instantiated with a specialized constructor that predefines a list
    // of resolutions to be displayed for the target intent (as in, e.g., the NFC use case).
@@ -177,6 +179,8 @@ public class ResolverActivity extends Activity implements
    // Whether or not this activity supports choosing a default handler for the intent.
    @VisibleForTesting
    protected boolean mSupportsAlwaysUseOption;
    // TODO(b/417351080): mResolverDrawerLayout is only used in ChooserActivity. Usage should be
    // refactored to use mContainer.
    protected ResolverDrawerLayout mResolverDrawerLayout;
    @UnsupportedAppUsage
    protected PackageManager mPm;
@@ -465,9 +469,10 @@ public class ResolverActivity extends Activity implements
        // We also turn it off when clonedProfile is present on the device, because we might have
        // different "last chosen" activities in the different profiles, and PackageManager doesn't
        // provide any more information to help us select between them.
        boolean filterLastUsed = mSupportsAlwaysUseOption && !isVoiceInteraction()
                && !shouldShowTabs() && !hasCloneProfile();
        mMultiProfilePagerAdapter = createMultiProfilePagerAdapter(initialIntents, rList, filterLastUsed);
        boolean filterLastUsed = filterLastUsedConfig() && mSupportsAlwaysUseOption
                && !isVoiceInteraction() && !shouldShowTabs() && !hasCloneProfile();
        mMultiProfilePagerAdapter =
                createMultiProfilePagerAdapter(initialIntents, rList, filterLastUsed);
        if (configureContentView()) {
            return;
        }
@@ -484,15 +489,19 @@ public class ResolverActivity extends Activity implements

        mRegistered = true;

        final ResolverDrawerLayout rdl = findViewById(R.id.contentPanel);
        if (rdl != null) {
            rdl.setOnDismissedListener(new ResolverDrawerLayout.OnDismissedListener() {
                @Override
                public void onDismissed() {
                    finish();
        mContainer = findViewById(R.id.contentPanel);
        if (mContainer != null) {
            // TODO(b/417351080): The flags and insets are the responsibility of the container
            // and should be moved to the container implementation.
            mContainer.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
            mContainer.setOnApplyWindowInsetsListener(this::onApplyWindowInsets);

            if (mContainer instanceof DismissableView container) {
                container.setOnDismissListener(this::finish);
            }
            });

            if (mContainer instanceof final ResolverDrawerLayout rdl) {
                boolean hasTouchScreen = getPackageManager()
                        .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN);

@@ -500,10 +509,6 @@ public class ResolverActivity extends Activity implements
                    rdl.setCollapsed(false);
                }

            rdl.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
            rdl.setOnApplyWindowInsetsListener(this::onApplyWindowInsets);

                mResolverDrawerLayout = rdl;

                for (int i = 0, size = mMultiProfilePagerAdapter.getCount(); i < size; i++) {
@@ -514,6 +519,7 @@ public class ResolverActivity extends Activity implements
                    }
                }
            }
        }

        mProfileView = findViewById(R.id.profile_button);
        if (mProfileView != null) {
@@ -529,6 +535,10 @@ public class ResolverActivity extends Activity implements
                        + (categories != null ? Arrays.toString(categories.toArray()) : ""));
    }

    protected boolean filterLastUsedConfig() {
        return getResources().getBoolean(R.bool.config_resolverActivityFilterLastUsed);
    }

    protected AbstractMultiProfilePagerAdapter createMultiProfilePagerAdapter(
            Intent[] initialIntents,
            List<ResolveInfo> rList,
@@ -922,8 +932,10 @@ public class ResolverActivity extends Activity implements
    protected WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
        mSystemWindowInsets = insets.getSystemWindowInsets();

        mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,
        if (mContainer != null) {
            mContainer.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,
                    mSystemWindowInsets.right, 0);
        }

        resetButtonBar();

@@ -953,8 +965,8 @@ public class ResolverActivity extends Activity implements
            updateIntentPickerPaddings();
        }

        if (mSystemWindowInsets != null) {
            mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,
        if (mSystemWindowInsets != null && mContainer != null) {
            mContainer.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,
                    mSystemWindowInsets.right, 0);
        }
    }
@@ -1430,8 +1442,7 @@ public class ResolverActivity extends Activity implements
        final ItemClickListener listener = new ItemClickListener();
        setupAdapterListView((ListView) mMultiProfilePagerAdapter.getActiveAdapterView(), listener);
        if (shouldShowTabs() && mIsIntentPicker) {
            final ResolverDrawerLayout rdl = findViewById(R.id.contentPanel);
            if (rdl != null) {
            if (findViewById(R.id.contentPanel) instanceof ResolverDrawerLayout rdl) {
                rdl.setMaxCollapsedHeight(getResources()
                        .getDimensionPixelSize(useLayoutWithDefault()
                                ? R.dimen.resolver_max_collapsed_height_with_default_with_tabs
@@ -2336,12 +2347,11 @@ public class ResolverActivity extends Activity implements
     * the screen.
     */
    private void setButtonBarIgnoreOffset(boolean ignoreOffset) {
        View buttonBarContainer = findViewById(R.id.button_bar_container);
        if (buttonBarContainer != null) {
            ResolverDrawerLayout.LayoutParams layoutParams =
                    (ResolverDrawerLayout.LayoutParams) buttonBarContainer.getLayoutParams();
            layoutParams.ignoreOffset = ignoreOffset;
            buttonBarContainer.setLayoutParams(layoutParams);
        View container = findViewById(R.id.button_bar_container);
        if (container != null
                && container.getLayoutParams() instanceof ResolverDrawerLayout.LayoutParams lp) {
            lp.ignoreOffset = ignoreOffset;
            container.setLayoutParams(lp);
        }
    }

+40 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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 com.android.internal.widget;

/**
 * Views that implement DismissableView allow the parent activity to listen for a dismiss event
 * (e.g. when the user clicks outside a bottom sheet container).
 */
public interface DismissableView {
    /**
     * Sets a dismiss callback.
     */
    void setOnDismissListener(OnDismissListener listener);


    /**
     * Listener for view dismiss events.
     */
    interface OnDismissListener {
        /**
         * Callback when the view is dismissed by the user.
         */
        void onDismiss();
    }
}
+5 −14
Original line number Diff line number Diff line
@@ -50,7 +50,7 @@ import com.android.internal.R;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;

public class ResolverDrawerLayout extends ViewGroup {
public class ResolverDrawerLayout extends ViewGroup implements DismissableView {
    private static final String TAG = "ResolverDrawerLayout";
    private MetricsLogger mMetricsLogger;

@@ -117,7 +117,7 @@ public class ResolverDrawerLayout extends ViewGroup {

    private Drawable mScrollIndicatorDrawable;

    private OnDismissedListener mOnDismissedListener;
    private OnDismissListener mOnDismissedListener;
    private RunOnDismissedListener mRunOnDismissedListener;
    private OnCollapsedChangedListener mOnCollapsedChangedListener;

@@ -311,7 +311,8 @@ public class ResolverDrawerLayout extends ViewGroup {
                + mCollapsibleHeightReserved;
    }

    public void setOnDismissedListener(OnDismissedListener listener) {
    @Override
    public void setOnDismissListener(OnDismissListener listener) {
        mOnDismissedListener = listener;
    }

@@ -651,7 +652,7 @@ public class ResolverDrawerLayout extends ViewGroup {

    void dispatchOnDismissed() {
        if (mOnDismissedListener != null) {
            mOnDismissedListener.onDismissed();
            mOnDismissedListener.onDismiss();
        }
        if (mRunOnDismissedListener != null) {
            removeCallbacks(mRunOnDismissedListener);
@@ -1258,16 +1259,6 @@ public class ResolverDrawerLayout extends ViewGroup {
        };
    }

    /**
     * Listener for sheet dismissed events.
     */
    public interface OnDismissedListener {
        /**
         * Callback when the sheet is dismissed by the user.
         */
        void onDismissed();
    }

    /**
     * Listener for sheet collapsed / expanded events.
     */
+5 −0
Original line number Diff line number Diff line
@@ -7625,6 +7625,11 @@
    <!-- Maximum number of devices that allows for audio sharing. -->
    <integer name="config_audio_sharing_maximum_sinks">2</integer>

    <!-- Whether ResolverActivity should highlight the last used app.
         When false, ResolverActivity shows a list of apps instead of highlighting the last used app
         and displaying other apps separately. -->
    <bool name="config_resolverActivityFilterLastUsed">true</bool>

    <!--For App Function, this configuration lists package names grouped as "Device Settings".
        Packages in this list share the same App Function access settings. In the UI, they are
        presented as a single "Device Settings" item rather than individually. -->
Loading