diff options
| author | 2024-02-05 14:49:05 +0000 | |
|---|---|---|
| committer | 2024-02-05 14:50:08 +0000 | |
| commit | 93fe4a8f70162ecb7f4a58fc3146d8d64eb61aad (patch) | |
| tree | 86f5a98f77c42399394ce46a584f7b4c9d7c8500 | |
| parent | fd56681afd11fb15ffcd42137a0ff5086e4a16e4 (diff) | |
Remove SoundPicker2
Fixes: 323853450
Test: n/a
Change-Id: I8e3af467ee039847421b818803aaf11b8e2bb4f5
39 files changed, 0 insertions, 3534 deletions
diff --git a/packages/SoundPicker2/Android.bp b/packages/SoundPicker2/Android.bp deleted file mode 100644 index f4d8bf2c76b5..000000000000 --- a/packages/SoundPicker2/Android.bp +++ /dev/null @@ -1,46 +0,0 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -android_library { - name: "SoundPicker2Lib", - srcs: [ - "src/**/*.java", - ], - resource_dirs: [ - "res", - ], - static_libs: [ - "androidx.appcompat_appcompat", - "hilt_android", - "guava", - "androidx.recyclerview_recyclerview", - "androidx-constraintlayout_constraintlayout", - "androidx.viewpager2_viewpager2", - "com.google.android.material_material", - ], -} - -android_app { - name: "SoundPicker2", - defaults: ["platform_app_defaults"], - manifest: "AndroidManifest.xml", - static_libs: ["SoundPicker2Lib"], - platform_apis: true, - certificate: "media", - privileged: true, - - optimize: { - enabled: true, - optimize: true, - shrink: true, - shrink_resources: true, - obfuscate: false, - proguard_compatibility: false, - }, -} diff --git a/packages/SoundPicker2/AndroidManifest.xml b/packages/SoundPicker2/AndroidManifest.xml deleted file mode 100644 index 934b003c605c..000000000000 --- a/packages/SoundPicker2/AndroidManifest.xml +++ /dev/null @@ -1,43 +0,0 @@ -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.soundpicker" - android:sharedUserId="android.media"> - - <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> - <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> - - <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> - <uses-permission android:name="android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY" /> - <uses-permission android:name="android.permission.WRITE_SETTINGS" /> - - <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> - - <application - android:name=".RingtonePickerApplication" - android:allowBackup="false" - android:label="@string/app_label" - android:theme="@style/Theme.AppCompat" - android:supportsRtl="true"> - <receiver android:name="RingtoneReceiver" - android:exported="true"> - <intent-filter> - <action android:name="android.intent.action.DEVICE_CUSTOMIZATION_READY"/> - </intent-filter> - </receiver> - - <service android:name="RingtoneOverlayService" /> - - <activity android:name="RingtonePickerActivity" - android:theme="@style/Theme.AppCompat.Dialog" - android:enabled="@*android:bool/config_defaultRingtonePickerEnabled" - android:excludeFromRecents="true" - android:exported="true"> - <intent-filter> - <action android:name="android.intent.action.RINGTONE_PICKER" /> - <category android:name="android.intent.category.DEFAULT" /> - <category android:name="android.intent.category.RINGTONE_PICKER_SOUND" /> - <category android:name="android.intent.category.RINGTONE_PICKER_VIBRATION" /> - <category android:name="android.intent.category.RINGTONE_PICKER_RINGTONE" /> - </intent-filter> - </activity> - </application> -</manifest> diff --git a/packages/SoundPicker2/OWNERS b/packages/SoundPicker2/OWNERS deleted file mode 100644 index 5bf46e039e96..000000000000 --- a/packages/SoundPicker2/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# Haptics team works on the SoundPicker -include platform/frameworks/base:/services/core/java/com/android/server/vibrator/OWNERS diff --git a/packages/SoundPicker2/res/drawable/ic_add.xml b/packages/SoundPicker2/res/drawable/ic_add.xml deleted file mode 100644 index 22b3fe9176e5..000000000000 --- a/packages/SoundPicker2/res/drawable/ic_add.xml +++ /dev/null @@ -1,24 +0,0 @@ -<!-- - Copyright (C) 2016 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. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24.0dp" - android:height="24.0dp" - android:viewportWidth="48.0" - android:viewportHeight="48.0"> - <path - android:fillColor="?android:attr/colorAccent" - android:pathData="M38.0,26.0L26.0,26.0l0.0,12.0l-4.0,0.0L22.0,26.0L10.0,26.0l0.0,-4.0l12.0,0.0L22.0,10.0l4.0,0.0l0.0,12.0l12.0,0.0l0.0,4.0z"/> -</vector>
\ No newline at end of file diff --git a/packages/SoundPicker2/res/drawable/ic_add_padded.xml b/packages/SoundPicker2/res/drawable/ic_add_padded.xml deleted file mode 100644 index c376867896d0..000000000000 --- a/packages/SoundPicker2/res/drawable/ic_add_padded.xml +++ /dev/null @@ -1,22 +0,0 @@ -<!-- - Copyright (C) 2017 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. ---> - -<inset xmlns:android="http://schemas.android.com/apk/res/android" - android:drawable="@drawable/ic_add" - android:insetTop="4dp" - android:insetRight="4dp" - android:insetBottom="4dp" - android:insetLeft="4dp"/> diff --git a/packages/SoundPicker2/res/layout-watch/add_new_sound_item.xml b/packages/SoundPicker2/res/layout-watch/add_new_sound_item.xml deleted file mode 100644 index edfc0aba5be7..000000000000 --- a/packages/SoundPicker2/res/layout-watch/add_new_sound_item.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2017 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. ---> - -<!-- - Currently, no file manager app on watch could handle ACTION_GET_CONTENT intent. - Make the visibility to "gone" to prevent failures. - --> -<TextView xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/add_new_sound_text" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:minHeight="?android:attr/listPreferredItemHeightSmall" - android:textAppearance="?android:attr/textAppearanceMedium" - android:text="@null" - android:textColor="?android:attr/colorAccent" - android:gravity="center_vertical" - android:paddingStart="?android:attr/listPreferredItemPaddingStart" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" - android:drawableStart="@drawable/ic_add_padded" - android:drawablePadding="8dp" - android:ellipsize="marquee" - android:visibility="gone" /> diff --git a/packages/SoundPicker2/res/layout-watch/radio_with_work_badge.xml b/packages/SoundPicker2/res/layout-watch/radio_with_work_badge.xml deleted file mode 100644 index ee29a3710143..000000000000 --- a/packages/SoundPicker2/res/layout-watch/radio_with_work_badge.xml +++ /dev/null @@ -1,47 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2017 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. ---> -<com.android.soundpicker.CheckedListItem xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:gravity="center_vertical" - android:background="?android:attr/selectableItemBackground" - > - - <CheckedTextView - android:id="@+id/checked_text_view" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:minHeight="?android:attr/listPreferredItemHeightSmall" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textColor="?android:attr/textColorAlertDialogListItem" - android:gravity="center_vertical" - android:paddingStart="?android:attr/listPreferredItemPaddingStart" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" - android:drawableStart="?android:attr/listChoiceIndicatorSingle" - android:drawablePadding="8dp" - android:ellipsize="marquee" - android:layout_toLeftOf="@+id/work_icon" - android:maxLines="3" /> - - <ImageView - android:id="@id/work_icon" - android:layout_width="18dp" - android:layout_height="18dp" - android:layout_alignParentRight="true" - android:layout_centerVertical="true" - android:scaleType="centerCrop" - android:layout_marginRight="20dp" /> -</com.android.soundpicker.CheckedListItem> diff --git a/packages/SoundPicker2/res/layout/activity_ringtone_picker.xml b/packages/SoundPicker2/res/layout/activity_ringtone_picker.xml deleted file mode 100644 index 6fc60801ad3a..000000000000 --- a/packages/SoundPicker2/res/layout/activity_ringtone_picker.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2023 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. ---> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent"/>
\ No newline at end of file diff --git a/packages/SoundPicker2/res/layout/add_new_sound_item.xml b/packages/SoundPicker2/res/layout/add_new_sound_item.xml deleted file mode 100644 index 024b97ef23be..000000000000 --- a/packages/SoundPicker2/res/layout/add_new_sound_item.xml +++ /dev/null @@ -1,49 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2016 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. ---> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:gravity="center_vertical" - android:background="?android:attr/selectableItemBackground" - android:focusable="true" - android:clickable="true"> - - <ImageView - android:layout_width="24dp" - android:layout_height="24dp" - android:layout_alignParentRight="true" - android:layout_centerVertical="true" - android:scaleType="centerCrop" - android:layout_marginRight="24dp" - android:layout_marginLeft="24dp" - android:src="@drawable/ic_add"/> - - <TextView - android:id="@+id/add_new_sound_text" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:minHeight="?android:attr/listPreferredItemHeightSmall" - android:text="@null" - android:textColor="?android:attr/colorAccent" - android:textAppearance="?android:attr/textAppearanceMedium" - android:maxLines="3" - android:gravity="center_vertical" - android:paddingEnd="?android:attr/dialogPreferredPadding" - android:drawablePadding="20dp" - android:ellipsize="marquee"/> -</LinearLayout>
\ No newline at end of file diff --git a/packages/SoundPicker2/res/layout/fragment_ringtone_picker.xml b/packages/SoundPicker2/res/layout/fragment_ringtone_picker.xml deleted file mode 100644 index 787f92ec06d6..000000000000 --- a/packages/SoundPicker2/res/layout/fragment_ringtone_picker.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2023 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. ---> -<androidx.recyclerview.widget.RecyclerView - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - android:id="@+id/recycler_view" - android:layout_width="match_parent" - android:layout_height="match_parent" -/>
\ No newline at end of file diff --git a/packages/SoundPicker2/res/layout/fragment_tabbed_dialog.xml b/packages/SoundPicker2/res/layout/fragment_tabbed_dialog.xml deleted file mode 100644 index 7efd91191b79..000000000000 --- a/packages/SoundPicker2/res/layout/fragment_tabbed_dialog.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2023 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. ---> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent"> - <com.google.android.material.tabs.TabLayout - android:id="@+id/tabLayout" - android:layout_width="match_parent" - android:layout_height="wrap_content"/> - <androidx.viewpager2.widget.ViewPager2 - android:id="@+id/masterViewPager" - android:paddingTop="12dp" - android:paddingBottom="12dp" - android:layout_width="match_parent" - android:layout_height="match_parent"/> -</LinearLayout>
\ No newline at end of file diff --git a/packages/SoundPicker2/res/layout/radio_with_work_badge.xml b/packages/SoundPicker2/res/layout/radio_with_work_badge.xml deleted file mode 100644 index 36ac93ed630b..000000000000 --- a/packages/SoundPicker2/res/layout/radio_with_work_badge.xml +++ /dev/null @@ -1,50 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2016 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. ---> - -<com.android.soundpicker.CheckedListItem - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:gravity="center_vertical" - android:background="?android:attr/selectableItemBackground" - android:focusable="true" - android:clickable="true"> - - <CheckedTextView - android:id="@+id/checked_text_view" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:minHeight="?android:attr/listPreferredItemHeightSmall" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textColor="?android:attr/textColorAlertDialogListItem" - android:gravity="center_vertical" - android:paddingStart="20dp" - android:paddingEnd="?android:attr/dialogPreferredPadding" - android:drawableStart="?android:attr/listChoiceIndicatorSingle" - android:drawablePadding="20dp" - android:ellipsize="marquee" - android:layout_toLeftOf="@+id/work_icon" - android:maxLines="3"/> - - <ImageView - android:id="@id/work_icon" - android:layout_width="18dp" - android:layout_height="18dp" - android:layout_alignParentRight="true" - android:layout_centerVertical="true" - android:scaleType="centerCrop" - android:layout_marginRight="20dp"/> -</com.android.soundpicker.CheckedListItem> diff --git a/packages/SoundPicker2/res/raw/default_alarm_alert.ogg b/packages/SoundPicker2/res/raw/default_alarm_alert.ogg deleted file mode 100644 index e69de29bb2d1..000000000000 --- a/packages/SoundPicker2/res/raw/default_alarm_alert.ogg +++ /dev/null diff --git a/packages/SoundPicker2/res/raw/default_notification_sound.ogg b/packages/SoundPicker2/res/raw/default_notification_sound.ogg deleted file mode 100644 index e69de29bb2d1..000000000000 --- a/packages/SoundPicker2/res/raw/default_notification_sound.ogg +++ /dev/null diff --git a/packages/SoundPicker2/res/raw/default_ringtone.ogg b/packages/SoundPicker2/res/raw/default_ringtone.ogg deleted file mode 100644 index e69de29bb2d1..000000000000 --- a/packages/SoundPicker2/res/raw/default_ringtone.ogg +++ /dev/null diff --git a/packages/SoundPicker2/res/values/config.xml b/packages/SoundPicker2/res/values/config.xml deleted file mode 100644 index 4e237a2f1644..000000000000 --- a/packages/SoundPicker2/res/values/config.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2016 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. ---> - -<!-- These resources are around just to allow their values to be customized - for different hardware and product builds. Do not translate. - - NOTE: The naming convention is "config_camelCaseValue". --> -<resources xmlns:android="http://schemas.android.com/apk/res/android"> - <!-- True if the ringtone picker should show the ok/cancel buttons. If it is not shown, the - ringtone will be automatically selected when the picker is closed. --> - <bool name="config_showOkCancelButtons">true</bool> -</resources> diff --git a/packages/SoundPicker2/res/values/strings.xml b/packages/SoundPicker2/res/values/strings.xml deleted file mode 100644 index ab7b95a09028..000000000000 --- a/packages/SoundPicker2/res/values/strings.xml +++ /dev/null @@ -1,47 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2009 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. ---> - -<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- Choice in the ringtone picker. If chosen, the default ringtone will be used. --> - <string name="ringtone_default">Default ringtone</string> - - <!-- Choice in the notification sound picker. If chosen, the default notification sound will be - used. --> - <string name="notification_sound_default">Default notification sound</string> - - <!-- Choice in the alarm sound picker. If chosen, the default alarm sound will be used. --> - <string name="alarm_sound_default">Default alarm sound</string> - - <!-- Text for the RingtonePicker item that allows adding a new ringtone. --> - <string name="add_ringtone_text">Add ringtone</string> - <!-- Text for the RingtonePicker item that allows adding a new alarm. --> - <string name="add_alarm_text">Add alarm</string> - <!-- Text for the RingtonePicker item that allows adding a new notification. --> - <string name="add_notification_text">Add notification</string> - <!-- Text for the RingtonePicker item ContextMenu that allows deleting a custom ringtone. --> - <string name="delete_ringtone_text">Delete</string> - <!-- Text for the Toast displayed when adding a custom ringtone fails. --> - <string name="unable_to_add_ringtone">Unable to add custom ringtone</string> - <!-- Text for the Toast displayed when deleting a custom ringtone fails. --> - <string name="unable_to_delete_ringtone">Unable to delete custom ringtone</string> - - <!-- Text for the name of the app. [CHAR LIMIT=12] --> - <string name="app_label">Sounds</string> - - <string name="empty_list">The list is empty</string> - <string name="sound_page_title">Sound</string> - <string name="vibration_page_title">Vibration</string> -</resources> diff --git a/packages/SoundPicker2/res/values/styles.xml b/packages/SoundPicker2/res/values/styles.xml deleted file mode 100644 index d22d9c43d0fb..000000000000 --- a/packages/SoundPicker2/res/values/styles.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2014 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. ---> - -<resources xmlns:android="http://schemas.android.com/apk/res/android"> - - <style name="PickerDialogTheme" parent="@*android:style/Theme.DeviceDefault.Settings.Dialog"> - </style> - -</resources> diff --git a/packages/SoundPicker2/src/com/android/soundpicker/BasePickerFragment.java b/packages/SoundPicker2/src/com/android/soundpicker/BasePickerFragment.java deleted file mode 100644 index 4fc2a86537c1..000000000000 --- a/packages/SoundPicker2/src/com/android/soundpicker/BasePickerFragment.java +++ /dev/null @@ -1,312 +0,0 @@ -/* - * Copyright (C) 2023 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.soundpicker; - -import android.app.Activity; -import android.content.ContentProvider; -import android.content.Intent; -import android.graphics.drawable.Drawable; -import android.media.RingtoneManager; -import android.net.Uri; -import android.os.Bundle; -import android.os.UserHandle; -import android.os.UserManager; -import android.provider.MediaStore; -import android.util.Log; -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; -import androidx.lifecycle.ViewModelProvider; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import dagger.hilt.android.AndroidEntryPoint; - -import java.util.Objects; - -/** - * Base class for generic picker fragments. - * - * <p>This fragment displays a recycler view that is populated by a {@link RingtoneListViewAdapter} - * with data provided by a {@link RingtoneListHandler}. Each item can be selected on click, - * which also triggers a ringtone preview performed by the shared {@link RingtonePickerViewModel}. - * The ringtone preview uses the selection state of all picker fragments (e.g. sound selected by - * one fragment and vibration selected by another). - */ -@AndroidEntryPoint(Fragment.class) -public abstract class BasePickerFragment extends Hilt_BasePickerFragment implements - RingtoneListViewAdapter.Callbacks { - - private static final String TAG = "BasePickerFragment"; - private static final String COLUMN_LABEL = MediaStore.Audio.Media.TITLE; - private boolean mIsManagedProfile; - private Drawable mWorkIconDrawable; - - protected RingtoneListViewAdapter mRingtoneListViewAdapter; - protected RecyclerView mRecyclerView; - protected RingtonePickerViewModel.Config mPickerConfig; - protected RingtonePickerViewModel mRingtonePickerViewModel; - protected RingtoneListHandler.Config mRingtoneListConfig; - protected RingtoneListHandler mRingtoneListHandler; - - public BasePickerFragment() { - super(R.layout.fragment_ringtone_picker); - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - mRingtonePickerViewModel = new ViewModelProvider(requireActivity()).get( - RingtonePickerViewModel.class); - mRingtoneListHandler = getRingtoneListHandler(); - mRecyclerView = view.requireViewById(R.id.recycler_view); - - mPickerConfig = mRingtonePickerViewModel.getPickerConfig(); - mRingtoneListConfig = mRingtoneListHandler.getRingtoneListConfig(); - - mIsManagedProfile = UserManager.get(requireActivity()).isManagedProfile( - mPickerConfig.userId); - - mRingtoneListViewAdapter = createRingtoneListViewAdapter(); - mRecyclerView.setHasFixedSize(true); - mRecyclerView.setAdapter(mRingtoneListViewAdapter); - mRecyclerView.setLayoutManager(new LinearLayoutManager(requireActivity())); - setSelectedItem(mRingtoneListHandler.getSelectedItemPosition()); - prepareRecyclerView(mRecyclerView); - } - - @Override - public boolean isWorkRingtone(int position) { - if (!mIsManagedProfile) { - return false; - } - - /* - * Display the work icon if the ringtone belongs to a work profile. We - * can tell that a ringtone belongs to a work profile if the picker user - * is a managed profile, the ringtone Uri is in external storage, and - * either the uri has no user id or has the id of the picker user - */ - Uri currentUri = mRingtoneListHandler.getRingtoneUri(position); - int uriUserId = ContentProvider.getUserIdFromUri(currentUri, - mPickerConfig.userId); - Uri uriWithoutUserId = ContentProvider.getUriWithoutUserId(currentUri); - - return uriUserId == mPickerConfig.userId - && uriWithoutUserId.toString().startsWith( - MediaStore.Audio.Media.EXTERNAL_CONTENT_URI.toString()); - } - - @Override - public Drawable getWorkIconDrawable() { - if (mWorkIconDrawable == null) { - mWorkIconDrawable = requireActivity().getPackageManager() - .getUserBadgeForDensityNoBackground( - UserHandle.of(mPickerConfig.userId), /* density= */ -1); - } - - return mWorkIconDrawable; - } - - @Override - public void onRingtoneSelected(int position) { - setSelectedItem(position); - - // In the buttonless (watch-only) version, preemptively set our result since - // we won't have another chance to do so before the activity closes. - if (!mPickerConfig.showOkCancelButtons) { - setSuccessResultWithSelectedRingtone(); - } - - // Play clip - mRingtonePickerViewModel.playRingtone(); - } - - @Override - public void onAddRingtoneSelected() { - addRingtoneAsync(); - } - - /** - * Sets up the list by adding fixed items to the top and bottom, if required. And sets the - * selected item in the list. - * @param recyclerView The recyclerview that contains the list of displayed items. - */ - protected void prepareRecyclerView(@NonNull RecyclerView recyclerView) { - // Reset the static item count, as this method can be called multiple times - mRingtoneListHandler.resetFixedItems(); - - if (mRingtoneListConfig.hasDefaultItem) { - int defaultItemPos = addDefaultRingtoneItem(); - - if (getSelectedItem() < 0 - && RingtoneManager.isDefault(mRingtoneListConfig.initialSelectedUri)) { - setSelectedItem(defaultItemPos); - } - } - - if (mRingtoneListConfig.hasSilentItem) { - int silentItemPos = addSilentItem(); - - // The 'Silent' item should use a null Uri - if (getSelectedItem() < 0 - && mRingtoneListConfig.initialSelectedUri == null) { - setSelectedItem(silentItemPos); - } - } - - if (getSelectedItem() < 0) { - setSelectedItem(mRingtoneListHandler.getRingtonePosition( - mRingtoneListConfig.initialSelectedUri)); - } - - // In the buttonless (watch-only) version, preemptively set our result since we won't - // have another chance to do so before the activity closes. - if (!mPickerConfig.showOkCancelButtons) { - setSuccessResultWithSelectedRingtone(); - } - - addNewRingtoneItem(); - - // Enable context menu in ringtone items - registerForContextMenu(recyclerView); - } - - /** - * Returns the fragment's sound/vibration list handler. - * @return The ringtone list handler. - */ - protected abstract RingtoneListHandler getRingtoneListHandler(); - - /** - * Starts the process to add a new ringtone to the list of ringtones asynchronously. - * Currently, only works for adding sound files. - */ - protected abstract void addRingtoneAsync(); - - /** - * Adds an item to the end of the list that can be used to add new ringtones to the list. - * Currently, only works for adding sound files. - */ - protected abstract void addNewRingtoneItem(); - - protected int getSelectedItem() { - return mRingtoneListHandler.getSelectedItemPosition(); - } - - /** - * Returns the selected URI to the caller activity. - */ - protected void setSuccessResultWithSelectedRingtone() { - requireActivity().setResult(Activity.RESULT_OK, - new Intent().putExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI, - mRingtonePickerViewModel.getSelectedRingtoneUri())); - } - - /** - * Creates a ringtone recyclerview adapter using the ringtone manager cursor. - * @return The created RingtoneListViewAdapter. - */ - protected RingtoneListViewAdapter createRingtoneListViewAdapter() { - LocalizedCursor cursor = new LocalizedCursor( - mRingtoneListHandler.getRingtoneCursor(), getResources(), COLUMN_LABEL); - return new RingtoneListViewAdapter(cursor, /* RingtoneListViewAdapterCallbacks= */ this); - } - - /** - * Sets the selected item in the list and scroll to the position in the recyclerview. - * @param pos the position of the selected item in the list. - */ - protected void setSelectedItem(int pos) { - Objects.requireNonNull(mRingtoneListViewAdapter); - mRingtoneListHandler.setSelectedItemPosition(pos); - mRingtoneListViewAdapter.setSelectedItem(pos); - mRingtoneListHandler.setSelectedItemId(mRingtoneListViewAdapter.getItemId(pos)); - mRecyclerView.scrollToPosition(pos); - } - - /** - * Adds a fixed item to the fixed items list . A fixed item is one that is not from - * the RingtoneManager. - * - * @param textResId The resource ID of the text for the item. - * @return The index of the inserted fixed item in the adapter. - */ - protected int addFixedItem(int textResId) { - return mRingtoneListViewAdapter.addTitleForFixedItem(textResId); - } - - /** - * Re-query RingtoneManager for the most recent set of installed ringtones. May move the - * selected item position to match the new position of the chosen ringtone. - * <p> - * This should only need to happen after adding or removing a ringtone. - */ - protected void requeryForAdapter() { - mRingtonePickerViewModel.reinit(); - // Refresh and set a new cursor, and closing the old one. - mRingtoneListViewAdapter = createRingtoneListViewAdapter(); - mRecyclerView.setAdapter(mRingtoneListViewAdapter); - prepareRecyclerView(mRecyclerView); - - // Update selected item location. - for (int i = 0; i < mRingtoneListViewAdapter.getItemCount(); i++) { - if (mRingtoneListViewAdapter.getItemId(i) - == mRingtoneListHandler.getSelectedItemId()) { - setSelectedItem(i); - return; - } - } - - // If selected item is still unknown, then set it to the default item, if available. - // If it's not available, then attempt to set it to the silent item in the list. - int selectedPosition = mRingtoneListHandler.getDefaultItemPosition(); - - if (selectedPosition < 0) { - selectedPosition = mRingtoneListHandler.getSilentItemPosition(); - } - - setSelectedItem(selectedPosition); - } - - private int addDefaultRingtoneItem() { - int defaultItemPosInAdapter = addFixedItem( - RingtonePickerViewModel.getDefaultRingtoneItemTextByType( - mPickerConfig.ringtoneType)); - int defaultItemPosInListHandler = mRingtoneListHandler.addDefaultItem(); - - if (defaultItemPosInAdapter != defaultItemPosInListHandler) { - Log.wtf(TAG, "Default item position in adapter and list handler must match."); - return RingtoneListHandler.ITEM_POSITION_UNKNOWN; - } - - return defaultItemPosInListHandler; - } - - private int addSilentItem() { - int silentItemPosInAdapter = addFixedItem(com.android.internal.R.string.ringtone_silent); - int silentItemPosInListHandler = mRingtoneListHandler.addSilentItem(); - - if (silentItemPosInAdapter != silentItemPosInListHandler) { - Log.wtf(TAG, "Silent item position in adapter and list handler must match."); - return RingtoneListHandler.ITEM_POSITION_UNKNOWN; - } - - return silentItemPosInListHandler; - } -} diff --git a/packages/SoundPicker2/src/com/android/soundpicker/CheckedListItem.java b/packages/SoundPicker2/src/com/android/soundpicker/CheckedListItem.java deleted file mode 100644 index 819ae987269d..000000000000 --- a/packages/SoundPicker2/src/com/android/soundpicker/CheckedListItem.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2016 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.soundpicker; - -import android.content.Context; -import android.util.AttributeSet; -import android.widget.Checkable; -import android.widget.CheckedTextView; -import android.widget.RelativeLayout; - -/** - * The {@link CheckedListItem} is a layout item that represents a ringtone, and is used in - * {@link RingtonePickerActivity}. It contains the ringtone's name, and a work badge to right of the - * name if the ringtone belongs to a work profile. - */ -public class CheckedListItem extends RelativeLayout implements Checkable { - - public CheckedListItem(Context context) { - super(context); - } - - public CheckedListItem(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public CheckedListItem(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public CheckedListItem(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - @Override - public void setChecked(boolean checked) { - getCheckedTextView().setChecked(checked); - } - - @Override - public boolean isChecked() { - return getCheckedTextView().isChecked(); - } - - @Override - public void toggle() { - getCheckedTextView().toggle(); - } - - private CheckedTextView getCheckedTextView() { - return (CheckedTextView) findViewById(R.id.checked_text_view); - } - -} diff --git a/packages/SoundPicker2/src/com/android/soundpicker/ListeningExecutorServiceFactory.java b/packages/SoundPicker2/src/com/android/soundpicker/ListeningExecutorServiceFactory.java deleted file mode 100644 index afdbf053ac22..000000000000 --- a/packages/SoundPicker2/src/com/android/soundpicker/ListeningExecutorServiceFactory.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2023 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.soundpicker; - -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; - -import java.util.concurrent.Executors; - -import javax.inject.Inject; -import javax.inject.Singleton; - -/** - * A factory class used to create {@link ListeningExecutorService}. - */ -@Singleton -public class ListeningExecutorServiceFactory { - - @Inject - ListeningExecutorServiceFactory() { - } - - /** - * Returns a single thread {@link ListeningExecutorService}. - * - */ - public ListeningExecutorService createSingleThreadExecutor() { - return MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()); - } -} diff --git a/packages/SoundPicker2/src/com/android/soundpicker/LocalizedCursor.java b/packages/SoundPicker2/src/com/android/soundpicker/LocalizedCursor.java deleted file mode 100644 index 83d04a345f8b..000000000000 --- a/packages/SoundPicker2/src/com/android/soundpicker/LocalizedCursor.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2023 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.soundpicker; - -import android.content.res.Resources; -import android.database.Cursor; -import android.database.CursorWrapper; -import android.util.Log; -import android.util.TypedValue; - -import androidx.annotation.Nullable; - -import java.util.Locale; -import java.util.regex.Pattern; - -/** - * A cursor wrapper class mainly used to guarantee getting a ringtone title - */ -final class LocalizedCursor extends CursorWrapper { - - private static final String TAG = "LocalizedCursor"; - private static final String SOUND_NAME_RES_PREFIX = "sound_name_"; - - private final int mTitleIndex; - private final Resources mResources; - private final Pattern mSanitizePattern; - private final String mNamePrefix; - - LocalizedCursor(Cursor cursor, Resources resources, String columnLabel) { - super(cursor); - mTitleIndex = mCursor.getColumnIndex(columnLabel); - mResources = resources; - mSanitizePattern = Pattern.compile("[^a-zA-Z0-9]"); - if (mTitleIndex == -1) { - Log.e(TAG, "No index for column " + columnLabel); - mNamePrefix = null; - } else { - mNamePrefix = buildNamePrefix(mResources); - } - } - - /** - * Builds the prefix for the name of the resource to look up. - * The format is: "ResourcePackageName::ResourceTypeName/" (the type name is expected to be - * "string" but let's not hardcode it). - * Here we use an existing resource "notification_sound_default" which is always expected to be - * found. - * - * @param resources Application's resources - * @return the built name prefix, or null if failed to build. - */ - @Nullable - private static String buildNamePrefix(Resources resources) { - try { - return String.format("%s:%s/%s", - resources.getResourcePackageName(R.string.notification_sound_default), - resources.getResourceTypeName(R.string.notification_sound_default), - SOUND_NAME_RES_PREFIX); - } catch (Resources.NotFoundException e) { - Log.e(TAG, "Failed to build the prefix for the name of the resource.", e); - } - - return null; - } - - /** - * Process resource name to generate a valid resource name. - * - * @return a non-null String - */ - private String sanitize(String input) { - if (input == null) { - return ""; - } - return mSanitizePattern.matcher(input).replaceAll("_").toLowerCase(Locale.ROOT); - } - - @Override - public String getString(int columnIndex) { - final String defaultName = mCursor.getString(columnIndex); - if ((columnIndex != mTitleIndex) || (mNamePrefix == null)) { - return defaultName; - } - TypedValue value = new TypedValue(); - try { - // the name currently in the database is used to derive a name to match - // against resource names in this package - mResources.getValue(mNamePrefix + sanitize(defaultName), value, - /* resolveRefs= */ false); - } catch (Resources.NotFoundException e) { - Log.d(TAG, "Failed to get localized string. Using default string instead.", e); - return defaultName; - } - if ((value != null) && (value.type == TypedValue.TYPE_STRING)) { - Log.d(TAG, String.format("Replacing name %s with %s", - defaultName, value.string.toString())); - return value.string.toString(); - } else { - Log.e(TAG, "Invalid value when looking up localized name, using " + defaultName); - return defaultName; - } - } -} diff --git a/packages/SoundPicker2/src/com/android/soundpicker/RingtoneFactory.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneFactory.java deleted file mode 100644 index cb41eabfa87a..000000000000 --- a/packages/SoundPicker2/src/com/android/soundpicker/RingtoneFactory.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2023 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.soundpicker; - -import android.content.Context; -import android.media.AudioAttributes; -import android.media.Ringtone; -import android.net.Uri; - -import dagger.hilt.android.qualifiers.ApplicationContext; - -import javax.inject.Inject; -import javax.inject.Singleton; - -/** - * A factory class used to create {@link Ringtone}. - */ -@Singleton -public class RingtoneFactory { - - private final Context mApplicationContext; - - @Inject - RingtoneFactory(@ApplicationContext Context applicationContext) { - mApplicationContext = applicationContext; - } - - /** - * Returns a {@link Ringtone} built from the provided URI and audio attributes flags. - * - * @param uri The URI used to build the {@link Ringtone}. - * @param audioAttributesFlags A combination of audio attribute flags that affect the volume - * and settings when playing the ringtone. - * @return the built {@link Ringtone}. - */ - public Ringtone create(Uri uri, int audioAttributesFlags) { - AudioAttributes audioAttributes = new AudioAttributes.Builder() - .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) - .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) - .setFlags(audioAttributesFlags) - .build(); - // TODO: We are currently only using MEDIA_SOUND for enabledMedia. This will change once we - // start playing sound and/or vibration. - return new Ringtone.Builder(mApplicationContext, Ringtone.MEDIA_SOUND, audioAttributes) - .setUri(uri) - .build(); - } -} diff --git a/packages/SoundPicker2/src/com/android/soundpicker/RingtoneListHandler.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneListHandler.java deleted file mode 100644 index bb38e0e2ecaa..000000000000 --- a/packages/SoundPicker2/src/com/android/soundpicker/RingtoneListHandler.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (C) 2023 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.soundpicker; - -import static java.util.Objects.requireNonNull; - -import android.annotation.Nullable; -import android.database.Cursor; -import android.media.RingtoneManager; -import android.net.Uri; -import android.util.Log; - -import androidx.annotation.NonNull; - -import javax.inject.Inject; - -/** - * Handles ringtone list state and actions. This includes keeping track of the selected item, - * ringtone manager cursor and added items to the list. - */ -public class RingtoneListHandler { - - // TODO: We're using an empty URI instead of null, because null URIs still produce a sound, - // while empty ones don't (Potentially this might be due to empty URIs being perceived as - // malformed ones). We will switch to using the official silent URIs (SOUND_OFF, VIBRATION_OFF) - // once they become available. - static final Uri SILENT_URI = Uri.EMPTY; - static final int ITEM_POSITION_UNKNOWN = -1; - - private static final String TAG = "RingtoneListHandler"; - - /** The position in the list of the 'Silent' item. */ - private int mSilentItemPosition = ITEM_POSITION_UNKNOWN; - /** The position in the list of the 'Default' item. */ - private int mDefaultItemPosition = ITEM_POSITION_UNKNOWN; - /** The number of fixed items in the list. */ - private int mFixedItemCount; - /** - * Stable ID for the ringtone that is currently selected (may be -1 if no ringtone is selected). - */ - private long mSelectedItemId = -1; - private int mSelectedItemPosition = ITEM_POSITION_UNKNOWN; - - private RingtoneManager mRingtoneManager; - private Config mRingtoneListConfig; - private Cursor mRingtoneCursor; - - /** - * Holds immutable info on the ringtone list that is displayed. - */ - static final class Config { - /** - * Whether this list has the 'Default' item. - */ - public final boolean hasDefaultItem; - /** - * The Uri to play when the 'Default' item is clicked. - */ - public final Uri uriForDefaultItem; - /** - * Whether this list has the 'Silent' item. - */ - public final boolean hasSilentItem; - /** - * The initially selected uri in the list. - */ - public final Uri initialSelectedUri; - - Config(boolean hasDefaultItem, Uri uriForDefaultItem, boolean hasSilentItem, - Uri initialSelectedUri) { - this.hasDefaultItem = hasDefaultItem; - this.uriForDefaultItem = uriForDefaultItem; - this.hasSilentItem = hasSilentItem; - this.initialSelectedUri = initialSelectedUri; - } - } - - @Inject - RingtoneListHandler() { - } - - void init(@NonNull Config ringtoneListConfig, - @NonNull RingtoneManager ringtoneManager, @NonNull Cursor ringtoneCursor) { - mRingtoneManager = requireNonNull(ringtoneManager); - mRingtoneListConfig = requireNonNull(ringtoneListConfig); - mRingtoneCursor = requireNonNull(ringtoneCursor); - } - - Config getRingtoneListConfig() { - return mRingtoneListConfig; - } - - Cursor getRingtoneCursor() { - requireInitCalled(); - return mRingtoneCursor; - } - - Uri getRingtoneUri(int position) { - if (position < 0) { - Log.w(TAG, "Selected item position is unknown."); - // When the selected item is ITEM_POSITION_UNKNOWN, it is not the case we expected. - // We return SILENT_URI for this case. - return SILENT_URI; - } else if (position == mDefaultItemPosition) { - // Use the default Uri that they originally gave us. - return mRingtoneListConfig.uriForDefaultItem; - } else if (position == mSilentItemPosition) { - // Use SILENT_URI for the 'Silent' item. - return SILENT_URI; - } else { - requireInitCalled(); - return mRingtoneManager.getRingtoneUri(mapListPositionToRingtonePosition(position)); - } - } - - int getRingtonePosition(Uri uri) { - requireInitCalled(); - return mapRingtonePositionToListPosition(mRingtoneManager.getRingtonePosition(uri)); - } - - void resetFixedItems() { - mFixedItemCount = 0; - mDefaultItemPosition = ITEM_POSITION_UNKNOWN; - mSilentItemPosition = ITEM_POSITION_UNKNOWN; - } - - int addDefaultItem() { - if (mDefaultItemPosition < 0) { - mDefaultItemPosition = addFixedItem(); - } - return mDefaultItemPosition; - } - - int getDefaultItemPosition() { - return mDefaultItemPosition; - } - - int addSilentItem() { - if (mSilentItemPosition < 0) { - mSilentItemPosition = addFixedItem(); - } - return mSilentItemPosition; - } - - public int getSilentItemPosition() { - return mSilentItemPosition; - } - - int getSelectedItemPosition() { - return mSelectedItemPosition; - } - - void setSelectedItemPosition(int selectedItemPosition) { - mSelectedItemPosition = selectedItemPosition; - } - - void setSelectedItemId(long selectedItemId) { - mSelectedItemId = selectedItemId; - } - - long getSelectedItemId() { - return mSelectedItemId; - } - - @Nullable - Uri getSelectedRingtoneUri() { - return getRingtoneUri(mSelectedItemPosition); - } - - /** - * Maps the item position in the list, to its equivalent position in the RingtoneManager. - * - * @param itemPosition the position of item in the list. - * @return position of the item in the RingtoneManager. - */ - private int mapListPositionToRingtonePosition(int itemPosition) { - // If the manager position is less than add items, then return that. - if (itemPosition < mFixedItemCount) return itemPosition; - - return itemPosition - mFixedItemCount; - } - - /** - * Maps the item position in the RingtoneManager, to its equivalent position in the list. - * - * @param itemPosition the position of the item in the RingtoneManager. - * @return position of the item in the list. - */ - private int mapRingtonePositionToListPosition(int itemPosition) { - // If the manager position is less than add items, then return that. - if (itemPosition < 0) return itemPosition; - - return itemPosition + mFixedItemCount; - } - - /** - * Increments the number of added fixed items and returns the index of the newest added item. - * @return index of the newest added fixed item. - */ - private int addFixedItem() { - return mFixedItemCount++; - } - - private void requireInitCalled() { - requireNonNull(mRingtoneManager); - requireNonNull(mRingtoneCursor); - } -} diff --git a/packages/SoundPicker2/src/com/android/soundpicker/RingtoneListViewAdapter.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneListViewAdapter.java deleted file mode 100644 index 4ca8943b5fd4..000000000000 --- a/packages/SoundPicker2/src/com/android/soundpicker/RingtoneListViewAdapter.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (C) 2023 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.soundpicker; - -import static com.android.internal.widget.RecyclerView.NO_ID; - -import android.database.Cursor; -import android.graphics.drawable.Drawable; -import android.media.RingtoneManager; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CheckedTextView; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.annotation.StringRes; -import androidx.recyclerview.widget.RecyclerView; - -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * The adapter presents a list of ringtones which may include fixed item in the list and an action - * button at the end. - * - * The adapter handles three different types of items: - * <ul> - * <li>FIXED: Fixed items are items added to the top of the list. These items can not be modified - * and their position will never change. - * <li>DYNAMIC: Dynamic items are items from the ringtone manager. These items can be modified - * and their position can change. - * <li>FOOTER: A footer item is an added button to the end of the list. This item can be clicked - * but not selected and its position will never change. - * </ul> - */ -final class RingtoneListViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { - - private static final int VIEW_TYPE_FIXED_ITEM = 0; - private static final int VIEW_TYPE_DYNAMIC_ITEM = 1; - private static final int VIEW_TYPE_ADD_RINGTONE_ITEM = 2; - private final Cursor mCursor; - private final List<Integer> mFixedItemTitles; - private final Callbacks mCallbacks; - private final int mRowIDColumn; - private int mSelectedItem = -1; - @StringRes private Integer mAddRingtoneItemTitle; - - /** Provides callbacks for the adapter. */ - interface Callbacks { - void onRingtoneSelected(int position); - void onAddRingtoneSelected(); - boolean isWorkRingtone(int position); - Drawable getWorkIconDrawable(); - } - - RingtoneListViewAdapter(Cursor cursor, - Callbacks callbacks) { - mCursor = cursor; - mCallbacks = callbacks; - mFixedItemTitles = new ArrayList<>(); - mRowIDColumn = mCursor != null ? mCursor.getColumnIndex("_id") : -1; - setHasStableIds(true); - } - - void setSelectedItem(int position) { - notifyItemChanged(mSelectedItem); - mSelectedItem = position; - notifyItemChanged(mSelectedItem); - } - - /** - * Adds title to the fixed items list and returns the index of the newest added item. - * @param textResId the title to add to the fixed items list. - * @return The index of the newest added item in the fixed items list. - */ - int addTitleForFixedItem(@StringRes int textResId) { - mFixedItemTitles.add(textResId); - notifyItemInserted(mFixedItemTitles.size() - 1); - return mFixedItemTitles.size() - 1; - } - - void addTitleForAddRingtoneItem(@StringRes int textResId) { - mAddRingtoneItemTitle = textResId; - notifyItemInserted(getItemCount() - 1); - } - - @NotNull - @Override - public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - LayoutInflater inflater = LayoutInflater.from(parent.getContext()); - - if (viewType == VIEW_TYPE_FIXED_ITEM) { - View fixedItemView = inflater.inflate( - com.android.internal.R.layout.select_dialog_singlechoice_material, parent, - false); - - return new FixedItemViewHolder(fixedItemView, mCallbacks); - } - - if (viewType == VIEW_TYPE_ADD_RINGTONE_ITEM) { - View addRingtoneItemView = inflater.inflate(R.layout.add_new_sound_item, parent, false); - - return new AddRingtoneItemViewHolder(addRingtoneItemView, - mCallbacks); - } - - View view = inflater.inflate(R.layout.radio_with_work_badge, parent, false); - - return new DynamicItemViewHolder(view, mCallbacks); - } - - @Override - public void onBindViewHolder(@NotNull RecyclerView.ViewHolder holder, int position) { - if (holder instanceof FixedItemViewHolder) { - FixedItemViewHolder viewHolder = (FixedItemViewHolder) holder; - - viewHolder.onBind(mFixedItemTitles.get(position), - /* isChecked= */ position == mSelectedItem); - return; - } - if (holder instanceof AddRingtoneItemViewHolder) { - AddRingtoneItemViewHolder viewHolder = (AddRingtoneItemViewHolder) holder; - - viewHolder.onBind(mAddRingtoneItemTitle); - return; - } - - if (!(holder instanceof DynamicItemViewHolder)) { - throw new IllegalArgumentException("holder type is not supported"); - } - - DynamicItemViewHolder viewHolder = (DynamicItemViewHolder) holder; - int pos = position - mFixedItemTitles.size(); - if (!mCursor.moveToPosition(pos)) { - throw new IllegalStateException("Could not move cursor to position: " + pos); - } - - Drawable workIcon = (mCallbacks != null) - && mCallbacks.isWorkRingtone(position) - ? mCallbacks.getWorkIconDrawable() : null; - - viewHolder.onBind(mCursor.getString(RingtoneManager.TITLE_COLUMN_INDEX), - /* isChecked= */ position == mSelectedItem, workIcon); - } - - @Override - public int getItemViewType(int position) { - if (!mFixedItemTitles.isEmpty() && position < mFixedItemTitles.size()) { - return VIEW_TYPE_FIXED_ITEM; - } - if (mAddRingtoneItemTitle != null && position == getItemCount() - 1) { - return VIEW_TYPE_ADD_RINGTONE_ITEM; - } - - return VIEW_TYPE_DYNAMIC_ITEM; - } - - @Override - public int getItemCount() { - int itemCount = mFixedItemTitles.size() + mCursor.getCount(); - - if (mAddRingtoneItemTitle != null) { - itemCount++; - } - - return itemCount; - } - - @Override - public long getItemId(int position) { - int itemViewType = getItemViewType(position); - if (itemViewType == VIEW_TYPE_FIXED_ITEM) { - // Since the item is a fixed item, then we can use the position as a stable ID - // since the order of the fixed items should never change. - return position; - } - if (itemViewType == VIEW_TYPE_DYNAMIC_ITEM && mCursor != null - && mCursor.moveToPosition(position - mFixedItemTitles.size()) - && mRowIDColumn != -1) { - return mCursor.getLong(mRowIDColumn) + mFixedItemTitles.size(); - } - - // The position is either invalid or the item is the add ringtone item view, so no stable - // ID is returned. Add ringtone item view cannot be selected and only include an action - // buttons. - return NO_ID; - } - - private static class DynamicItemViewHolder extends RecyclerView.ViewHolder { - private final CheckedTextView mTitleTextView; - private final ImageView mWorkIcon; - - DynamicItemViewHolder(View itemView, Callbacks listener) { - super(itemView); - - mTitleTextView = itemView.requireViewById(R.id.checked_text_view); - mWorkIcon = itemView.requireViewById(R.id.work_icon); - itemView.setOnClickListener(v -> listener.onRingtoneSelected(this.getLayoutPosition())); - } - - void onBind(String title, boolean isChecked, Drawable workIcon) { - mTitleTextView.setText(title); - mTitleTextView.setChecked(isChecked); - - if (workIcon == null) { - mWorkIcon.setVisibility(View.GONE); - } else { - mWorkIcon.setImageDrawable(workIcon); - mWorkIcon.setVisibility(View.VISIBLE); - } - } - } - - private static class FixedItemViewHolder extends RecyclerView.ViewHolder { - private final CheckedTextView mTitleTextView; - - FixedItemViewHolder(View itemView, Callbacks listener) { - super(itemView); - - mTitleTextView = (CheckedTextView) itemView; - itemView.setOnClickListener(v -> listener.onRingtoneSelected(this.getLayoutPosition())); - } - - void onBind(@StringRes int title, boolean isChecked) { - Objects.requireNonNull(mTitleTextView); - - mTitleTextView.setText(title); - mTitleTextView.setChecked(isChecked); - } - } - - private static class AddRingtoneItemViewHolder extends RecyclerView.ViewHolder { - private final TextView mTitleTextView; - - AddRingtoneItemViewHolder(View itemView, Callbacks listener) { - super(itemView); - - mTitleTextView = itemView.requireViewById(R.id.add_new_sound_text); - itemView.setOnClickListener(v -> listener.onAddRingtoneSelected()); - } - - void onBind(@StringRes int title) { - mTitleTextView.setText(title); - } - } -} diff --git a/packages/SoundPicker2/src/com/android/soundpicker/RingtoneManagerFactory.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneManagerFactory.java deleted file mode 100644 index f08eb24ec20d..000000000000 --- a/packages/SoundPicker2/src/com/android/soundpicker/RingtoneManagerFactory.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2023 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.soundpicker; - -import android.content.Context; -import android.media.RingtoneManager; - -import dagger.hilt.android.qualifiers.ApplicationContext; - -import javax.inject.Inject; -import javax.inject.Singleton; - -/** - * A factory class used to create {@link RingtoneManager}. - */ -@Singleton -public class RingtoneManagerFactory { - - private final Context mApplicationContext; - - @Inject - RingtoneManagerFactory(@ApplicationContext Context applicationContext) { - mApplicationContext = applicationContext; - } - - /** - * Creates a new {@link RingtoneManager} and returns it. - * - * @return a {@link RingtoneManager} - */ - public RingtoneManager create() { - return new RingtoneManager(mApplicationContext, /* includeParentRingtones */ true); - } -} - diff --git a/packages/SoundPicker2/src/com/android/soundpicker/RingtoneOverlayService.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneOverlayService.java deleted file mode 100644 index b94ebebd825b..000000000000 --- a/packages/SoundPicker2/src/com/android/soundpicker/RingtoneOverlayService.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2018 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.soundpicker; - -import android.app.Service; -import android.content.Intent; -import android.media.RingtoneManager; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Environment; -import android.os.FileUtils; -import android.os.IBinder; -import android.provider.MediaStore; -import android.provider.Settings.System; -import android.util.Log; - -import androidx.annotation.IdRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; - -/** - * Service to copy and set customization of default sounds - */ -public class RingtoneOverlayService extends Service { - private static final String TAG = "RingtoneOverlayService"; - private static final boolean DEBUG = false; - - @Override - public int onStartCommand(@Nullable final Intent intent, final int flags, final int startId) { - AsyncTask.execute(() -> { - updateRingtones(); - stopSelf(); - }); - - // Try again later if we are killed before we finish. - return Service.START_REDELIVER_INTENT; - } - - @Override - public IBinder onBind(@Nullable final Intent intent) { - return null; - } - - private void updateRingtones() { - copyResourceAndSetAsSound(R.raw.default_ringtone, - System.RINGTONE, Environment.DIRECTORY_RINGTONES); - copyResourceAndSetAsSound(R.raw.default_notification_sound, - System.NOTIFICATION_SOUND, Environment.DIRECTORY_NOTIFICATIONS); - copyResourceAndSetAsSound(R.raw.default_alarm_alert, - System.ALARM_ALERT, Environment.DIRECTORY_ALARMS); - } - - /* If the resource contains any data, copy a resource to the file system, scan it, and set the - * file URI as the default for a sound. */ - private void copyResourceAndSetAsSound(@IdRes final int id, @NonNull final String name, - @NonNull final String subPath) { - final File destDir = Environment.getExternalStoragePublicDirectory(subPath); - if (!destDir.exists() && !destDir.mkdirs()) { - Log.e(TAG, "can't create " + destDir.getAbsolutePath()); - return; - } - - final File dest = new File(destDir, "default_" + name + ".ogg"); - try ( - InputStream is = getResources().openRawResource(id); - FileOutputStream os = new FileOutputStream(dest); - ) { - if (is.available() > 0) { - FileUtils.copy(is, os); - final Uri uri = scanFile(dest); - if (uri != null) { - set(name, uri); - } - } else { - // TODO Shall we remove any former copied resource in this case and unset - // the defaults if we use this event a second time to clear the data? - if (DEBUG) Log.d(TAG, "Resource for " + name + " has no overlay"); - } - } catch (IOException e) { - Log.e(TAG, "Unable to open resource for " + name + ": " + e); - } - } - - private Uri scanFile(@NonNull final File file) { - return MediaStore.scanFile(getContentResolver(), file); - } - - private void set(@NonNull final String name, @NonNull final Uri uri) { - final Uri settingUri = System.getUriFor(name); - RingtoneManager.setActualDefaultRingtoneUri(this, - RingtoneManager.getDefaultType(settingUri), uri); - System.putInt(getContentResolver(), name + "_set", 1); - } -} diff --git a/packages/SoundPicker2/src/com/android/soundpicker/RingtonePickerActivity.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtonePickerActivity.java deleted file mode 100644 index 90a14f9717db..000000000000 --- a/packages/SoundPicker2/src/com/android/soundpicker/RingtonePickerActivity.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (C) 2007 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.soundpicker; - -import android.content.Intent; -import android.media.RingtoneManager; -import android.net.Uri; -import android.os.Bundle; -import android.os.UserHandle; -import android.util.Log; - -import androidx.appcompat.app.AppCompatActivity; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentTransaction; -import androidx.lifecycle.ViewModelProvider; - -import dagger.hilt.android.AndroidEntryPoint; - -/** - * The {@link RingtonePickerActivity} allows the user to choose one from all of the - * available ringtones. The chosen ringtone's URI will be persisted as a string. - * - * @see RingtoneManager#ACTION_RINGTONE_PICKER - */ -@AndroidEntryPoint(AppCompatActivity.class) -public final class RingtonePickerActivity extends Hilt_RingtonePickerActivity { - - private static final String TAG = "RingtonePickerActivity"; - // TODO: Use the extra keys from RingtoneManager once they're added. - private static final String EXTRA_RINGTONE_PICKER_CATEGORY = "EXTRA_RINGTONE_PICKER_CATEGORY"; - private static final String EXTRA_VIBRATION_SHOW_DEFAULT = "EXTRA_VIBRATION_SHOW_DEFAULT"; - private static final String EXTRA_VIBRATION_DEFAULT_URI = "EXTRA_VIBRATION_DEFAULT_URI"; - private static final String EXTRA_VIBRATION_SHOW_SILENT = "EXTRA_VIBRATION_SHOW_SILENT"; - private static final String EXTRA_VIBRATION_EXISTING_URI = "EXTRA_VIBRATION_EXISTING_URI"; - private static final boolean RINGTONE_PICKER_CATEGORY_FEATURE_ENABLED = false; - - private RingtonePickerViewModel mRingtonePickerViewModel; - private int mAttributesFlags; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_ringtone_picker); - - mRingtonePickerViewModel = new ViewModelProvider(this).get(RingtonePickerViewModel.class); - - Intent intent = getIntent(); - /** - * Id of the user to which the ringtone picker should list the ringtones - */ - int pickerUserId = UserHandle.myUserId(); - - // Get the types of ringtones to show - int ringtoneType = intent.getIntExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, - RingtonePickerViewModel.RINGTONE_TYPE_UNKNOWN); - - // AudioAttributes flags - mAttributesFlags |= intent.getIntExtra( - RingtoneManager.EXTRA_RINGTONE_AUDIO_ATTRIBUTES_FLAGS, - 0 /*defaultValue == no flags*/); - - boolean showOkCancelButtons = getResources().getBoolean(R.bool.config_showOkCancelButtons); - - String title = intent.getStringExtra(RingtoneManager.EXTRA_RINGTONE_TITLE); - if (title == null) { - title = getString(RingtonePickerViewModel.getTitleByType(ringtoneType)); - } - String ringtonePickerCategory = intent.getStringExtra(EXTRA_RINGTONE_PICKER_CATEGORY); - RingtonePickerViewModel.PickerType pickerType = mapCategoryToPickerType( - ringtonePickerCategory); - - RingtoneListHandler.Config soundListConfig = getSoundListConfig(pickerType, intent, - ringtoneType); - RingtoneListHandler.Config vibrationListConfig = getVibrationListConfig(pickerType, intent); - - RingtonePickerViewModel.Config pickerConfig = - new RingtonePickerViewModel.Config(title, pickerUserId, ringtoneType, - showOkCancelButtons, mAttributesFlags, pickerType); - - mRingtonePickerViewModel.init(pickerConfig, soundListConfig, vibrationListConfig); - - if (savedInstanceState == null) { - TabbedDialogFragment dialogFragment = new TabbedDialogFragment(); - - FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); - Fragment prev = getSupportFragmentManager().findFragmentByTag(TabbedDialogFragment.TAG); - if (prev != null) { - ft.remove(prev); - } - ft.addToBackStack(null); - dialogFragment.show(ft, TabbedDialogFragment.TAG); - } - - // The volume keys will control the stream that we are choosing a ringtone for - setVolumeControlStream(mRingtonePickerViewModel.getRingtoneStreamType()); - } - - private RingtoneListHandler.Config getSoundListConfig( - RingtonePickerViewModel.PickerType pickerType, Intent intent, int ringtoneType) { - if (pickerType != RingtonePickerViewModel.PickerType.SOUND_PICKER - && pickerType != RingtonePickerViewModel.PickerType.RINGTONE_PICKER) { - // This ringtone picker does not require a sound picker. - return null; - } - - // Get whether to show the 'Default' sound item, and the URI to play when it's clicked - boolean hasDefaultSoundItem = - intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true); - - // The Uri to play when the 'Default' sound item is clicked. - Uri uriForDefaultSoundItem = - intent.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI); - if (uriForDefaultSoundItem == null) { - uriForDefaultSoundItem = RingtonePickerViewModel.getDefaultItemUriByType(ringtoneType); - } - - // Get whether this list has the 'Silent' sound item. - boolean hasSilentSoundItem = - intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true); - - // AudioAttributes flags - mAttributesFlags |= intent.getIntExtra( - RingtoneManager.EXTRA_RINGTONE_AUDIO_ATTRIBUTES_FLAGS, - 0 /*defaultValue == no flags*/); - - // Get the sound URI whose list item should have a checkmark - Uri existingSoundUri = intent - .getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI); - - return new RingtoneListHandler.Config(hasDefaultSoundItem, - uriForDefaultSoundItem, hasSilentSoundItem, existingSoundUri); - } - - private RingtoneListHandler.Config getVibrationListConfig( - RingtonePickerViewModel.PickerType pickerType, Intent intent) { - if (pickerType != RingtonePickerViewModel.PickerType.VIBRATION_PICKER - && pickerType != RingtonePickerViewModel.PickerType.RINGTONE_PICKER) { - // This ringtone picker does not require a vibration picker. - return null; - } - - // Get whether to show the 'Default' vibration item, and the URI to play when it's clicked - boolean hasDefaultVibrationItem = - intent.getBooleanExtra(EXTRA_VIBRATION_SHOW_DEFAULT, false); - - // The Uri to play when the 'Default' vibration item is clicked. - Uri uriForDefaultVibrationItem = intent.getParcelableExtra(EXTRA_VIBRATION_DEFAULT_URI); - - // Get whether this list has the 'Silent' vibration item. - boolean hasSilentVibrationItem = - intent.getBooleanExtra(EXTRA_VIBRATION_SHOW_SILENT, true); - - // Get the vibration URI whose list item should have a checkmark - Uri existingVibrationUri = intent.getParcelableExtra(EXTRA_VIBRATION_EXISTING_URI); - - return new RingtoneListHandler.Config( - hasDefaultVibrationItem, uriForDefaultVibrationItem, hasSilentVibrationItem, - existingVibrationUri); - } - - @Override - public void onDestroy() { - mRingtonePickerViewModel.cancelPendingAsyncTasks(); - super.onDestroy(); - } - - @Override - protected void onStop() { - super.onStop(); - mRingtonePickerViewModel.onStop(isChangingConfigurations()); - } - - @Override - protected void onPause() { - super.onPause(); - mRingtonePickerViewModel.onPause(isChangingConfigurations()); - } - - /** - * Maps the ringtone picker category to the appropriate PickerType. - * If the category is null or the feature is still not released, then it defaults to sound - * picker. - * - * @param category the ringtone picker category. - * @return the corresponding picker type. - */ - private static RingtonePickerViewModel.PickerType mapCategoryToPickerType(String category) { - if (category == null || !RINGTONE_PICKER_CATEGORY_FEATURE_ENABLED) { - return RingtonePickerViewModel.PickerType.SOUND_PICKER; - } - - switch (category) { - case "android.intent.category.RINGTONE_PICKER_RINGTONE": - return RingtonePickerViewModel.PickerType.RINGTONE_PICKER; - case "android.intent.category.RINGTONE_PICKER_SOUND": - return RingtonePickerViewModel.PickerType.SOUND_PICKER; - case "android.intent.category.RINGTONE_PICKER_VIBRATION": - return RingtonePickerViewModel.PickerType.VIBRATION_PICKER; - default: - Log.w(TAG, "Unrecognized category: " + category + ". Defaulting to sound picker."); - return RingtonePickerViewModel.PickerType.SOUND_PICKER; - } - } -} diff --git a/packages/SoundPicker2/src/com/android/soundpicker/RingtonePickerApplication.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtonePickerApplication.java deleted file mode 100644 index 48fd4fe2f15e..000000000000 --- a/packages/SoundPicker2/src/com/android/soundpicker/RingtonePickerApplication.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2023 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.soundpicker; - -import android.app.Application; - -import dagger.hilt.android.HiltAndroidApp; - -/** - * The main application class for the project. - */ -@HiltAndroidApp(Application.class) -public class RingtonePickerApplication extends Hilt_RingtonePickerApplication { -} diff --git a/packages/SoundPicker2/src/com/android/soundpicker/RingtonePickerViewModel.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtonePickerViewModel.java deleted file mode 100644 index 2c0971121ccd..000000000000 --- a/packages/SoundPicker2/src/com/android/soundpicker/RingtonePickerViewModel.java +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright (C) 2023 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.soundpicker; - -import static java.util.Objects.requireNonNull; - -import android.annotation.Nullable; -import android.annotation.StringRes; -import android.media.Ringtone; -import android.media.RingtoneManager; -import android.net.Uri; -import android.provider.Settings; - -import androidx.annotation.NonNull; -import androidx.lifecycle.ViewModel; - -import com.android.internal.annotations.VisibleForTesting; - -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListeningExecutorService; - -import dagger.hilt.android.lifecycle.HiltViewModel; - -import java.io.IOException; -import java.util.concurrent.Executor; - -import javax.inject.Inject; - -/** - * A view model which holds immutable info about the picker state and means to retrieve and play - * currently selected ringtones. - */ -@HiltViewModel -public final class RingtonePickerViewModel extends ViewModel { - - static final int RINGTONE_TYPE_UNKNOWN = -1; - - /** - * Keep the currently playing ringtone around when changing orientation, so that it - * can be stopped later, after the activity is recreated. - */ - @VisibleForTesting - static Ringtone sPlayingRingtone; - - private static final String TAG = "RingtonePickerViewModel"; - - private final RingtoneManagerFactory mRingtoneManagerFactory; - private final RingtoneFactory mRingtoneFactory; - private final RingtoneListHandler mSoundListHandler; - private final RingtoneListHandler mVibrationListHandler; - private final ListeningExecutorService mListeningExecutorService; - - private RingtoneManager mRingtoneManager; - - /** - * The ringtone that's currently playing. - */ - private Ringtone mCurrentRingtone; - - private Config mPickerConfig; - - private ListenableFuture<Uri> mAddCustomRingtoneFuture; - - public enum PickerType { - RINGTONE_PICKER, - SOUND_PICKER, - VIBRATION_PICKER - } - - /** - * Holds immutable info on the picker that should be displayed. - */ - static final class Config { - public final String title; - /** - * Id of the user to which the ringtone picker should list the ringtones. - */ - public final int userId; - /** - * Ringtone type. - */ - public final int ringtoneType; - /** - * AudioAttributes flags. - */ - public final int audioAttributesFlags; - /** - * In the buttonless (watch-only) version we don't show the OK/Cancel buttons. - */ - public final boolean showOkCancelButtons; - - public final PickerType mPickerType; - - Config(String title, int userId, int ringtoneType, boolean showOkCancelButtons, - int audioAttributesFlags, PickerType pickerType) { - this.title = title; - this.userId = userId; - this.ringtoneType = ringtoneType; - this.showOkCancelButtons = showOkCancelButtons; - this.audioAttributesFlags = audioAttributesFlags; - this.mPickerType = pickerType; - } - } - - @Inject - RingtonePickerViewModel(RingtoneManagerFactory ringtoneManagerFactory, - RingtoneFactory ringtoneFactory, - ListeningExecutorServiceFactory listeningExecutorServiceFactory, - RingtoneListHandler soundListHandler, - RingtoneListHandler vibrationListHandler) { - mRingtoneManagerFactory = ringtoneManagerFactory; - mRingtoneFactory = ringtoneFactory; - mListeningExecutorService = listeningExecutorServiceFactory.createSingleThreadExecutor(); - mSoundListHandler = soundListHandler; - mVibrationListHandler = vibrationListHandler; - } - - @StringRes - static int getTitleByType(int ringtoneType) { - switch (ringtoneType) { - case RingtoneManager.TYPE_ALARM: - return com.android.internal.R.string.ringtone_picker_title_alarm; - case RingtoneManager.TYPE_NOTIFICATION: - return com.android.internal.R.string.ringtone_picker_title_notification; - default: - return com.android.internal.R.string.ringtone_picker_title; - } - } - - static Uri getDefaultItemUriByType(int ringtoneType) { - switch (ringtoneType) { - case RingtoneManager.TYPE_ALARM: - return Settings.System.DEFAULT_ALARM_ALERT_URI; - case RingtoneManager.TYPE_NOTIFICATION: - return Settings.System.DEFAULT_NOTIFICATION_URI; - default: - return Settings.System.DEFAULT_RINGTONE_URI; - } - } - - @StringRes - static int getAddNewItemTextByType(int ringtoneType) { - switch (ringtoneType) { - case RingtoneManager.TYPE_ALARM: - return R.string.add_alarm_text; - case RingtoneManager.TYPE_NOTIFICATION: - return R.string.add_notification_text; - default: - return R.string.add_ringtone_text; - } - } - - @StringRes - static int getDefaultRingtoneItemTextByType(int ringtoneType) { - switch (ringtoneType) { - case RingtoneManager.TYPE_ALARM: - return R.string.alarm_sound_default; - case RingtoneManager.TYPE_NOTIFICATION: - return R.string.notification_sound_default; - default: - return R.string.ringtone_default; - } - } - - void init(@NonNull Config pickerConfig, - RingtoneListHandler.Config soundListConfig, - RingtoneListHandler.Config vibrationListConfig) { - mRingtoneManager = mRingtoneManagerFactory.create(); - mPickerConfig = pickerConfig; - if (mPickerConfig.ringtoneType != RINGTONE_TYPE_UNKNOWN) { - mRingtoneManager.setType(mPickerConfig.ringtoneType); - } - if (soundListConfig != null) { - mSoundListHandler.init(soundListConfig, mRingtoneManager, - mRingtoneManager.getCursor()); - } - if (vibrationListConfig != null) { - // TODO: Switch to the vibration cursor, once the API is made available. - mVibrationListHandler.init(vibrationListConfig, mRingtoneManager, - mRingtoneManager.getCursor()); - } - } - - /** - * Re-initializes the view model which is required after updating any of the picker lists. - * This could happen when adding a custom ringtone. - */ - void reinit() { - init(mPickerConfig, mSoundListHandler.getRingtoneListConfig(), - mVibrationListHandler.getRingtoneListConfig()); - } - - @NonNull - Config getPickerConfig() { - requireInitCalled(); - return mPickerConfig; - } - - @NonNull - RingtoneListHandler getSoundListHandler() { - return mSoundListHandler; - } - - @NonNull - RingtoneListHandler getVibrationListHandler() { - return mVibrationListHandler; - } - - /** - * Combined the currently selected sound and vibration URIs and returns a unified URI. If the - * picker does not show either sound or vibration, that portion of the URI will be null. - * - * Currently only the sound URI is returned, since we don't have the API to retrieve vibrations - * yet. - * @return Combined sound and vibration URI. - */ - Uri getSelectedRingtoneUri() { - // TODO: Combine sound and vibration URIs before returning. - return mSoundListHandler.getSelectedRingtoneUri(); - } - - int getRingtoneStreamType() { - requireInitCalled(); - return mRingtoneManager.inferStreamType(); - } - - void onPause(boolean isChangingConfigurations) { - if (!isChangingConfigurations) { - stopAnyPlayingRingtone(); - } - } - - void onStop(boolean isChangingConfigurations) { - if (isChangingConfigurations) { - saveAnyPlayingRingtone(); - } else { - stopAnyPlayingRingtone(); - } - } - - /** - * Plays a ringtone which is created using the currently selected sound and vibration URIs. If - * this is a sound or vibration only picker, then the other portion of the URI will be empty - * and should not affect the played ringtone. - * - * Currently, we only use the sound URI to create the ringtone, since we still don't have the - * API to retrieve the available vibrations list. - */ - void playRingtone() { - requireInitCalled(); - stopAnyPlayingRingtone(); - - mCurrentRingtone = mRingtoneFactory.create(getSelectedRingtoneUri(), - mPickerConfig.audioAttributesFlags); - - if (mCurrentRingtone != null) { - mCurrentRingtone.play(); - } - } - - /** - * Cancels all pending async tasks. - */ - void cancelPendingAsyncTasks() { - if (mAddCustomRingtoneFuture != null && !mAddCustomRingtoneFuture.isDone()) { - mAddCustomRingtoneFuture.cancel(/* mayInterruptIfRunning= */ true); - } - } - - /** - * Adds an audio file to the list of ringtones asynchronously. - * Any previous async tasks are canceled before start the new one. - * - * @param uri Uri of the file to be added as ringtone. Must be a media file. - * @param type The type of the ringtone to be added. - * @param callback The callback to invoke when the task is completed. - * @param executor The executor to run the callback on when the task completes. - */ - void addSoundRingtoneAsync(Uri uri, int type, FutureCallback<Uri> callback, Executor executor) { - // Cancel any currently running add ringtone tasks before starting a new one - cancelPendingAsyncTasks(); - mAddCustomRingtoneFuture = mListeningExecutorService.submit( - () -> addRingtone(uri, type)); - Futures.addCallback(mAddCustomRingtoneFuture, callback, executor); - } - - /** - * Adds an audio file to the list of ringtones. - * - * @param uri Uri of the file to be added as ringtone. Must be a media file. - * @param type The type of the ringtone to be added. - * @return The Uri of the installed ringtone, which may be the {@code uri} if it - * is already in ringtone storage. Or null if it failed to add the audio file. - */ - @Nullable - private Uri addRingtone(Uri uri, int type) throws IOException { - requireInitCalled(); - return mRingtoneManager.addCustomExternalRingtone(uri, type); - } - - private void saveAnyPlayingRingtone() { - if (mCurrentRingtone != null && mCurrentRingtone.isPlaying()) { - sPlayingRingtone = mCurrentRingtone; - } - mCurrentRingtone = null; - } - - private void stopAnyPlayingRingtone() { - if (sPlayingRingtone != null && sPlayingRingtone.isPlaying()) { - sPlayingRingtone.stop(); - } - sPlayingRingtone = null; - - if (mCurrentRingtone != null && mCurrentRingtone.isPlaying()) { - mCurrentRingtone.stop(); - } - mCurrentRingtone = null; - } - - private void requireInitCalled() { - requireNonNull(mRingtoneManager); - requireNonNull(mPickerConfig); - } -} diff --git a/packages/SoundPicker2/src/com/android/soundpicker/RingtoneReceiver.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneReceiver.java deleted file mode 100644 index 6a349366e744..000000000000 --- a/packages/SoundPicker2/src/com/android/soundpicker/RingtoneReceiver.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2007 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.soundpicker; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - -public class RingtoneReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (Intent.ACTION_DEVICE_CUSTOMIZATION_READY.equals(action)) { - initResourceRingtones(context); - } - } - - private void initResourceRingtones(Context context) { - context.startService( - new Intent(context, RingtoneOverlayService.class)); - } -} diff --git a/packages/SoundPicker2/src/com/android/soundpicker/SoundPickerFragment.java b/packages/SoundPicker2/src/com/android/soundpicker/SoundPickerFragment.java deleted file mode 100644 index a37191f33668..000000000000 --- a/packages/SoundPicker2/src/com/android/soundpicker/SoundPickerFragment.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2023 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.soundpicker; - -import android.app.Activity; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.os.Environment; -import android.util.Log; -import android.view.View; -import android.widget.Toast; - -import androidx.activity.result.ActivityResult; -import androidx.activity.result.ActivityResultCallback; -import androidx.activity.result.ActivityResultLauncher; -import androidx.activity.result.contract.ActivityResultContracts; -import androidx.core.content.ContextCompat; -import androidx.lifecycle.ViewModelProvider; - -import com.google.common.util.concurrent.FutureCallback; - -import org.jetbrains.annotations.NotNull; - -/** - * A fragment that displays a picker used to select sound or silent. It also includes the - * ability to add custom sounds. - */ -public class SoundPickerFragment extends BasePickerFragment { - - private static final String TAG = "SoundPickerFragment"; - - private final FutureCallback<Uri> mAddCustomRingtoneCallback = new FutureCallback<>() { - @Override - public void onSuccess(Uri ringtoneUri) { - requeryForAdapter(); - } - - @Override - public void onFailure(Throwable throwable) { - Log.e(TAG, "Failed to add custom ringtone.", throwable); - // Ringtone was not added, display error Toast - Toast.makeText(requireActivity().getApplicationContext(), - R.string.unable_to_add_ringtone, Toast.LENGTH_SHORT).show(); - } - }; - - ActivityResultLauncher<Intent> mActivityResultLauncher = registerForActivityResult( - new ActivityResultContracts.StartActivityForResult(), - new ActivityResultCallback<ActivityResult>() { - @Override - public void onActivityResult(ActivityResult result) { - if (result.getResultCode() == Activity.RESULT_OK) { - // There are no request codes - Intent data = result.getData(); - mRingtonePickerViewModel.addSoundRingtoneAsync(data.getData(), - mPickerConfig.ringtoneType, - mAddCustomRingtoneCallback, - // Causes the callback to be executed on the main thread. - ContextCompat.getMainExecutor( - requireActivity().getApplicationContext())); - } - } - }); - - @Override - public void onViewCreated(@NotNull View view, Bundle savedInstanceState) { - mRingtonePickerViewModel = new ViewModelProvider(requireActivity()).get( - RingtonePickerViewModel.class); - super.onViewCreated(view, savedInstanceState); - } - - @Override - protected RingtoneListHandler getRingtoneListHandler() { - return mRingtonePickerViewModel.getSoundListHandler(); - } - - @Override - protected void addRingtoneAsync() { - // The "Add new ringtone" item was clicked. Start a file picker intent to - // select only audio files (MIME type "audio/*") - final Intent chooseFile = getMediaFilePickerIntent(); - mActivityResultLauncher.launch(chooseFile); - } - - @Override - protected void addNewRingtoneItem() { - // If external storage is available, add a button to install sounds from storage. - if (resolvesMediaFilePicker() - && Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - mRingtoneListViewAdapter.addTitleForAddRingtoneItem( - RingtonePickerViewModel.getAddNewItemTextByType(mPickerConfig.ringtoneType)); - } - } - - private boolean resolvesMediaFilePicker() { - return getMediaFilePickerIntent().resolveActivity(requireActivity().getPackageManager()) - != null; - } - - private Intent getMediaFilePickerIntent() { - final Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT); - chooseFile.setType("audio/*"); - chooseFile.putExtra(Intent.EXTRA_MIME_TYPES, - new String[]{"audio/*", "application/ogg"}); - return chooseFile; - } -} diff --git a/packages/SoundPicker2/src/com/android/soundpicker/TabbedDialogFragment.java b/packages/SoundPicker2/src/com/android/soundpicker/TabbedDialogFragment.java deleted file mode 100644 index 50ea9d7d3056..000000000000 --- a/packages/SoundPicker2/src/com/android/soundpicker/TabbedDialogFragment.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (C) 2023 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.soundpicker; - -import static android.app.Activity.RESULT_CANCELED; - -import android.app.Activity; -import android.app.Dialog; -import android.content.DialogInterface; -import android.content.Intent; -import android.media.RingtoneManager; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; -import androidx.fragment.app.DialogFragment; -import androidx.lifecycle.ViewModelProvider; -import androidx.viewpager2.widget.ViewPager2; - -import com.google.android.material.tabs.TabLayout; -import com.google.android.material.tabs.TabLayoutMediator; - -import dagger.hilt.android.AndroidEntryPoint; - -import org.jetbrains.annotations.NotNull; - -/** - * A dialog fragment with a sound and/or vibration tab based on the picker type. - * <ul> - * <li> Ringtone Pickers will display both sound and vibration tabs. - * <li> Sound Pickers will only display the sound tab. - * <li> Vibration Pickers will only display the vibration tab. - * </ul> - */ -@AndroidEntryPoint(DialogFragment.class) -public class TabbedDialogFragment extends Hilt_TabbedDialogFragment { - - static final String TAG = "TabbedDialogFragment"; - - private RingtonePickerViewModel mRingtonePickerViewModel; - - private final ViewPager2.OnPageChangeCallback mOnPageChangeCallback = - new ViewPager2.OnPageChangeCallback() { - @Override - public void onPageScrollStateChanged(int state) { - super.onPageScrollStateChanged(state); - if (state == ViewPager2.SCROLL_STATE_IDLE) { - mRingtonePickerViewModel.onStop(/* isChangingConfigurations= */ false); - } - } - }; - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mRingtonePickerViewModel = new ViewModelProvider(requireActivity()).get( - RingtonePickerViewModel.class); - } - - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(requireActivity(), - android.R.style.ThemeOverlay_Material_Dialog) - .setTitle(mRingtonePickerViewModel.getPickerConfig().title); - // Do not show OK/Cancel buttons in the buttonless (watch-only) version. - if (mRingtonePickerViewModel.getPickerConfig().showOkCancelButtons) { - dialogBuilder - .setPositiveButton(getString(com.android.internal.R.string.ok), - (dialog, whichButton) -> { - setSuccessResultWithSelectedRingtone(); - requireActivity().finish(); - }) - .setNegativeButton(getString(com.android.internal.R.string.cancel), - (dialog, whichButton) -> { - requireActivity().setResult(RESULT_CANCELED); - requireActivity().finish(); - }); - } - - View view = buildTabbedView(requireActivity().getLayoutInflater()); - dialogBuilder.setView(view); - - return dialogBuilder.create(); - } - - @Override - public void onCancel(@NonNull @NotNull DialogInterface dialog) { - super.onCancel(dialog); - if (!requireActivity().isChangingConfigurations()) { - requireActivity().finish(); - } - } - - @Override - public void onDismiss(@NonNull @NotNull DialogInterface dialog) { - super.onDismiss(dialog); - if (!requireActivity().isChangingConfigurations()) { - requireActivity().finish(); - } - } - - private void setSuccessResultWithSelectedRingtone() { - requireActivity().setResult(Activity.RESULT_OK, - new Intent().putExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI, - mRingtonePickerViewModel.getSelectedRingtoneUri())); - } - - /** - * Inflates the tabbed layout view and adds the required fragments. If there's only one - * fragment to display, then the tab area is hidden. - * @param inflater The LayoutInflater that is used to inflate the tabbed view. - * @return The tabbed view. - */ - private View buildTabbedView(@NonNull LayoutInflater inflater) { - View view = inflater.inflate(R.layout.fragment_tabbed_dialog, null, false); - TabLayout tabLayout = view.requireViewById(R.id.tabLayout); - ViewPager2 viewPager = view.requireViewById(R.id.masterViewPager); - - ViewPagerAdapter adapter = new ViewPagerAdapter(requireActivity()); - addFragments(adapter); - - if (adapter.getItemCount() == 1) { - // Hide the tab area since there's only one fragment to display. - tabLayout.setVisibility(View.GONE); - } - - viewPager.setAdapter(adapter); - viewPager.registerOnPageChangeCallback(mOnPageChangeCallback); - new TabLayoutMediator(tabLayout, viewPager, - (tab, position) -> tab.setText(adapter.getTitle(position))).attach(); - - return view; - } - - /** - * Adds the appropriate fragments to the adapter based on the PickerType. - * - * @param adapter The adapter to add the fragments to. - */ - private void addFragments(ViewPagerAdapter adapter) { - switch (mRingtonePickerViewModel.getPickerConfig().mPickerType) { - case RINGTONE_PICKER: - adapter.addFragment(getString(R.string.sound_page_title), - new SoundPickerFragment()); - adapter.addFragment(getString(R.string.vibration_page_title), - new VibrationPickerFragment()); - break; - case SOUND_PICKER: - adapter.addFragment(getString(R.string.sound_page_title), - new SoundPickerFragment()); - break; - case VIBRATION_PICKER: - adapter.addFragment(getString(R.string.vibration_page_title), - new VibrationPickerFragment()); - break; - default: - adapter.addFragment(getString(R.string.sound_page_title), - new SoundPickerFragment()); - break; - } - } -} diff --git a/packages/SoundPicker2/src/com/android/soundpicker/VibrationPickerFragment.java b/packages/SoundPicker2/src/com/android/soundpicker/VibrationPickerFragment.java deleted file mode 100644 index 7412c1995b5a..000000000000 --- a/packages/SoundPicker2/src/com/android/soundpicker/VibrationPickerFragment.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2023 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.soundpicker; - -import android.os.Bundle; -import android.view.View; - -import androidx.lifecycle.ViewModelProvider; - -import org.jetbrains.annotations.NotNull; - -/** - * A fragment that displays a picker used to select vibration or silent (no vibration). - */ -public class VibrationPickerFragment extends BasePickerFragment { - - @Override - public void onViewCreated(@NotNull View view, Bundle savedInstanceState) { - mRingtonePickerViewModel = new ViewModelProvider(requireActivity()).get( - RingtonePickerViewModel.class); - super.onViewCreated(view, savedInstanceState); - } - - @Override - protected RingtoneListHandler getRingtoneListHandler() { - return mRingtonePickerViewModel.getVibrationListHandler(); - } - - @Override - protected void addRingtoneAsync() { - // no-op - } - - @Override - protected void addNewRingtoneItem() { - // no-op - } -} diff --git a/packages/SoundPicker2/src/com/android/soundpicker/ViewPagerAdapter.java b/packages/SoundPicker2/src/com/android/soundpicker/ViewPagerAdapter.java deleted file mode 100644 index 179068e9f20f..000000000000 --- a/packages/SoundPicker2/src/com/android/soundpicker/ViewPagerAdapter.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2023 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.soundpicker; - -import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentActivity; -import androidx.viewpager2.adapter.FragmentStateAdapter; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * An adapter used to populate pages inside a ViewPager. - */ -public class ViewPagerAdapter extends FragmentStateAdapter { - - private final List<Fragment> mFragments = new ArrayList<>(); - private final List<String> mTitles = new ArrayList<>(); - - public ViewPagerAdapter(@NonNull FragmentActivity fragmentActivity) { - super(fragmentActivity); - } - - /** - * Adds a fragment and page title to the adapter. - * @param title the title of the page in the ViewPager. - * @param fragment the fragment that will be inflated on this page. - */ - public void addFragment(String title, Fragment fragment) { - mTitles.add(title); - mFragments.add(fragment); - } - - /** - * Returns the title of the requested page. - * @param position the position of the page in the Viewpager. - * @return The title of the requested page. - */ - public String getTitle(int position) { - return mTitles.get(position); - } - - @NonNull - @Override - public Fragment createFragment(int position) { - return Objects.requireNonNull(mFragments.get(position), - "Could not find a fragment using position: " + position); - } - - @Override - public int getItemCount() { - return mFragments.size(); - } -} diff --git a/packages/SoundPicker2/tests/Android.bp b/packages/SoundPicker2/tests/Android.bp deleted file mode 100644 index d88d442afa17..000000000000 --- a/packages/SoundPicker2/tests/Android.bp +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2023, 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 { - default_applicable_licenses: ["frameworks_base_license"], -} - -android_test { - name: "SoundPicker2Tests", - certificate: "platform", - libs: [ - "android.test.runner", - "android.test.base", - ], - static_libs: [ - "androidx.test.core", - "androidx.test.rules", - "androidx.test.ext.junit", - "androidx.test.ext.truth", - "mockito-target-minus-junit4", - "guava-android-testlib", - "SoundPicker2Lib", - ], - srcs: [ - "src/**/*.java", - ], -} diff --git a/packages/SoundPicker2/tests/AndroidManifest.xml b/packages/SoundPicker2/tests/AndroidManifest.xml deleted file mode 100644 index 295aeb1faa55..000000000000 --- a/packages/SoundPicker2/tests/AndroidManifest.xml +++ /dev/null @@ -1,11 +0,0 @@ -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.soundpicker.tests"> - - <application android:debuggable="true"> - <uses-library android:name="android.test.runner" /> - </application> - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.soundpicker.tests" - android:label="Sound picker tests"> - </instrumentation> -</manifest> diff --git a/packages/SoundPicker2/tests/src/com/android/soundpicker/RingtoneListHandlerTest.java b/packages/SoundPicker2/tests/src/com/android/soundpicker/RingtoneListHandlerTest.java deleted file mode 100644 index 80e71e200a53..000000000000 --- a/packages/SoundPicker2/tests/src/com/android/soundpicker/RingtoneListHandlerTest.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (C) 2023 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.soundpicker; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -import android.database.Cursor; -import android.media.RingtoneManager; -import android.net.Uri; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -public class RingtoneListHandlerTest { - - private static final Uri DEFAULT_URI = Uri.parse("media://custom/ringtone/default_uri"); - private static final Uri RINGTONE_URI = Uri.parse("media://custom/ringtone/uri"); - private static final int SILENT_RINGTONE_POSITION = 0; - private static final int DEFAULT_RINGTONE_POSITION = 1; - private static final int RINGTONE_POSITION = 2; - - @Mock - private RingtoneManager mMockRingtoneManager; - @Mock - private Cursor mMockCursor; - - private RingtoneListHandler mRingtoneListHandler; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - RingtoneListHandler.Config mRingtoneListConfig = createRingtoneListConfig(); - - mRingtoneListHandler = new RingtoneListHandler(); - - // Add silent and default options to the list. - mRingtoneListHandler.addSilentItem(); - mRingtoneListHandler.addDefaultItem(); - - mRingtoneListHandler.init(mRingtoneListConfig, mMockRingtoneManager, mMockCursor); - } - - @Test - public void testGetRingtoneCursor_returnsTheCorrectRingtoneCursor() { - assertThat(mRingtoneListHandler.getRingtoneCursor()).isEqualTo(mMockCursor); - } - - @Test - public void testGetRingtoneUri_returnsTheCorrectRingtoneUri() { - Uri expectedUri = RINGTONE_URI; - when(mMockRingtoneManager.getRingtoneUri(eq(0))).thenReturn(expectedUri); - - // Request 3rd item from list. - Uri actualUri = mRingtoneListHandler.getRingtoneUri(RINGTONE_POSITION); - assertThat(actualUri).isEqualTo(expectedUri); - } - - @Test - public void testGetRingtoneUri_withSelectedItemUnknown_returnsTheCorrectRingtoneUri() { - Uri uri = mRingtoneListHandler.getRingtoneUri(RingtoneListHandler.ITEM_POSITION_UNKNOWN); - assertThat(uri).isEqualTo(RingtoneListHandler.SILENT_URI); - } - - @Test - public void testGetRingtoneUri_withSelectedItemDefaultPosition_returnsTheCorrectRingtoneUri() { - Uri actualUri = mRingtoneListHandler.getRingtoneUri(DEFAULT_RINGTONE_POSITION); - assertThat(actualUri).isEqualTo(DEFAULT_URI); - } - - @Test - public void testGetRingtoneUri_withSelectedItemSilentPosition_returnsTheCorrectRingtoneUri() { - Uri uri = mRingtoneListHandler.getRingtoneUri(SILENT_RINGTONE_POSITION); - assertThat(uri).isEqualTo(RingtoneListHandler.SILENT_URI); - } - - @Test - public void testGetCurrentlySelectedRingtoneUri_returnsTheCorrectRingtoneUri() { - mRingtoneListHandler.setSelectedItemPosition(RingtoneListHandler.ITEM_POSITION_UNKNOWN); - Uri actualUri = mRingtoneListHandler.getSelectedRingtoneUri(); - assertThat(actualUri).isEqualTo(RingtoneListHandler.SILENT_URI); - - mRingtoneListHandler.setSelectedItemPosition(DEFAULT_RINGTONE_POSITION); - actualUri = mRingtoneListHandler.getSelectedRingtoneUri(); - assertThat(actualUri).isEqualTo(DEFAULT_URI); - - mRingtoneListHandler.setSelectedItemPosition(SILENT_RINGTONE_POSITION); - actualUri = mRingtoneListHandler.getSelectedRingtoneUri(); - assertThat(actualUri).isEqualTo(RingtoneListHandler.SILENT_URI); - - when(mMockRingtoneManager.getRingtoneUri(eq(0))).thenReturn(RINGTONE_URI); - mRingtoneListHandler.setSelectedItemPosition(RINGTONE_POSITION); - actualUri = mRingtoneListHandler.getSelectedRingtoneUri(); - assertThat(actualUri).isEqualTo(RINGTONE_URI); - } - - @Test - public void testGetRingtonePosition_returnsTheCorrectRingtonePosition() { - when(mMockRingtoneManager.getRingtonePosition(RINGTONE_URI)).thenReturn(0); - - int actualPosition = mRingtoneListHandler.getRingtonePosition(RINGTONE_URI); - - assertThat(actualPosition).isEqualTo(RINGTONE_POSITION); - - } - - @Test - public void testFixedItems_onlyAddsItemsOnceAndInOrder() { - // Clear fixed items before testing the add methods. - mRingtoneListHandler.resetFixedItems(); - - assertThat(mRingtoneListHandler.getSilentItemPosition()).isEqualTo( - RingtoneListHandler.ITEM_POSITION_UNKNOWN); - assertThat(mRingtoneListHandler.getDefaultItemPosition()).isEqualTo( - RingtoneListHandler.ITEM_POSITION_UNKNOWN); - - mRingtoneListHandler.addSilentItem(); - mRingtoneListHandler.addDefaultItem(); - mRingtoneListHandler.addSilentItem(); - mRingtoneListHandler.addDefaultItem(); - - assertThat(mRingtoneListHandler.getSilentItemPosition()).isEqualTo( - SILENT_RINGTONE_POSITION); - assertThat(mRingtoneListHandler.getDefaultItemPosition()).isEqualTo( - DEFAULT_RINGTONE_POSITION); - } - - @Test - public void testResetFixedItems_resetsSilentAndDefaultItemPositions() { - assertThat(mRingtoneListHandler.getSilentItemPosition()).isEqualTo( - SILENT_RINGTONE_POSITION); - assertThat(mRingtoneListHandler.getDefaultItemPosition()).isEqualTo( - DEFAULT_RINGTONE_POSITION); - - mRingtoneListHandler.resetFixedItems(); - - assertThat(mRingtoneListHandler.getSilentItemPosition()).isEqualTo( - RingtoneListHandler.ITEM_POSITION_UNKNOWN); - assertThat(mRingtoneListHandler.getDefaultItemPosition()).isEqualTo( - RingtoneListHandler.ITEM_POSITION_UNKNOWN); - } - - private RingtoneListHandler.Config createRingtoneListConfig() { - return new RingtoneListHandler.Config(/* hasDefaultItem= */ true, - /* uriForDefaultItem= */ DEFAULT_URI, /* hasSilentItem= */ true, - /* existingUri= */ DEFAULT_URI); - } -} diff --git a/packages/SoundPicker2/tests/src/com/android/soundpicker/RingtonePickerViewModelTest.java b/packages/SoundPicker2/tests/src/com/android/soundpicker/RingtonePickerViewModelTest.java deleted file mode 100644 index cde6c76d27ff..000000000000 --- a/packages/SoundPicker2/tests/src/com/android/soundpicker/RingtonePickerViewModelTest.java +++ /dev/null @@ -1,534 +0,0 @@ -/* - * Copyright (C) 2023 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.soundpicker; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertNull; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.database.Cursor; -import android.media.AudioAttributes; -import android.media.AudioManager; -import android.media.Ringtone; -import android.media.RingtoneManager; -import android.net.Uri; -import android.provider.Settings; - -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.testing.TestingExecutors; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.io.IOException; -import java.util.concurrent.ExecutorService; - -@RunWith(AndroidJUnit4.class) -public class RingtonePickerViewModelTest { - - private static final Uri DEFAULT_URI = Uri.parse("media://custom/ringtone/default_uri"); - private static final Uri RINGTONE_URI = Uri.parse("media://custom/ringtone/uri"); - private static final int RINGTONE_TYPE_UNKNOWN = -1; - private static final int DEFAULT_RINGTONE_POSITION = 1; - - @Mock - private RingtoneManagerFactory mMockRingtoneManagerFactory; - @Mock - private RingtoneFactory mMockRingtoneFactory; - @Mock - private RingtoneManager mMockRingtoneManager; - @Mock - private ListeningExecutorServiceFactory mMockListeningExecutorServiceFactory; - @Mock - private Cursor mMockCursor; - - private RingtoneListHandler mSoundListHandler; - private RingtoneListHandler mVibrationListHandler; - private ExecutorService mMainThreadExecutor; - private ListeningExecutorService mBackgroundThreadExecutor; - private Ringtone mMockDefaultRingtone; - private Ringtone mMockRingtone; - private RingtonePickerViewModel mViewModel; - private RingtoneListHandler.Config mSoundListConfig; - private RingtoneListHandler.Config mVibrationListConfig; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - mSoundListHandler = new RingtoneListHandler(); - mVibrationListHandler = new RingtoneListHandler(); - mSoundListConfig = createRingtoneListConfig(); - mVibrationListConfig = createRingtoneListConfig(); - mMockDefaultRingtone = createMockRingtone(); - mMockRingtone = createMockRingtone(); - when(mMockRingtoneManagerFactory.create()).thenReturn(mMockRingtoneManager); - when(mMockRingtoneFactory.create(DEFAULT_URI, - AudioAttributes.FLAG_AUDIBILITY_ENFORCED)).thenReturn(mMockDefaultRingtone); - when(mMockRingtoneManager.getRingtoneUri(anyInt())).thenReturn(RINGTONE_URI); - when(mMockRingtoneManager.getCursor()).thenReturn(mMockCursor); - mMainThreadExecutor = TestingExecutors.sameThreadScheduledExecutor(); - mBackgroundThreadExecutor = TestingExecutors.sameThreadScheduledExecutor(); - when(mMockListeningExecutorServiceFactory.createSingleThreadExecutor()).thenReturn( - mBackgroundThreadExecutor); - - mViewModel = new RingtonePickerViewModel(mMockRingtoneManagerFactory, mMockRingtoneFactory, - mMockListeningExecutorServiceFactory, mSoundListHandler, - mVibrationListHandler); - - // Add silent and default options to the sound list. - mSoundListHandler.addSilentItem(); - mSoundListHandler.addDefaultItem(); - - // Add silent and default options to the vibration list. - mVibrationListHandler.addSilentItem(); - mVibrationListHandler.addDefaultItem(); - - mSoundListHandler.setSelectedItemPosition(DEFAULT_RINGTONE_POSITION); - mVibrationListHandler.setSelectedItemPosition(DEFAULT_RINGTONE_POSITION); - } - - @After - public void teardown() { - if (mMainThreadExecutor != null && !mMainThreadExecutor.isShutdown()) { - mMainThreadExecutor.shutdown(); - } - if (mBackgroundThreadExecutor != null && !mBackgroundThreadExecutor.isShutdown()) { - mBackgroundThreadExecutor.shutdown(); - } - } - - @Test - public void testInitRingtoneManager_whenTypeIsUnknown_createManagerButDoNotSetType() { - mViewModel.init(createPickerConfig(RINGTONE_TYPE_UNKNOWN), mSoundListConfig, - mVibrationListConfig); - - verify(mMockRingtoneManagerFactory).create(); - verify(mMockRingtoneManager, never()).setType(anyInt()); - assertNotNull(mViewModel.getSoundListHandler().getRingtoneListConfig()); - assertNotNull(mViewModel.getVibrationListHandler().getRingtoneListConfig()); - } - - @Test - public void testInitRingtoneManager_whenTypeIsNotUnknown_createManagerAndSetType() { - mViewModel.init(createPickerConfig(RingtoneManager.TYPE_NOTIFICATION), mSoundListConfig, - mVibrationListConfig); - - verify(mMockRingtoneManagerFactory).create(); - verify(mMockRingtoneManager).setType(RingtoneManager.TYPE_NOTIFICATION); - assertNotNull(mViewModel.getSoundListHandler().getRingtoneListConfig()); - assertNotNull(mViewModel.getVibrationListHandler().getRingtoneListConfig()); - } - - @Test - public void testInitRingtoneManager_bothListConfigsAreNull_onlyRecreateRingtoneManager() { - mViewModel.init( - createPickerConfig(RingtoneManager.TYPE_NOTIFICATION), - /* soundListConfig= */ null, /* vibrationListConfig= */ null); - - verify(mMockRingtoneManagerFactory).create(); - verify(mMockRingtoneManager).setType(RingtoneManager.TYPE_NOTIFICATION); - assertNull(mViewModel.getSoundListHandler().getRingtoneListConfig()); - assertNull(mViewModel.getVibrationListHandler().getRingtoneListConfig()); - } - - @Test - public void testReinitialize_bothListConfigsInitialized_recreateManagerAndReinitHandlers() { - mViewModel.init(createPickerConfig(RingtoneManager.TYPE_NOTIFICATION), mSoundListConfig, - mVibrationListConfig); - mViewModel.reinit(); - - verify(mMockRingtoneManagerFactory, times(2)).create(); - verify(mMockRingtoneManager, times(2)).setType(RingtoneManager.TYPE_NOTIFICATION); - assertNotNull(mViewModel.getSoundListHandler().getRingtoneListConfig()); - assertNotNull(mViewModel.getVibrationListHandler().getRingtoneListConfig()); - } - - @Test - public void testReinitialize_bothListConfigsAlreadyNull_onlyRecreateRingtoneManager() { - mViewModel.init( - createPickerConfig(RingtoneManager.TYPE_NOTIFICATION), - /* soundListConfig= */ null, /* vibrationListConfig= */ null); - mViewModel.reinit(); - - verify(mMockRingtoneManagerFactory, times(2)).create(); - verify(mMockRingtoneManager, times(2)).setType(RingtoneManager.TYPE_NOTIFICATION); - assertNull(mViewModel.getSoundListHandler().getRingtoneListConfig()); - assertNull(mViewModel.getVibrationListHandler().getRingtoneListConfig()); - } - - @Test - public void testGetStreamType_returnsTheCorrectStreamType() { - when(mMockRingtoneManager.inferStreamType()).thenReturn(AudioManager.STREAM_ALARM); - mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE), mSoundListConfig, - mVibrationListConfig); - assertEquals(mViewModel.getRingtoneStreamType(), AudioManager.STREAM_ALARM); - } - - @Test - public void testOnPause_withChangingConfigurationTrue_doNotStopPlayingRingtone() { - mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE), mSoundListConfig, - mVibrationListConfig); - mViewModel.playRingtone(); - verifyRingtonePlayCalledAndMockPlayingState(mMockDefaultRingtone); - mViewModel.onPause(/* isChangingConfigurations= */ true); - verify(mMockDefaultRingtone, never()).stop(); - } - - @Test - public void testOnPause_withChangingConfigurationFalse_stopPlayingRingtone() { - mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE), mSoundListConfig, - mVibrationListConfig); - mViewModel.playRingtone(); - verifyRingtonePlayCalledAndMockPlayingState(mMockDefaultRingtone); - mViewModel.onPause(/* isChangingConfigurations= */ false); - verify(mMockDefaultRingtone).stop(); - } - - @Test - public void testOnViewModelRecreated_previousRingtoneCanStillBeStopped() { - mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE), mSoundListConfig, - mVibrationListConfig); - Ringtone mockRingtone1 = createMockRingtone(); - Ringtone mockRingtone2 = createMockRingtone(); - - when(mMockRingtoneFactory.create(any(), anyInt())).thenReturn(mockRingtone1, mockRingtone2); - mViewModel.playRingtone(); - verifyRingtonePlayCalledAndMockPlayingState(mockRingtone1); - // Fake a scenario where the activity is destroyed and recreated due to a config change. - // This will result in a new view model getting created. - mViewModel.onStop(/* isChangingConfigurations= */ true); - verify(mockRingtone1, never()).stop(); - mViewModel = new RingtonePickerViewModel(mMockRingtoneManagerFactory, mMockRingtoneFactory, - mMockListeningExecutorServiceFactory, mSoundListHandler, - mVibrationListHandler); - mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE), mSoundListConfig, - mVibrationListConfig); - mViewModel.playRingtone(); - verifyRingtonePlayCalledAndMockPlayingState(mockRingtone2); - verify(mockRingtone1).stop(); - verify(mockRingtone2, never()).stop(); - } - - @Test - public void testOnStop_withChangingConfigurationTrueAndDefaultRingtonePlaying_saveRingtone() { - mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE), mSoundListConfig, - mVibrationListConfig); - mViewModel.playRingtone(); - verifyRingtonePlayCalledAndMockPlayingState(mMockDefaultRingtone); - mViewModel.onStop(/* isChangingConfigurations= */ true); - assertEquals(RingtonePickerViewModel.sPlayingRingtone, mMockDefaultRingtone); - } - - @Test - public void testOnStop_withChangingConfigurationTrueAndCurrentRingtonePlaying_saveRingtone() { - mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE), mSoundListConfig, - mVibrationListConfig); - mViewModel.playRingtone(); - verifyRingtonePlayCalledAndMockPlayingState(mMockDefaultRingtone); - mViewModel.onStop(/* isChangingConfigurations= */ true); - assertEquals(RingtonePickerViewModel.sPlayingRingtone, mMockDefaultRingtone); - } - - @Test - public void testOnStop_withChangingConfigurationTrueAndNoPlayingRingtone_saveNothing() { - mViewModel.onStop(/* isChangingConfigurations= */ true); - assertNull(RingtonePickerViewModel.sPlayingRingtone); - } - - @Test - public void testOnStop_withChangingConfigurationFalse_stopPlayingRingtone() { - mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE), mSoundListConfig, - mVibrationListConfig); - - mViewModel.playRingtone(); - verifyRingtonePlayCalledAndMockPlayingState(mMockDefaultRingtone); - mViewModel.onStop(/* isChangingConfigurations= */ false); - verify(mMockDefaultRingtone).stop(); - } - - @Test - public void testGetCurrentlySelectedRingtoneUri_returnsTheCorrectRingtoneUri() { - mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE), mSoundListConfig, - mVibrationListConfig); - - assertEquals(DEFAULT_URI, mViewModel.getSelectedRingtoneUri()); - } - - @Test - public void testPlayRingtone_playTheCorrectRingtone() { - mSoundListHandler.setSelectedItemPosition(DEFAULT_RINGTONE_POSITION); - mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE), mSoundListConfig, - mVibrationListConfig); - - mViewModel.playRingtone(); - verifyRingtonePlayCalledAndMockPlayingState(mMockDefaultRingtone); - } - - @Test - public void testPlayRingtone_stopsPreviouslyRunningRingtone() { - // Start playing the first ringtone - mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE), mSoundListConfig, - mVibrationListConfig); - mViewModel.playRingtone(); - verifyRingtonePlayCalledAndMockPlayingState(mMockDefaultRingtone); - // Start playing the second ringtone - when(mMockRingtoneFactory.create(DEFAULT_URI, - AudioAttributes.FLAG_AUDIBILITY_ENFORCED)).thenReturn(mMockRingtone); - mViewModel.playRingtone(); - verifyRingtonePlayCalledAndMockPlayingState(mMockRingtone); - - verify(mMockDefaultRingtone).stop(); - } - - @Test - public void testDefaultItemUri_withNotificationIntent_returnDefaultNotificationUri() { - Uri uri = RingtonePickerViewModel.getDefaultItemUriByType( - RingtoneManager.TYPE_NOTIFICATION); - assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, uri); - } - - @Test - public void testDefaultItemUri_withAlarmIntent_returnDefaultAlarmUri() { - Uri uri = RingtonePickerViewModel.getDefaultItemUriByType(RingtoneManager.TYPE_ALARM); - assertEquals(Settings.System.DEFAULT_ALARM_ALERT_URI, uri); - } - - @Test - public void testDefaultItemUri_withRingtoneIntent_returnDefaultRingtoneUri() { - Uri uri = RingtonePickerViewModel.getDefaultItemUriByType(RingtoneManager.TYPE_RINGTONE); - assertEquals(Settings.System.DEFAULT_RINGTONE_URI, uri); - } - - @Test - public void testDefaultItemUri_withInvalidRingtoneType_returnDefaultRingtoneUri() { - Uri uri = RingtonePickerViewModel.getDefaultItemUriByType(-1); - assertEquals(Settings.System.DEFAULT_RINGTONE_URI, uri); - } - - @Test - public void testTitle_withNotificationRingtoneType_returnRingtoneNotificationTitle() { - int title = RingtonePickerViewModel.getTitleByType(RingtoneManager.TYPE_NOTIFICATION); - assertEquals(com.android.internal.R.string.ringtone_picker_title_notification, title); - } - - @Test - public void testTitle_withAlarmRingtoneType_returnRingtoneAlarmTitle() { - int title = RingtonePickerViewModel.getTitleByType(RingtoneManager.TYPE_ALARM); - assertEquals(com.android.internal.R.string.ringtone_picker_title_alarm, title); - } - - @Test - public void testTitle_withInvalidRingtoneType_returnDefaultRingtoneTitle() { - int title = RingtonePickerViewModel.getTitleByType(/*ringtoneType= */ -1); - assertEquals(com.android.internal.R.string.ringtone_picker_title, title); - } - - @Test - public void testAddNewItemText_withAlarmType_returnAlarmAddItemText() { - int addNewItemTextResId = RingtonePickerViewModel.getAddNewItemTextByType( - RingtoneManager.TYPE_ALARM); - assertEquals(R.string.add_alarm_text, addNewItemTextResId); - } - - @Test - public void testAddNewItemText_withNotificationType_returnNotificationAddItemText() { - int addNewItemTextResId = RingtonePickerViewModel.getAddNewItemTextByType( - RingtoneManager.TYPE_NOTIFICATION); - assertEquals(R.string.add_notification_text, addNewItemTextResId); - } - - @Test - public void testAddNewItemText_withRingtoneType_returnRingtoneAddItemText() { - int addNewItemTextResId = RingtonePickerViewModel.getAddNewItemTextByType( - RingtoneManager.TYPE_RINGTONE); - assertEquals(R.string.add_ringtone_text, addNewItemTextResId); - } - - @Test - public void testAddNewItemText_withInvalidType_returnRingtoneAddItemText() { - int addNewItemTextResId = RingtonePickerViewModel.getAddNewItemTextByType(-1); - assertEquals(R.string.add_ringtone_text, addNewItemTextResId); - } - - @Test - public void testDefaultItemText_withNotificationType_returnNotificationDefaultItemText() { - int defaultRingtoneItemText = RingtonePickerViewModel.getDefaultRingtoneItemTextByType( - RingtoneManager.TYPE_NOTIFICATION); - assertEquals(R.string.notification_sound_default, defaultRingtoneItemText); - } - - @Test - public void testDefaultItemText_withAlarmType_returnAlarmDefaultItemText() { - int defaultRingtoneItemText = RingtonePickerViewModel.getDefaultRingtoneItemTextByType( - RingtoneManager.TYPE_NOTIFICATION); - assertEquals(R.string.notification_sound_default, defaultRingtoneItemText); - } - - @Test - public void testDefaultItemText_withRingtoneType_returnRingtoneDefaultItemText() { - int defaultRingtoneItemText = RingtonePickerViewModel.getDefaultRingtoneItemTextByType( - RingtoneManager.TYPE_RINGTONE); - assertEquals(R.string.ringtone_default, defaultRingtoneItemText); - } - - @Test - public void testDefaultItemText_withInvalidType_returnRingtoneDefaultItemText() { - int defaultRingtoneItemText = RingtonePickerViewModel.getDefaultRingtoneItemTextByType(-1); - assertEquals(R.string.ringtone_default, defaultRingtoneItemText); - } - - @Test - public void testCancelPendingAsyncTasks_correctlyCancelsPendingTasks() - throws IOException { - FutureCallback<Uri> mockCallback = mock(FutureCallback.class); - - when(mMockListeningExecutorServiceFactory.createSingleThreadExecutor()).thenReturn( - TestingExecutors.noOpScheduledExecutor()); - - mViewModel = new RingtonePickerViewModel(mMockRingtoneManagerFactory, mMockRingtoneFactory, - mMockListeningExecutorServiceFactory, mSoundListHandler, - mVibrationListHandler); - mViewModel.addSoundRingtoneAsync(DEFAULT_URI, RingtoneManager.TYPE_NOTIFICATION, - mockCallback, mMainThreadExecutor); - verify(mockCallback, never()).onFailure(any()); - // Calling cancelPendingAsyncTasks should cancel the pending task. Cancelling an async - // task invokes the onFailure method in the callable. - mViewModel.cancelPendingAsyncTasks(); - verify(mockCallback).onFailure(any()); - verify(mockCallback, never()).onSuccess(any()); - - } - - @Test - public void testAddRingtoneAsync_cancelPreviousTaskBeforeStartingNewOne() - throws IOException { - FutureCallback<Uri> mockCallback1 = mock(FutureCallback.class); - FutureCallback<Uri> mockCallback2 = mock(FutureCallback.class); - - when(mMockListeningExecutorServiceFactory.createSingleThreadExecutor()).thenReturn( - TestingExecutors.noOpScheduledExecutor()); - - mViewModel = new RingtonePickerViewModel(mMockRingtoneManagerFactory, mMockRingtoneFactory, - mMockListeningExecutorServiceFactory, mSoundListHandler, - mVibrationListHandler); - mViewModel.addSoundRingtoneAsync(DEFAULT_URI, RingtoneManager.TYPE_NOTIFICATION, - mockCallback1, mMainThreadExecutor); - verify(mockCallback1, never()).onFailure(any()); - // We call addRingtoneAsync again to cancel the previous task and start a new one. - // Cancelling an async task invokes the onFailure method in the callable. - mViewModel.addSoundRingtoneAsync(DEFAULT_URI, RingtoneManager.TYPE_NOTIFICATION, - mockCallback2, mMainThreadExecutor); - verify(mockCallback1).onFailure(any()); - verify(mockCallback1, never()).onSuccess(any()); - verifyNoMoreInteractions(mockCallback2); - } - - @Test - public void testAddRingtoneAsync_whenAddRingtoneIsSuccessful_successCallbackIsInvoked() - throws IOException { - Uri expectedUri = DEFAULT_URI; - FutureCallback<Uri> mockCallback = mock(FutureCallback.class); - - when(mMockRingtoneManager.addCustomExternalRingtone(DEFAULT_URI, - RingtoneManager.TYPE_NOTIFICATION)).thenReturn(expectedUri); - - mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE), mSoundListConfig, - mVibrationListConfig); - - mViewModel.addSoundRingtoneAsync(DEFAULT_URI, RingtoneManager.TYPE_NOTIFICATION, - mockCallback, mMainThreadExecutor); - - verify(mockCallback).onSuccess(expectedUri); - verify(mockCallback, never()).onFailure(any()); - } - - @Test - public void testAddRingtoneAsync_whenAddRingtoneFailed_failureCallbackIsInvoked() - throws IOException { - FutureCallback<Uri> mockCallback = mock(FutureCallback.class); - - when(mMockRingtoneManager.addCustomExternalRingtone(any(), anyInt())).thenThrow( - IOException.class); - - mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE), mSoundListConfig, - mVibrationListConfig); - - mViewModel.addSoundRingtoneAsync(DEFAULT_URI, RingtoneManager.TYPE_NOTIFICATION, - mockCallback, mMainThreadExecutor); - - verify(mockCallback).onFailure(any(IOException.class)); - verify(mockCallback, never()).onSuccess(any()); - } - - private Ringtone createMockRingtone() { - Ringtone mockRingtone = mock(Ringtone.class); - when(mockRingtone.getAudioAttributes()).thenReturn( - audioAttributes(AudioAttributes.USAGE_NOTIFICATION_RINGTONE, 0)); - - return mockRingtone; - } - - private void verifyRingtonePlayCalledAndMockPlayingState(Ringtone ringtone) { - verify(ringtone).play(); - when(ringtone.isPlaying()).thenReturn(true); - } - - private static AudioAttributes audioAttributes(int audioUsage, int flags) { - return new AudioAttributes.Builder() - .setUsage(audioUsage) - .setFlags(flags) - .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) - .build(); - } - - private RingtonePickerViewModel.Config createPickerConfig(int ringtoneType, - int audioAttributes) { - return new RingtonePickerViewModel.Config("Phone ringtone", /* userId= */ 1, - ringtoneType, /* showOkCancelButtons= */ true, - audioAttributes, RingtonePickerViewModel.PickerType.RINGTONE_PICKER); - } - - private RingtonePickerViewModel.Config createPickerConfig(int ringtoneType) { - return new RingtonePickerViewModel.Config("Phone ringtone", /* userId= */ 1, - ringtoneType, /* showOkCancelButtons= */ true, - AudioAttributes.FLAG_AUDIBILITY_ENFORCED, - RingtonePickerViewModel.PickerType.RINGTONE_PICKER); - } - - private RingtoneListHandler.Config createRingtoneListConfig() { - return new RingtoneListHandler.Config(/* hasDefaultItem= */ true, - /* uriForDefaultItem= */ DEFAULT_URI, /* hasSilentItem= */ true, - /* existingUri= */ Uri.parse("")); - } -} |