Loading res/values/strings.xml +7 −0 Original line number Diff line number Diff line Loading @@ -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. --> Loading Loading @@ -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> res/xml/usb_details_fragment.xml +4 −1 Original line number Diff line number Diff line Loading @@ -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> src/com/android/settings/connecteddevice/usb/UsbDetailsFragment.java +1 −0 Original line number Diff line number Diff line Loading @@ -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; } Loading src/com/android/settings/connecteddevice/usb/UsbDetailsTranscodeMtpController.java 0 → 100644 +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); } } tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsTranscodeMtpControllerTest.java 0 → 100644 +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); } } Loading
res/values/strings.xml +7 −0 Original line number Diff line number Diff line Loading @@ -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. --> Loading Loading @@ -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>
res/xml/usb_details_fragment.xml +4 −1 Original line number Diff line number Diff line Loading @@ -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>
src/com/android/settings/connecteddevice/usb/UsbDetailsFragment.java +1 −0 Original line number Diff line number Diff line Loading @@ -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; } Loading
src/com/android/settings/connecteddevice/usb/UsbDetailsTranscodeMtpController.java 0 → 100644 +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); } }
tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsTranscodeMtpControllerTest.java 0 → 100644 +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); } }