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

Commit 1cb0765f authored by ryanlwlin's avatar ryanlwlin
Browse files

[Refactor] extract multi-display module

DisplayIdIndexSupplier is a multi-display module extracted from
ModeSwitchesController. It encapsulates a SparseArray and a
Supplier to provide the convenience for multi-display feature
support.

Bug: 158277851
Test: ModeSwitchesControllerTest, DisplayIdIndexSupplierTest
Change-Id: Ida7f6f812826b6c389ae93aa87281a5a38ad876e
parent b7f02a3e
Loading
Loading
Loading
Loading
+81 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.systemui.accessibility;

import android.annotation.Nullable;
import android.hardware.display.DisplayManager;
import android.util.SparseArray;
import android.view.Display;

import androidx.annotation.NonNull;

/**
 * Supplies the instance with given display Id. It generates a new instance if the corresponding
 * one is not existed. It should run in single thread to avoid race conditions.
 *
 * @param <T> the type of results supplied by {@link #createInstance(Display)}.
 */
abstract class DisplayIdIndexSupplier<T> {

    private final SparseArray<T> mSparseArray = new SparseArray<>();
    private final DisplayManager mDisplayManager;

    /**
     * @param displayManager DisplayManager
     */
    DisplayIdIndexSupplier(DisplayManager displayManager) {
        mDisplayManager = displayManager;
    }

    /**
     * @param displayId the logical display Id
     * @return {@code null} if the given display id is invalid
     */
    @Nullable
    public T get(int displayId) {
        T instance = mSparseArray.get(displayId);
        if (instance != null) {
            return instance;
        }
        final Display display = mDisplayManager.getDisplay(displayId);
        if (display == null) {
            return null;
        }
        instance = createInstance(display);
        mSparseArray.put(displayId, instance);
        return instance;
    }

    @NonNull
    protected abstract T createInstance(Display display);

    /**
     * Removes the instance with given display Id.
     *
     * @param displayId the logical display id
     */
    public void remove(int displayId) {
        mSparseArray.remove(displayId);
    }

    /**
     * Clears all elements.
     */
    public void clear() {
        mSparseArray.clear();
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import com.android.systemui.R;
/**
 * Shows/hides a {@link android.widget.ImageView} on the screen and changes the values of
 * {@link Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE} when the UI is toggled.
 * The button UI would automatically be dismissed after displaying for a period of time.
 */
class MagnificationModeSwitch {

+44 −41
Original line number Diff line number Diff line
@@ -19,31 +19,33 @@ package com.android.systemui.accessibility;
import android.annotation.MainThread;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;

import com.android.internal.annotations.VisibleForTesting;

import javax.inject.Singleton;

/**
 * Class to control magnification mode switch button. Shows the button UI when both full-screen
 * and window magnification mode are capable, and when the magnification scale is changed. And
 * the button UI would automatically be dismissed after displaying for a period of time.
 * A class to control {@link MagnificationModeSwitch}. It should show the button UI with following
 * conditions:
 * <ol>
 *   <li> Both full-screen and window magnification mode are capable.</li>
 *   <li> The magnification scale is changed by a user.</li>
 * <ol>
 */
@Singleton
public class ModeSwitchesController {

    private static final String TAG = "ModeSwitchesController";

    private final Context mContext;
    private final DisplayManager mDisplayManager;

    private final SparseArray<MagnificationModeSwitch> mDisplaysToSwitches =
            new SparseArray<>();
    private final SwitchSupplier mSwitchSupplier;

    public ModeSwitchesController(Context context) {
        mContext = context;
        mDisplayManager = mContext.getSystemService(DisplayManager.class);
        mSwitchSupplier = new SwitchSupplier(context,
                context.getSystemService(DisplayManager.class));
    }

    @VisibleForTesting
    ModeSwitchesController(SwitchSupplier switchSupplier) {
        mSwitchSupplier = switchSupplier;
    }

    /**
@@ -52,20 +54,17 @@ public class ModeSwitchesController {
     *
     * @param displayId The logical display id
     * @param mode      The magnification mode
     *
     * @see android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
     * @see android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
     */
    @MainThread
    void showButton(int displayId, int mode) {
        if (mDisplaysToSwitches.get(displayId) == null) {
        final MagnificationModeSwitch magnificationModeSwitch =
                    createMagnificationSwitchController(displayId);
                mSwitchSupplier.get(displayId);
        if (magnificationModeSwitch == null) {
            return;
        }
        }
        mDisplaysToSwitches.get(displayId).showButton(mode);
        magnificationModeSwitch.showButton(mode);
    }

    /**
@@ -74,30 +73,34 @@ public class ModeSwitchesController {
     * @param displayId The logical display id
     */
    void removeButton(int displayId) {
        if (mDisplaysToSwitches.get(displayId) == null) {
        final MagnificationModeSwitch magnificationModeSwitch =
                mSwitchSupplier.get(displayId);
        if (magnificationModeSwitch == null) {
            return;
        }
        mDisplaysToSwitches.get(displayId).removeButton();
        magnificationModeSwitch.removeButton();
    }

    private MagnificationModeSwitch createMagnificationSwitchController(int displayId) {
        if (mDisplayManager.getDisplay(displayId) == null) {
            Log.w(TAG, "createMagnificationSwitchController displayId is invalid.");
            return null;
        }
        final MagnificationModeSwitch
                magnificationModeSwitch = new MagnificationModeSwitch(
                getDisplayContext(displayId));
        mDisplaysToSwitches.put(displayId, magnificationModeSwitch);
        return magnificationModeSwitch;
    @VisibleForTesting
    static class SwitchSupplier extends DisplayIdIndexSupplier<MagnificationModeSwitch> {

        private final Context mContext;

        /**
         * @param context        Context
         * @param displayManager DisplayManager
         */
        SwitchSupplier(Context context, DisplayManager displayManager) {
            super(displayManager);
            mContext = context;
        }

    private Context getDisplayContext(int displayId) {
        final Display display = mDisplayManager.getDisplay(displayId);
        final Context context = (displayId == Display.DEFAULT_DISPLAY)
        @Override
        protected MagnificationModeSwitch createInstance(Display display) {
            final Context context = (display.getDisplayId() == Display.DEFAULT_DISPLAY)
                    ? mContext
                    : mContext.createDisplayContext(display);
        return context;
            return new MagnificationModeSwitch(context);
        }
    }

}
+79 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.systemui.accessibility;

import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;

import android.hardware.display.DisplayManager;
import android.testing.AndroidTestingRunner;
import android.view.Display;

import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;

import com.android.systemui.SysuiTestCase;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

@SmallTest
@RunWith(AndroidTestingRunner.class)
public class DisplayIdIndexSupplierTest extends SysuiTestCase {

    private DisplayIdIndexSupplier<Object> mDisplayIdIndexSupplier;

    @Before
    public void setUp() throws Exception {
        mDisplayIdIndexSupplier = new DisplayIdIndexSupplier(
                mContext.getSystemService(DisplayManager.class)) {

            @NonNull
            @Override
            protected Object createInstance(Display display) {
                return new Object();
            }
        };
    }

    @Test
    public void get_instanceIsNotNull() {
        Object object = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
        assertNotNull(object);
    }

    @Test
    public void get_removeExistedObject_newObject() {
        Object object = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
        mDisplayIdIndexSupplier.remove(Display.DEFAULT_DISPLAY);

        Object newObject = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);

        assertNotEquals(object, newObject);
    }

    @Test
    public void get_clearAllObjects_newObject() {
        Object object = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
        mDisplayIdIndexSupplier.clear();

        Object newObject = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);

        assertNotEquals(object, newObject);
    }
}
+14 −23
Original line number Diff line number Diff line
@@ -16,20 +16,13 @@

package com.android.systemui.accessibility;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.content.Context;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.Display;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowMetrics;

import androidx.test.filters.SmallTest;

@@ -38,45 +31,43 @@ import com.android.systemui.SysuiTestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
/** Tests the ModeSwitchesController. */
public class ModeSwitchesControllerTest extends SysuiTestCase {

    private WindowManager mWindowManager;
    @Mock
    private ModeSwitchesController.SwitchSupplier mSupplier;
    @Mock
    private MagnificationModeSwitch mModeSwitch;
    private ModeSwitchesController mModeSwitchesController;


    @Before
    public void setUp() {
        mWindowManager = mock(WindowManager.class);
        Display display = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
        when(mWindowManager.getDefaultDisplay()).thenReturn(display);
        WindowMetrics metrics = mContext.getSystemService(WindowManager.class)
                .getMaximumWindowMetrics();
        when(mWindowManager.getMaximumWindowMetrics()).thenReturn(metrics);
        mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
        mModeSwitchesController = new ModeSwitchesController(mContext);
        MockitoAnnotations.initMocks(this);
        when(mSupplier.get(anyInt())).thenReturn(mModeSwitch);
        mModeSwitchesController = new ModeSwitchesController(mSupplier);
    }

    @Test
    public void testShowButton() {
        mModeSwitchesController.showButton(Display.DEFAULT_DISPLAY,
                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
        verify(mWindowManager).addView(any(), any());

        verify(mModeSwitch).showButton(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
    }

    @Test
    public void testRemoveButton() {
        mModeSwitchesController.showButton(Display.DEFAULT_DISPLAY,
                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
        ArgumentCaptor<View> captor = ArgumentCaptor.forClass(View.class);
        verify(mWindowManager).addView(captor.capture(), any(WindowManager.LayoutParams.class));

        mModeSwitchesController.removeButton(Display.DEFAULT_DISPLAY);

        verify(mWindowManager).removeView(eq(captor.getValue()));
        verify(mModeSwitch).removeButton();
    }
}