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

Commit 70920bd9 authored by Beth Thibodeau's avatar Beth Thibodeau
Browse files

[DO NOT MERGE] Revert "Allow user to block individual apps from resuming."

Revert submission 12392268-cherrypick-mediaappsettings-5fxuo164a2

Bug: 161813143

Reason for revert: no longer want change in QPR
Reverted Changes:
Id3de52419:Allow user to block individual apps from resuming....
I8c85bc937:Allow user to block individual apps from resuming

Change-Id: Ieacef217587476532246b466d680219351bc1657
parent 48ce5892
Loading
Loading
Loading
Loading
+1 −3
Original line number Diff line number Diff line
@@ -12160,7 +12160,7 @@
    <!-- Title for media control settings [CHAR LIMIT=50]-->
    <string name="media_controls_title">Media</string>
    <!-- Summary for media control settings [CHAR LIMIT=60]-->
    <!-- Summary for media control settings [CHAR LIMIT=NONE]-->
    <string name="media_controls_summary">Media player in Quick Settings</string>
    <!-- Description of toggle to enable or disable the media resumption feature in quick settings [CHAR LIMIT=NONE]-->
    <string name="media_controls_resume_description">Show media player for an extended period to easily resume playback</string>
@@ -12170,8 +12170,6 @@
    <string name="media_controls_show_player">Show player</string>
    <!-- Subtext for media settings when no players can be shown [CHAR LIMIT=50] -->
    <string name="media_controls_no_players">No players available</string>
    <!-- Subtitle for section of media control settings that shows which apps are allowed [CHAR LIMIT=50] -->
    <string name="media_controls_apps_title">Allowed apps</string>
    <!-- Keywords for the media controls setting [CHAR LIMIT=NONE]-->
    <string name="keywords_media_controls">media</string>
</resources>
+0 −5
Original line number Diff line number Diff line
@@ -28,9 +28,4 @@
        app:controller="com.android.settings.sound.MediaControlsPreferenceController"
        app:allowDividerAbove="true" />

    <PreferenceCategory
        android:key="media_controls_resumable_apps"
        android:title="@string/media_controls_apps_title"
        app:controller="com.android.settings.sound.ResumableMediaAppsController" />

</PreferenceScreen>
+0 −138
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.settings.sound;

import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.provider.Settings;
import android.service.media.MediaBrowserService;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;

import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;

import com.android.settings.core.BasePreferenceController;

import java.util.Collections;
import java.util.List;
import java.util.Set;

/**
 * Section of media controls settings that contains a list of potentially resumable apps
 */
public class ResumableMediaAppsController extends BasePreferenceController {
    private static final String TAG = "ResumableMediaAppsCtrl";

    private PreferenceGroup mPreferenceGroup;
    private PackageManager mPackageManager;
    private List<ResolveInfo> mResumeInfo;

    public ResumableMediaAppsController(Context context, String key) {
        super(context, key);
        mPackageManager = mContext.getPackageManager();
        Intent serviceIntent = new Intent(MediaBrowserService.SERVICE_INTERFACE);
        mResumeInfo = mPackageManager.queryIntentServices(serviceIntent, 0);
    }

    @Override
    public int getAvailabilityStatus() {
        // Update list, since this will be called when the app goes to onStart / onPause
        Intent serviceIntent = new Intent(MediaBrowserService.SERVICE_INTERFACE);
        mResumeInfo = mPackageManager.queryIntentServices(serviceIntent, 0);
        return (mResumeInfo.size() > 0) ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
    }

    @Override
    public void displayPreference(PreferenceScreen screen) {
        super.displayPreference(screen);
        mPreferenceGroup = screen.findPreference(getPreferenceKey());
        Set<String> blockedApps = getBlockedMediaApps();
        for (ResolveInfo inf : mResumeInfo) {
            String packageName = inf.getComponentInfo().packageName;
            MediaSwitchPreference pref = new MediaSwitchPreference(mContext, packageName);
            CharSequence appTitle = packageName;
            try {
                appTitle = mPackageManager.getApplicationLabel(
                        mPackageManager.getApplicationInfo(packageName, 0));
                Drawable appIcon = mPackageManager.getApplicationIcon(packageName);
                pref.setIcon(appIcon);
            } catch (PackageManager.NameNotFoundException e) {
                Log.e(TAG, "Couldn't get app title", e);
            }
            pref.setTitle(appTitle);

            pref.setOnPreferenceChangeListener((preference, status) -> {
                MediaSwitchPreference mediaPreference = (MediaSwitchPreference) preference;
                boolean isEnabled = (boolean) status;
                Log.d(TAG, "preference " + mediaPreference + " changed " + isEnabled);

                if (isEnabled) {
                    blockedApps.remove(mediaPreference.getPackageName());
                } else {
                    blockedApps.add(mediaPreference.getPackageName());
                }
                setBlockedMediaApps(blockedApps);
                return true;
            });

            pref.setChecked(!blockedApps.contains(packageName));
            mPreferenceGroup.addPreference(pref);
        }
    }

    class MediaSwitchPreference extends SwitchPreference {
        private String mPackageName;

        MediaSwitchPreference(Context context, String packageName) {
            super(context);
            mPackageName = packageName;
        }

        public String getPackageName() {
            return mPackageName;
        }
    }

    private Set<String> getBlockedMediaApps() {
        String list = Settings.Secure.getString(mContext.getContentResolver(),
                Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED);
        if (TextUtils.isEmpty(list)) {
            return new ArraySet<>();
        }
        String[] names = list.split(":");
        Set<String> set = new ArraySet<>(names.length);
        Collections.addAll(set, names);
        return set;
    }

    private void setBlockedMediaApps(Set<String> apps) {
        if (apps == null || apps.size() == 0) {
            Settings.Secure.putString(mContext.getContentResolver(),
                    Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED, "");
            return;
        }
        String list = String.join(":", apps);
        Settings.Secure.putString(mContext.getContentResolver(),
                Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED, list);
    }
}
+0 −168
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.settings.sound;

import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.provider.Settings;

import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;

import com.android.settings.testutils.ResolveInfoBuilder;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;

import java.util.ArrayList;
import java.util.List;

@RunWith(RobolectricTestRunner.class)
public class ResumableMediaAppsControllerTest {

    private static final String KEY = "media_controls_resumable_apps";

    private static final String FAKE_APP = "com.test.fakeapp1";

    private Context mContext;
    private int mOriginalQs;
    private int mOriginalResume;
    private String mOriginalBlocked;
    private ContentResolver mContentResolver;
    private ResumableMediaAppsController mController;
    @Mock
    private PackageManager mPackageManager;
    @Mock
    private PreferenceScreen mPreferenceScreen;
    @Mock
    private PreferenceGroup mPreferenceGroup;

    @Before
    public void setUp() {
        mContext = spy(RuntimeEnvironment.application);
        mContentResolver = mContext.getContentResolver();
        mOriginalQs = Settings.Global.getInt(mContentResolver,
                Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1);
        mOriginalResume = Settings.Secure.getInt(mContentResolver,
                Settings.Secure.MEDIA_CONTROLS_RESUME, 1);
        mOriginalBlocked = Settings.Secure.getString(mContentResolver,
                Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED);

        // Start all tests with feature enabled, nothing blocked
        Settings.Global.putInt(mContentResolver, Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1);
        Settings.Secure.putInt(mContentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME, 1);
        Settings.Secure.putString(mContentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED,
                mOriginalBlocked);

        mPreferenceScreen = mock(PreferenceScreen.class);
        mPreferenceGroup = mock(PreferenceGroup.class);
        mPackageManager = mock(PackageManager.class);

        List<ResolveInfo> fakeInfo = new ArrayList<>();
        fakeInfo.add(createResolveInfo(FAKE_APP));
        when(mPackageManager.queryIntentServices(any(), anyInt())).thenReturn(fakeInfo);

        when(mContext.getPackageManager()).thenReturn(mPackageManager);
        when(mPreferenceScreen.findPreference(KEY)).thenReturn(mPreferenceGroup);

        mController = new ResumableMediaAppsController(mContext, KEY);
    }

    @After
    public void tearDown() {
        Settings.Global.putInt(mContentResolver, Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS,
                mOriginalQs);
        Settings.Secure.putInt(mContentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME,
                mOriginalResume);
        Settings.Secure.putString(mContentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED,
                mOriginalBlocked);
    }

    @Test
    public void getAvailability_hasEligibleApps_isAvailable() {
        // The package manager already has an eligible app from setUp()
        assertEquals(AVAILABLE, mController.getAvailabilityStatus());
    }

    @Test
    public void getAvailability_noEligibleApps_isConditionallyUnavailable() {
        Context context = mock(Context.class);
        PackageManager packageManager = mock(PackageManager.class);
        List<ResolveInfo> fakeInfo = new ArrayList<>();
        when(packageManager.queryIntentServices(any(), anyInt())).thenReturn(fakeInfo);
        when(context.getPackageManager()).thenReturn(packageManager);
        ResumableMediaAppsController controller = new ResumableMediaAppsController(context, KEY);

        assertEquals(CONDITIONALLY_UNAVAILABLE, controller.getAvailabilityStatus());
    }

    @Test
    public void displayPreference_addsApps() {
        mController.displayPreference(mPreferenceScreen);
        verify(mPreferenceGroup, times(1)).addPreference(any());
    }

    @Test
    public void unblockedApp_isChecked() {
        ArgumentCaptor<ResumableMediaAppsController.MediaSwitchPreference> argument =
                ArgumentCaptor.forClass(ResumableMediaAppsController.MediaSwitchPreference.class);
        mController.displayPreference(mPreferenceScreen);
        verify(mPreferenceGroup).addPreference(argument.capture());
        assertTrue(argument.getValue().isChecked());
    }

    @Test
    public void blockedApp_isNotChecked() {
        Settings.Secure.putString(mContentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED,
                FAKE_APP);

        ArgumentCaptor<ResumableMediaAppsController.MediaSwitchPreference> argument =
                ArgumentCaptor.forClass(ResumableMediaAppsController.MediaSwitchPreference.class);
        mController.displayPreference(mPreferenceScreen);
        verify(mPreferenceGroup).addPreference(argument.capture());

        assertFalse(argument.getValue().isChecked());
    }

    private ResolveInfo createResolveInfo(String name) {
        ResolveInfoBuilder builder = new ResolveInfoBuilder(name);
        builder.setActivity(name, name);
        return builder.build();
    }
}