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

Commit 2f639aba authored by Bill Lin's avatar Bill Lin
Browse files

Runtime apply overlay by ThemeOverlayManager

a.Runtime overlay package should not be static
  since it's unable to FOTA upgrade
b.Implement ThemeOverlayManager to manage overlay packages

Test: atest testOverlayPackagesForDocumentsUI_shouldBeNonStatic
      can ensure installed RRO for Docsui is Non-Static
Test: atest DocumentsUIGoogleTests
Test: atest OverlayableTest
Test: manual check UI visual

Fixes: 123734953
Bug: 131331107

Change-Id: Iafbf27cad03e7fcc38027b68e80a76947447499d
parent 508601ae
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static com.android.documentsui.base.SharedMinimal.DEBUG;
import static com.android.documentsui.base.State.MODE_GRID;

import android.content.Intent;
import android.content.om.OverlayManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
@@ -73,6 +74,7 @@ import com.android.documentsui.roots.ProvidersCache;
import com.android.documentsui.sidebar.RootsFragment;
import com.android.documentsui.sorting.SortController;
import com.android.documentsui.sorting.SortModel;
import com.android.documentsui.theme.ThemeOverlayManager;

import com.google.android.material.appbar.AppBarLayout;

@@ -101,6 +103,8 @@ public abstract class BaseActivity
    protected NavigationViewManager mNavigator;
    protected SortController mSortController;

    protected ThemeOverlayManager mThemeOverlayManager;

    private final List<EventListener> mEventListeners = new ArrayList<>();
    private final String mTag;

@@ -132,6 +136,9 @@ public abstract class BaseActivity
        // Record the time when onCreate is invoked for metric.
        mStartTime = new Date().getTime();

        mThemeOverlayManager = new ThemeOverlayManager(getSystemService(OverlayManager.class),
                getPackageName());
        mThemeOverlayManager.applyOverlays(true);
        // ToDo Create tool to check resource version before applyStyle for the theme
        // If version code is not match, we should reset overlay package to default,
        // in case Activity continueusly encounter resource not found exception
+82 −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.documentsui.theme;

import android.annotation.TargetApi;
import android.content.om.OverlayInfo;
import android.content.om.OverlayManager;
import android.os.Build;
import android.os.UserHandle;
import android.util.Log;

import java.util.List;

/**
 * ThemeOverlayManager manage runtime resource overlay packages of DocumentsUI
 */
public class ThemeOverlayManager {
    private static final String TAG = ThemeOverlayManager.class.getSimpleName();

    private static final boolean DEBUG = false;

    private OverlayManager mOverlayManager;
    private String mTargetPackageId;
    private UserHandle mUserHandle;

    @TargetApi(Build.VERSION_CODES.M)
    public ThemeOverlayManager(OverlayManager overlayManager, String targetPackageId) {
        mOverlayManager = overlayManager;
        mTargetPackageId = targetPackageId;
        mUserHandle = UserHandle.of(UserHandle.myUserId());
    }

    /**
     * Apply runtime overlay package
     *
     * @param enabled whether or not enable overlay package
     */
    public void applyOverlays(boolean enabled) {
        setOverlaysEnabled(getOverlayInfo(), enabled);
    }

    private List<OverlayInfo> getOverlayInfo() {
        if (mOverlayManager != null) {
            return mOverlayManager.getOverlayInfosForTarget(mTargetPackageId, mUserHandle);
        }
        return null;
    }

    private void setOverlaysEnabled(List<OverlayInfo> overlayInfos, boolean enabled) {
        if (mOverlayManager != null && overlayInfos != null) {
            for (OverlayInfo info : overlayInfos) {
                if (info.isEnabled() != enabled) {
                    setEnabled(info.getPackageName(), mUserHandle, enabled);
                } else {
                    Log.w(TAG, "Overlay package:" + info.getPackageName()
                            + ", already enabled, UserHandle:");
                }
            }
        }
    }

    private void setEnabled(String pkg, UserHandle userHandle, boolean enabled) {
        if (DEBUG) {
            Log.d(TAG, String.format("setEnabled: %s %s %b", pkg, userHandle, enabled));
        }
        mOverlayManager.setEnabled(pkg, enabled, userHandle);
    }
}
+169 −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.documentsui.theme;

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

import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.content.om.OverlayInfo;
import android.content.om.OverlayManager;
import android.os.UserHandle;

import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;

import com.google.common.collect.Lists;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.List;

@SmallTest
@RunWith(AndroidJUnit4.class)
public class ThemeOverlayManagerTest {
    private static final String TEST_DISABLED_PREFIX = "com.example.";
    private static final String TEST_ENABLED_PREFIX = "com.example.enabled.";
    private static final String TEST_OVERLAY_PACKAGE = "test.overlay";
    private static final String TEST_TARGET_PACKAGE = "test.target";

    @Mock
    OverlayManager mOverlayManager;

    ThemeOverlayManager mThemeOverlayManager;

    UserHandle mUserHandle;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        mUserHandle = UserHandle.of(UserHandle.myUserId());

        when(mOverlayManager.getOverlayInfosForTarget(getEnabledTargetPackageId(),
                mUserHandle)).thenReturn(Lists.newArrayList(
                createOverlayInfo(getOverlayPackageId(), getEnabledTargetPackageId(), true)));

        when(mOverlayManager.getOverlayInfosForTarget(getDisabledTargetPackageId(),
                mUserHandle)).thenReturn(Lists.newArrayList(
                createOverlayInfo(getOverlayPackageId(), getDisabledTargetPackageId(), false)));
    }

    @Test
    public void testOverlayPackagesForDocumentsUI_shouldBeNonStatic() {
        final String docsuiPkgId =
                InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageName();
        final OverlayManager manager =
                InstrumentationRegistry.getInstrumentation().getTargetContext().getSystemService(
                        OverlayManager.class);
        final List<OverlayInfo> infos = manager.getOverlayInfosForTarget(docsuiPkgId, mUserHandle);
        for (OverlayInfo info : infos) {
            assertThat(info.isStatic).isFalse();
        }
    }

    @Test
    public void testApplyOverlays_shouldSetEnabled() {
        final boolean enabled = true;

        mThemeOverlayManager = new ThemeOverlayManager(mOverlayManager,
                getDisabledTargetPackageId());
        mThemeOverlayManager.applyOverlays(enabled);

        verify(mOverlayManager, times(1)).setEnabled(getOverlayPackageId(), enabled,
                mUserHandle);
    }

    @Test
    public void testApplyOverlays_shouldGetOverlayInfo() {
        final boolean enabled = true;

        mThemeOverlayManager = new ThemeOverlayManager(mOverlayManager,
                getEnabledTargetPackageId());
        mThemeOverlayManager.applyOverlays(enabled);

        verify(mOverlayManager, times(1)).getOverlayInfosForTarget(getEnabledTargetPackageId(),
                mUserHandle);
    }

    @Test
    public void testApplyOverlays_shouldCheckEnabled_beforeSetEnabled() {
        final boolean enabled = true;

        mThemeOverlayManager = new ThemeOverlayManager(mOverlayManager,
                getEnabledTargetPackageId());
        mThemeOverlayManager.applyOverlays(enabled);

        verify(mOverlayManager, never()).setEnabled(getOverlayPackageId(), enabled,
                mUserHandle);
    }

    @Test
    public void testDefaultDisabled_applyOverlays_shouldEnabled() {
        final boolean enabled = true;

        assertThat(mOverlayManager.getOverlayInfosForTarget(getDisabledTargetPackageId(),
                mUserHandle).get(0).isEnabled()).isEqualTo(!enabled);

        mThemeOverlayManager = new ThemeOverlayManager(mOverlayManager,
                getDisabledTargetPackageId());
        mThemeOverlayManager.applyOverlays(enabled);

        verify(mOverlayManager, times(1)).setEnabled(getOverlayPackageId(), enabled,
                mUserHandle);
    }

    @Test
    public void testDefaultEnabled_applyOverlays_shouldDisabled() {
        final boolean enabled = false;

        assertThat(mOverlayManager.getOverlayInfosForTarget(getEnabledTargetPackageId(),
                mUserHandle).get(0).isEnabled()).isEqualTo(!enabled);

        mThemeOverlayManager = new ThemeOverlayManager(mOverlayManager,
                getEnabledTargetPackageId());
        mThemeOverlayManager.applyOverlays(enabled);

        verify(mOverlayManager, times(1)).setEnabled(getOverlayPackageId(), enabled,
                mUserHandle);
    }

    private static OverlayInfo createOverlayInfo(String packageName, String targetPackageName,
            boolean enabled) {
        return new OverlayInfo(packageName, targetPackageName, null, null, "",
                enabled ? OverlayInfo.STATE_ENABLED : OverlayInfo.STATE_DISABLED, 0, 0, false);
    }

    private static String getDisabledTargetPackageId() {
        return TEST_DISABLED_PREFIX + TEST_TARGET_PACKAGE;
    }

    private static String getEnabledTargetPackageId() {
        return TEST_ENABLED_PREFIX + TEST_TARGET_PACKAGE;
    }

    private static String getOverlayPackageId() {
        return TEST_DISABLED_PREFIX + TEST_OVERLAY_PACKAGE;
    }
}