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

Commit 16912c15 authored by Manish Singh's avatar Manish Singh Committed by Automerger Merge Worker
Browse files

Merge "Add Settings UI for MTP transcoding over USB." into sc-dev am: becac249

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Settings/+/13435373

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: Ib8ff96979f01d4cdd8f3a881750eca56941d414c
parents d6a8f92e becac249
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -9958,6 +9958,10 @@
         select what the USB connection for this device should be used for. This choice
         is for transferring photos via PTP. -->
    <string name="usb_use_photo_transfers">PTP</string>
    <!-- Title of one of the choices in a dialog (with title defined in usb_use) that lets the user
         select what the USB connection for this device should be used for. This choice
         is for transcoding the files that are transferred via MTP. -->
    <string name="usb_transcode_files">Transcode exported media</string>
    <!-- Description of one of the choices in a dialog (with title defined in usb_use) that lets the user
         select what the USB connection for this device should be used for. This choice
         is for transferring photos via PTP. -->
@@ -9986,6 +9990,9 @@
    <!-- The title used in USB Preferences which provides the user with the control over this
         device's power role. -->
    <string name="usb_power_title">Power options</string>
    <!-- The title used in USB Preferences which lets the user control the options for the file
         transfer mode. -->
    <string name="usb_file_transfer_title">File transfer options</string>
    <!-- Settings item title for USB preference [CHAR LIMIT=35] -->
    <string name="usb_pref">USB</string>
+4 −1
Original line number Diff line number Diff line
@@ -33,8 +33,11 @@
        android:key="usb_details_functions"
        android:title="@string/usb_use"/>

    <PreferenceCategory
        android:key="usb_transcode_mtp"
        android:title="@string/usb_file_transfer_title"/>

    <PreferenceCategory
        android:key="usb_details_power_role"
        android:title="@string/usb_power_title"/>

</PreferenceScreen>
+1 −0
Original line number Diff line number Diff line
@@ -92,6 +92,7 @@ public class UsbDetailsFragment extends DashboardFragment {
        ret.add(new UsbDetailsDataRoleController(context, fragment, usbBackend));
        ret.add(new UsbDetailsFunctionsController(context, fragment, usbBackend));
        ret.add(new UsbDetailsPowerRoleController(context, fragment, usbBackend));
        ret.add(new UsbDetailsTranscodeMtpController(context, fragment, usbBackend));
        return ret;
    }

+95 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.connecteddevice.usb;

import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;

import android.content.Context;
import android.hardware.usb.UsbManager;
import android.os.SystemProperties;

import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;

import com.android.settings.R;
import com.android.settings.Utils;

/**
 * This class controls the switch for setting if we should transcode files transferred via MTP over
 * USB.
 */
public class UsbDetailsTranscodeMtpController extends UsbDetailsController
        implements Preference.OnPreferenceClickListener {
    private static final String TRANSCODE_MTP_SYS_PROP_KEY = "sys.fuse.transcode_mtp";
    private static final String PREFERENCE_KEY = "usb_transcode_mtp";

    private PreferenceCategory mPreferenceCategory;
    private SwitchPreference mSwitchPreference;

    public UsbDetailsTranscodeMtpController(Context context, UsbDetailsFragment fragment,
            UsbBackend backend) {
        super(context, fragment, backend);
    }


    @Override
    public void displayPreference(PreferenceScreen screen) {
        super.displayPreference(screen);
        mPreferenceCategory = screen.findPreference(getPreferenceKey());
        mSwitchPreference = new SwitchPreference(mPreferenceCategory.getContext());
        mSwitchPreference.setTitle(R.string.usb_transcode_files);
        mSwitchPreference.setOnPreferenceClickListener(this);
        mPreferenceCategory.addPreference(mSwitchPreference);
    }

    @Override
    protected void refresh(boolean connected, long functions, int powerRole, int dataRole) {
        if (mUsbBackend.areFunctionsSupported(UsbManager.FUNCTION_MTP | UsbManager.FUNCTION_PTP)) {
            mFragment.getPreferenceScreen().addPreference(mPreferenceCategory);
        } else {
            mFragment.getPreferenceScreen().removePreference(mPreferenceCategory);
        }

        mSwitchPreference.setChecked(SystemProperties.getBoolean(TRANSCODE_MTP_SYS_PROP_KEY, true));
        mPreferenceCategory.setEnabled(
                connected && isDeviceInFileTransferMode(functions, dataRole));
    }

    @Override
    public boolean onPreferenceClick(Preference preference) {
        SystemProperties.set(TRANSCODE_MTP_SYS_PROP_KEY,
                Boolean.toString(mSwitchPreference.isChecked()));
        return true;
    }

    @Override
    public boolean isAvailable() {
        return !Utils.isMonkeyRunning();
    }

    @Override
    public String getPreferenceKey() {
        return PREFERENCE_KEY;
    }

    private static boolean isDeviceInFileTransferMode(long functions, int dataRole) {
        return dataRole == DATA_ROLE_DEVICE && ((functions & UsbManager.FUNCTION_MTP) != 0
                || (functions & UsbManager.FUNCTION_PTP) != 0);
    }
}
+178 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.connecteddevice.usb;

import static android.hardware.usb.UsbPortStatus.DATA_ROLE_NONE;
import static android.hardware.usb.UsbPortStatus.POWER_ROLE_NONE;

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

import static org.mockito.Mockito.when;

import android.content.Context;
import android.hardware.usb.UsbManager;
import android.os.SystemProperties;

import androidx.fragment.app.FragmentActivity;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;

import com.android.settings.testutils.shadow.ShadowUtils;

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;
import org.robolectric.annotation.Config;

@RunWith(RobolectricTestRunner.class)
public class UsbDetailsTranscodeMtpControllerTest {
    private static final String TRANSCODE_MTP_SYS_PROP_KEY = "sys.fuse.transcode_mtp";

    private Context mContext;
    private PreferenceCategory mPreference;
    private PreferenceManager mPreferenceManager;
    private PreferenceScreen mScreen;
    private UsbDetailsTranscodeMtpController mUnderTest;

    @Mock
    private UsbBackend mUsbBackend;
    @Mock
    private UsbDetailsFragment mFragment;
    @Mock
    private FragmentActivity mActivity;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);

        mContext = RuntimeEnvironment.application;
        mPreferenceManager = new PreferenceManager(mContext);
        mScreen = mPreferenceManager.createPreferenceScreen(mContext);

        when(mFragment.getActivity()).thenReturn(mActivity);
        when(mActivity.getApplicationContext()).thenReturn(mContext);
        when(mFragment.getContext()).thenReturn(mContext);
        when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager);
        when(mFragment.getPreferenceScreen()).thenReturn(mScreen);

        mUnderTest = new UsbDetailsTranscodeMtpController(mContext, mFragment, mUsbBackend);

        mPreference = new PreferenceCategory(mContext);
        mPreference.setKey(mUnderTest.getPreferenceKey());
        mScreen.addPreference(mPreference);
    }

    @Test
    public void displayRefresh_noUsbConnection_shouldDisablePrefCategory() {
        mUnderTest.displayPreference(mScreen);
        when(mUsbBackend.areAllRolesSupported()).thenReturn(true);

        mUnderTest.refresh(false /* connected */, UsbManager.FUNCTION_MTP, POWER_ROLE_NONE,
                DATA_ROLE_NONE);

        assertThat(mPreference.isEnabled()).isFalse();
    }

    @Test
    public void displayRefresh_noDataTransfer_shouldDisablePrefCategory() {
        mUnderTest.displayPreference(mScreen);
        when(mUsbBackend.areAllRolesSupported()).thenReturn(true);

        mUnderTest.refresh(true /* connected */, UsbManager.FUNCTION_NONE, POWER_ROLE_NONE,
                DATA_ROLE_NONE);

        assertThat(mPreference.isEnabled()).isFalse();
    }

    @Test
    public void displayRefresh_noDataRole_shouldDisablePrefCategory() throws InterruptedException {
        mUnderTest.displayPreference(mScreen);
        when(mUsbBackend.areAllRolesSupported()).thenReturn(true);

        mUnderTest.refresh(true /* connected */, UsbManager.FUNCTION_MTP, POWER_ROLE_NONE,
                DATA_ROLE_NONE);

        assertThat(mPreference.isEnabled()).isFalse();
    }

    @Test
    public void displayRefresh_fileTransfer_withAbsentProp_shouldCheck() {
        mUnderTest.displayPreference(mScreen);
        when(mUsbBackend.areAllRolesSupported()).thenReturn(true);

        mUnderTest.refresh(true /* connected */, UsbManager.FUNCTION_MTP, POWER_ROLE_NONE,
                DATA_ROLE_NONE);

        assertThat(getSwitchPreference().isChecked()).isTrue();
    }

    @Test
    public void displayRefresh_fileTransfer_withUnsetProp_shouldUncheck() {
        mUnderTest.displayPreference(mScreen);
        SystemProperties.set(TRANSCODE_MTP_SYS_PROP_KEY, Boolean.toString(false));
        when(mUsbBackend.areAllRolesSupported()).thenReturn(true);

        mUnderTest.refresh(true /* connected */, UsbManager.FUNCTION_MTP, POWER_ROLE_NONE,
                DATA_ROLE_NONE);

        assertThat(getSwitchPreference().isChecked()).isFalse();
    }

    @Test
    public void displayRefresh_fileTransfer_withSetProp_shouldCheck() {
        mUnderTest.displayPreference(mScreen);
        SystemProperties.set(TRANSCODE_MTP_SYS_PROP_KEY, Boolean.toString(true));
        when(mUsbBackend.areAllRolesSupported()).thenReturn(true);

        mUnderTest.refresh(true /* connected */, UsbManager.FUNCTION_MTP, POWER_ROLE_NONE,
                DATA_ROLE_NONE);

        assertThat(getSwitchPreference().isChecked()).isTrue();
    }

    @Test
    public void click_checked_shouldSetSystemProperty() {
        mUnderTest.displayPreference(mScreen);
        getSwitchPreference().performClick();
        assertThat(SystemProperties.getBoolean(TRANSCODE_MTP_SYS_PROP_KEY, false)).isTrue();
    }

    @Test
    public void click_unChecked_shouldUnsetSystemProperty() {
        mUnderTest.displayPreference(mScreen);
        getSwitchPreference().performClick();
        getSwitchPreference().performClick();
        assertThat(SystemProperties.getBoolean(TRANSCODE_MTP_SYS_PROP_KEY, false)).isFalse();
    }

    @Test
    @Config(shadows = ShadowUtils.class)
    public void isAvailable_isMonkey_shouldReturnFalse() {
        ShadowUtils.setIsUserAMonkey(true);
        assertThat(mUnderTest.isAvailable()).isFalse();
    }

    private SwitchPreference getSwitchPreference() {
        return (SwitchPreference) mPreference.getPreference(0);
    }
}