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

Commit 9b378dd3 authored by Mehdi Alizadeh's avatar Mehdi Alizadeh Committed by android-build-merger
Browse files

Merge "Disables the Gesture nav option if 3P launcher is default" into qt-dev

am: cad67cdc

Change-Id: Icba26f525b33f2a09a014fb88a4076362a289681
parents c816aa26 cad67cdc
Loading
Loading
Loading
Loading
+106 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
  ~ Copyright (C) 2019 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
  -->
<!-- This file is copied from preference_radio.xml with modification to
     support an extra clickable icon on the opposite side horizontally -->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:settings="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?android:attr/selectableItemBackground"
    android:gravity="center_vertical"
    android:minHeight="?android:attr/listPreferredItemHeightSmall"
    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">

    <LinearLayout
        android:id="@android:id/widget_frame"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:gravity="center"
        android:minWidth="56dp"
        android:layout_marginEnd="16dp"
        android:orientation="vertical" />

    <LinearLayout
        android:id="@+id/icon_frame"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:minWidth="32dp"
        android:orientation="horizontal"
        android:layout_marginEnd="16dp"
        android:paddingTop="4dp"
        android:paddingBottom="4dp">
        <androidx.preference.internal.PreferenceImageView
            android:id="@android:id/icon"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            settings:maxWidth="@dimen/secondary_app_icon_size"
            settings:maxHeight="@dimen/secondary_app_icon_size" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="vertical"
        android:paddingTop="16dp"
        android:paddingBottom="16dp">

        <TextView android:id="@android:id/title"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:singleLine="true"
                  android:textAppearance="@style/TextAppearance.TileTitle"
                  android:ellipsize="marquee"
                  android:fadingEdge="horizontal" />

        <TextView android:id="@android:id/summary"
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  android:layout_weight="1"
                  android:textAppearance="@style/TextAppearance.Small"
                  android:textAlignment="viewStart"
                  android:textColor="?android:attr/textColorSecondary" />
    </LinearLayout>
    <LinearLayout
        android:id="@+id/radio_extra_widget_container"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:orientation="horizontal"
        android:gravity="center_vertical">
        <View
            android:id="@+id/radio_extra_widget_divider"
            android:layout_width="@dimen/vertical_divider_width"
            android:layout_height="match_parent"
            android:layout_marginTop="36dp"
            android:layout_marginBottom="36dp"
            android:layout_marginStart="8dp"
            android:background="?android:attr/dividerVertical" />
        <ImageView
            android:id="@+id/radio_extra_widget"
            android:layout_width="wrap_content"
            android:layout_height="fill_parent"
            android:paddingStart="16dp"
            android:paddingEnd="16dp"
            android:src="@drawable/ic_settings_about"
            android:contentDescription="@string/information_label"
            android:layout_gravity="center"
            android:background="?android:attr/selectableItemBackground" />
    </LinearLayout>

</LinearLayout>
+59 −5
Original line number Diff line number Diff line
@@ -21,6 +21,10 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVE
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY;

import static com.android.settings.widget.RadioButtonPreferenceWithExtraWidget.EXTRA_WIDGET_VISIBILITY_GONE;
import static com.android.settings.widget.RadioButtonPreferenceWithExtraWidget.EXTRA_WIDGET_VISIBILITY_INFO;

import android.app.AlertDialog;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.SharedPreferences;
@@ -29,7 +33,6 @@ import android.graphics.drawable.Drawable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.SearchIndexableResource;
import android.view.View;

import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceScreen;
@@ -41,6 +44,7 @@ import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Indexable;
import com.android.settings.widget.RadioButtonPickerFragment;
import com.android.settings.widget.RadioButtonPreference;
import com.android.settings.widget.RadioButtonPreferenceWithExtraWidget;
import com.android.settings.widget.VideoPreference;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.widget.CandidateInfo;
@@ -94,8 +98,25 @@ public class SystemNavigationGestureSettings extends RadioButtonPickerFragment {
    }

    @Override
    protected void addStaticPreferences(PreferenceScreen screen) {
    public void updateCandidates() {
        final String defaultKey = getDefaultKey();
        final String systemDefaultKey = getSystemDefaultKey();
        final PreferenceScreen screen = getPreferenceScreen();
        screen.removeAll();
        screen.addPreference(mVideoPreference);

        final List<? extends CandidateInfo> candidateList = getCandidates();
        if (candidateList == null) {
            return;
        }
        for (CandidateInfo info : candidateList) {
            RadioButtonPreferenceWithExtraWidget pref =
                    new RadioButtonPreferenceWithExtraWidget(getPrefContext());
            bindPreference(pref, info.getKey(), info, defaultKey);
            bindPreferenceExtra(pref, info.getKey(), info, defaultKey, systemDefaultKey);
            screen.addPreference(pref);
        }
        mayCheckOnlyRadioButton();
    }

    @Override
@@ -135,6 +156,13 @@ public class SystemNavigationGestureSettings extends RadioButtonPickerFragment {

    @Override
    protected boolean setDefaultKey(String key) {
        final Context c = getContext();
        if (key == KEY_SYSTEM_NAV_GESTURAL &&
                !SystemNavigationPreferenceController.isGestureNavSupportedByDefaultLauncher(c)) {
            // This should not happen since the preference is disabled. Return to be safe.
            return false;
        }

        setCurrentSystemNavigationMode(mOverlayManager, key);
        setIllustrationVideo(mVideoPreference, key);
        return true;
@@ -196,10 +224,36 @@ public class SystemNavigationGestureSettings extends RadioButtonPickerFragment {
    @Override
    public void bindPreferenceExtra(RadioButtonPreference pref,
            String key, CandidateInfo info, String defaultKey, String systemDefaultKey) {
        if (info instanceof NavModeCandidateInfo) {
        if (!(info instanceof NavModeCandidateInfo)
                || !(pref instanceof RadioButtonPreferenceWithExtraWidget)) {
            return;
        }

        pref.setSummary(((NavModeCandidateInfo) info).loadSummary());
            pref.setAppendixVisibility(View.GONE);

        RadioButtonPreferenceWithExtraWidget p = (RadioButtonPreferenceWithExtraWidget) pref;
        if (info.getKey() == KEY_SYSTEM_NAV_GESTURAL
                && !SystemNavigationPreferenceController.isGestureNavSupportedByDefaultLauncher(
                        getContext())) {
            p.setEnabled(false);
            p.setExtraWidgetVisibility(EXTRA_WIDGET_VISIBILITY_INFO);
            p.setExtraWidgetOnClickListener((v) -> {
                showGestureNavDisabledDialog();
            });
        } else {
            p.setExtraWidgetVisibility(EXTRA_WIDGET_VISIBILITY_GONE);
        }
    }

    private void showGestureNavDisabledDialog() {
        final String defaultHomeAppName = SystemNavigationPreferenceController
                .getDefaultHomeAppName(getContext());
        final String message = getString(R.string.gesture_not_supported_dialog_message,
                defaultHomeAppName);
        AlertDialog d = new AlertDialog.Builder(getContext())
                .setMessage(message)
                .setPositiveButton(R.string.okay, null)
                .show();
    }

    static class NavModeCandidateInfo extends CandidateInfo {
+30 −0
Original line number Diff line number Diff line
@@ -22,11 +22,14 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;

import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;

import java.util.ArrayList;

public class SystemNavigationPreferenceController extends BasePreferenceController {

    static final String PREF_KEY_SYSTEM_NAVIGATION = "gesture_system_navigation";
@@ -98,4 +101,31 @@ public class SystemNavigationPreferenceController extends BasePreferenceControll
        return NAV_BAR_MODE_GESTURAL == context.getResources().getInteger(
                com.android.internal.R.integer.config_navBarInteractionMode);
    }

    static boolean isGestureNavSupportedByDefaultLauncher(Context context) {
        final ComponentName cn = context.getPackageManager().getHomeActivities(new ArrayList<>());
        if (cn == null) {
            // There is no default home app set for the current user, don't make any changes yet.
            return true;
        }
        ComponentName recentsComponentName = ComponentName.unflattenFromString(context.getString(
                com.android.internal.R.string.config_recentsComponentName));
        return recentsComponentName.getPackageName().equals(cn.getPackageName());
    }

    static String getDefaultHomeAppName(Context context) {
        final PackageManager pm = context.getPackageManager();
        final ComponentName cn = pm.getHomeActivities(new ArrayList<>());
        if (cn != null) {
            try {
                ApplicationInfo ai = pm.getApplicationInfo(cn.getPackageName(), 0);
                if (ai != null) {
                    return pm.getApplicationLabel(ai).toString();
                }
            } catch (final PackageManager.NameNotFoundException e) {
                // Do nothing
            }
        }
        return "";
    }
}
+79 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.settings.widget;

import android.content.Context;
import android.view.View;
import android.widget.ImageView;

import androidx.preference.PreferenceViewHolder;

import com.android.settings.R;

public class RadioButtonPreferenceWithExtraWidget extends RadioButtonPreference {
    public static final int EXTRA_WIDGET_VISIBILITY_GONE = 0;
    public static final int EXTRA_WIDGET_VISIBILITY_INFO = 1;

    private View mExtraWidgetDivider;
    private ImageView mExtraWidget;

    private int mExtraWidgetVisibility = EXTRA_WIDGET_VISIBILITY_GONE;
    private View.OnClickListener mExtraWidgetOnClickListener;

    public RadioButtonPreferenceWithExtraWidget(Context context) {
        super(context, null);
        setLayoutResource(R.layout.preference_radio_with_extra_widget);
    }

    @Override
    public void onBindViewHolder(PreferenceViewHolder view) {
        super.onBindViewHolder(view);

        mExtraWidget = (ImageView) view.findViewById(R.id.radio_extra_widget);
        mExtraWidgetDivider = view.findViewById(R.id.radio_extra_widget_divider);
        setExtraWidgetVisibility(mExtraWidgetVisibility);

        if (mExtraWidgetOnClickListener != null) {
            setExtraWidgetOnClickListener(mExtraWidgetOnClickListener);
        }
    }

    public void setExtraWidgetVisibility(int visibility) {
        mExtraWidgetVisibility = visibility;
        if (mExtraWidget == null || mExtraWidgetDivider == null) {
            return;
        }

        if (visibility == EXTRA_WIDGET_VISIBILITY_GONE) {
            mExtraWidget.setClickable(false);
            mExtraWidget.setVisibility(View.GONE);
            mExtraWidgetDivider.setVisibility(View.GONE);
        } else {
            mExtraWidget.setClickable(true);
            mExtraWidget.setVisibility(View.VISIBLE);
            mExtraWidgetDivider.setVisibility(View.VISIBLE);
        }
    }

    public void setExtraWidgetOnClickListener(View.OnClickListener listener) {
        mExtraWidgetOnClickListener = listener;
        if (mExtraWidget != null) {
            mExtraWidget.setEnabled(true);
            mExtraWidget.setOnClickListener(listener);
        }
    }
}
 No newline at end of file
+57 −0
Original line number Diff line number Diff line
@@ -24,10 +24,14 @@ import static com.android.settings.gestures.SystemNavigationPreferenceController

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.text.TextUtils;
@@ -39,6 +43,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@@ -53,9 +58,15 @@ public class SystemNavigationPreferenceControllerTest {
    private Context mContext;
    private ShadowPackageManager mPackageManager;

    @Mock
    private Context mMockContext;
    @Mock
    private PackageManager mMockPackageManager;

    private SystemNavigationPreferenceController mController;

    private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
    private static final String TEST_RECENTS_COMPONENT_NAME = "test.component.name/.testActivity";

    @Before
    public void setUp() {
@@ -69,6 +80,10 @@ public class SystemNavigationPreferenceControllerTest {

        mController = new SystemNavigationPreferenceController(mContext,
                PREF_KEY_SYSTEM_NAVIGATION);

        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
        when(mMockContext.getString(com.android.internal.R.string.config_recentsComponentName))
                .thenReturn(TEST_RECENTS_COMPONENT_NAME);
    }

    @After
@@ -166,4 +181,46 @@ public class SystemNavigationPreferenceControllerTest {
        assertThat(TextUtils.equals(mController.getSummary(), mContext.getText(
                com.android.settings.R.string.swipe_up_to_switch_apps_title))).isTrue();
    }

    @Test
    public void testIsGestureNavSupportedByDefaultLauncher_noDefaultLauncher() {
        when(mMockPackageManager.getHomeActivities(any())).thenReturn(null);
        assertThat(SystemNavigationPreferenceController
                .isGestureNavSupportedByDefaultLauncher(mMockContext)).isTrue();
    }

    @Test
    public void testIsGestureNavSupportedByDefaultLauncher_supported() {
        when(mMockPackageManager.getHomeActivities(any())).thenReturn(
                ComponentName.unflattenFromString(TEST_RECENTS_COMPONENT_NAME));
        assertThat(SystemNavigationPreferenceController
                .isGestureNavSupportedByDefaultLauncher(mMockContext)).isTrue();
    }

    @Test
    public void testIsGestureNavSupportedByDefaultLauncher_notSupported() {
        when(mMockPackageManager.getHomeActivities(any())).thenReturn(
                new ComponentName("unsupported", "launcher"));
        assertThat(SystemNavigationPreferenceController
                .isGestureNavSupportedByDefaultLauncher(mMockContext)).isFalse();
    }

    @Test
    public void testGetDefaultHomeAppName_noDefaultLauncher() {
        when(mMockPackageManager.getHomeActivities(any())).thenReturn(null);
        assertThat(SystemNavigationPreferenceController
                .getDefaultHomeAppName(mMockContext)).isEqualTo("");
    }

    @Test
    public void testGetDefaultHomeAppName_defaultLauncherExists() throws Exception {
        when(mMockPackageManager.getHomeActivities(any())).thenReturn(
                new ComponentName("supported", "launcher"));
        ApplicationInfo info = new ApplicationInfo();
        when(mMockPackageManager.getApplicationInfo("supported", 0)).thenReturn(info);
        when(mMockPackageManager.getApplicationLabel(info)).thenReturn("Test Home App");

        assertThat(SystemNavigationPreferenceController
                .getDefaultHomeAppName(mMockContext)).isEqualTo("Test Home App");
    }
}
 No newline at end of file
Loading