summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 04:47:16 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 04:47:16 +0000
commit2df05e4505990f438e928ae5c597b5383d57832f (patch)
tree0ebc0230124cdaa344d382be5ca21b0d3618c09e
parente3602e4250f7d950fab50be45574fdba80b37819 (diff)
parent4f1d7f1fcc787d6fb54e2255405e30477c0d00c4 (diff)
downloadatv-android14-mainline-media-release.tar.gz
Change-Id: Ie7b8d11784f2429d797fe482c600b3f43647d256
-rw-r--r--FrameworkPackageStubs/AndroidManifest.xml31
-rw-r--r--FrameworkPackageStubs/res/values-az/strings.xml2
-rw-r--r--FrameworkPackageStubs/res/values-en-rCA/strings.xml2
-rw-r--r--FrameworkPackageStubs/res/values-ro/strings.xml2
-rw-r--r--FrameworkPackageStubs/src/com/android/tv/frameworkpackagestubs/Stubs.java5
-rw-r--r--OWNERS16
-rw-r--r--TvSampleLeanbackLauncher/Android.bp31
-rw-r--r--TvSampleLeanbackLauncher/TvSampleLeanbackLauncher.apkbin2778832 -> 0 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/androidTest/java/com/example/sampleleanbacklauncher/apps/LaunchItemTest.java78
-rw-r--r--TvSampleLeanbackLauncher/src/main/AndroidManifest.xml57
-rw-r--r--TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/LauncherActivity.java80
-rw-r--r--TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/LauncherConstants.java29
-rw-r--r--TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/SystemUpdateChecker.java95
-rw-r--r--TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/AppFragment.java450
-rw-r--r--TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/LaunchItem.java116
-rw-r--r--TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/LaunchItemsDbHelper.java260
-rw-r--r--TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/LaunchItemsManager.java496
-rw-r--r--TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/NetworkLaunchItem.java156
-rw-r--r--TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/NotificationsLaunchItem.java46
-rw-r--r--TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/SettingsLaunchItem.java47
-rw-r--r--TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationPanelDismissibleItemView.java162
-rw-r--r--TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationPanelItemView.java245
-rw-r--r--TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsContract.java69
-rw-r--r--TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsPanelAdapter.java144
-rw-r--r--TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsSidePanelActivity.java101
-rw-r--r--TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsUtils.java41
-rw-r--r--TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/TvNotification.java192
-rw-r--r--TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/search/SearchFragment.java139
-rw-r--r--TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/util/LauncherAsyncTaskLoader.java61
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_0_bar_launcher.pngbin0 -> 866 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_1_bar_launcher.pngbin0 -> 1212 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_2_bar_launcher.pngbin0 -> 1185 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_3_bar_launcher.pngbin0 -> 1174 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_4_bar_launcher.pngbin0 -> 962 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_0_bar_launcher.pngbin0 -> 1188 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_1_bar_launcher.pngbin0 -> 1483 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_2_bar_launcher.pngbin0 -> 1456 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_3_bar_launcher.pngbin0 -> 1532 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_4_bar_launcher.pngbin0 -> 1330 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_sim_launcher.pngbin0 -> 1565 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_null_launcher.pngbin0 -> 1175 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_off_launcher.pngbin0 -> 1536 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ethernet_active_launcher.pngbin0 -> 2647 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ethernet_no_internet_launcher.pngbin0 -> 2418 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ic_notifications.pngbin0 -> 378 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_0_launcher.pngbin0 -> 2223 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_1_launcher.pngbin0 -> 3706 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_2_launcher.pngbin0 -> 4184 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_3_launcher.pngbin0 -> 4524 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_4_launcher.pngbin0 -> 4686 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_no_internet_launcher.pngbin0 -> 2322 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_not_connected_launcher.pngbin0 -> 3314 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_0_bar_launcher.pngbin0 -> 787 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_1_bar_launcher.pngbin0 -> 1186 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_2_bar_launcher.pngbin0 -> 1229 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_3_bar_launcher.pngbin0 -> 1267 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_4_bar_launcher.pngbin0 -> 796 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_0_bar_launcher.pngbin0 -> 1029 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_1_bar_launcher.pngbin0 -> 1338 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_2_bar_launcher.pngbin0 -> 1301 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_3_bar_launcher.pngbin0 -> 1793 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_4_bar_launcher.pngbin0 -> 1192 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_sim_launcher.pngbin0 -> 1338 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_null_launcher.pngbin0 -> 1004 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_off_launcher.pngbin0 -> 1408 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ethernet_active_launcher.pngbin0 -> 1963 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ethernet_no_internet_launcher.pngbin0 -> 1738 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ic_notifications.pngbin0 -> 265 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_0_launcher.pngbin0 -> 1522 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_1_launcher.pngbin0 -> 2509 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_2_launcher.pngbin0 -> 2815 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_3_launcher.pngbin0 -> 3053 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_4_launcher.pngbin0 -> 3122 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_no_internet_launcher.pngbin0 -> 1711 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_not_connected_launcher.pngbin0 -> 2175 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_0_bar_launcher.pngbin0 -> 1187 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_1_bar_launcher.pngbin0 -> 1973 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_2_bar_launcher.pngbin0 -> 2061 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_3_bar_launcher.pngbin0 -> 2264 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_4_bar_launcher.pngbin0 -> 1319 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_0_bar_launcher.pngbin0 -> 1632 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_1_bar_launcher.pngbin0 -> 2127 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_2_bar_launcher.pngbin0 -> 2339 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_3_bar_launcher.pngbin0 -> 3121 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_4_bar_launcher.pngbin0 -> 1889 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_sim_launcher.pngbin0 -> 2326 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_null_launcher.pngbin0 -> 1724 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_off_launcher.pngbin0 -> 2489 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ethernet_active_launcher.pngbin0 -> 4002 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ethernet_no_internet_launcher.pngbin0 -> 3378 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ic_notifications.pngbin0 -> 482 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_0_launcher.pngbin0 -> 2871 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_1_launcher.pngbin0 -> 4882 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_2_launcher.pngbin0 -> 5574 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_3_launcher.pngbin0 -> 5967 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_4_launcher.pngbin0 -> 6328 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_no_internet_launcher.pngbin0 -> 3016 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_not_connected_launcher.pngbin0 -> 4444 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_0_bar_launcher.pngbin0 -> 1042 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_1_bar_launcher.pngbin0 -> 1059 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_2_bar_launcher.pngbin0 -> 1075 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_3_bar_launcher.pngbin0 -> 1064 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_4_bar_launcher.pngbin0 -> 1048 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_0_bar_launcher.pngbin0 -> 1159 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_1_bar_launcher.pngbin0 -> 1175 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_2_bar_launcher.pngbin0 -> 1183 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_3_bar_launcher.pngbin0 -> 1193 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_4_bar_launcher.pngbin0 -> 1156 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_sim_launcher.pngbin0 -> 1485 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_null_launcher.pngbin0 -> 1135 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_off_launcher.pngbin0 -> 1413 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ethernet_active_launcher.pngbin0 -> 4424 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ethernet_no_internet_launcher.pngbin0 -> 3797 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ic_notifications.pngbin0 -> 713 bytes
-rwxr-xr-xTvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_0_launcher.pngbin0 -> 3526 bytes
-rwxr-xr-xTvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_1_launcher.pngbin0 -> 4850 bytes
-rwxr-xr-xTvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_2_launcher.pngbin0 -> 5355 bytes
-rwxr-xr-xTvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_3_launcher.pngbin0 -> 5643 bytes
-rwxr-xr-xTvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_4_launcher.pngbin0 -> 5313 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_no_internet_launcher.pngbin0 -> 3451 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_not_connected_launcher.pngbin0 -> 4879 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable/ic_keyboard_blue.xml26
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable/ic_keyboard_grey.xml26
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable/ic_mic_color.xml34
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable/ic_remove_circle_black.xml9
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable/keyboard_search.xml21
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable/notification_background_left.xml19
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable/notification_background_right.xml19
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable/tab_background_left.xml21
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/drawable/tab_background_right.xml21
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/layout/app_launch_row.xml37
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/layout/launch_item.xml52
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/layout/launcher.xml41
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/layout/leanback_banner_preference.xml84
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item.xml32
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item_dismissible.xml50
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item_expanded_text.xml30
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item_main_content.xml67
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/layout/notifications_panel_view.xml68
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/layout/pref_wall_of_text.xml46
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/layout/search.xml71
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/mipmap-hdpi/ic_launcher.pngbin0 -> 3418 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/mipmap-mdpi/ic_launcher.pngbin0 -> 2206 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/mipmap-xhdpi/ic_launcher.pngbin0 -> 4842 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/mipmap-xxhdpi/ic_launcher.pngbin0 -> 7718 bytes
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/values/arrays.xml27
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/values/colors.xml36
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/values/dimens.xml103
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/values/ids.xml22
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/values/strings.xml58
-rw-r--r--TvSampleLeanbackLauncher/src/main/res/values/styles.xml38
-rw-r--r--audio_proxy/Android.bp2
-rw-r--r--audio_proxy/AudioProxyDevice.cpp36
-rw-r--r--audio_proxy/AudioProxyStreamOut.cpp49
-rw-r--r--audio_proxy/AudioProxyStreamOut.h12
-rw-r--r--audio_proxy/OutputStreamImpl.cpp34
-rw-r--r--audio_proxy/OutputStreamImpl.h11
-rw-r--r--audio_proxy/interfaces/aidl/Android.bp25
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/.hash1
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioChannelMask.aidl24
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioConfig.aidl27
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioDrain.aidl24
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioFormat.aidl25
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioOutputFlag.aidl25
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/IAudioProxy.aidl23
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/IOutputStream.aidl32
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/IStreamProvider.aidl23
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/MessageQueueFlag.aidl24
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/PresentationPosition.aidl24
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/TimeSpec.aidl24
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/WriteStatus.aidl24
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/.hash1
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioChannelMask.aidl24
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioConfig.aidl27
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioDrain.aidl24
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioFormat.aidl25
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioOutputFlag.aidl25
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/IAudioProxy.aidl23
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/IOutputStream.aidl36
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/IStreamProvider.aidl23
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/MessageQueueFlag.aidl24
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/MmapBufferInfo.aidl26
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/PresentationPosition.aidl24
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/TimeSpec.aidl24
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/WriteStatus.aidl24
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/current/device/google/atv/audio_proxy/AudioConfig.aidl2
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/current/device/google/atv/audio_proxy/IOutputStream.aidl6
-rw-r--r--audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/current/device/google/atv/audio_proxy/MmapBufferInfo.aidl26
-rw-r--r--audio_proxy/interfaces/aidl/device/google/atv/audio_proxy/AudioConfig.aidl8
-rw-r--r--audio_proxy/interfaces/aidl/device/google/atv/audio_proxy/IOutputStream.aidl24
-rw-r--r--audio_proxy/interfaces/aidl/device/google/atv/audio_proxy/MmapBufferInfo.aidl15
-rw-r--r--audio_proxy/public/audio_proxy.h74
-rw-r--r--audio_proxy/sepolicy/OWNERS2
-rw-r--r--audio_proxy/sepolicy/public/hal_audio_proxy.te1
-rw-r--r--audio_proxy/sepolicy/vendor/dumpstate.te1
-rw-r--r--audio_proxy/sepolicy/vendor/hal_audio_proxy_default.te11
-rw-r--r--audio_proxy/sepolicy/vendor/service.te2
-rw-r--r--audio_proxy/service/AidlTypes.h6
-rw-r--r--audio_proxy/service/Android.bp3
-rw-r--r--audio_proxy/service/AudioUtil.cpp38
-rw-r--r--audio_proxy/service/AudioUtil.h25
-rw-r--r--audio_proxy/service/BusOutputStream.cpp16
-rw-r--r--audio_proxy/service/BusOutputStream.h5
-rw-r--r--audio_proxy/service/BusStreamProvider.cpp38
-rw-r--r--audio_proxy/service/BusStreamProvider.h7
-rw-r--r--audio_proxy/service/DeviceImpl.cpp32
-rw-r--r--audio_proxy/service/DummyBusOutputStream.cpp13
-rw-r--r--audio_proxy/service/DummyBusOutputStream.h5
-rw-r--r--audio_proxy/service/RemoteBusOutputStream.cpp17
-rw-r--r--audio_proxy/service/RemoteBusOutputStream.h5
-rw-r--r--audio_proxy/service/StreamOutImpl.cpp59
-rw-r--r--audio_proxy/service/StreamOutImpl.h5
-rw-r--r--audio_proxy/service/audio_proxy_policy_configuration.xml12
-rw-r--r--audio_proxy/service/audio_proxy_policy_configuration_hw_av_sync.xml57
-rw-r--r--audio_proxy/service/device.google.atv.audio_proxy@5.1-service.rc2
-rw-r--r--audio_proxy/service/device.google.atv.audio_proxy@6.0-service.rc2
-rw-r--r--audio_proxy/service/device.google.atv.audio_proxy@7.0-service.rc2
-rw-r--r--audio_proxy/service/device.google.atv.audio_proxy@7.1-service.rc2
-rw-r--r--audio_proxy/service/manifest_audio_proxy_5_0.xml2
-rw-r--r--audio_proxy/service/manifest_audio_proxy_6_0.xml2
-rw-r--r--audio_proxy/service/manifest_audio_proxy_7_0.xml2
-rw-r--r--audio_proxy/service/manifest_audio_proxy_7_1.xml2
-rw-r--r--libraries/BluetoothServices/Android.bp1
-rw-r--r--libraries/BluetoothServices/lint-baseline.xml15
-rw-r--r--libraries/BluetoothServices/src/com/google/android/tv/btservices/pairing/profiles/PairingProfileWrapperA2dp.java2
-rw-r--r--libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/DefaultProxy.java63
-rw-r--r--libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/RemoteProxy.java5
-rw-r--r--libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/Version.java4
-rw-r--r--libraries/BluetoothServices/src/com/google/android/tv/btservices/settings/ConnectedDevicesSliceProvider.java75
-rw-r--r--overlay/TvDisableLowPowerStandbyOverlay/Android.bp16
-rw-r--r--overlay/TvDisableLowPowerStandbyOverlay/AndroidManifest.xml28
-rw-r--r--overlay/TvDisableLowPowerStandbyOverlay/res/values/config.xml21
-rw-r--r--overlay/TvFrameworkOverlay/res/values-de/strings.xml4
-rw-r--r--overlay/TvFrameworkOverlay/res/values-en-rCA/strings.xml8
-rw-r--r--overlay/TvFrameworkOverlay/res/values-fa/strings.xml2
-rw-r--r--overlay/TvFrameworkOverlay/res/values-ky/strings.xml6
-rw-r--r--overlay/TvFrameworkOverlay/res/values-or/strings.xml4
-rw-r--r--overlay/TvFrameworkOverlay/res/values-ro/strings.xml14
-rw-r--r--overlay/TvFrameworkOverlay/res/values/config.xml165
-rw-r--r--overlay/TvFrameworkOverlay/res/values/config_device_idle.xml50
-rw-r--r--overlay/TvNetworkStackOverlay/Android.bp18
-rw-r--r--overlay/TvNetworkStackOverlay/AndroidManifest.xml28
-rw-r--r--overlay/TvNetworkStackOverlay/res/values/config.xml23
-rw-r--r--overlay/TvWifiOverlay/Android.bp7
-rw-r--r--overlay/TvWifiOverlay/AndroidManifest.xml2
-rw-r--r--overlay/TvWifiOverlay/res/values/config.xml18
-rw-r--r--permissions/tv_core_hardware.xml2
-rw-r--r--products/AndroidProducts.mk2
-rw-r--r--products/aosp_tv_arm.mk86
-rw-r--r--products/atv_emulator_vendor.mk14
-rw-r--r--products/atv_generic_system.mk5
-rw-r--r--products/atv_lowram_defaults.mk7
-rw-r--r--products/atv_product.mk12
-rw-r--r--products/atv_system.mk8
-rw-r--r--products/gsi_tv_base.mk2
-rw-r--r--products/sdk_atv_arm64.mk (renamed from products/sdk_atv_armv7.mk)18
-rw-r--r--products/sdk_atv_x86.mk2
-rw-r--r--sdk/images_armeabi-v7a_source.prop_template2
-rw-r--r--sdk/images_source.prop_template2
259 files changed, 5900 insertions, 382 deletions
diff --git a/FrameworkPackageStubs/AndroidManifest.xml b/FrameworkPackageStubs/AndroidManifest.xml
index 2803156..393021b 100644
--- a/FrameworkPackageStubs/AndroidManifest.xml
+++ b/FrameworkPackageStubs/AndroidManifest.xml
@@ -107,6 +107,33 @@
</intent-filter>
</activity>
+ <!-- Camera package stubs -->
+ <activity android:name=".Stubs$CameraStub"
+ android:exported="true"
+ android:label="@string/stub_name"
+ android:excludeFromRecents="true"
+ android:visibleToInstantApps="true"
+ >
+ <meta-data android:name="com.android.tv.stub" android:value="true" />
+ <intent-filter android:priority="-1">
+ <action android:name="android.media.action.IMAGE_CAPTURE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter android:priority="-1">
+ <action android:name="android.media.action.VIDEO_CAPTURE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter android:priority="-1">
+ <action android:name="android.media.action.STILL_IMAGE_CAMERA" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter android:priority="-1">
+ <action android:name="android.media.action.VIDEO_CAMERA" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+
<!-- Contacts package stubs.
Copied all intent filters from all activities
@@ -634,6 +661,10 @@
<action android:name="android.settings.REQUEST_MANAGE_MEDIA" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
+ <intent-filter android:priority="-1">
+ <action android:name="android.intent.action.SET_WALLPAPER" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
</activity>
<activity android:name=".Stubs$SettingsPrivacyStub"
diff --git a/FrameworkPackageStubs/res/values-az/strings.xml b/FrameworkPackageStubs/res/values-az/strings.xml
index 889abc9..e811c68 100644
--- a/FrameworkPackageStubs/res/values-az/strings.xml
+++ b/FrameworkPackageStubs/res/values-az/strings.xml
@@ -3,5 +3,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1051886861071228918">"Fəaliyyət Genişlənməsi"</string>
<string name="message_not_supported" msgid="5269947674108844893">"Bunu edə bilən tətbiqiniz yoxdur"</string>
- <string name="stub_name" msgid="2907730040872891281">"Heç bir"</string>
+ <string name="stub_name" msgid="2907730040872891281">"Heç biri"</string>
</resources>
diff --git a/FrameworkPackageStubs/res/values-en-rCA/strings.xml b/FrameworkPackageStubs/res/values-en-rCA/strings.xml
index 0867d5e..8442f5f 100644
--- a/FrameworkPackageStubs/res/values-en-rCA/strings.xml
+++ b/FrameworkPackageStubs/res/values-en-rCA/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="1051886861071228918">"Activity stub"</string>
+ <string name="app_name" msgid="1051886861071228918">"Activity Stub"</string>
<string name="message_not_supported" msgid="5269947674108844893">"You don\'t have an app that can do this"</string>
<string name="stub_name" msgid="2907730040872891281">"None"</string>
</resources>
diff --git a/FrameworkPackageStubs/res/values-ro/strings.xml b/FrameworkPackageStubs/res/values-ro/strings.xml
index ecffa59..963f0e9 100644
--- a/FrameworkPackageStubs/res/values-ro/strings.xml
+++ b/FrameworkPackageStubs/res/values-ro/strings.xml
@@ -2,6 +2,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1051886861071228918">"Activity Stub"</string>
- <string name="message_not_supported" msgid="5269947674108844893">"Nu aveți o aplicație care poate face această acțiune"</string>
+ <string name="message_not_supported" msgid="5269947674108844893">"Nu ai o aplicație care poate face această acțiune"</string>
<string name="stub_name" msgid="2907730040872891281">"Nu există"</string>
</resources>
diff --git a/FrameworkPackageStubs/src/com/android/tv/frameworkpackagestubs/Stubs.java b/FrameworkPackageStubs/src/com/android/tv/frameworkpackagestubs/Stubs.java
index 34523b1..88a5810 100644
--- a/FrameworkPackageStubs/src/com/android/tv/frameworkpackagestubs/Stubs.java
+++ b/FrameworkPackageStubs/src/com/android/tv/frameworkpackagestubs/Stubs.java
@@ -88,6 +88,11 @@ public final class Stubs {
public static class CalendarStub extends BaseActivity {}
/**
+ * Stub activity for camera events.
+ */
+ public static class CameraStub extends BaseActivity {}
+
+ /**
* Stub activity for contacts events.
*/
public static class ContactsStub extends BaseActivity {}
diff --git a/OWNERS b/OWNERS
index d823953..da87638 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,6 +1,16 @@
# Bug component: 760438
-hgchen@google.com
+# OS-wide changes in device/google/atv and Cuttlefish
+aabdagic@google.com
rgl@google.com
-shaopengjia@google.com
-wyau@google.com \ No newline at end of file
+robhor@google.com
+shaopengjia@google.com #{LAST_RESORT_SUGGESTION}
+tolstykh@google.com
+
+# TV emulator and boot logo
+alpic@google.com
+kozachuk@google.com
+
+# GSI targets
+hgchen@google.com
+wyau@google.com
diff --git a/TvSampleLeanbackLauncher/Android.bp b/TvSampleLeanbackLauncher/Android.bp
index 8b525f0..d24dc12 100644
--- a/TvSampleLeanbackLauncher/Android.bp
+++ b/TvSampleLeanbackLauncher/Android.bp
@@ -15,14 +15,25 @@ prebuilt_etc {
filename_from_src: true,
}
-android_app_import {
- name: "TvSampleLeanbackLauncher",
- apk: "TvSampleLeanbackLauncher.apk",
- presigned: true,
- privileged: true,
- product_specific: true,
- required: ["privapp_whitelist_com.example.sampleleanbacklauncher"],
- dex_preopt: {
- enabled: false,
- },
+
+android_app {
+ name: "TvSampleLeanbackLauncher",
+ srcs: ["src/main/**/*.java"],
+ resource_dirs: ["src/main/res"],
+ privileged: true,
+ product_specific: true,
+ sdk_version: "current",
+ target_sdk_version: "current",
+ required: ["privapp_whitelist_com.example.sampleleanbacklauncher"],
+ static_libs: [
+ "androidx.annotation_annotation",
+ "androidx.appcompat_appcompat",
+ "androidx.recyclerview_recyclerview",
+ "androidx.leanback_leanback",
+ "androidx.leanback_leanback-preference",
+ "androidx.preference_preference",
+ "androidx.legacy_legacy-preference-v14",
+ "androidx.localbroadcastmanager_localbroadcastmanager",
+ ],
+ manifest: "src/main/AndroidManifest.xml",
}
diff --git a/TvSampleLeanbackLauncher/TvSampleLeanbackLauncher.apk b/TvSampleLeanbackLauncher/TvSampleLeanbackLauncher.apk
deleted file mode 100644
index 6afbf52..0000000
--- a/TvSampleLeanbackLauncher/TvSampleLeanbackLauncher.apk
+++ /dev/null
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/androidTest/java/com/example/sampleleanbacklauncher/apps/LaunchItemTest.java b/TvSampleLeanbackLauncher/src/androidTest/java/com/example/sampleleanbacklauncher/apps/LaunchItemTest.java
new file mode 100644
index 0000000..2acb53f
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/androidTest/java/com/example/sampleleanbacklauncher/apps/LaunchItemTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.example.sampleleanbacklauncher.apps;
+
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(AndroidJUnit4.class)
+public class LaunchItemTest {
+
+ private LaunchItem createTestLaunchItem(String packageName, String name, Drawable banner,
+ Drawable icon, CharSequence label, long priority) {
+ final ActivityInfo activityInfo = mock(ActivityInfo.class);
+ when(activityInfo.loadBanner(any(PackageManager.class))).thenReturn(banner);
+ activityInfo.packageName = packageName;
+ activityInfo.name = name;
+
+ final ResolveInfo resolveInfo = mock(ResolveInfo.class);
+ when(resolveInfo.loadIcon(any(PackageManager.class))).thenReturn(icon);
+ when(resolveInfo.loadLabel(any(PackageManager.class))).thenReturn(label);
+ resolveInfo.activityInfo = activityInfo;
+
+ final Context context = mock(Context.class);
+ when(context.getPackageManager()).thenReturn(null);
+
+ return new LaunchItem(context, resolveInfo, priority);
+ }
+
+ @Test
+ public void testGetIntent() throws Exception {
+ final LaunchItem item =
+ createTestLaunchItem("com.example.testPackage", "com.example.testName",
+ null, null, null, 0);
+ assertNotNull(item.getIntent());
+ assertNotNull(item.getIntent().getComponent());
+ assertEquals("com.example.testPackage", item.getIntent().getComponent().getPackageName());
+ assertEquals("com.example.testName", item.getIntent().getComponent().getClassName());
+ }
+
+ @Test
+ public void testCompareTo() throws Exception {
+ final LaunchItem item = createTestLaunchItem("", "", null, null, "A label", 0);
+ final LaunchItem priorityItem = createTestLaunchItem("", "", null, null, "Z label", 1);
+ final LaunchItem alphaItem = createTestLaunchItem("", "", null, null, "Z label", 0);
+
+ assertTrue("priorityItem should sort before item", item.compareTo(priorityItem) > 0);
+ assertTrue("alphaItem should sort after item", item.compareTo(alphaItem) < 0);
+ assertTrue("item should sort equal to item", item.compareTo(item) == 0);
+ }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/AndroidManifest.xml b/TvSampleLeanbackLauncher/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..070967b
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/AndroidManifest.xml
@@ -0,0 +1,57 @@
+<!--
+ ~ 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
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.sampleleanbacklauncher">
+
+ <!-- ACCESS_WIFI_STATE and ACCESS_NETWORK_STATE are used for updating the network settings entry icon -->
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.ACCESS_NOTIFICATIONS" />
+
+ <uses-feature android:name="android.software.leanback" android:required="true" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:supportsRtl="true"
+ android:theme="@style/AppTheme">
+ <activity android:name=".LauncherActivity"
+ android:launchMode="singleTask"
+ android:resumeWhilePausing="true"
+ android:exported="true">
+ <!-- Need to be priority 2 to avoid conflict with SetupWraith (= 3) -->
+ <intent-filter android:priority="2">
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.HOME" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+
+ </activity>
+ <activity
+ android:name=".notifications.NotificationsSidePanelActivity"
+ android:theme="@style/NotificationsSidePanel"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="com.android.tv.NOTIFICATIONS_PANEL"/>
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ <service android:name=".apps.LaunchItemsManager" android:exported="false" />
+ </application>
+</manifest>
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/LauncherActivity.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/LauncherActivity.java
new file mode 100644
index 0000000..b14ac59
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/LauncherActivity.java
@@ -0,0 +1,80 @@
+/*
+ * 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.example.sampleleanbacklauncher;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+import com.example.sampleleanbacklauncher.apps.AppFragment;
+import com.example.sampleleanbacklauncher.search.SearchFragment;
+
+public class LauncherActivity extends Activity {
+ private SystemUpdateChecker mSystemUpdateChecker;
+ private static final int SYSTEM_UPDATE_CHECKER_REQUEST = 0;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.launcher);
+
+ if (savedInstanceState == null) {
+ getFragmentManager().beginTransaction()
+ // Search row
+ .add(R.id.launcher_container,
+ SearchFragment.newInstance())
+ // Apps row
+ .add(R.id.launcher_container,
+ AppFragment.newInstance(AppFragment.ROW_TYPE_APPS))
+ // Games row
+ .add(R.id.launcher_container,
+ AppFragment.newInstance(AppFragment.ROW_TYPE_GAMES))
+ // Settings row
+ .add(R.id.launcher_container,
+ AppFragment.newInstance(AppFragment.ROW_TYPE_SETTINGS))
+ .commit();
+ }
+
+ mSystemUpdateChecker = new SystemUpdateChecker(this);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ findViewById(R.id.launcher_container).animate().alpha(1f);
+
+ Intent intent = mSystemUpdateChecker.getSystemUpdateCheckerIntent();
+ if (intent != null) {
+ startActivityForResult(intent, SYSTEM_UPDATE_CHECKER_REQUEST);
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ findViewById(R.id.launcher_container).animate().alpha(0f);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == SYSTEM_UPDATE_CHECKER_REQUEST && resultCode == RESULT_OK) {
+ mSystemUpdateChecker.onSystemUpdateCheckerComplete();
+ }
+ }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/LauncherConstants.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/LauncherConstants.java
new file mode 100644
index 0000000..0ddab37
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/LauncherConstants.java
@@ -0,0 +1,29 @@
+/*
+ * 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.example.sampleleanbacklauncher;
+
+public final class LauncherConstants {
+ /**
+ * Intent category for launching TV Settings. This was copied from the system API for sample
+ * purposes. If building using a system SDK, use Intent.CATEGORY_LEANBACK_SETTINGS instead.
+ */
+ public static final String CATEGORY_LEANBACK_SETTINGS =
+ "android.intent.category.LEANBACK_SETTINGS";
+
+ public static final String TVRECOMMENDATIONS_PACKAGE_NAME =
+ "com.google.android.tvrecommendations";
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/SystemUpdateChecker.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/SystemUpdateChecker.java
new file mode 100644
index 0000000..2a65845
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/SystemUpdateChecker.java
@@ -0,0 +1,95 @@
+/*
+ * 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.example.sampleleanbacklauncher;
+
+import android.annotation.TargetApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
+
+import java.util.List;
+
+/**
+ * This is an optional component whose job is to ensure that the system is up-to-date
+ * at boot time. It delegates this operation to a separate application on the device.
+ */
+@TargetApi(24)
+public class SystemUpdateChecker {
+
+ /**
+ * The application that handles the system update check must be a system application that
+ * has an activity found by this action.
+ */
+ private static final String ACTION_TV_BOOT_COMPLETED =
+ "android.intent.action.TV_BOOT_COMPLETED";
+
+ private static final String PREF_SYSTEM_CHECKED_BOOT_COUNT = "system.checked.boot.count";
+
+ private final Context mContext;
+
+ public SystemUpdateChecker(Context context) {
+ mContext = context;
+ }
+
+ Intent getSystemUpdateCheckerIntent() {
+ // See if we have already performed a system update check since the latest boot
+ // of the device
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
+ int checkedBootCount = prefs.getInt(PREF_SYSTEM_CHECKED_BOOT_COUNT, -1);
+ if (checkedBootCount >= getBootCount()) {
+ return null;
+ }
+
+ PackageManager pm = mContext.getPackageManager();
+ List<ResolveInfo> infoList = pm.queryIntentActivities(
+ new Intent(ACTION_TV_BOOT_COMPLETED), 0);
+ if (infoList != null) {
+ for (ResolveInfo resolveInfo : infoList) {
+ if (isSystemApp(resolveInfo)) {
+ Intent intent = new Intent();
+ intent.setComponent(new ComponentName(resolveInfo.activityInfo.packageName,
+ resolveInfo.activityInfo.name));
+ return intent;
+ }
+ }
+ }
+
+ onSystemUpdateCheckerComplete();
+ return null;
+ }
+
+ void onSystemUpdateCheckerComplete() {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
+ prefs.edit().putInt(PREF_SYSTEM_CHECKED_BOOT_COUNT, getBootCount()).apply();
+ }
+
+ private int getBootCount() {
+ // This API is available on Android N+
+ return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.BOOT_COUNT, 0);
+ }
+
+ private static boolean isSystemApp(ResolveInfo info) {
+ return (info.activityInfo != null && info.activityInfo.applicationInfo != null &&
+ (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
+ }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/AppFragment.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/AppFragment.java
new file mode 100644
index 0000000..3795625
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/AppFragment.java
@@ -0,0 +1,450 @@
+/*
+ * 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.example.sampleleanbacklauncher.apps;
+
+import android.app.Fragment;
+import android.app.LoaderManager;
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.Loader;
+import android.content.ServiceConnection;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.os.Bundle;
+import android.os.IBinder;
+import androidx.annotation.IdRes;
+import androidx.annotation.MainThread;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringDef;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+import androidx.collection.ArrayMap;
+import androidx.recyclerview.widget.SortedList;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.SortedListAdapterCallback;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.Toast;
+import android.widget.TextView;
+
+import com.example.sampleleanbacklauncher.R;
+import com.example.sampleleanbacklauncher.notifications.NotificationsContract;
+import com.example.sampleleanbacklauncher.util.LauncherAsyncTaskLoader;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+
+public class AppFragment extends Fragment {
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef({ROW_TYPE_APPS, ROW_TYPE_GAMES, ROW_TYPE_SETTINGS})
+ public @interface RowType {}
+ public static final String ROW_TYPE_APPS = "apps";
+ public static final String ROW_TYPE_GAMES = "games";
+ public static final String ROW_TYPE_SETTINGS = "settings";
+
+ private static final String TAG = "AppFragment";
+ private static final boolean DEBUG = false;
+
+ private static final String ARG_ROW_TYPE = "AppFragment.ROW_TYPE";
+
+ private static final int ITEM_LOADER_ID = 1;
+ private static final int NOTIFS_COUNT_LOADER_ID = 2;
+
+ @RowType
+ private String mRowType;
+
+ private AppAdapter mAdapter;
+
+ private LaunchItemsManager mLaunchItemsManager;
+ private final ServiceConnection mLaunchItemsServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName componentName, IBinder service) {
+ mLaunchItemsManager =
+ ((LaunchItemsManager.LocalBinder) service).getLaunchItemsManager();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName componentName) {
+ mLaunchItemsManager = null;
+ }
+ };
+
+ public static AppFragment newInstance(@RowType String rowType) {
+ Bundle args = new Bundle(1);
+ args.putString(ARG_ROW_TYPE, rowType);
+
+ AppFragment fragment = new AppFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ //noinspection WrongConstant
+ mRowType = getArguments().getString(ARG_ROW_TYPE, ROW_TYPE_APPS);
+ final Context context = getContext();
+ context.bindService(new Intent(context, LaunchItemsManager.class),
+ mLaunchItemsServiceConnection, Context.BIND_AUTO_CREATE);
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ final View root = inflater.inflate(R.layout.app_launch_row, container, false);
+ // Since there's multiple instances of this root view in the parent, they need a unique
+ // id so that view state save/restore works correctly.
+ root.setId(getRootViewId());
+ final TextView rowTitle = (TextView) root.findViewById(R.id.row_title);
+ rowTitle.setText(getRowTitle());
+ final RecyclerView list = (RecyclerView) root.findViewById(android.R.id.list);
+ mAdapter = new AppAdapter();
+ getLoaderManager().initLoader(ITEM_LOADER_ID, null, new ItemLoaderCallbacks());
+ list.setAdapter(mAdapter);
+
+ if (mRowType == ROW_TYPE_SETTINGS) {
+ getLoaderManager().initLoader(NOTIFS_COUNT_LOADER_ID, null,
+ new NotifsCountLoaderCallbacks());
+ }
+
+ return root;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ Context context = getContext();
+ context.unbindService(mLaunchItemsServiceConnection);
+ }
+
+ private @IdRes int getRootViewId() {
+ switch (mRowType) {
+ case ROW_TYPE_APPS:
+ return R.id.apps_row;
+ case ROW_TYPE_GAMES:
+ return R.id.games_row;
+ case ROW_TYPE_SETTINGS:
+ return R.id.settings_row;
+ }
+ throw new IllegalStateException("Unknown row type");
+ }
+
+ public CharSequence getRowTitle() {
+ switch (mRowType) {
+ case ROW_TYPE_APPS:
+ return getText(R.string.apps_row_title);
+ case ROW_TYPE_GAMES:
+ return getText(R.string.games_row_title);
+ case ROW_TYPE_SETTINGS:
+ return getText(R.string.settings_row_title);
+ }
+ throw new IllegalStateException("Unknown row type");
+ }
+
+ @MainThread
+ private void updateList(Set<LaunchItem> newItems) {
+ if (!isAdded() || mAdapter == null) {
+ return;
+ }
+
+ final SortedList<LaunchItem> items = mAdapter.getLaunchItems();
+
+ if (newItems == null) {
+ items.clear();
+ return;
+ }
+
+ items.beginBatchedUpdates();
+ try {
+ for (int i = 0; i < items.size(); ) {
+ final LaunchItem item = items.get(i);
+ if (newItems.contains(item)) {
+ i++;
+ } else {
+ items.remove(item);
+ }
+ }
+ items.addAll(newItems);
+ } finally {
+ items.endBatchedUpdates();
+ }
+ }
+
+ void onItemClicked(int adapterPosition) {
+ SortedList<LaunchItem> launchItems = mAdapter.getLaunchItems();
+ if (adapterPosition >= launchItems.size()) {
+ Log.e(TAG, "Item clicked out of bounds, index " + adapterPosition +
+ " size " + launchItems.size());
+ return;
+ }
+ final LaunchItem item = launchItems.get(adapterPosition);
+ try {
+ startActivity(item.getIntent());
+ if (mLaunchItemsManager != null) {
+ mLaunchItemsManager.notifyItemLaunched(item);
+ }
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "Exception launching intent " + item.getIntent(), e);
+ Toast.makeText(getContext(), getString(R.string.app_unavailable),
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ public class AppViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
+ private final View mView;
+
+ public AppViewHolder(View v) {
+ super(v);
+ mView = v;
+ }
+
+ public void bind(LaunchItem launchItem) {
+ mView.findViewById(R.id.frame).setContentDescription(launchItem.getLabel());
+ mView.findViewById(R.id.frame).setOnClickListener(this);
+
+ int bannerVisibility = launchItem.getBanner() == null ? View.GONE: View.VISIBLE;
+ mView.findViewById(R.id.banner).setVisibility(bannerVisibility);
+
+ int llVisibility = launchItem.getBanner() == null ? View.GONE: View.VISIBLE;
+ mView.findViewById(R.id.ll).setVisibility(llVisibility);
+
+ TextView launchItemLabel = mView.findViewById(R.id.label);
+ launchItemLabel.setText(launchItem.getLabel());
+
+ ImageView launchItemIcon = mView.findViewById(R.id.icon);
+ launchItemIcon.setImageDrawable(launchItem.getIcon());
+ }
+
+ @Override
+ public void onClick(View v) {
+ final int position = getAdapterPosition();
+ if (position != RecyclerView.NO_POSITION) {
+ onItemClicked(position);
+ }
+ }
+ }
+
+ public class AppAdapter extends RecyclerView.Adapter<AppViewHolder> {
+ private final SortedList<LaunchItem> mLaunchItems;
+ private final Map<ComponentName, Long> mItemIdMap = new ArrayMap<>();
+ private long mNextItemId = 0;
+
+ public AppAdapter() {
+ mLaunchItems = new SortedList<>(LaunchItem.class,
+ new SortedListAdapterCallback<LaunchItem>(this) {
+ @Override
+ public int compare(LaunchItem o1, LaunchItem o2) {
+ return o1.compareTo(o2);
+ }
+
+ @Override
+ public boolean areContentsTheSame(LaunchItem oldItem, LaunchItem newItem) {
+ return oldItem.areContentsTheSame(newItem);
+ }
+
+ @Override
+ public boolean areItemsTheSame(LaunchItem item1, LaunchItem item2) {
+ return Objects.equals(item1, item2);
+ }
+ });
+ setHasStableIds(true);
+ }
+
+ @Override
+ public AppViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
+ return new AppViewHolder(inflater.inflate(R.layout.launch_item, parent, false));
+ }
+
+ @Override
+ public void onBindViewHolder(AppViewHolder holder, int position) {
+ holder.bind(mLaunchItems.get(position));
+ }
+
+ @Override
+ public int getItemCount() {
+ return mLaunchItems.size();
+ }
+
+ public SortedList<LaunchItem> getLaunchItems() {
+ return mLaunchItems;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ ComponentName componentName = mLaunchItems.get(position).getIntent().getComponent();
+ Long id = mItemIdMap.get(componentName);
+ if (id != null) {
+ return id;
+ } else {
+ long newId = mNextItemId++;
+ mItemIdMap.put(componentName, newId);
+ return newId;
+ }
+ }
+ }
+
+ private class ItemLoaderCallbacks implements LoaderManager.LoaderCallbacks<Set<LaunchItem>> {
+ @Override
+ public Loader<Set<LaunchItem>> onCreateLoader(int id, Bundle args) {
+ return new ItemLoader(getContext(), mRowType);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Set<LaunchItem>> loader, Set<LaunchItem> data) {
+ updateList(data);
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Set<LaunchItem>> loader) {}
+ }
+
+ private static class ItemLoader extends LauncherAsyncTaskLoader<Set<LaunchItem>> {
+
+ private final String mRowType;
+
+ private LaunchItemsManager mLaunchItemsManager;
+ private ServiceConnection mConnection;
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onContentChanged();
+ }
+ };
+
+ public ItemLoader(Context context, String rowType) {
+ super(context);
+ mRowType = rowType;
+ }
+
+ @Override
+ protected void onStartLoading() {
+ super.onStartLoading();
+ if (mConnection == null) {
+ mConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mLaunchItemsManager =
+ ((LaunchItemsManager.LocalBinder) service).getLaunchItemsManager();
+ startListening();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ stopListening();
+ mLaunchItemsManager = null;
+ }
+ };
+ getContext().bindService(new Intent(getContext(), LaunchItemsManager.class),
+ mConnection, Context.BIND_AUTO_CREATE);
+ }
+ }
+
+ private void startListening() {
+ switch (mRowType) {
+ case ROW_TYPE_APPS:
+ LocalBroadcastManager.getInstance(getContext()).registerReceiver(mReceiver,
+ new IntentFilter(LaunchItemsManager.ACTION_APP_LIST_INVALIDATED));
+ break;
+ case ROW_TYPE_GAMES:
+ LocalBroadcastManager.getInstance(getContext()).registerReceiver(mReceiver,
+ new IntentFilter(LaunchItemsManager.ACTION_GAME_LIST_INVALIDATED));
+ break;
+ case ROW_TYPE_SETTINGS:
+ LocalBroadcastManager.getInstance(getContext()).registerReceiver(mReceiver,
+ new IntentFilter(LaunchItemsManager.ACTION_SETTINGS_LIST_INVALIDATED));
+ break;
+ }
+ // Force a load to pick up any changes since we stopped listening
+ onContentChanged();
+ }
+
+ private void stopListening() {
+ LocalBroadcastManager.getInstance(getContext())
+ .unregisterReceiver(mReceiver);
+ }
+
+ @Override
+ protected void onReset() {
+ super.onReset();
+ if (mConnection != null) {
+ getContext().unbindService(mConnection);
+ mConnection = null;
+ }
+ }
+
+ @Override
+ public Set<LaunchItem> loadInBackground() {
+ if (mLaunchItemsManager != null) {
+ switch (mRowType) {
+ case ROW_TYPE_APPS:
+ return mLaunchItemsManager.getAppItems();
+ case ROW_TYPE_GAMES:
+ return mLaunchItemsManager.getGameItems();
+ case ROW_TYPE_SETTINGS:
+ return mLaunchItemsManager.getSettingsItems();
+ default:
+ throw new IllegalStateException("Unknown row type");
+ }
+ } else {
+ return null;
+ }
+ }
+ }
+
+ private class NotifsCountLoaderCallbacks implements LoaderManager.LoaderCallbacks<Cursor> {
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ return new CursorLoader(getContext(), NotificationsContract.NOTIFS_COUNT_URI,
+ null, null, null, null);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ if (DEBUG) {
+ Log.d(TAG, "onLoadFinished() called with: " + "loader = [" + loader + "], data = ["
+ + DatabaseUtils.dumpCursorToString(data) + "]");
+ }
+ if (mLaunchItemsManager != null) {
+ mLaunchItemsManager.updateNotifsCountCursor(data);
+ }
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ if (DEBUG) {
+ Log.d(TAG, "onLoaderReset() called with: " + "loader = [" + loader + "]");
+ }
+ if (mLaunchItemsManager != null) {
+ mLaunchItemsManager.updateNotifsCountCursor(null);
+ }
+ }
+ }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/LaunchItem.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/LaunchItem.java
new file mode 100644
index 0000000..4f59bde
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/LaunchItem.java
@@ -0,0 +1,116 @@
+/*
+ * 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.example.sampleleanbacklauncher.apps;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import androidx.annotation.NonNull;
+
+import java.util.Objects;
+
+public class LaunchItem implements Comparable<LaunchItem> {
+ protected final Intent mIntent;
+ private final Drawable mBanner;
+ private final Drawable mIcon;
+ private final CharSequence mLabel;
+ private final long mPriority;
+
+ LaunchItem(Context context, Intent intent, Drawable icon, CharSequence label) {
+ mIntent = intent;
+ mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ final PackageManager packageManager = context.getPackageManager();
+ mBanner = null;
+ mIcon = icon;
+ mLabel = label;
+ mPriority = 0;
+ }
+
+ LaunchItem(Context context, ResolveInfo info, long priority) {
+ mIntent = Intent.makeMainActivity(
+ new ComponentName(info.activityInfo.packageName, info.activityInfo.name));
+ mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ final PackageManager packageManager = context.getPackageManager();
+ mBanner = info.activityInfo.loadBanner(packageManager);
+ mIcon = info.loadIcon(packageManager);
+ mLabel = info.loadLabel(packageManager);
+ mPriority = priority;
+ }
+
+ public Intent getIntent() {
+ return mIntent;
+ }
+
+ public Drawable getBanner() {
+ return mBanner;
+ }
+
+ public Drawable getIcon() {
+ return mIcon;
+ }
+
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+
+ /**
+ * Returns whether this item should appear identical to the given item.
+ * @param another Item to compare to
+ * @return True if items should appear identical
+ */
+ public boolean areContentsTheSame(LaunchItem another) {
+ return Objects.equals(another.getBanner(), getBanner())
+ && Objects.equals(another.getIcon(), getIcon())
+ && Objects.equals(another.getLabel(), getLabel());
+ }
+
+ @Override
+ public int compareTo(@NonNull LaunchItem another) {
+ long priorityDiff = another.mPriority - mPriority;
+ if (priorityDiff != 0) {
+ return priorityDiff < 0L ? -1 : 1;
+ } else {
+ return getLabel().toString().compareTo(another.getLabel().toString());
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof LaunchItem && Objects.equals(mIntent, ((LaunchItem)o).getIntent());
+ }
+
+ @Override
+ public int hashCode() {
+ return mIntent.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return mLabel + " -- " + mIntent.getComponent().toString();
+ }
+
+ public String toDebugString() {
+ return "Label: " + mLabel
+ + " Intent: " + mIntent
+ + " Banner: " + mBanner
+ + " Icon: " + mIcon
+ + " Priority: " + Long.toString(mPriority);
+ }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/LaunchItemsDbHelper.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/LaunchItemsDbHelper.java
new file mode 100644
index 0000000..b857bb7
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/LaunchItemsDbHelper.java
@@ -0,0 +1,260 @@
+/*
+ * 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.example.sampleleanbacklauncher.apps;
+
+import android.content.ComponentName;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteCursor;
+import android.database.sqlite.SQLiteCursorDriver;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteQuery;
+import android.os.Trace;
+import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
+import android.util.ArrayMap;
+
+import java.util.Date;
+import java.util.Map;
+
+@WorkerThread
+public class LaunchItemsDbHelper extends SQLiteOpenHelper {
+
+ // Increment whenever schema changes
+ private static final int DATABASE_VERSION = 1;
+ // Database name
+ private static final String DATABASE_NAME = "launch_items";
+
+ private static final String CREATE_APP_TABLE =
+ "CREATE TABLE IF NOT EXISTS " + AppDbItem.TABLE_NAME + "("
+ + AppDbItem.COLUMN_COMPONENT + " TEXT NOT NULL PRIMARY KEY , "
+ + AppDbItem.COLUMN_ORDER_PRIORITY + " INTEGER NOT NULL DEFAULT 0 , "
+ + AppDbItem.COLUMN_LAST_OPEN + " INTEGER "
+ + " )";
+
+ public LaunchItemsDbHelper(Context context) {
+ super(context, DATABASE_NAME, new SQLiteDatabase.CursorFactory() {
+ @Override
+ public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery,
+ String editTable, SQLiteQuery query) {
+ return new SQLiteCursor(masterQuery, editTable, query);
+ }
+ }, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL(CREATE_APP_TABLE);
+ }
+
+ @Override
+ public void onConfigure(SQLiteDatabase db) {
+ super.onConfigure(db);
+ setWriteAheadLoggingEnabled(true);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ // DO NOT USE CONSTANTS FOR DB UPGRADE STEPS, USE ONLY LITERAL SQL STRINGS!
+ }
+
+ @Override
+ public SQLiteDatabase getWritableDatabase() {
+ Trace.beginSection("getWritableDatabase");
+ try {
+ return super.getWritableDatabase();
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ @Override
+ public SQLiteDatabase getReadableDatabase() {
+ Trace.beginSection("getReadableDatabase");
+ try {
+ return super.getReadableDatabase();
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ @Nullable
+ public Map<ComponentName, Date> readLastOpens() {
+ Trace.beginSection("readLastOpens");
+ try {
+ final SQLiteDatabase db = getReadableDatabase();
+
+ try (Cursor c = db.query(
+ AppDbItem.TABLE_NAME,
+ new String[] {AppDbItem.COLUMN_COMPONENT, AppDbItem.COLUMN_LAST_OPEN},
+ null, null, null, null, null, null)) {
+
+ final int componentIndex = c.getColumnIndex(AppDbItem.COLUMN_COMPONENT);
+ final int lastOpenIndex = c.getColumnIndex(AppDbItem.COLUMN_LAST_OPEN);
+
+ final Map<ComponentName, Date> map = new ArrayMap<>(c.getCount());
+ while (c.moveToNext()) {
+ final ComponentName componentName =
+ ComponentName.unflattenFromString(c.getString(componentIndex));
+ if (c.isNull(lastOpenIndex)) {
+ map.put(componentName, null);
+ } else {
+ map.put(componentName, new Date(c.getLong(lastOpenIndex)));
+ }
+ }
+ return map;
+
+ }
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ public void writeLastOpen(ComponentName componentName, Date lastOpen) {
+ Trace.beginSection("writeLastOpen");
+ try {
+ final SQLiteDatabase db = getWritableDatabase();
+
+ db.beginTransactionNonExclusive();
+ try {
+ final ContentValues contentValues = new ContentValues(2);
+ contentValues.put(AppDbItem.COLUMN_LAST_OPEN, lastOpen.getTime());
+
+ final int cnt = db.update(
+ AppDbItem.TABLE_NAME,
+ contentValues,
+ AppDbItem.COLUMN_COMPONENT + "=?",
+ new String[]{componentName.flattenToString()});
+
+ if (cnt < 1) {
+ contentValues.put(AppDbItem.COLUMN_COMPONENT, componentName.flattenToString());
+ db.insert(AppDbItem.TABLE_NAME, AppDbItem.COLUMN_COMPONENT, contentValues);
+ }
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ public Map<ComponentName, Long> readOrderPriorities() {
+ Trace.beginSection("readOrderPriorities");
+ try {
+ final SQLiteDatabase db = getReadableDatabase();
+
+ try (Cursor c = db.query(
+ AppDbItem.TABLE_NAME,
+ new String[] {AppDbItem.COLUMN_COMPONENT, AppDbItem.COLUMN_ORDER_PRIORITY},
+ null, null, null, null, null, null)) {
+
+ final int componentIndex = c.getColumnIndex(AppDbItem.COLUMN_COMPONENT);
+ final int priorityIndex = c.getColumnIndex(AppDbItem.COLUMN_ORDER_PRIORITY);
+
+ final Map<ComponentName, Long> map = new ArrayMap<>(c.getCount());
+ while (c.moveToNext()) {
+ final ComponentName componentName =
+ ComponentName.unflattenFromString(c.getString(componentIndex));
+ map.put(componentName, c.getLong(priorityIndex));
+ }
+ return map;
+ }
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ /**
+ * Writes the order priority field, and does not touch other items' priorities
+ * @param componentName {@link ComponentName} of item to update
+ * @param priority New priority
+ */
+ public void writeOrderPriority(ComponentName componentName, long priority) {
+ Trace.beginSection("writeOrderPriority");
+ try {
+ final SQLiteDatabase db = getWritableDatabase();
+
+ db.beginTransactionNonExclusive();
+ try {
+ final ContentValues contentValues = new ContentValues(2);
+ contentValues.put(AppDbItem.COLUMN_ORDER_PRIORITY, priority);
+
+ final int cnt = db.update(
+ AppDbItem.TABLE_NAME,
+ contentValues,
+ AppDbItem.COLUMN_COMPONENT + "=?",
+ new String[]{componentName.flattenToString()});
+
+ if (cnt < 1) {
+ contentValues.put(AppDbItem.COLUMN_COMPONENT, componentName.flattenToString());
+ db.insert(AppDbItem.TABLE_NAME, AppDbItem.COLUMN_COMPONENT, contentValues);
+ }
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ /**
+ * Writes the order priority field, and increments all priorities equal or greater if the new
+ * priority is greater than 0
+ * @param componentName {@link ComponentName} of item to update
+ * @param priority New priority
+ */
+ public void writeOrderPriorityAndShift(ComponentName componentName, int priority) {
+ Trace.beginSection("writeOrderPriorityAndShift");
+ try {
+ final SQLiteDatabase db = getWritableDatabase();
+
+ db.beginTransactionNonExclusive();
+ try {
+ if (priority > 0) {
+ db.execSQL("UPDATE " + AppDbItem.TABLE_NAME
+ + " SET " + AppDbItem.COLUMN_ORDER_PRIORITY + "="
+ + AppDbItem.COLUMN_ORDER_PRIORITY + "+1"
+ + " WHERE " + AppDbItem.COLUMN_ORDER_PRIORITY + ">=?",
+ new String[] {Integer.toString(priority)});
+ }
+
+ writeOrderPriority(componentName, priority);
+
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ private static class AppDbItem {
+ public static final String TABLE_NAME = "app_items";
+
+ // Primary key
+ public static final String COLUMN_COMPONENT = "component";
+ // Ordering priority: higher numbers are higher in list
+ public static final String COLUMN_ORDER_PRIORITY = "order_priority";
+ // Recency date
+ public static final String COLUMN_LAST_OPEN = "last_open";
+ }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/LaunchItemsManager.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/LaunchItemsManager.java
new file mode 100644
index 0000000..fd68abb
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/LaunchItemsManager.java
@@ -0,0 +1,496 @@
+/*
+ * 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.example.sampleleanbacklauncher.apps;
+
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.ComponentCallbacks2;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.database.Cursor;
+import android.net.ConnectivityManager;
+import android.net.Uri;
+import android.net.wifi.WifiManager;
+import android.os.AsyncTask;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Trace;
+import android.provider.Settings;
+import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+import android.telephony.PhoneStateListener;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.example.sampleleanbacklauncher.R;
+import com.example.sampleleanbacklauncher.LauncherConstants;
+import com.example.sampleleanbacklauncher.notifications.NotificationsContract;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class LaunchItemsManager extends Service implements ComponentCallbacks2 {
+ private static final String TAG = "LaunchItemsManager";
+
+ public static final String ACTION_APP_LIST_INVALIDATED =
+ "LaunchItemsManager.APP_LIST_INVALIDATED";
+ public static final String ACTION_GAME_LIST_INVALIDATED =
+ "LaunchItemsManager.GAME_LIST_INVALIDATED";
+ public static final String ACTION_SETTINGS_LIST_INVALIDATED =
+ "LaunchItemsManager.SETTINGS_LIST_INVALIDATED";
+
+ private LaunchItemsDbHelper mDbHelper;
+ private final Object mDbHelperLock = new Object();
+
+ private Set<LaunchItem> mAppItems = new ArraySet<>();
+ private Set<LaunchItem> mGameItems = new ArraySet<>();
+ private Set<LaunchItem> mSettingsItems = new ArraySet<>();
+
+ private Map<ComponentName, Date> mLastOpenMap;
+ private Map<ComponentName, Long> mPriorityMap;
+ private Map<String, Long> mOobPriority;
+
+ private volatile boolean mAppListValid;
+ private volatile boolean mGameListValid;
+ private volatile boolean mSettingsListValid;
+
+ private NotificationsLaunchItem mNotifsLaunchItem;
+ private Cursor mNotifsCountCursor = null;
+
+ private final PackageListener mPackageListener = new PackageListener();
+ private final NetworkListener mNetworkListener = new NetworkListener();
+ private final IBinder mLocalBinder = new LocalBinder();
+
+ // We can't query the signal strength directly, so we have to just listen for it all the time.
+ private SignalStrength mSignalStrength;
+ private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+ @Override
+ public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+ mSignalStrength = signalStrength;
+ invalidateSettingsList();
+ }
+ };
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ IntentFilter packageIntentFilter = new IntentFilter();
+ packageIntentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ packageIntentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ packageIntentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ packageIntentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+ packageIntentFilter.addDataScheme("package");
+ registerReceiver(mPackageListener, packageIntentFilter);
+
+ IntentFilter networkIntentFilter = new IntentFilter();
+ networkIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ networkIntentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
+ networkIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ networkIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+ registerReceiver(mNetworkListener, networkIntentFilter);
+
+ registerComponentCallbacks(this);
+
+ getSystemService(TelephonyManager.class).listen(mPhoneStateListener,
+ PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
+
+ invalidateAllLists();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+
+ unregisterReceiver(mPackageListener);
+ unregisterReceiver(mNetworkListener);
+
+ unregisterComponentCallbacks(this);
+ getSystemService(TelephonyManager.class).listen(mPhoneStateListener,
+ PhoneStateListener.LISTEN_NONE);
+ }
+
+ @Nullable
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mLocalBinder;
+ }
+
+ @WorkerThread
+ private void ensureDatabase() {
+ synchronized (mDbHelperLock) {
+ if (mDbHelper == null) {
+ mDbHelper = new LaunchItemsDbHelper(getApplicationContext());
+ mLastOpenMap = mDbHelper.readLastOpens();
+ mPriorityMap = mDbHelper.readOrderPriorities();
+ final String[] oobOrder = getResources().getStringArray(R.array.oob_order);
+ mOobPriority = new ArrayMap<>(oobOrder.length);
+ for (int i = 0; i < oobOrder.length; i++) {
+ mOobPriority.put(oobOrder[i], (long) oobOrder.length - i);
+ }
+ }
+ }
+ }
+
+ @WorkerThread
+ private long getPackagePriority(ResolveInfo info) {
+ ensureDatabase();
+
+ final ComponentName cn =
+ new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
+
+ // Since 1970 was quite a while ago, the last open time should be much larger than
+ // the numeric priority
+ Long priority = mPriorityMap.get(cn);
+ final Date lastOpen = mLastOpenMap.get(cn);
+ if (lastOpen != null) {
+ priority = lastOpen.getTime();
+ }
+ if (priority == null) {
+ if (mOobPriority.containsKey(info.activityInfo.packageName)) {
+ priority = mOobPriority.get(info.activityInfo.packageName);
+ } else {
+ priority = 0L;
+ }
+ mDbHelper.writeOrderPriority(cn, priority);
+ mPriorityMap.put(cn, priority);
+ }
+
+ return priority;
+ }
+
+ @WorkerThread
+ private void updateAppList() {
+ Trace.beginSection("updateAppList");
+ try {
+ final PackageManager packageManager = getPackageManager();
+ List<ResolveInfo> infos = packageManager.queryIntentActivities(
+ new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LEANBACK_LAUNCHER),
+ 0);
+ final Set<LaunchItem> appItems = new ArraySet<>(infos.size());
+ for (ResolveInfo info : infos) {
+
+ ApplicationInfo appInfo = info.activityInfo.applicationInfo;
+ if ((appInfo.flags & ApplicationInfo.FLAG_IS_GAME) == 0) {
+ appItems.add(new LaunchItem(this, info, getPackagePriority(info)));
+ }
+ }
+ mAppItems = appItems;
+ mAppListValid = true;
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ @WorkerThread
+ private void updateGamesList() {
+ Trace.beginSection("updateGamesList");
+ try {
+ final PackageManager packageManager = getPackageManager();
+ List<ResolveInfo> infos = packageManager.queryIntentActivities(
+ new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LEANBACK_LAUNCHER),
+ 0);
+ final Set<LaunchItem> gameItems = new ArraySet<>(infos.size());
+ for (ResolveInfo info : infos) {
+ ApplicationInfo appInfo = info.activityInfo.applicationInfo;
+ if ((appInfo.flags & ApplicationInfo.FLAG_IS_GAME) != 0) {
+ gameItems.add(new LaunchItem(this, info, getPackagePriority(info)));
+ }
+ }
+ mGameItems = gameItems;
+ mGameListValid = true;
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ @WorkerThread
+ private void updateSettingsList() {
+ Trace.beginSection("updateSettingsList");
+ try {
+ final PackageManager packageManager = getPackageManager();
+ List<ResolveInfo> networkInfos =
+ packageManager.queryIntentActivities(new Intent(Settings.ACTION_WIFI_SETTINGS)
+ .addCategory(LauncherConstants.CATEGORY_LEANBACK_SETTINGS), 0);
+ List<ResolveInfo> settingsInfos = packageManager.queryIntentActivities(
+ new Intent(Intent.ACTION_MAIN)
+ .addCategory(LauncherConstants.CATEGORY_LEANBACK_SETTINGS),
+ PackageManager.GET_RESOLVED_FILTER);
+ final Set<LaunchItem> settingsItems = new ArraySet<>(settingsInfos.size());
+ for (ResolveInfo info : settingsInfos) {
+ if (info.activityInfo == null) {
+ continue;
+ }
+ boolean isNetwork = false;
+ for (ResolveInfo networkInfo : networkInfos) {
+ if (networkInfo.activityInfo == null) {
+ continue;
+ }
+ if (TextUtils.equals(networkInfo.activityInfo.name,
+ info.activityInfo.name)
+ && TextUtils.equals(networkInfo.activityInfo.packageName,
+ info.activityInfo.packageName)) {
+ isNetwork = true;
+ break;
+ }
+ }
+ int priority = info.priority;
+ if (isNetwork) {
+ settingsItems.add(new NetworkLaunchItem(this, info, mSignalStrength, priority));
+ } else {
+ settingsItems.add(new SettingsLaunchItem(this, info, priority));
+ }
+ }
+
+ mNotifsLaunchItem = new NotificationsLaunchItem(this);
+ mNotifsLaunchItem.setNotificationsCount(getNotifsCount());
+ settingsItems.add(mNotifsLaunchItem);
+
+ mSettingsItems = settingsItems;
+ mSettingsListValid = true;
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ public void updateNotifsCountCursor(Cursor cursor) {
+ mNotifsCountCursor = cursor;
+ invalidateSettingsList();
+ }
+
+ @WorkerThread
+ private int getNotifsCount() {
+ if (mNotifsCountCursor != null && mNotifsCountCursor.moveToFirst()) {
+ mNotifsCountCursor.moveToFirst();
+ int index = mNotifsCountCursor.getColumnIndex(NotificationsContract.COLUMN_COUNT);
+ return mNotifsCountCursor.getInt(index);
+ }
+ return 0;
+ }
+
+ private void invalidateAllLists() {
+ invalidateAppList();
+ invalidateGameList();
+ invalidateSettingsList();
+ }
+
+ private void invalidateAppList() {
+ mAppListValid = false;
+ LocalBroadcastManager.getInstance(this)
+ .sendBroadcast(new Intent(ACTION_APP_LIST_INVALIDATED));
+ }
+
+ private void invalidateGameList() {
+ mGameListValid = false;
+ LocalBroadcastManager.getInstance(this)
+ .sendBroadcast(new Intent(ACTION_GAME_LIST_INVALIDATED));
+ }
+
+ private void invalidateSettingsList() {
+ mSettingsListValid = false;
+ LocalBroadcastManager.getInstance(this)
+ .sendBroadcast(new Intent(ACTION_SETTINGS_LIST_INVALIDATED));
+ }
+
+ @WorkerThread
+ public Set<LaunchItem> getAppItems() {
+ if (!mAppListValid) {
+ updateAppList();
+ }
+ return mAppItems;
+ }
+
+ @WorkerThread
+ public Set<LaunchItem> getGameItems() {
+ if (!mGameListValid) {
+ updateGamesList();
+ }
+ return mGameItems;
+ }
+
+ @WorkerThread
+ public Set<LaunchItem> getSettingsItems() {
+ if (!mSettingsListValid) {
+ updateSettingsList();
+ }
+ return mSettingsItems;
+ }
+
+ private void invalidateListsForPackage(String packageName) {
+ boolean updateApps = false;
+ boolean updateGames = false;
+ boolean updateSettings = false;
+
+ // Check if the package was previously listed in apps, games or settings
+ for (final LaunchItem item : mAppItems) {
+ if (TextUtils.equals(item.getIntent().getComponent().getPackageName(), packageName)) {
+ updateApps = true;
+ break;
+ }
+ }
+ if (!updateApps) {
+ // Can't be both an app and a game at the same time
+ for (final LaunchItem item : mGameItems) {
+ if (TextUtils.equals(item.getIntent().getComponent().getPackageName(),
+ packageName)) {
+ updateGames = true;
+ break;
+ }
+ }
+ }
+ for (final LaunchItem item : mSettingsItems) {
+ if (TextUtils.equals(item.getIntent().getPackage(), packageName)) {
+ updateSettings = true;
+ break;
+ }
+ }
+
+ // Check if the app will be listed in apps, games or settings
+ final List<ResolveInfo> leanbackInfos =
+ getPackageManager().queryIntentActivities(
+ new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_LEANBACK_LAUNCHER)
+ .setPackage(packageName),
+ 0);
+ if (!leanbackInfos.isEmpty()) {
+ ApplicationInfo applicationInfo = leanbackInfos.get(0).activityInfo.applicationInfo;
+ if ((applicationInfo.flags & ApplicationInfo.FLAG_IS_GAME) == 0) {
+ updateApps = true;
+ } else {
+ updateGames = true;
+ }
+ }
+ if (!updateSettings) {
+ final List<ResolveInfo> settingsInfos =
+ getPackageManager().queryIntentActivities(
+ new Intent(Intent.ACTION_MAIN)
+ .addCategory(LauncherConstants.CATEGORY_LEANBACK_SETTINGS)
+ .setPackage(packageName),
+ 0);
+ if (!settingsInfos.isEmpty()) {
+ updateSettings = true;
+ }
+ }
+
+ if (updateApps) {
+ invalidateAppList();
+ }
+ if (updateGames) {
+ invalidateGameList();
+ }
+ if (updateSettings) {
+ invalidateSettingsList();
+ }
+ }
+
+ public void notifyItemLaunched(LaunchItem item) {
+ final Date now = new Date();
+ final ComponentName component = item.getIntent().getComponent();
+ mLastOpenMap.put(component, now);
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ ensureDatabase();
+ if (component != null) {
+ mDbHelper.writeLastOpen(component, now);
+ }
+ return null;
+ }
+ }.execute();
+ if (mAppItems.contains(item)) {
+ invalidateAppList();
+ } else if (mGameItems.contains(item)) {
+ invalidateGameList();
+ }
+ // No recency for settings row
+ }
+
+ @Override
+ public void onTrimMemory(int level) {}
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ invalidateAllLists();
+ }
+
+ @Override
+ public void onLowMemory() {}
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ writer.println("App Items");
+ if (mAppItems != null) {
+ for (final LaunchItem item : mAppItems.toArray(new LaunchItem[mAppItems.size()])) {
+ writer.println(item.toDebugString());
+ }
+ } else {
+ writer.println("Null");
+ }
+ writer.println("Game Items");
+ if (mGameItems != null) {
+ for (final LaunchItem item : mGameItems.toArray(new LaunchItem[mGameItems.size()])) {
+ writer.println(item.toDebugString());
+ }
+ } else {
+ writer.println("Null");
+ }
+ writer.println("Settings Items");
+ if (mSettingsItems != null) {
+ for (final LaunchItem item :
+ mSettingsItems.toArray(new LaunchItem[mSettingsItems.size()])) {
+ writer.println(item.toDebugString());
+ }
+ } else {
+ writer.println("Null");
+ }
+ }
+
+ public class LocalBinder extends Binder {
+ public LaunchItemsManager getLaunchItemsManager() {
+ return LaunchItemsManager.this;
+ }
+ }
+
+ public class PackageListener extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final Uri packageUri = Uri.parse(intent.getDataString());
+ invalidateListsForPackage(packageUri.getSchemeSpecificPart());
+ }
+ }
+
+ public class NetworkListener extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ invalidateSettingsList();
+ }
+ }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/NetworkLaunchItem.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/NetworkLaunchItem.java
new file mode 100644
index 0000000..1d9378e
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/NetworkLaunchItem.java
@@ -0,0 +1,156 @@
+/*
+ * 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.example.sampleleanbacklauncher.apps;
+
+import android.content.Context;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import androidx.annotation.WorkerThread;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
+
+import com.example.sampleleanbacklauncher.R;
+
+public class NetworkLaunchItem extends SettingsLaunchItem {
+
+ private final Drawable mNetworkStateDrawable;
+ private final CharSequence mNetworkStateLabel;
+
+ @WorkerThread
+ public NetworkLaunchItem(Context context, ResolveInfo info, SignalStrength signalStrength,
+ long priority) {
+ super(context, info, priority);
+ final NetworkInfo networkInfo =
+ context.getSystemService(ConnectivityManager.class).getActiveNetworkInfo();
+ if (networkInfo == null) {
+ mNetworkStateDrawable = context.getDrawable(R.drawable.wifi_not_connected_launcher);
+ mNetworkStateLabel = context.getString(R.string.network_settings_disconnected);
+ } else if (networkInfo.getType() == ConnectivityManager.TYPE_ETHERNET) {
+ mNetworkStateDrawable = getEthernetDrawable(context, networkInfo);
+ mNetworkStateLabel = context.getString(R.string.network_settings_disconnected);
+ } else if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
+ final WifiInfo wifiInfo =
+ context.getSystemService(WifiManager.class).getConnectionInfo();
+ mNetworkStateDrawable = getWifiDrawable(context, networkInfo, wifiInfo);
+ mNetworkStateLabel = removeDoubleQuotes(wifiInfo.getSSID());
+ } else if (networkInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
+ mNetworkStateDrawable = getCellularDrawable(context, networkInfo, signalStrength);
+ mNetworkStateLabel = removeDoubleQuotes(
+ context.getSystemService(TelephonyManager.class).getNetworkOperatorName());
+ } else {
+ mNetworkStateDrawable = context.getDrawable(R.drawable.wifi_not_connected_launcher);
+ mNetworkStateLabel = context.getString(R.string.network_settings_disconnected);
+ }
+ }
+
+ private static Drawable getEthernetDrawable(Context context, NetworkInfo networkInfo) {
+ if (networkInfo.isConnected()) {
+ return context.getDrawable(R.drawable.ethernet_active_launcher);
+ } else {
+ return context.getDrawable(R.drawable.ethernet_no_internet_launcher);
+ }
+ }
+
+ private static Drawable getWifiDrawable(Context context, NetworkInfo networkInfo,
+ WifiInfo wifiInfo) {
+ final int signal = WifiManager.calculateSignalLevel(wifiInfo.getRssi(), 5);
+ if (networkInfo.isConnected()) {
+ switch (signal) {
+ case 4:
+ return context.getDrawable(R.drawable.wifi_active_4_launcher);
+ case 3:
+ return context.getDrawable(R.drawable.wifi_active_3_launcher);
+ case 2:
+ return context.getDrawable(R.drawable.wifi_active_2_launcher);
+ case 1:
+ return context.getDrawable(R.drawable.wifi_active_1_launcher);
+ case 0:
+ default:
+ return context.getDrawable(R.drawable.wifi_active_0_launcher);
+ }
+ } else {
+ return context.getDrawable(R.drawable.wifi_no_internet_launcher);
+ }
+ }
+
+ private static Drawable getCellularDrawable(Context context, NetworkInfo networkInfo,
+ SignalStrength signalStrength) {
+ final TelephonyManager telephonyManager =
+ context.getSystemService(TelephonyManager.class);
+ if (telephonyManager.getSimState() != TelephonyManager.SIM_STATE_READY) {
+ return context.getDrawable(R.drawable.cellular_no_sim_launcher);
+ } else if (signalStrength == null) {
+ return context.getDrawable(R.drawable.cellular_null_launcher);
+ } else {
+ if (networkInfo.isConnected()) {
+ switch (signalStrength.getLevel()) {
+ case 4:
+ return context.getDrawable(R.drawable.cellular_4_bar_launcher);
+ case 3:
+ return context.getDrawable(R.drawable.cellular_3_bar_launcher);
+ case 2:
+ return context.getDrawable(R.drawable.cellular_2_bar_launcher);
+ case 1:
+ return context.getDrawable(R.drawable.cellular_1_bar_launcher);
+ case 0:
+ default:
+ return context.getDrawable(R.drawable.cellular_0_bar_launcher);
+ }
+ } else {
+ switch (signalStrength.getLevel()) {
+ case 4:
+ return context.getDrawable(R.drawable.cellular_no_internet_4_bar_launcher);
+ case 3:
+ return context.getDrawable(R.drawable.cellular_no_internet_3_bar_launcher);
+ case 2:
+ return context.getDrawable(R.drawable.cellular_no_internet_2_bar_launcher);
+ case 1:
+ return context.getDrawable(R.drawable.cellular_no_internet_1_bar_launcher);
+ case 0:
+ default:
+ return context.getDrawable(R.drawable.cellular_no_internet_0_bar_launcher);
+ }
+ }
+ }
+ }
+
+ @Override
+ public Drawable getIcon() {
+ return mNetworkStateDrawable;
+ }
+
+ @Override
+ public CharSequence getLabel() {
+ return mNetworkStateLabel;
+ }
+
+ private static String removeDoubleQuotes(String string) {
+ if (string == null) {
+ return null;
+ }
+ final int length = string.length();
+ if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) {
+ return string.substring(1, length - 1);
+ }
+ return string;
+ }
+
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/NotificationsLaunchItem.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/NotificationsLaunchItem.java
new file mode 100644
index 0000000..5b3bbf4
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/NotificationsLaunchItem.java
@@ -0,0 +1,46 @@
+package com.example.sampleleanbacklauncher.apps;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+
+import com.example.sampleleanbacklauncher.R;
+
+public class NotificationsLaunchItem extends LaunchItem {
+ private static final String ACTION_OPEN_NOTIFICATIONS = "com.android.tv.NOTIFICATIONS_PANEL";
+
+ // Change this to use a notification panel activity from a different package.
+ private static final String NOTIFICATIONS_PKG = "com.example.sampleleanbacklauncher";
+
+ private final Context mContext;
+ private int mNotifsCount = 0;
+
+ public NotificationsLaunchItem(Context context) {
+ super(context, new Intent(ACTION_OPEN_NOTIFICATIONS).setPackage(NOTIFICATIONS_PKG),
+ context.getResources().getDrawable(R.drawable.ic_notifications, null),
+ context.getResources().getString(R.string.system_notifications));
+ mContext = context;
+ }
+
+ public void setNotificationsCount(int count) {
+ mNotifsCount = count;
+ }
+
+ @Override
+ public Intent getIntent() {
+ return mIntent;
+ }
+
+ @Override
+ public Drawable getBanner() {
+ // No banner for notifications launch item.
+ return null;
+ }
+
+ @Override
+ public CharSequence getLabel() {
+ return mContext.getResources().getQuantityString(R.plurals.notifications_title,
+ mNotifsCount, mNotifsCount);
+ }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/SettingsLaunchItem.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/SettingsLaunchItem.java
new file mode 100644
index 0000000..419c28d
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/SettingsLaunchItem.java
@@ -0,0 +1,47 @@
+/*
+ * 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.example.sampleleanbacklauncher.apps;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+
+public class SettingsLaunchItem extends LaunchItem {
+
+ private final Intent mIntent;
+
+ public SettingsLaunchItem(Context context, ResolveInfo info, long priority) {
+ super(context, info, priority);
+ mIntent = Intent.makeMainActivity(
+ new ComponentName(info.activityInfo.packageName, info.activityInfo.name));
+ mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ mIntent.removeCategory(Intent.CATEGORY_LAUNCHER);
+ }
+
+ @Override
+ public Intent getIntent() {
+ return mIntent;
+ }
+
+ @Override
+ public Drawable getBanner() {
+ // Settings items don't use a banner.
+ return null;
+ }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationPanelDismissibleItemView.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationPanelDismissibleItemView.java
new file mode 100644
index 0000000..55ebb22
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationPanelDismissibleItemView.java
@@ -0,0 +1,162 @@
+/*
+ * 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.example.sampleleanbacklauncher.notifications;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.TextView;
+
+import com.example.sampleleanbacklauncher.R;
+
+/**
+ * View for a dismissible notification displayed in the notifications side panel.
+ * Handles swapping of background in RTL layouts.
+ * Handles dismiss button focus animation.
+ */
+public class NotificationPanelDismissibleItemView extends NotificationPanelItemView {
+ private View mDismissButton;
+ private TextView mDismissText;
+ private int mViewFocusTranslationX;
+ private int mDismissTranslationX;
+ private boolean mIsRtl;
+
+ public NotificationPanelDismissibleItemView(Context context) {
+ super(context);
+ initializeTranslationValues();
+ }
+
+ public NotificationPanelDismissibleItemView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initializeTranslationValues();
+ }
+
+ private void initializeTranslationValues() {
+ mIsRtl = (getResources().getConfiguration().getLayoutDirection() == LAYOUT_DIRECTION_RTL);
+
+ mViewFocusTranslationX = getResources().getDimensionPixelSize(
+ R.dimen.notification_panel_item_show_button_translate_x);
+ mDismissTranslationX = getResources().getDimensionPixelSize(
+ R.dimen.notification_panel_item_dismiss_translate_x);
+
+
+ if (mIsRtl) {
+ mViewFocusTranslationX *= -1;
+ mDismissTranslationX *= -1;
+ }
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ // This is to set the focus search to the default behavior, so that on dismissible views
+ // the user can navigate left/right to the dismiss button.
+ mMainContentText.setNextFocusLeftId(NO_ID);
+ mMainContentText.setNextFocusRightId(NO_ID);
+
+ mDismissButton = findViewById(R.id.dismiss_button);
+ mDismissText = findViewById(R.id.dismiss_text);
+
+ if (mIsRtl) {
+ mMainContentText.setBackgroundResource(R.drawable.notification_background_left);
+ mDismissButton.setBackgroundResource(R.drawable.notification_background_right);
+ } else {
+ mMainContentText.setBackgroundResource(R.drawable.notification_background_right);
+ mDismissButton.setBackgroundResource(R.drawable.notification_background_left);
+ }
+
+ final AnimatorSet dismissAnimator = new AnimatorSet();
+ ObjectAnimator containerSlide = ObjectAnimator.ofFloat(mMainContentText, View.TRANSLATION_X,
+ mViewFocusTranslationX, mDismissTranslationX);
+ ObjectAnimator dismissButtonSlide = ObjectAnimator.ofFloat(mDismissButton,
+ View.TRANSLATION_X, mViewFocusTranslationX, mDismissTranslationX);
+
+ dismissAnimator.playTogether(dismissButtonSlide, containerSlide);
+
+ dismissAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ collapseText();
+ mDismissText.setVisibility(GONE);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mDismissButton.setVisibility(INVISIBLE);
+ mMainContentText.setVisibility(INVISIBLE);
+ setBackgroundColor(getContext().getColor(R.color.notification_selection_color));
+ NotificationsUtils.dismissNotification(getContext(), mNotificationKey);
+ }
+ });
+
+ final AnimatorSet gainFocus = new AnimatorSet();
+ ObjectAnimator containerSlideOut = ObjectAnimator.ofFloat(mMainContentText,
+ View.TRANSLATION_X, 0, mViewFocusTranslationX);
+ ObjectAnimator dismissButtonFocusGain = ObjectAnimator.ofFloat(mDismissButton,
+ View.TRANSLATION_X, 0, mViewFocusTranslationX);
+ gainFocus.playTogether(dismissButtonFocusGain, containerSlideOut);
+
+ final AnimatorSet loseFocus = new AnimatorSet();
+ ObjectAnimator containerSlideIn = ObjectAnimator.ofFloat(mMainContentText,
+ View.TRANSLATION_X, mViewFocusTranslationX, 0);
+ ObjectAnimator dismissButtonFocusLost = ObjectAnimator.ofFloat(mDismissButton,
+ View.TRANSLATION_X, mViewFocusTranslationX, 0);
+ loseFocus.playTogether(dismissButtonFocusLost, containerSlideIn);
+ loseFocus.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mDismissText.setVisibility(GONE);
+ }
+ });
+
+ mDismissButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mNotificationKey != null) {
+ dismissAnimator.start();
+ }
+ }
+ });
+ mDismissButton.setOnFocusChangeListener(new OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View view, boolean focused) {
+ if (focused) {
+ // Slide the views to the side and show the dismiss text
+ mDismissText.setVisibility(VISIBLE);
+ gainFocus.start();
+ } else {
+ // Slide the views back to their original positions and hide the dismiss text
+ loseFocus.start();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void setNotification(TvNotification notif) {
+ super.setNotification(notif);
+ mDismissText.setText(notif.getDismissButtonLabel());
+ mDismissButton.setVisibility(VISIBLE);
+ }
+}
+
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationPanelItemView.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationPanelItemView.java
new file mode 100644
index 0000000..92dfe76
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationPanelItemView.java
@@ -0,0 +1,245 @@
+/*
+ * 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.example.sampleleanbacklauncher.notifications;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.text.Layout;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.example.sampleleanbacklauncher.R;
+
+/**
+ * View for a non-dismissible notification displayed in the notifications side panel.
+ */
+public class NotificationPanelItemView extends LinearLayout
+ implements ViewTreeObserver.OnGlobalFocusChangeListener {
+ private RectF mProgressBounds;
+ private Paint mProgressPaint;
+ private Paint mProgressMaxPaint;
+ private int mProgressStrokeWidth;
+ private int mProgressDiameter;
+ private int mProgressPaddingStart;
+ private int mProgressPaddingTop;
+ private int mProgressColor;
+ private int mProgressMaxColor;
+ private int mProgress;
+ private int mProgressMax;
+ private boolean mIsRtl;
+ private ImageView mIcon;
+ private TextView mTitle;
+ private TextView mText;
+ private TextView mExpandedText;
+ protected View mMainContentText;
+ protected String mNotificationKey;
+ private TvNotification mNotification;
+
+ public NotificationPanelItemView(Context context) {
+ super(context);
+ initializeLayoutValues();
+ }
+
+ public NotificationPanelItemView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initializeLayoutValues();
+ }
+
+ private void initializeLayoutValues() {
+ Configuration config = getContext().getResources().getConfiguration();
+ mIsRtl = (config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
+
+ Resources res = getResources();
+ mProgressStrokeWidth = res.getDimensionPixelSize(
+ R.dimen.notification_progress_stroke_width);
+ mProgressColor = res.getColor(R.color.notification_progress_stroke_color, null);
+ mProgressMaxColor = res.getColor(R.color.notification_progress_stroke_max_color, null);
+ mProgressDiameter = res.getDimensionPixelSize(R.dimen.notification_progress_circle_size);
+ mProgressPaddingTop = res.getDimensionPixelOffset(
+ R.dimen.notification_progress_circle_padding_top);
+ mProgressPaddingStart = res.getDimensionPixelOffset(
+ R.dimen.notification_progress_circle_padding_start);
+
+ mProgressPaint = new Paint();
+ mProgressPaint.setAntiAlias(true);
+ mProgressPaint.setStyle(Paint.Style.STROKE);
+ mProgressPaint.setColor(mProgressColor);
+ mProgressPaint.setStrokeWidth(mProgressStrokeWidth);
+
+ mProgressMaxPaint = new Paint();
+ mProgressMaxPaint.setAntiAlias(true);
+ mProgressMaxPaint.setStyle(Paint.Style.STROKE);
+ mProgressMaxPaint.setColor(mProgressMaxColor);
+ mProgressMaxPaint.setStrokeWidth(mProgressStrokeWidth);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ getViewTreeObserver().addOnGlobalFocusChangeListener(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ getViewTreeObserver().removeOnGlobalFocusChangeListener(this);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mIcon = findViewById(R.id.notification_icon);
+ mTitle = findViewById(R.id.notification_title);
+ mText = findViewById(R.id.notification_text);
+ mMainContentText = findViewById(R.id.notification_container);
+ mExpandedText = findViewById(R.id.notification_expanded_text);
+
+ mMainContentText.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (mNotificationKey != null) {
+ NotificationsUtils.openNotification(view.getContext(), mNotificationKey);
+ }
+ }
+ });
+ }
+
+ public void setNotification(TvNotification notif) {
+ mNotification = notif;
+ mNotificationKey = notif.getNotificationKey();
+ mTitle.setText(notif.getTitle());
+ mText.setText(notif.getText());
+ if (!TextUtils.isEmpty(notif.getTitle())) {
+ if (!TextUtils.isEmpty(notif.getText())) {
+ String formatting = getResources().getString(
+ R.string.notification_content_description_format);
+ mMainContentText.setContentDescription(
+ String.format(formatting, notif.getTitle(), notif.getText()));
+ } else {
+ mMainContentText.setContentDescription(notif.getTitle());
+ }
+ } else {
+ mMainContentText.setContentDescription(notif.getText());
+ }
+ mExpandedText.setText(notif.getText());
+ mIcon.setImageIcon(notif.getSmallIcon());
+ setProgress(notif.getProgress(), notif.getProgressMax());
+ mMainContentText.setVisibility(VISIBLE);
+ }
+
+ public void setProgress(int progress, int progressMax) {
+ mProgress = progress;
+ mProgressMax = progressMax;
+ if (mProgressMax != 0) {
+ if (mProgressBounds == null) {
+ mProgressBounds = new RectF();
+ }
+ setWillNotDraw(false);
+ } else {
+ mProgressBounds = null;
+ setWillNotDraw(true);
+ }
+ requestLayout();
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ if (mProgressBounds != null) {
+ int left, right;
+ int top = mProgressPaddingTop;
+ int bottom = top + mProgressDiameter;
+ if (mIsRtl) {
+ right = r - mProgressPaddingStart;
+ left = right - mProgressDiameter;
+ } else {
+ left = mProgressPaddingStart;
+ right = left + mProgressDiameter;
+ }
+
+ mProgressBounds.set(left, top, right, bottom);
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ if (mProgressMax != 0) {
+ float sweepAngle = 360f * mProgress / mProgressMax;
+ if (mIsRtl) {
+ canvas.drawArc(mProgressBounds, -90, -sweepAngle, false, mProgressPaint);
+ canvas.drawArc(mProgressBounds, -90, 360 - sweepAngle, false,
+ mProgressMaxPaint);
+ } else {
+ canvas.drawArc(mProgressBounds, -90, sweepAngle, false, mProgressPaint);
+ canvas.drawArc(mProgressBounds, sweepAngle - 90, 360 - sweepAngle, false,
+ mProgressMaxPaint);
+ }
+ }
+ }
+
+ private boolean isContentTextCutOff() {
+ Layout layout = mText.getLayout();
+ if (layout != null) {
+ int lines = layout.getLineCount();
+ if (lines > 0) {
+ int ellipsisCount = layout.getEllipsisCount(lines - 1);
+ if (ellipsisCount > 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ protected void expandText() {
+ mText.setVisibility(GONE);
+ mTitle.setMaxLines(2);
+ mExpandedText.setVisibility(VISIBLE);
+ setBackgroundColor(
+ getResources().getColor(R.color.notification_expanded_text_background));
+ }
+
+ protected void collapseText() {
+ mExpandedText.setVisibility(GONE);
+ mTitle.setMaxLines(1);
+ mText.setVisibility(VISIBLE);
+ setBackgroundColor(Color.TRANSPARENT);
+ }
+
+ @Override
+ public void onGlobalFocusChanged(View oldFocus, View newFocus) {
+ View currentFocus = getFocusedChild();
+ if (currentFocus == null) {
+ collapseText();
+ } else if ((newFocus == currentFocus || newFocus.getParent() == currentFocus)
+ && isContentTextCutOff()) {
+ expandText();
+ }
+ }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsContract.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsContract.java
new file mode 100644
index 0000000..fb7606a
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsContract.java
@@ -0,0 +1,69 @@
+/*
+ * 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
+ */
+
+package com.example.sampleleanbacklauncher.notifications;
+
+import android.net.Uri;
+
+/**
+ * Constants which represent the "contract" for interacting with TV notifications.
+ */
+
+public final class NotificationsContract {
+ private static final String PATH_NOTIFS = "notifications";
+ private static final String PATH_NOTIFS_COUNT = PATH_NOTIFS + "/count";
+
+ // Content provider for notifications
+ private static final String AUTHORITY =
+ "com.android.tv.notifications.NotificationContentProvider";
+
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" +
+ PATH_NOTIFS);
+ public static final Uri NOTIFS_COUNT_URI = Uri.parse("content://" + AUTHORITY + "/" +
+ PATH_NOTIFS_COUNT);
+
+ public static final String ACTION_NOTIFICATION_HIDE =
+ "android.tvservice.action.NOTIFICATION_HIDE";
+
+ public static final String ACTION_SHOW_UNSHOWN_NOTIFICATIONS =
+ "android.tvservice.action.SHOW_UNSHOWN_NOTIFICATIONS";
+
+ public static final String ACTION_OPEN_NOTIFICATION_PANEL =
+ "com.android.tv.NOTIFICATIONS_PANEL";
+
+ public static final String NOTIFICATION_KEY = "sbn_key";
+
+ public static final String COLUMN_SBN_KEY = "sbn_key";
+ public static final String COLUMN_PACKAGE_NAME = "package_name";
+ public static final String COLUMN_NOTIF_TITLE = "title";
+ public static final String COLUMN_NOTIF_TEXT = "text";
+ public static final String COLUMN_AUTODISMISS = "is_auto_dismiss";
+ public static final String COLUMN_DISMISSIBLE = "dismissible";
+ public static final String COLUMN_ONGOING = "ongoing";
+ public static final String COLUMN_SMALL_ICON = "small_icon";
+ public static final String COLUMN_CHANNEL = "channel";
+ public static final String COLUMN_PROGRESS = "progress";
+ public static final String COLUMN_PROGRESS_MAX = "progress_max";
+ public static final String COLUMN_NOTIFICATION_HIDDEN = "notification_hidden";
+ public static final String COLUMN_FLAGS = "flags";
+ public static final String COLUMN_HAS_CONTENT_INTENT = "has_content_intent";
+ public static final String COLUMN_BIG_PICTURE = "big_picture";
+ public static final String COLUMN_CONTENT_BUTTON_LABEL = "content_button_label";
+ public static final String COLUMN_DISMISS_BUTTON_LABEL = "dismiss_button_label";
+ public static final String COLUMN_TAG = "tag";
+
+ public static final String COLUMN_COUNT = "count";
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsPanelAdapter.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsPanelAdapter.java
new file mode 100644
index 0000000..2e9344a
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsPanelAdapter.java
@@ -0,0 +1,144 @@
+/*
+ * 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.example.sampleleanbacklauncher.notifications;
+
+import android.app.NotificationManager;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import androidx.recyclerview.widget.RecyclerView;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.example.sampleleanbacklauncher.R;
+
+/**
+ * Adapter for the {@link RecyclerView} in the notifications side panel which displayed
+ * both dismissible and non-dismissible notifications.
+ */
+public class NotificationsPanelAdapter<VH extends RecyclerView.ViewHolder>
+ extends RecyclerView.Adapter<NotificationsPanelAdapter.NotificationPanelViewHolder> {
+ private static final String TAG = "NotifsPanelAdapter";
+ private static final boolean DEBUG = false;
+
+ private static final int TYPE_DISMISSIBLE = 0;
+ private static final int TYPE_PERSISTENT = 1;
+
+ private Cursor mCursor;
+
+ public NotificationsPanelAdapter(Context context, Cursor cursor) {
+ mCursor = cursor;
+ setHasStableIds(true);
+ }
+
+ public Cursor getCursor() {
+ return mCursor;
+ }
+
+ @Override
+ public int getItemCount() {
+ if (mCursor != null) {
+ return mCursor.getCount();
+ }
+ return 0;
+ }
+
+ @Override
+ public NotificationPanelViewHolder onCreateViewHolder(
+ ViewGroup parent, int viewType) {
+ LayoutInflater inflater = LayoutInflater.from(parent.getContext());
+
+ View trayItem;
+ if (viewType == TYPE_DISMISSIBLE) {
+ trayItem = inflater.inflate(R.layout.notification_panel_item_dismissible,
+ parent, false);
+ } else {
+ trayItem = inflater.inflate(R.layout.notification_panel_item,
+ parent, false);
+ }
+
+ return new NotificationPanelViewHolder(trayItem);
+ }
+
+ @Override
+ public void onBindViewHolder(NotificationPanelViewHolder holder,
+ int position) {
+ if (!mCursor.moveToPosition(position)) {
+ throw new IllegalStateException("Can't move cursor to position " + position);
+ }
+ onBindViewHolder(holder, mCursor);
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if (!mCursor.moveToPosition(position)) {
+ throw new IllegalStateException("Can't move cursor to position " + position);
+ }
+
+ boolean dismissible = mCursor.getInt(TvNotification.COLUMN_INDEX_DISMISSIBLE) != 0;
+ boolean ongoing = mCursor.getInt(TvNotification.COLUMN_INDEX_ONGOING) != 0;
+ if (ongoing || !dismissible) {
+ return TYPE_PERSISTENT;
+ } else {
+ return TYPE_DISMISSIBLE;
+ }
+ }
+
+ @Override
+ public long getItemId(int position) {
+ if (!mCursor.moveToPosition(position)) {
+ Log.wtf(TAG, "Can't move cursor to position " + position);
+ return View.NO_ID;
+ }
+
+ String key = mCursor.getString(TvNotification.COLUMN_INDEX_KEY);
+ return key.hashCode();
+ }
+
+ public void onBindViewHolder(NotificationPanelViewHolder viewHolder, Cursor cursor) {
+ TvNotification notif = TvNotification.fromCursor(cursor);
+ viewHolder.setNotification(notif);
+ }
+
+ public static class NotificationPanelViewHolder extends RecyclerView.ViewHolder {
+ public NotificationPanelViewHolder(View itemView) {
+ super(itemView);
+ }
+
+ public void setNotification(TvNotification notification) {
+ ((NotificationPanelItemView) itemView).setNotification(notification);
+ }
+ }
+
+ /**
+ * Swap in a new Cursor, and close the old Cursor.
+ *
+ * @param newCursor The new cursor to be used.
+ */
+ public void changeCursor(Cursor newCursor) {
+ if (DEBUG) {
+ Log.d(TAG, "changeCursor() called with: " + "newCursor = [" +
+ DatabaseUtils.dumpCursorToString(newCursor) + "]");
+ }
+
+ mCursor = newCursor;
+ notifyDataSetChanged();
+ }
+} \ No newline at end of file
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsSidePanelActivity.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsSidePanelActivity.java
new file mode 100644
index 0000000..367a348
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsSidePanelActivity.java
@@ -0,0 +1,101 @@
+/*
+ * 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.example.sampleleanbacklauncher.notifications;
+
+import android.app.Activity;
+import android.app.LoaderManager;
+import android.content.CursorLoader;
+import android.content.Loader;
+import android.database.Cursor;
+import android.os.Bundle;
+import androidx.annotation.Nullable;
+import androidx.leanback.widget.VerticalGridView;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import android.transition.Scene;
+import android.transition.Slide;
+import android.transition.Transition;
+import android.transition.TransitionManager;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+
+import com.example.sampleleanbacklauncher.R;
+
+/**
+ * Displays a side panel containing a list of notifications.
+ */
+
+public class NotificationsSidePanelActivity extends Activity
+ implements LoaderManager.LoaderCallbacks<Cursor>{
+ private static final String TAG = "NotifsSidePanel";
+ private NotificationsPanelAdapter mPanelAdapter;
+ private VerticalGridView mNotifsList;
+ private View mNoNotifsMessage;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final ViewGroup root = findViewById(android.R.id.content);
+
+ mPanelAdapter = new NotificationsPanelAdapter(
+ NotificationsSidePanelActivity.this,null);
+
+ setContentView(R.layout.notifications_panel_view);
+
+ mNoNotifsMessage = findViewById(R.id.no_notifications_message);
+ mNotifsList = findViewById(R.id.notifications_list);
+ mNotifsList.setAdapter(mPanelAdapter);
+ mNotifsList.setFocusable(true);
+
+ getLoaderManager().initLoader(0, null,
+ NotificationsSidePanelActivity.this);
+ }
+
+ private void showNoNotificationsMessage() {
+ mNotifsList.setVisibility(View.GONE);
+ mNoNotifsMessage.setVisibility(View.VISIBLE);
+ }
+
+ private void showNotifications() {
+ mNoNotifsMessage.setVisibility(View.GONE);
+ mNotifsList.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ return new CursorLoader(this, NotificationsContract.CONTENT_URI,
+ TvNotification.PROJECTION, null, null, null);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ mPanelAdapter.changeCursor(data);
+ if (data != null && data.getCount() > 0) {
+ showNotifications();
+ } else {
+ showNoNotificationsMessage();
+ }
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ mPanelAdapter.changeCursor(null);
+ }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsUtils.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsUtils.java
new file mode 100644
index 0000000..9f7da82
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsUtils.java
@@ -0,0 +1,41 @@
+/*
+ * 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.example.sampleleanbacklauncher.notifications;
+
+import android.content.Context;
+import android.content.Intent;
+
+import com.example.sampleleanbacklauncher.LauncherConstants;
+
+import java.util.List;
+
+public final class NotificationsUtils {
+ static void dismissNotification(Context context, String key) {
+ Intent dismiss = new Intent(Intent.ACTION_DELETE);
+ dismiss.setPackage(LauncherConstants.TVRECOMMENDATIONS_PACKAGE_NAME);
+
+ dismiss.putExtra(NotificationsContract.NOTIFICATION_KEY, key);
+ context.sendBroadcast(dismiss);
+ }
+
+ static void openNotification(Context context, String key) {
+ Intent open = new Intent(Intent.ACTION_VIEW);
+ open.setPackage(LauncherConstants.TVRECOMMENDATIONS_PACKAGE_NAME);
+ open.putExtra(NotificationsContract.NOTIFICATION_KEY, key);
+ context.sendBroadcast(open);
+ }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/TvNotification.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/TvNotification.java
new file mode 100644
index 0000000..32deaba
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/TvNotification.java
@@ -0,0 +1,192 @@
+package com.example.sampleleanbacklauncher.notifications;
+
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+
+public class TvNotification {
+ /**
+ * This projection MUST be used for the query when using {@link #fromCursor(Cursor)}.
+ */
+ public static final String[] PROJECTION =
+ {NotificationsContract.COLUMN_SBN_KEY,
+ NotificationsContract.COLUMN_PACKAGE_NAME,
+ NotificationsContract.COLUMN_NOTIF_TITLE,
+ NotificationsContract.COLUMN_NOTIF_TEXT,
+ NotificationsContract.COLUMN_DISMISSIBLE,
+ NotificationsContract.COLUMN_ONGOING,
+ NotificationsContract.COLUMN_SMALL_ICON,
+ NotificationsContract.COLUMN_CHANNEL,
+ NotificationsContract.COLUMN_PROGRESS,
+ NotificationsContract.COLUMN_PROGRESS_MAX,
+ NotificationsContract.COLUMN_HAS_CONTENT_INTENT,
+ NotificationsContract.COLUMN_BIG_PICTURE,
+ NotificationsContract.COLUMN_CONTENT_BUTTON_LABEL,
+ NotificationsContract.COLUMN_DISMISS_BUTTON_LABEL,
+ NotificationsContract.COLUMN_TAG};
+
+ public static final int COLUMN_INDEX_KEY = 0;
+ public static final int COLUMN_INDEX_PACKAGE_NAME = 1;
+ public static final int COLUMN_INDEX_NOTIF_TITLE = 2;
+ public static final int COLUMN_INDEX_NOTIF_TEXT = 3;
+ public static final int COLUMN_INDEX_DISMISSIBLE = 4;
+ public static final int COLUMN_INDEX_ONGOING = 5;
+ public static final int COLUMN_INDEX_SMALL_ICON = 6;
+ public static final int COLUMN_INDEX_CHANNEL = 7;
+ public static final int COLUMN_INDEX_PROGRESS = 8;
+ public static final int COLUMN_INDEX_PROGRESS_MAX = 9;
+ public static final int COLUMN_INDEX_HAS_CONTENT_INTENT = 10;
+ public static final int COLUMN_INDEX_BIG_PICTURE = 11;
+ public static final int COLUMN_INDEX_CONTENT_BUTTON_LABEL = 12;
+ public static final int COLUMN_INDEX_DISMISS_BUTTON_LABEL = 13;
+ public static final int COLUMN_INDEX_TAG = 14;
+
+ private String mNotificationKey;
+ private String mPackageName;
+ private String mTitle;
+ private String mText;
+ private boolean mDismissible;
+ private boolean mIsOngoing;
+ private Icon mSmallIcon;
+ private int mChannel;
+ private int mProgress;
+ private int mProgressMax;
+ private boolean mHasContentIntent;
+ private Bitmap mBigPicture;
+ private String mContentButtonLabel;
+ private String mDismissButtonLabel;
+ private String mTag;
+
+ public TvNotification(String key, String packageName, String title, String text,
+ boolean dismissible, boolean ongoing, Icon smallIcon, int channel,
+ int progress, int progressMax, boolean hasContentIntent, Bitmap bigPicture,
+ String contentButtonLabel, String dismissButtonLabel, String tag) {
+ mNotificationKey = key;
+ mPackageName = packageName;
+ mTitle = title;
+ mText = text;
+ mDismissible = dismissible;
+ mIsOngoing = ongoing;
+ mSmallIcon = smallIcon;
+ mChannel = channel;
+ mProgress = progress;
+ mProgressMax = progressMax;
+ mHasContentIntent = hasContentIntent;
+ mBigPicture = bigPicture;
+ mContentButtonLabel = contentButtonLabel;
+ mDismissButtonLabel = dismissButtonLabel;
+ mTag = tag;
+ }
+
+ public String getNotificationKey() {
+ return mNotificationKey;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public String getText() {
+ return mText;
+ }
+
+ public boolean isDismissible() {
+ return mDismissible;
+ }
+
+ public boolean isOngoing() {
+ return mIsOngoing;
+ }
+
+ public Icon getSmallIcon() {
+ return mSmallIcon;
+ }
+
+ public int getChannel() {
+ return mChannel;
+ }
+
+ public int getProgress() {
+ return mProgress;
+ }
+
+ public int getProgressMax() {
+ return mProgressMax;
+ }
+
+ public boolean hasContentIntent() {
+ return mHasContentIntent;
+ }
+
+ public Bitmap getBigPicture() {
+ return mBigPicture;
+ }
+
+ public String getContentButtonLabel() {
+ return mContentButtonLabel;
+ }
+
+ public String getDismissButtonLabel() {
+ return mDismissButtonLabel;
+ }
+
+ public String getTag() {
+ return mTag;
+ }
+
+ // Converts cursor returned from query with PROJECTION
+ public static TvNotification fromCursor(Cursor cursor) {
+ int index = 0;
+ String key = cursor.getString(index++);
+ String packageName = cursor.getString(index++);
+ String title = cursor.getString(index++);
+ String text = cursor.getString(index++);
+ boolean dismissible = cursor.getInt(index++) != 0;
+ boolean ongoing = cursor.getInt(index++) != 0;
+ byte[] smallIconData = cursor.getBlob(index++);
+ Icon smallIcon = getIconFromBytes(smallIconData);
+
+ int channel = cursor.getInt(index++);
+ int progress = cursor.getInt(index++);
+ int progressMax = cursor.getInt(index++);
+ boolean hasContentIntent = cursor.getInt(index++) != 0;
+ byte[] bigPictureData = cursor.getBlob(index++);
+ Bitmap bigPicture = getBitmapFromBytes(bigPictureData);
+ String contentButtonLabel = cursor.getString(index++);
+ String dismissButtonLabel = cursor.getString(index++);
+ String tag = cursor.getString(index);
+
+ return new TvNotification(key, packageName, title, text, dismissible, ongoing,
+ smallIcon, channel, progress, progressMax, hasContentIntent, bigPicture,
+ contentButtonLabel, dismissButtonLabel, tag);
+ }
+
+ private static Bitmap getBitmapFromBytes(byte[] blob) {
+ if (blob != null) {
+ Bitmap bitmap = BitmapFactory.decodeByteArray(blob, 0, blob.length);
+ return bitmap;
+ }
+
+ return null;
+ }
+
+ private static Icon getIconFromBytes(byte[] blob) {
+ Parcel in = Parcel.obtain();
+ Icon icon = null;
+ if (blob != null) {
+ in.unmarshall(blob, 0, blob.length);
+ in.setDataPosition(0);
+ icon = in.readParcelable(Icon.class.getClassLoader());
+ }
+
+ in.recycle();
+ return icon;
+ }
+}
+
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/search/SearchFragment.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/search/SearchFragment.java
new file mode 100644
index 0000000..b8285f6
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/search/SearchFragment.java
@@ -0,0 +1,139 @@
+/*
+ * 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.example.sampleleanbacklauncher.search;
+
+import android.app.Fragment;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.Outline;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Trace;
+import androidx.annotation.Nullable;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.widget.Toast;
+
+import com.example.sampleleanbacklauncher.R;
+
+public class SearchFragment extends Fragment
+ implements View.OnClickListener, View.OnFocusChangeListener {
+ private static final String TAG = "SearchFragment";
+
+ private static final String EXTRA_SEARCH_TYPE = "search_type";
+
+ private static final int SEARCH_TYPE_VOICE = 1;
+ private static final int SEARCH_TYPE_KEYBOARD = 2;
+
+ private View mSearchOrbVoice;
+ private View mSearchOrbKeyboard;
+
+ public static SearchFragment newInstance() {
+ Bundle args = new Bundle();
+
+ SearchFragment fragment = new SearchFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ final View root = inflater.inflate(R.layout.search, container, false);
+
+ final ViewOutlineProvider outlineProvider = new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setOval(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
+ }
+ };
+
+ mSearchOrbVoice = root.findViewById(R.id.search_orb_voice);
+ mSearchOrbVoice.setOnFocusChangeListener(this);
+ mSearchOrbVoice.setOutlineProvider(outlineProvider);
+ mSearchOrbVoice.setClipToOutline(true);
+ mSearchOrbVoice.setOnClickListener(this);
+
+ mSearchOrbKeyboard = root.findViewById(R.id.search_orb_keyboard);
+ mSearchOrbKeyboard.setOnFocusChangeListener(this);
+ mSearchOrbKeyboard.setOutlineProvider(outlineProvider);
+ mSearchOrbKeyboard.setClipToOutline(true);
+ mSearchOrbKeyboard.setOnClickListener(this);
+
+ return root;
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ mSearchOrbVoice.setOnFocusChangeListener(null);
+ mSearchOrbKeyboard.setOnFocusChangeListener(null);
+ }
+
+ @Override
+ public void onClick(View v) {
+ final Intent intent = new Intent(Intent.ACTION_ASSIST)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ intent.putExtra(EXTRA_SEARCH_TYPE,
+ v == mSearchOrbKeyboard ? SEARCH_TYPE_KEYBOARD : SEARCH_TYPE_VOICE);
+ try {
+ startActivity(intent);
+ mSearchOrbVoice.requestFocus();
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "Exception launching intent " + intent, e);
+ Toast.makeText(getContext(), getString(R.string.app_unavailable),
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ Trace.beginSection("SearchFragment.onFocusChange");
+ try {
+ final View root = getView();
+ if (root == null) {
+ return;
+ }
+ root.requestRectangleOnScreen(
+ new Rect(0, 0, root.getMeasuredWidth(), root.getMeasuredHeight()));
+
+ final int visibility = hasFocus ? View.VISIBLE : View.GONE;
+ if (v == mSearchOrbKeyboard) {
+ root.findViewById(R.id.search_text_keyboard).setVisibility(visibility);
+ } else {
+ root.findViewById(R.id.search_text_voice).setVisibility(visibility);
+ }
+
+ final Resources resources = getResources();
+ float elevation = resources.getDimension(hasFocus
+ ? R.dimen.search_item_focused_z : R.dimen.search_item_unfocused_z);
+ float scale = hasFocus
+ ? resources.getFraction(R.fraction.search_item_focused_zoom, 1, 1) : 1.0f;
+ int duration = resources.getInteger(R.integer.search_orb_scale_duration_ms);
+
+ v.animate().z(elevation).scaleX(scale).scaleY(scale).setDuration(duration);
+ } finally {
+ Trace.endSection();
+ }
+ }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/util/LauncherAsyncTaskLoader.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/util/LauncherAsyncTaskLoader.java
new file mode 100644
index 0000000..32516f6
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/util/LauncherAsyncTaskLoader.java
@@ -0,0 +1,61 @@
+/*
+ * 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.example.sampleleanbacklauncher.util;
+
+import android.content.AsyncTaskLoader;
+import android.content.Context;
+
+public abstract class LauncherAsyncTaskLoader<T> extends AsyncTaskLoader<T> {
+ private T mResult;
+
+ public LauncherAsyncTaskLoader(final Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onStartLoading() {
+ if (mResult != null) {
+ deliverResult(mResult);
+ }
+ if (takeContentChanged() || mResult == null) {
+ forceLoad();
+ }
+ }
+
+ @Override
+ protected void onStopLoading() {
+ cancelLoad();
+ }
+
+ @Override
+ public void deliverResult(final T data) {
+ if (isReset()) {
+ return;
+ }
+ mResult = data;
+ if (isStarted()) {
+ super.deliverResult(data);
+ }
+ }
+
+ @Override
+ protected void onReset() {
+ super.onReset();
+ onStopLoading();
+ mResult = null;
+ }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_0_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_0_bar_launcher.png
new file mode 100644
index 0000000..71ccedd
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_0_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_1_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_1_bar_launcher.png
new file mode 100644
index 0000000..62983e6
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_1_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_2_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_2_bar_launcher.png
new file mode 100644
index 0000000..83f5f60
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_2_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_3_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_3_bar_launcher.png
new file mode 100644
index 0000000..b9a1257
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_3_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_4_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_4_bar_launcher.png
new file mode 100644
index 0000000..83eec97
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_4_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_0_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_0_bar_launcher.png
new file mode 100644
index 0000000..c40f219
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_0_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_1_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_1_bar_launcher.png
new file mode 100644
index 0000000..ced92d4
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_1_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_2_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_2_bar_launcher.png
new file mode 100644
index 0000000..8411980
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_2_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_3_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_3_bar_launcher.png
new file mode 100644
index 0000000..71bc360
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_3_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_4_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_4_bar_launcher.png
new file mode 100644
index 0000000..98b9177
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_4_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_sim_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_sim_launcher.png
new file mode 100644
index 0000000..3a956e6
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_sim_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_null_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_null_launcher.png
new file mode 100644
index 0000000..ee0ec27
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_null_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_off_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_off_launcher.png
new file mode 100644
index 0000000..17bc81a
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_off_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ethernet_active_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ethernet_active_launcher.png
new file mode 100644
index 0000000..ef43441
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ethernet_active_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ethernet_no_internet_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ethernet_no_internet_launcher.png
new file mode 100644
index 0000000..1ce500a
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ethernet_no_internet_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ic_notifications.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ic_notifications.png
new file mode 100644
index 0000000..cbd0c1e
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ic_notifications.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_0_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_0_launcher.png
new file mode 100644
index 0000000..9879210
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_0_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_1_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_1_launcher.png
new file mode 100644
index 0000000..709d421
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_1_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_2_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_2_launcher.png
new file mode 100644
index 0000000..98a35c8
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_2_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_3_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_3_launcher.png
new file mode 100644
index 0000000..6036042
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_3_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_4_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_4_launcher.png
new file mode 100644
index 0000000..383c2ba
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_4_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_no_internet_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_no_internet_launcher.png
new file mode 100644
index 0000000..eda95d9
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_no_internet_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_not_connected_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_not_connected_launcher.png
new file mode 100644
index 0000000..d95a67d
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_not_connected_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_0_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_0_bar_launcher.png
new file mode 100644
index 0000000..2ae8ade
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_0_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_1_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_1_bar_launcher.png
new file mode 100644
index 0000000..709e05e
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_1_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_2_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_2_bar_launcher.png
new file mode 100644
index 0000000..9f4a7de
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_2_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_3_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_3_bar_launcher.png
new file mode 100644
index 0000000..64d2479
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_3_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_4_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_4_bar_launcher.png
new file mode 100644
index 0000000..d3c6afd
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_4_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_0_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_0_bar_launcher.png
new file mode 100644
index 0000000..8dc8813
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_0_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_1_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_1_bar_launcher.png
new file mode 100644
index 0000000..a43642a
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_1_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_2_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_2_bar_launcher.png
new file mode 100644
index 0000000..88747c3
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_2_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_3_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_3_bar_launcher.png
new file mode 100644
index 0000000..bce337b
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_3_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_4_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_4_bar_launcher.png
new file mode 100644
index 0000000..6a4fb75
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_4_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_sim_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_sim_launcher.png
new file mode 100644
index 0000000..23035f4
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_sim_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_null_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_null_launcher.png
new file mode 100644
index 0000000..ee23581
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_null_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_off_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_off_launcher.png
new file mode 100644
index 0000000..2b666da
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_off_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ethernet_active_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ethernet_active_launcher.png
new file mode 100644
index 0000000..08a16e3
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ethernet_active_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ethernet_no_internet_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ethernet_no_internet_launcher.png
new file mode 100644
index 0000000..1184777
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ethernet_no_internet_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ic_notifications.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ic_notifications.png
new file mode 100644
index 0000000..9718ccf
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ic_notifications.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_0_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_0_launcher.png
new file mode 100644
index 0000000..cac8e26
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_0_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_1_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_1_launcher.png
new file mode 100644
index 0000000..8b69f9b
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_1_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_2_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_2_launcher.png
new file mode 100644
index 0000000..178aa8a
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_2_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_3_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_3_launcher.png
new file mode 100644
index 0000000..ccd5f0b
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_3_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_4_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_4_launcher.png
new file mode 100644
index 0000000..a694fd8
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_4_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_no_internet_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_no_internet_launcher.png
new file mode 100644
index 0000000..eb57262
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_no_internet_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_not_connected_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_not_connected_launcher.png
new file mode 100644
index 0000000..e270196
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_not_connected_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_0_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_0_bar_launcher.png
new file mode 100644
index 0000000..55e4423
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_0_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_1_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_1_bar_launcher.png
new file mode 100644
index 0000000..8085ee6
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_1_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_2_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_2_bar_launcher.png
new file mode 100644
index 0000000..4543409
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_2_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_3_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_3_bar_launcher.png
new file mode 100644
index 0000000..8e0d081
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_3_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_4_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_4_bar_launcher.png
new file mode 100644
index 0000000..87abeb1
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_4_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_0_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_0_bar_launcher.png
new file mode 100644
index 0000000..6a1c11c
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_0_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_1_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_1_bar_launcher.png
new file mode 100644
index 0000000..82f9a06
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_1_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_2_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_2_bar_launcher.png
new file mode 100644
index 0000000..89e8be7
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_2_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_3_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_3_bar_launcher.png
new file mode 100644
index 0000000..2bf5e9d
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_3_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_4_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_4_bar_launcher.png
new file mode 100644
index 0000000..e6e16ff
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_4_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_sim_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_sim_launcher.png
new file mode 100644
index 0000000..73cd783
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_sim_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_null_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_null_launcher.png
new file mode 100644
index 0000000..b140005
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_null_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_off_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_off_launcher.png
new file mode 100644
index 0000000..3246266
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_off_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ethernet_active_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ethernet_active_launcher.png
new file mode 100644
index 0000000..ee00c2b
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ethernet_active_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ethernet_no_internet_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ethernet_no_internet_launcher.png
new file mode 100644
index 0000000..bcf6645
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ethernet_no_internet_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ic_notifications.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ic_notifications.png
new file mode 100644
index 0000000..1e70b63
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ic_notifications.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_0_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_0_launcher.png
new file mode 100644
index 0000000..e1a1b28
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_0_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_1_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_1_launcher.png
new file mode 100644
index 0000000..8efbf08
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_1_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_2_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_2_launcher.png
new file mode 100644
index 0000000..bb64d06
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_2_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_3_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_3_launcher.png
new file mode 100644
index 0000000..ed91075
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_3_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_4_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_4_launcher.png
new file mode 100644
index 0000000..d1dce01
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_4_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_no_internet_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_no_internet_launcher.png
new file mode 100644
index 0000000..c2f34d5
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_no_internet_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_not_connected_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_not_connected_launcher.png
new file mode 100644
index 0000000..3ea207e
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_not_connected_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_0_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_0_bar_launcher.png
new file mode 100644
index 0000000..e89adac
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_0_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_1_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_1_bar_launcher.png
new file mode 100644
index 0000000..b9b5c05
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_1_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_2_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_2_bar_launcher.png
new file mode 100644
index 0000000..33c3f0c
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_2_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_3_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_3_bar_launcher.png
new file mode 100644
index 0000000..bc9f70a
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_3_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_4_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_4_bar_launcher.png
new file mode 100644
index 0000000..6e55476
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_4_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_0_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_0_bar_launcher.png
new file mode 100644
index 0000000..ce014ca
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_0_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_1_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_1_bar_launcher.png
new file mode 100644
index 0000000..efc8f83
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_1_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_2_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_2_bar_launcher.png
new file mode 100644
index 0000000..c77aeb8
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_2_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_3_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_3_bar_launcher.png
new file mode 100644
index 0000000..e563533
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_3_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_4_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_4_bar_launcher.png
new file mode 100644
index 0000000..d0318c6
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_4_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_sim_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_sim_launcher.png
new file mode 100644
index 0000000..0884e3c
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_sim_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_null_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_null_launcher.png
new file mode 100644
index 0000000..72d13cb
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_null_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_off_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_off_launcher.png
new file mode 100644
index 0000000..0177906
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_off_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ethernet_active_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ethernet_active_launcher.png
new file mode 100644
index 0000000..06dc98b
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ethernet_active_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ethernet_no_internet_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ethernet_no_internet_launcher.png
new file mode 100644
index 0000000..d71f28b
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ethernet_no_internet_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ic_notifications.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ic_notifications.png
new file mode 100644
index 0000000..c5fb9ef
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ic_notifications.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_0_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_0_launcher.png
new file mode 100755
index 0000000..988c426
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_0_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_1_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_1_launcher.png
new file mode 100755
index 0000000..c8a42b7
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_1_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_2_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_2_launcher.png
new file mode 100755
index 0000000..133ca1d
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_2_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_3_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_3_launcher.png
new file mode 100755
index 0000000..1752147
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_3_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_4_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_4_launcher.png
new file mode 100755
index 0000000..a9cf1da
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_4_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_no_internet_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_no_internet_launcher.png
new file mode 100644
index 0000000..25f1cdc
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_no_internet_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_not_connected_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_not_connected_launcher.png
new file mode 100644
index 0000000..781e512
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_not_connected_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable/ic_keyboard_blue.xml b/TvSampleLeanbackLauncher/src/main/res/drawable/ic_keyboard_blue.xml
new file mode 100644
index 0000000..f3a8deb
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable/ic_keyboard_blue.xml
@@ -0,0 +1,26 @@
+<?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
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF4285F4"
+ android:pathData="M20,5L4,5c-1.1,0 -1.99,0.9 -1.99,2L2,17c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,7c0,-1.1 -0.9,-2 -2,-2zM11,8h2v2h-2L11,8zM11,11h2v2h-2v-2zM8,8h2v2L8,10L8,8zM8,11h2v2L8,13v-2zM7,13L5,13v-2h2v2zM7,10L5,10L5,8h2v2zM16,17L8,17v-2h8v2zM16,13h-2v-2h2v2zM16,10h-2L14,8h2v2zM19,13h-2v-2h2v2zM19,10h-2L17,8h2v2z"/>
+</vector>
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable/ic_keyboard_grey.xml b/TvSampleLeanbackLauncher/src/main/res/drawable/ic_keyboard_grey.xml
new file mode 100644
index 0000000..7034b32
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable/ic_keyboard_grey.xml
@@ -0,0 +1,26 @@
+<?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
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFB1B1B1"
+ android:pathData="M20,5L4,5c-1.1,0 -1.99,0.9 -1.99,2L2,17c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,7c0,-1.1 -0.9,-2 -2,-2zM11,8h2v2h-2L11,8zM11,11h2v2h-2v-2zM8,8h2v2L8,10L8,8zM8,11h2v2L8,13v-2zM7,13L5,13v-2h2v2zM7,10L5,10L5,8h2v2zM16,17L8,17v-2h8v2zM16,13h-2v-2h2v2zM16,10h-2L14,8h2v2zM19,13h-2v-2h2v2zM19,10h-2L17,8h2v2z"/>
+</vector>
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable/ic_mic_color.xml b/TvSampleLeanbackLauncher/src/main/res/drawable/ic_mic_color.xml
new file mode 100644
index 0000000..4725dab
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable/ic_mic_color.xml
@@ -0,0 +1,34 @@
+<!--
+ ~ 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="24dp"
+ android:height="24dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:pathData="M22,37.84h4V44h-4z"
+ android:fillColor="#34A853"/>
+ <path
+ android:pathData="M24,30c3.31,0 5.98,-2.69 5.98,-6L30,10c0,-3.32 -2.68,-6 -6,-6 -3.31,0 -6,2.68 -6,6v14c0,3.31 2.69,6 6,6z"
+ android:fillColor="#4285F4"/>
+ <path
+ android:pathData="M24.01,34c-2.76,0 -5.26,-1.12 -7.07,-2.93l-2.82,2.82c2.53,2.54 6.04,4.11 9.91,4.11 7.73,0 13.95,-6.27 13.95,-14h-4c-0.01,5.52 -4.45,10 -9.97,10z"
+ android:fillColor="#EA4335"/>
+ <path
+ android:pathData="M14.01,24h-3.99c0,3.86 1.56,7.36 4.09,9.89l2.82,-2.82c-1.81,-1.81 -2.92,-4.31 -2.92,-7.07z"
+ android:fillColor="#F4B400"/>
+</vector>
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable/ic_remove_circle_black.xml b/TvSampleLeanbackLauncher/src/main/res/drawable/ic_remove_circle_black.xml
new file mode 100644
index 0000000..3ed2d55
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable/ic_remove_circle_black.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM17,13L7,13v-2h10v2z"/>
+</vector> \ No newline at end of file
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable/keyboard_search.xml b/TvSampleLeanbackLauncher/src/main/res/drawable/keyboard_search.xml
new file mode 100644
index 0000000..cfde6dc
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable/keyboard_search.xml
@@ -0,0 +1,21 @@
+<?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
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true" android:drawable="@drawable/ic_keyboard_blue" />
+ <item android:drawable="@drawable/ic_keyboard_grey" />
+</selector>
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable/notification_background_left.xml b/TvSampleLeanbackLauncher/src/main/res/drawable/notification_background_left.xml
new file mode 100644
index 0000000..a663519
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable/notification_background_left.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true" android:drawable="@drawable/tab_background_left"/>
+</selector> \ No newline at end of file
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable/notification_background_right.xml b/TvSampleLeanbackLauncher/src/main/res/drawable/notification_background_right.xml
new file mode 100644
index 0000000..b28f062
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable/notification_background_right.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true" android:drawable="@drawable/tab_background_right"/>
+</selector> \ No newline at end of file
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable/tab_background_left.xml b/TvSampleLeanbackLauncher/src/main/res/drawable/tab_background_left.xml
new file mode 100644
index 0000000..469826a
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable/tab_background_left.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="@color/notification_selection_color" />
+ <corners android:bottomLeftRadius="100dp" android:topLeftRadius="100dp" />
+</shape> \ No newline at end of file
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable/tab_background_right.xml b/TvSampleLeanbackLauncher/src/main/res/drawable/tab_background_right.xml
new file mode 100644
index 0000000..b0bd1ff
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable/tab_background_right.xml
@@ -0,0 +1,21 @@
+<?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
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="@color/notification_selection_color" />
+ <corners android:bottomRightRadius="100dp" android:topRightRadius="100dp" />
+</shape> \ No newline at end of file
diff --git a/TvSampleLeanbackLauncher/src/main/res/layout/app_launch_row.xml b/TvSampleLeanbackLauncher/src/main/res/layout/app_launch_row.xml
new file mode 100644
index 0000000..23ac572
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/layout/app_launch_row.xml
@@ -0,0 +1,37 @@
+<?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_height="wrap_content"
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:paddingStart="@dimen/overscan_horizontal"
+ android:paddingEnd="@dimen/overscan_horizontal"
+ android:clipToPadding="false"
+ android:clipChildren="false">
+ <TextView
+ android:id="@+id/row_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="@dimen/app_row_title_padding"
+ android:text="rowTitle"
+ android:textSize="@dimen/app_row_title_text_size" />
+ <androidx.leanback.widget.HorizontalGridView
+ android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/app_item_height" />
+</LinearLayout>
diff --git a/TvSampleLeanbackLauncher/src/main/res/layout/launch_item.xml b/TvSampleLeanbackLauncher/src/main/res/layout/launch_item.xml
new file mode 100644
index 0000000..4f52c7e
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/layout/launch_item.xml
@@ -0,0 +1,52 @@
+<?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
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/frame"
+ android:layout_width="@dimen/app_item_width"
+ android:layout_height="@dimen/app_item_height"
+ android:background="?android:attr/selectableItemBackground"
+ android:clickable="true"
+ android:focusable="true"
+ android:padding="@dimen/app_item_padding"
+ >
+ <ImageView
+ android:id="@+id/banner"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+ <LinearLayout
+ android:id="@+id/ll"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:orientation="vertical"
+ >
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="@dimen/app_icon_size"
+ android:layout_height="@dimen/app_icon_size"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="@dimen/app_icon_margin"
+ />
+ <TextView
+ android:id="@+id/label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ />
+ </LinearLayout>
+</FrameLayout>
diff --git a/TvSampleLeanbackLauncher/src/main/res/layout/launcher.xml b/TvSampleLeanbackLauncher/src/main/res/layout/launcher.xml
new file mode 100644
index 0000000..1b05005
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/layout/launcher.xml
@@ -0,0 +1,41 @@
+<?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
+ -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/mostly_transparent_wrapper"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipToPadding="false"
+ android:clipChildren="false">
+ <ScrollView
+ android:id="@+id/scroll_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginTop="@dimen/overscan_vertical"
+ android:layout_marginBottom="@dimen/overscan_vertical"
+ android:clipToPadding="false"
+ android:clipChildren="false"
+ android:scrollbars="none">
+ <LinearLayout
+ android:id="@+id/launcher_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:clipChildren="false"
+ android:clipToPadding="false"/>
+ </ScrollView>
+</FrameLayout>
diff --git a/TvSampleLeanbackLauncher/src/main/res/layout/leanback_banner_preference.xml b/TvSampleLeanbackLauncher/src/main/res/layout/leanback_banner_preference.xml
new file mode 100644
index 0000000..2c7593f
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/layout/leanback_banner_preference.xml
@@ -0,0 +1,84 @@
+<?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:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:clickable="true"
+ android:focusable="true"
+ android:descendantFocusability="blocksDescendants"
+ android:orientation="horizontal"
+ android:paddingStart="@dimen/lb_preference_item_padding_start"
+ android:paddingEnd="@dimen/lb_preference_item_padding_end" >
+
+ <FrameLayout
+ android:layout_width="@dimen/preference_item_banner_width"
+ android:layout_height="@dimen/preference_item_banner_height"
+ android:layout_gravity="center_vertical"
+ android:layout_marginEnd="@dimen/lb_preference_item_icon_margin_end">
+ <FrameLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/preference_item_banner_background">
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="@dimen/preference_item_icon_size"
+ android:layout_height="@dimen/preference_item_icon_size"
+ android:layout_gravity="center"/>
+ </FrameLayout>
+
+ <ImageView
+ android:id="@+id/banner"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+ </FrameLayout>
+
+ <LinearLayout android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical">
+ <Space android:layout_width="0dp" android:layout_height="@dimen/lb_preference_item_text_space_top" />
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/lb_preference_item_primary_text_margin_bottom"
+ android:fontFamily="sans-serif-condensed"
+ android:textColor="@color/lb_preference_item_primary_text_color"
+ android:textSize="@dimen/lb_preference_item_primary_text_size"/>
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="sans-serif-condensed"
+ android:textColor="@color/lb_preference_item_secondary_text_color"
+ android:textSize="@dimen/lb_preference_item_secondary_text_size"
+ android:maxLines="4" />
+ <Space android:layout_width="0dp" android:layout_height="@dimen/lb_preference_item_text_space_bottom" />
+ </LinearLayout>
+
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:orientation="vertical" />
+
+</LinearLayout>
diff --git a/TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item.xml b/TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item.xml
new file mode 100644
index 0000000..1acdd1f
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<com.example.sampleleanbacklauncher.notifications.NotificationPanelItemView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <include
+ layout="@layout/notification_panel_item_main_content"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_panel_item_height" />
+
+ <include
+ layout="@layout/notification_panel_item_expanded_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+</com.example.sampleleanbacklauncher.notifications.NotificationPanelItemView> \ No newline at end of file
diff --git a/TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item_dismissible.xml b/TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item_dismissible.xml
new file mode 100644
index 0000000..1269227
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item_dismissible.xml
@@ -0,0 +1,50 @@
+<com.example.sampleleanbacklauncher.notifications.NotificationPanelDismissibleItemView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <include
+ layout="@layout/notification_panel_item_main_content"
+ android:layout_width="@dimen/notification_panel_item_container_width"
+ android:layout_height="@dimen/notification_panel_item_height" />
+
+ <LinearLayout
+ android:id="@+id/dismiss_button"
+ android:layout_width="@dimen/notification_panel_item_dismiss_button_width"
+ android:layout_height="@dimen/notification_panel_item_height"
+ android:background="@drawable/notification_background_left"
+ android:clickable="true"
+ android:focusable="true">
+
+ <ImageView
+ android:layout_width="@dimen/notification_panel_item_dismiss_icon_size"
+ android:layout_height="@dimen/notification_panel_item_dismiss_icon_size"
+ android:layout_gravity="center_vertical"
+ android:layout_marginEnd="@dimen/notification_panel_item_dismiss_icon_margin_end"
+ android:layout_marginStart="@dimen/notification_panel_item_dismiss_icon_margin_start"
+ android:src="@drawable/ic_remove_circle_black"
+ android:tint="@color/notification_icon_tint" />
+
+ <TextView
+ android:id="@+id/dismiss_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:fontFamily="sans-serif-condensed"
+ android:textColor="@color/notification_dimiss_text_color"
+ android:textSize="@dimen/notification_panel_item_primary_text_size"
+ android:visibility="gone" />
+ </LinearLayout>
+ </LinearLayout>
+
+ <include
+ layout="@layout/notification_panel_item_expanded_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+</com.example.sampleleanbacklauncher.notifications.NotificationPanelDismissibleItemView> \ No newline at end of file
diff --git a/TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item_expanded_text.xml b/TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item_expanded_text.xml
new file mode 100644
index 0000000..502e125
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item_expanded_text.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/notification_expanded_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start"
+ android:fontFamily="sans-serif-condensed"
+ android:paddingBottom="@dimen/notification_panel_item_padding_bottom"
+ android:paddingEnd="@dimen/notification_panel_item_expanded_text_padding_end"
+ android:paddingStart="@dimen/notification_panel_item_expanded_text_padding_start"
+ android:paddingTop="@dimen/notification_panel_item_padding_top"
+ android:textColor="@color/secondary_text_color"
+ android:textSize="@dimen/notification_panel_item_secondary_text_size"
+ android:visibility="gone" /> \ No newline at end of file
diff --git a/TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item_main_content.xml b/TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item_main_content.xml
new file mode 100644
index 0000000..b6d08bd
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item_main_content.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/notification_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:nextFocusLeft="@id/notification_container"
+ android:nextFocusRight="@id/notification_container"
+ android:clickable="true"
+ android:focusable="true"
+ android:orientation="horizontal"
+ android:paddingBottom="@dimen/notification_panel_item_padding_bottom"
+ android:paddingEnd="@dimen/notification_panel_item_padding_end"
+ android:paddingStart="@dimen/notification_panel_item_padding_start"
+ android:paddingTop="@dimen/notification_panel_item_padding_top">
+
+ <ImageView
+ android:id="@+id/notification_icon"
+ android:layout_width="@dimen/notification_panel_item_icon_size"
+ android:layout_height="@dimen/notification_panel_item_icon_size"
+ android:layout_gravity="center_vertical"
+ android:layout_marginEnd="@dimen/notification_panel_item_icon_margin_end"
+ android:tint="@color/notification_icon_tint" />
+
+ <LinearLayout
+ android:layout_width="@dimen/notification_panel_item_text_width"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/notification_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/notification_panel_item_text_margin_bottom"
+ android:ellipsize="end"
+ android:fontFamily="sans-serif-condensed"
+ android:maxLines="1"
+ android:textColor="@color/primary_text_color"
+ android:textSize="@dimen/notification_panel_item_primary_text_size" />
+
+ <TextView
+ android:id="@+id/notification_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:fontFamily="sans-serif-condensed"
+ android:maxLines="1"
+ android:textColor="@color/secondary_text_color"
+ android:textSize="@dimen/notification_panel_item_secondary_text_size" />
+ </LinearLayout>
+</LinearLayout> \ No newline at end of file
diff --git a/TvSampleLeanbackLauncher/src/main/res/layout/notifications_panel_view.xml b/TvSampleLeanbackLauncher/src/main/res/layout/notifications_panel_view.xml
new file mode 100644
index 0000000..e87343c
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/layout/notifications_panel_view.xml
@@ -0,0 +1,68 @@
+<?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
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/notifications_panel_view"
+ android:layout_width="@dimen/notification_panel_width"
+ android:layout_height="match_parent"
+ android:layout_gravity="end"
+ android:animateLayoutChanges="true"
+ android:background="@color/notification_panel_background"
+ android:orientation="vertical">
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?attr/defaultBrandColor"
+ android:elevation="@dimen/lb_preference_decor_title_container_elevation"
+ android:transitionGroup="false">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/lb_preference_decor_title_text_height"
+ android:layout_marginEnd="@dimen/lb_preference_decor_title_margin_end"
+ android:layout_marginStart="@dimen/lb_preference_decor_title_margin_start"
+ android:layout_marginTop="@dimen/lb_preference_decor_title_margin_top"
+ android:fontFamily="sans-serif-condensed"
+ android:gravity="center_vertical"
+ android:maxLines="1"
+ android:text="@string/system_notifications"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="@dimen/lb_preference_decor_title_text_size" />
+ </FrameLayout>
+
+ <TextView
+ android:id="@+id/no_notifications_message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:paddingBottom="@dimen/notification_panel_vertical_padding"
+ android:paddingEnd="@dimen/notification_panel_horizontal_padding"
+ android:paddingStart="@dimen/notification_panel_horizontal_padding"
+ android:paddingTop="@dimen/notification_panel_title_padding"
+ android:text="@string/no_notifications_message"
+ android:textColor="@color/secondary_text_color"
+ android:visibility="gone" />
+
+ <androidx.leanback.widget.VerticalGridView
+ android:id="@+id/notifications_list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipToPadding="false">
+
+ <requestFocus />
+ </androidx.leanback.widget.VerticalGridView>
+</LinearLayout> \ No newline at end of file
diff --git a/TvSampleLeanbackLauncher/src/main/res/layout/pref_wall_of_text.xml b/TvSampleLeanbackLauncher/src/main/res/layout/pref_wall_of_text.xml
new file mode 100644
index 0000000..90d72cd
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/layout/pref_wall_of_text.xml
@@ -0,0 +1,46 @@
+<?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:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:clickable="false"
+ android:focusable="false"
+ android:descendantFocusability="blocksDescendants"
+ android:orientation="horizontal"
+ android:paddingStart="@dimen/lb_preference_item_padding_start"
+ android:paddingEnd="@dimen/lb_preference_item_padding_end" >
+
+ <LinearLayout android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical">
+ <Space android:layout_width="0dp" android:layout_height="@dimen/lb_preference_item_text_space_top" />
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/lb_preference_item_primary_text_margin_bottom"
+ android:fontFamily="sans-serif-condensed"
+ android:textColor="@color/lb_preference_item_secondary_text_color"
+ android:textSize="@dimen/lb_preference_item_secondary_text_size" />
+ <Space android:layout_width="0dp" android:layout_height="@dimen/lb_preference_item_text_space_bottom" />
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/TvSampleLeanbackLauncher/src/main/res/layout/search.xml b/TvSampleLeanbackLauncher/src/main/res/layout/search.xml
new file mode 100644
index 0000000..85cf306
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/layout/search.xml
@@ -0,0 +1,71 @@
+<?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:id="@+id/search_row"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/search_row_height"
+ android:paddingStart="@dimen/search_row_padding_start"
+ android:paddingEnd="@dimen/overscan_horizontal"
+ android:paddingTop="@dimen/search_row_padding_top"
+ android:clipChildren="false"
+ android:clipToPadding="false" >
+ <ImageView
+ android:id="@+id/search_orb_voice"
+ android:layout_width="@dimen/search_item_size"
+ android:layout_height="@dimen/search_item_size"
+ android:background="@android:color/white"
+ android:clickable="true"
+ android:contentDescription="@string/search_click_to_speak"
+ android:elevation="@dimen/search_item_unfocused_z"
+ android:focusable="true"
+ android:focusedByDefault="true"
+ android:padding="@dimen/search_item_padding"
+ android:src="@drawable/ic_mic_color"/>
+ <ImageView
+ android:id="@+id/search_orb_keyboard"
+ android:layout_width="@dimen/search_item_size"
+ android:layout_height="@dimen/search_item_size"
+ android:layout_marginStart="@dimen/search_item_spacing"
+ android:background="@android:color/white"
+ android:clickable="true"
+ android:contentDescription="@string/search_click_to_type"
+ android:elevation="@dimen/search_item_unfocused_z"
+ android:focusable="true"
+ android:focusedByDefault="false"
+ android:padding="@dimen/search_item_padding"
+ android:src="@drawable/keyboard_search"/>
+ <TextView
+ android:id="@+id/search_text_voice"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/search_item_size"
+ android:layout_marginStart="@dimen/search_label_spacing"
+ android:gravity="center_vertical"
+ android:text="@string/search_click_to_speak"
+ android:textColor="@android:color/white"
+ android:textSize="@dimen/search_label_text_size"
+ android:textStyle="italic"/>
+ <TextView
+ android:id="@+id/search_text_keyboard"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/search_item_size"
+ android:layout_marginStart="@dimen/search_label_spacing"
+ android:gravity="center_vertical"
+ android:text="@string/search_click_to_type"
+ android:textColor="@android:color/white"
+ android:textSize="@dimen/search_label_text_size"
+ android:textStyle="italic"/>
+</LinearLayout>
diff --git a/TvSampleLeanbackLauncher/src/main/res/mipmap-hdpi/ic_launcher.png b/TvSampleLeanbackLauncher/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/mipmap-mdpi/ic_launcher.png b/TvSampleLeanbackLauncher/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/mipmap-xhdpi/ic_launcher.png b/TvSampleLeanbackLauncher/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/mipmap-xxhdpi/ic_launcher.png b/TvSampleLeanbackLauncher/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/values/arrays.xml b/TvSampleLeanbackLauncher/src/main/res/values/arrays.xml
new file mode 100644
index 0000000..d93b991
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/values/arrays.xml
@@ -0,0 +1,27 @@
+<?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
+ -->
+
+<resources>
+ <!-- Out of box ordering for system apps. You probably shouldn't modify this [DO NOT TRANSLATE] -->
+ <string-array name="oob_order" translatable="false">
+ <item>com.android.vending</item>
+ <item>com.google.android.videos</item>
+ <item>com.google.android.music</item>
+ <item>com.google.android.youtube.tv</item>
+ <item>com.google.android.play.games</item>
+ </string-array>
+</resources>
diff --git a/TvSampleLeanbackLauncher/src/main/res/values/colors.xml b/TvSampleLeanbackLauncher/src/main/res/values/colors.xml
new file mode 100644
index 0000000..b5d956b
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/values/colors.xml
@@ -0,0 +1,36 @@
+<?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
+ -->
+
+<resources>
+ <color name="preference_item_banner_background">#455a64</color>
+
+ <color name="primary_text_color">#e5e5e5</color>
+ <color name="secondary_text_color">#787b7f</color>
+
+ <color name="notification_text_color">#787b7f</color>
+ <color name="notification_background">#FFCCCCCC</color>
+ <color name="notification_heads_up_decorator">#1784d7</color>
+ <color name="notification_heads_up_background">#c0000000</color>
+
+ <color name="notification_panel_background">#263238</color>
+ <color name="notification_selection_color">#26eeeeee</color>
+ <color name="notification_dimiss_text_color">#b2eeeeee</color>
+ <color name="notification_icon_tint">#FFFFFF</color>
+ <color name="notification_expanded_text_background">#0aeeeeee</color>
+ <color name="notification_progress_stroke_color">#17ffff</color>
+ <color name="notification_progress_stroke_max_color">#3317ffff</color>
+</resources>
diff --git a/TvSampleLeanbackLauncher/src/main/res/values/dimens.xml b/TvSampleLeanbackLauncher/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..8936f39
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/values/dimens.xml
@@ -0,0 +1,103 @@
+<?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
+ -->
+
+<resources>
+ <dimen name="overscan_vertical">27dp</dimen>
+ <dimen name="overscan_horizontal">48dp</dimen>
+
+ <dimen name="app_item_width">190dp</dimen>
+ <dimen name="app_item_height">120dp</dimen>
+ <dimen name="app_item_padding">15dp</dimen>
+ <dimen name="app_row_title_text_size">20sp</dimen>
+ <dimen name="app_row_title_padding">5dp</dimen>
+ <dimen name="app_icon_size">48dp</dimen>
+ <dimen name="app_icon_margin">10dp</dimen>
+
+ <dimen name="search_row_height">108dp</dimen>
+ <dimen name="search_row_padding_start">56dp</dimen>
+ <dimen name="search_row_padding_top">4dp</dimen>
+ <dimen name="search_item_spacing">28dp</dimen>
+ <dimen name="search_item_size">52dp</dimen>
+ <dimen name="search_item_padding">14dp</dimen>
+ <dimen name="search_item_focused_z">8dp</dimen>
+ <dimen name="search_item_unfocused_z">2dp</dimen>
+ <item name="search_item_focused_zoom" type="fraction">120%</item>
+ <item name="search_orb_scale_duration_ms" type="integer">150</item>
+ <dimen name="search_label_spacing">24dp</dimen>
+ <dimen name="search_label_text_size">18sp</dimen>
+
+ <dimen name="rec_item_padding">15dp</dimen>
+ <dimen name="rec_card_image_height">156dp</dimen>
+ <dimen name="rec_card_image_min_width">108dp</dimen>
+ <dimen name="rec_card_image_max_width">313dp</dimen>
+ <dimen name="rec_title_size">16sp</dimen>
+ <dimen name="rec_subtitle_size">12sp</dimen>
+ <dimen name="rec_icon_size">16dp</dimen>
+
+ <dimen name="now_playing_padding">15dp</dimen>
+ <dimen name="now_playing_text_margin">15dp</dimen>
+ <dimen name="now_playing_progress_margin">15dp</dimen>
+ <dimen name="now_playing_image_height">156dp</dimen>
+ <dimen name="now_playing_image_min_width">108dp</dimen>
+ <dimen name="now_playing_image_max_width">313dp</dimen>
+
+ <dimen name="preference_item_banner_width">64dp</dimen>
+ <dimen name="preference_item_banner_height">36dp</dimen>
+ <dimen name="preference_item_icon_size">24dp</dimen>
+
+ <dimen name="notification_panel_vertical_padding">6dp</dimen>
+ <dimen name="notification_panel_horizontal_padding">24dp</dimen>
+ <dimen name="notification_panel_width">360dp</dimen>
+ <dimen name="notification_panel_title_padding">24dp</dimen>
+
+ <dimen name="notification_progress_stroke_width">2dp</dimen>
+ <dimen name="notification_progress_circle_size">40dp</dimen>
+ <dimen name="notification_progress_circle_padding_start">20dp</dimen>
+ <dimen name="notification_progress_circle_padding_top">13dp</dimen>
+
+ <dimen name="notification_panel_item_icon_size">32dp</dimen>
+ <dimen name="notification_panel_item_icon_margin_start">14dp</dimen>
+ <dimen name="notification_panel_item_icon_margin_end">14dp</dimen>
+ <dimen name="notification_panel_item_show_button_translate_x">-96dp</dimen>
+ <dimen name="notification_panel_item_dismiss_translate_x">-380dp</dimen>
+ <dimen name="notification_panel_item_margin_start">24dp</dimen>
+ <dimen name="notification_panel_item_margin_bottom">12dp</dimen>
+ <dimen name="notification_panel_item_margin_top">14dp</dimen>
+ <dimen name="notification_panel_item_margin_end">56dp</dimen>
+ <dimen name="notification_panel_item_padding_end">16dp</dimen>
+ <dimen name="notification_panel_item_padding_bottom">12dp</dimen>
+ <dimen name="notification_panel_item_padding_start">24dp</dimen>
+ <dimen name="notification_panel_item_padding_top">14dp</dimen>
+ <dimen name="notification_panel_item_height">64dp</dimen>
+ <dimen name="notification_panel_item_container_width">278dp</dimen>
+ <dimen name="notification_panel_item_text_width">192dp</dimen>
+ <dimen name="notification_panel_item_text_margin_bottom">2dp</dimen>
+ <dimen name="notification_panel_item_primary_text_size">14sp</dimen>
+ <dimen name="notification_panel_item_secondary_text_size">12sp</dimen>
+ <dimen name="notification_panel_item_dismiss_icon_size">22dp</dimen>
+ <dimen name="notification_panel_item_dismiss_button_width">500dp</dimen>
+ <dimen name="notification_panel_item_dismiss_icon_margin_start">15.5dp</dimen>
+ <dimen name="notification_panel_item_dismiss_icon_margin_end">7.5dp</dimen>
+ <dimen name="notification_panel_item_expanded_text_padding_start">71dp</dimen>
+ <dimen name="notification_panel_item_expanded_text_padding_end">50dp</dimen>
+
+ <dimen name="lb_preference_decor_title_text_height">2dp</dimen>
+ <dimen name="lb_preference_decor_title_margin_end">2dp</dimen>
+ <dimen name="lb_preference_decor_title_margin_start">2dp</dimen>
+ <dimen name="lb_preference_decor_title_margin_top">2dp</dimen>
+ <dimen name="lb_preference_decor_title_container_elevation">2dp</dimen>
+</resources>
diff --git a/TvSampleLeanbackLauncher/src/main/res/values/ids.xml b/TvSampleLeanbackLauncher/src/main/res/values/ids.xml
new file mode 100644
index 0000000..41a3670
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/values/ids.xml
@@ -0,0 +1,22 @@
+<?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
+ -->
+
+<resources>
+ <item type="id" name="apps_row"/>
+ <item type="id" name="games_row"/>
+ <item type="id" name="settings_row"/>
+</resources>
diff --git a/TvSampleLeanbackLauncher/src/main/res/values/strings.xml b/TvSampleLeanbackLauncher/src/main/res/values/strings.xml
new file mode 100644
index 0000000..86d3d79
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/values/strings.xml
@@ -0,0 +1,58 @@
+<!--
+ ~ 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
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name">Sample Leanback Launcher</string>
+ <string name="apps_row_title">Apps</string>
+ <string name="games_row_title">Games</string>
+ <string name="settings_row_title">Settings</string>
+
+ <string name="network_settings_disconnected">Network Settings</string>
+
+ <string name="app_unavailable">Application unavailable</string>
+
+ <string name="search_click_to_speak">Click to speak</string>
+ <string name="search_click_to_type">Click to type</string>
+
+ <string name="now_playing_state_playing">Now Playing</string>
+ <string name="now_playing_state_paused">Paused</string>
+
+ <!-- Title of dialog which allows user to select which apps will be allowed to post recommendations -->
+ <string name="rec_prefs_title">Recommendations row</string>
+
+ <!-- Description of dialog which allows user to select which apps will be allowed to post recommendations -->
+ <string name="rec_prefs_wall_of_text">Selected sources may display recommendations on the home screen. To hide these, turn off recommendations from specific sources.</string>
+
+ <!-- Recommendations missing reasons -->
+ <string name="rec_missing_empty">No recommendations</string>
+ <string name="rec_missing_disabled">Some recommendation sources disabled</string>
+ <string name="rec_missing_preparing">Preparing recommendations</string>
+
+ <!-- Notifications -->
+ <plurals name="notifications_title">
+ <item quantity="one">%d notification</item>
+ <item quantity="other">%d notifications</item>
+ </plurals>
+
+ <!-- Title displayed at the top of the notifications panel. -->
+ <string name="system_notifications">Notifications</string>
+
+ <!-- Text displayed in the notifications panel if there are no notifications. -->
+ <string name="no_notifications_message">No notifications</string>
+
+ <!-- Format for a content description for a notification where the first argument is the notification title and the second is the notification text -->
+ <string name="notification_content_description_format"><xliff:g id="title" example="USB debugging connected">%1$s </xliff:g><xliff:g id="text" example="Select to disable USB debugging.">%2$s</xliff:g></string>
+</resources>
diff --git a/TvSampleLeanbackLauncher/src/main/res/values/styles.xml b/TvSampleLeanbackLauncher/src/main/res/values/styles.xml
new file mode 100644
index 0000000..4dcde15
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/values/styles.xml
@@ -0,0 +1,38 @@
+<?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
+ -->
+
+<resources>
+
+ <style name="AppTheme" parent="@style/Theme.Leanback">
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:windowShowWallpaper">true</item>
+ </style>
+ <style name="SettingsTheme" parent="@style/Theme.Leanback">
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:backgroundDimAmount">.8</item>
+ <item name="android:backgroundDimEnabled">true</item>
+ <item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Leanback</item>
+ </style>
+ <style name="NotificationsSidePanel" parent="@style/Theme.Leanback">
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:backgroundDimAmount">.8</item>
+ <item name="android:backgroundDimEnabled">true</item>
+ <item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Leanback</item>
+ </style>
+</resources>
diff --git a/audio_proxy/Android.bp b/audio_proxy/Android.bp
index 2c33cf2..eca5f07 100644
--- a/audio_proxy/Android.bp
+++ b/audio_proxy/Android.bp
@@ -22,7 +22,7 @@ cc_library {
],
shared_libs: [
- "device.google.atv.audio_proxy-aidl-V1-ndk",
+ "device.google.atv.audio_proxy-aidl-V3-ndk",
"libbase",
"libbinder_ndk",
"libcutils",
diff --git a/audio_proxy/AudioProxyDevice.cpp b/audio_proxy/AudioProxyDevice.cpp
index 4c409a1..1721cdd 100644
--- a/audio_proxy/AudioProxyDevice.cpp
+++ b/audio_proxy/AudioProxyDevice.cpp
@@ -20,9 +20,9 @@
using aidl::device::google::atv::audio_proxy::AudioConfig;
-#define CHECK_API(func) \
+#define CHECK_API(st, func) \
do { \
- if (!stream->func) { \
+ if (!st->func) { \
LOG(ERROR) << "Undefined API " << #func; \
return false; \
} \
@@ -31,14 +31,23 @@ using aidl::device::google::atv::audio_proxy::AudioConfig;
namespace audio_proxy {
namespace {
bool isValidStreamOut(const audio_proxy_stream_out_t* stream) {
- CHECK_API(standby);
- CHECK_API(pause);
- CHECK_API(resume);
- CHECK_API(flush);
- CHECK_API(drain);
- CHECK_API(write);
- CHECK_API(get_presentation_position);
- CHECK_API(set_volume);
+ CHECK_API(stream, standby);
+ CHECK_API(stream, pause);
+ CHECK_API(stream, resume);
+ CHECK_API(stream, flush);
+ CHECK_API(stream, drain);
+ CHECK_API(stream, write);
+ CHECK_API(stream, get_presentation_position);
+ CHECK_API(stream, set_volume);
+ CHECK_API(stream, get_buffer_size);
+ CHECK_API(stream, get_latency);
+
+ if (stream->v2) {
+ CHECK_API(stream->v2, start);
+ CHECK_API(stream->v2, stop);
+ CHECK_API(stream->v2, create_mmap_buffer);
+ CHECK_API(stream->v2, get_mmap_position);
+ }
return true;
}
@@ -55,13 +64,18 @@ const char* AudioProxyDevice::getServiceName() {
std::unique_ptr<AudioProxyStreamOut> AudioProxyDevice::openOutputStream(
const std::string& address, const AudioConfig& aidlConfig, int32_t flags) {
+ audio_proxy_config_v2_t config_v2 = {
+ .buffer_size_bytes = aidlConfig.bufferSizeBytes,
+ .latency_ms = aidlConfig.latencyMs,
+ .extension = nullptr};
+
audio_proxy_config_t config = {
.format = static_cast<audio_proxy_format_t>(aidlConfig.format),
.sample_rate = static_cast<uint32_t>(aidlConfig.sampleRateHz),
.channel_mask =
static_cast<audio_proxy_channel_mask_t>(aidlConfig.channelMask),
.frame_count = 0,
- .extension = nullptr};
+ .v2 = &config_v2};
// TODO(yucliu): Pass address to the app. For now, the only client app
// (MediaShell) can use flags to distinguish different streams.
diff --git a/audio_proxy/AudioProxyStreamOut.cpp b/audio_proxy/AudioProxyStreamOut.cpp
index f1f47eb..cb60fe4 100644
--- a/audio_proxy/AudioProxyStreamOut.cpp
+++ b/audio_proxy/AudioProxyStreamOut.cpp
@@ -54,4 +54,53 @@ void AudioProxyStreamOut::setVolume(float left, float right) {
mStream->set_volume(mStream, left, right);
}
+int64_t AudioProxyStreamOut::getBufferSizeBytes() {
+ return mStream->get_buffer_size(mStream);
+}
+
+int32_t AudioProxyStreamOut::getLatencyMs() {
+ return mStream->get_latency(mStream);
+}
+
+void AudioProxyStreamOut::start() {
+ if (mStream->v2) {
+ mStream->v2->start(mStream->v2);
+ }
+}
+
+void AudioProxyStreamOut::stop() {
+ if (mStream->v2) {
+ mStream->v2->stop(mStream->v2);
+ }
+}
+
+MmapBufferInfo AudioProxyStreamOut::createMmapBuffer(
+ int32_t minBufferSizeFrames) {
+ MmapBufferInfo aidlInfo;
+ if (!mStream->v2) {
+ return aidlInfo;
+ }
+
+ audio_proxy_mmap_buffer_info_t info =
+ mStream->v2->create_mmap_buffer(mStream->v2, minBufferSizeFrames);
+ aidlInfo.sharedMemoryFd.set(info.shared_memory_fd);
+ aidlInfo.bufferSizeFrames = info.buffer_size_frames;
+ aidlInfo.burstSizeFrames = info.burst_size_frames;
+ aidlInfo.flags = info.flags;
+ return aidlInfo;
+}
+
+PresentationPosition AudioProxyStreamOut::getMmapPosition() {
+ PresentationPosition position;
+ if (!mStream->v2) {
+ return position;
+ }
+
+ int64_t frames = 0;
+ struct timespec ts = {0, 0};
+ mStream->v2->get_mmap_position(mStream->v2, &frames, &ts);
+ position.frames = frames;
+ position.timestamp = {ts.tv_sec, ts.tv_nsec};
+ return position;
+}
} // namespace audio_proxy
diff --git a/audio_proxy/AudioProxyStreamOut.h b/audio_proxy/AudioProxyStreamOut.h
index 8a773ae..3ecd4fd 100644
--- a/audio_proxy/AudioProxyStreamOut.h
+++ b/audio_proxy/AudioProxyStreamOut.h
@@ -15,6 +15,8 @@
#pragma once
#include <aidl/device/google/atv/audio_proxy/AudioDrain.h>
+#include <aidl/device/google/atv/audio_proxy/MmapBufferInfo.h>
+#include <aidl/device/google/atv/audio_proxy/PresentationPosition.h>
#include <aidl/device/google/atv/audio_proxy/TimeSpec.h>
#include <memory>
@@ -24,6 +26,8 @@
namespace audio_proxy {
using aidl::device::google::atv::audio_proxy::AudioDrain;
+using aidl::device::google::atv::audio_proxy::MmapBufferInfo;
+using aidl::device::google::atv::audio_proxy::PresentationPosition;
using aidl::device::google::atv::audio_proxy::TimeSpec;
// C++ friendly wrapper of audio_proxy_stream_out. It handles type conversion
@@ -45,6 +49,14 @@ class AudioProxyStreamOut final {
void setVolume(float left, float right);
+ int64_t getBufferSizeBytes();
+ int32_t getLatencyMs();
+
+ void start();
+ void stop();
+ MmapBufferInfo createMmapBuffer(int32_t minBufferSizeFrames);
+ PresentationPosition getMmapPosition();
+
private:
audio_proxy_stream_out_t* const mStream;
audio_proxy_device_t* const mDevice;
diff --git a/audio_proxy/OutputStreamImpl.cpp b/audio_proxy/OutputStreamImpl.cpp
index 07eaff6..935a879 100644
--- a/audio_proxy/OutputStreamImpl.cpp
+++ b/audio_proxy/OutputStreamImpl.cpp
@@ -23,7 +23,6 @@
#include "AudioProxyStreamOut.h"
using aidl::device::google::atv::audio_proxy::MessageQueueFlag;
-using aidl::device::google::atv::audio_proxy::PresentationPosition;
using android::status_t;
namespace audio_proxy {
@@ -254,4 +253,37 @@ ndk::ScopedAStatus OutputStreamImpl::setVolume(float left, float right) {
return ndk::ScopedAStatus::ok();
}
+ndk::ScopedAStatus OutputStreamImpl::getBufferSizeBytes(
+ int64_t* bufferSizeBytes) {
+ *bufferSizeBytes = mStream->getBufferSizeBytes();
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus OutputStreamImpl::getLatencyMs(int32_t* latencyMs) {
+ *latencyMs = mStream->getLatencyMs();
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus OutputStreamImpl::start() {
+ mStream->start();
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus OutputStreamImpl::stop() {
+ mStream->stop();
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus OutputStreamImpl::createMmapBuffer(
+ int32_t minBufferSizeFrames, MmapBufferInfo* info) {
+ *info = mStream->createMmapBuffer(minBufferSizeFrames);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus OutputStreamImpl::getMmapPosition(
+ PresentationPosition* position) {
+ *position = mStream->getMmapPosition();
+ return ndk::ScopedAStatus::ok();
+}
+
} // namespace audio_proxy \ No newline at end of file
diff --git a/audio_proxy/OutputStreamImpl.h b/audio_proxy/OutputStreamImpl.h
index c783eb0..7a3f7dc 100644
--- a/audio_proxy/OutputStreamImpl.h
+++ b/audio_proxy/OutputStreamImpl.h
@@ -28,6 +28,8 @@ using android::hardware::EventFlag;
using aidl::device::google::atv::audio_proxy::AudioDrain;
using aidl::device::google::atv::audio_proxy::BnOutputStream;
+using aidl::device::google::atv::audio_proxy::MmapBufferInfo;
+using aidl::device::google::atv::audio_proxy::PresentationPosition;
using aidl::device::google::atv::audio_proxy::WriteStatus;
namespace audio_proxy {
@@ -56,6 +58,15 @@ class OutputStreamImpl : public BnOutputStream {
ndk::ScopedAStatus setVolume(float left, float right) override;
+ ndk::ScopedAStatus getBufferSizeBytes(int64_t* bufferSizeBytes) override;
+ ndk::ScopedAStatus getLatencyMs(int32_t* latencyMs) override;
+
+ ndk::ScopedAStatus start() override;
+ ndk::ScopedAStatus stop() override;
+ ndk::ScopedAStatus createMmapBuffer(int32_t minBufferSizeFrames,
+ MmapBufferInfo* info) override;
+ ndk::ScopedAStatus getMmapPosition(PresentationPosition* position) override;
+
private:
typedef void (*EventFlagDeleter)(EventFlag*);
diff --git a/audio_proxy/interfaces/aidl/Android.bp b/audio_proxy/interfaces/aidl/Android.bp
index 543cb0d..56ad152 100644
--- a/audio_proxy/interfaces/aidl/Android.bp
+++ b/audio_proxy/interfaces/aidl/Android.bp
@@ -14,6 +14,7 @@ aidl_interface {
"android.hardware.common.fmq-V1",
],
stability: "vintf",
+ frozen: true,
backend: {
ndk: {
enabled: true,
@@ -25,5 +26,27 @@ aidl_interface {
enabled: false,
},
},
- versions: ["1"],
+ versions_with_info: [
+ {
+ version: "1",
+ imports: [
+ "android.hardware.common-V2",
+ "android.hardware.common.fmq-V1",
+ ],
+ },
+ {
+ version: "2",
+ imports: [
+ "android.hardware.common-V2",
+ "android.hardware.common.fmq-V1",
+ ],
+ },
+ {
+ version: "3",
+ imports: [
+ "android.hardware.common-V2",
+ "android.hardware.common.fmq-V1",
+ ],
+ },
+ ],
}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/.hash b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/.hash
new file mode 100644
index 0000000..87f9d36
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/.hash
@@ -0,0 +1 @@
+36478e9608536b679d90121e62177577f2aae0b7
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioChannelMask.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioChannelMask.aidl
new file mode 100644
index 0000000..821a29a
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioChannelMask.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@Backing(type="int") @VintfStability
+enum AudioChannelMask {
+ MONO = 1,
+ STEREO = 3,
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioConfig.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioConfig.aidl
new file mode 100644
index 0000000..b75f906
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioConfig.aidl
@@ -0,0 +1,27 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@VintfStability
+parcelable AudioConfig {
+ device.google.atv.audio_proxy.AudioFormat format;
+ int sampleRateHz;
+ device.google.atv.audio_proxy.AudioChannelMask channelMask;
+ long bufferSizeBytes;
+ int latencyMs;
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioDrain.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioDrain.aidl
new file mode 100644
index 0000000..3b78e78
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioDrain.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@Backing(type="int") @VintfStability
+enum AudioDrain {
+ ALL = 0,
+ EARLY_NOTIFY = 1,
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioFormat.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioFormat.aidl
new file mode 100644
index 0000000..778ce15
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioFormat.aidl
@@ -0,0 +1,25 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@Backing(type="int") @VintfStability
+enum AudioFormat {
+ PCM_16_BIT = 1,
+ PCM_8_BIT = 2,
+ PCM_FLOAT = 5,
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioOutputFlag.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioOutputFlag.aidl
new file mode 100644
index 0000000..100762a
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioOutputFlag.aidl
@@ -0,0 +1,25 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@Backing(type="int") @VintfStability
+enum AudioOutputFlag {
+ NONE = 0,
+ DIRECT = 1,
+ HW_AV_SYNC = 64,
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/IAudioProxy.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/IAudioProxy.aidl
new file mode 100644
index 0000000..eadd1a5
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/IAudioProxy.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@VintfStability
+interface IAudioProxy {
+ void start(in device.google.atv.audio_proxy.IStreamProvider provider);
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/IOutputStream.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/IOutputStream.aidl
new file mode 100644
index 0000000..44abe39
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/IOutputStream.aidl
@@ -0,0 +1,32 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@VintfStability
+interface IOutputStream {
+ void standby();
+ void close();
+ void pause();
+ void resume();
+ void drain(device.google.atv.audio_proxy.AudioDrain drain);
+ void flush();
+ void prepareForWriting(in int frameSize, in int framesCount, out android.hardware.common.fmq.MQDescriptor<byte,android.hardware.common.fmq.SynchronizedReadWrite> dataMQ, out android.hardware.common.fmq.MQDescriptor<device.google.atv.audio_proxy.WriteStatus,android.hardware.common.fmq.SynchronizedReadWrite> statusMQ);
+ void setVolume(float left, float right);
+ long getBufferSizeBytes();
+ int getLatencyMs();
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/IStreamProvider.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/IStreamProvider.aidl
new file mode 100644
index 0000000..4d05698
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/IStreamProvider.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@VintfStability
+interface IStreamProvider {
+ device.google.atv.audio_proxy.IOutputStream openOutputStream(in String address, in device.google.atv.audio_proxy.AudioConfig config, in int flags);
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/MessageQueueFlag.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/MessageQueueFlag.aidl
new file mode 100644
index 0000000..736bfc3
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/MessageQueueFlag.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@Backing(type="int") @VintfStability
+enum MessageQueueFlag {
+ NOT_EMPTY = 1,
+ NOT_FULL = 2,
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/PresentationPosition.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/PresentationPosition.aidl
new file mode 100644
index 0000000..4fa7cb9
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/PresentationPosition.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@FixedSize @VintfStability
+parcelable PresentationPosition {
+ long frames;
+ device.google.atv.audio_proxy.TimeSpec timestamp;
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/TimeSpec.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/TimeSpec.aidl
new file mode 100644
index 0000000..daed6ac
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/TimeSpec.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@FixedSize @VintfStability
+parcelable TimeSpec {
+ long tvSec;
+ long tvNSec;
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/WriteStatus.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/WriteStatus.aidl
new file mode 100644
index 0000000..169163a
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/WriteStatus.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@FixedSize @VintfStability
+parcelable WriteStatus {
+ long written;
+ device.google.atv.audio_proxy.PresentationPosition position;
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/.hash b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/.hash
new file mode 100644
index 0000000..a5ef705
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/.hash
@@ -0,0 +1 @@
+70fdeee12fa5bd9b169842e36699920dd0283c56
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioChannelMask.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioChannelMask.aidl
new file mode 100644
index 0000000..821a29a
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioChannelMask.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@Backing(type="int") @VintfStability
+enum AudioChannelMask {
+ MONO = 1,
+ STEREO = 3,
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioConfig.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioConfig.aidl
new file mode 100644
index 0000000..b75f906
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioConfig.aidl
@@ -0,0 +1,27 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@VintfStability
+parcelable AudioConfig {
+ device.google.atv.audio_proxy.AudioFormat format;
+ int sampleRateHz;
+ device.google.atv.audio_proxy.AudioChannelMask channelMask;
+ long bufferSizeBytes;
+ int latencyMs;
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioDrain.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioDrain.aidl
new file mode 100644
index 0000000..3b78e78
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioDrain.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@Backing(type="int") @VintfStability
+enum AudioDrain {
+ ALL = 0,
+ EARLY_NOTIFY = 1,
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioFormat.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioFormat.aidl
new file mode 100644
index 0000000..778ce15
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioFormat.aidl
@@ -0,0 +1,25 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@Backing(type="int") @VintfStability
+enum AudioFormat {
+ PCM_16_BIT = 1,
+ PCM_8_BIT = 2,
+ PCM_FLOAT = 5,
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioOutputFlag.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioOutputFlag.aidl
new file mode 100644
index 0000000..100762a
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioOutputFlag.aidl
@@ -0,0 +1,25 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@Backing(type="int") @VintfStability
+enum AudioOutputFlag {
+ NONE = 0,
+ DIRECT = 1,
+ HW_AV_SYNC = 64,
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/IAudioProxy.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/IAudioProxy.aidl
new file mode 100644
index 0000000..eadd1a5
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/IAudioProxy.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@VintfStability
+interface IAudioProxy {
+ void start(in device.google.atv.audio_proxy.IStreamProvider provider);
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/IOutputStream.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/IOutputStream.aidl
new file mode 100644
index 0000000..7b5f9ce
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/IOutputStream.aidl
@@ -0,0 +1,36 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@VintfStability
+interface IOutputStream {
+ void standby();
+ void close();
+ void pause();
+ void resume();
+ void drain(device.google.atv.audio_proxy.AudioDrain drain);
+ void flush();
+ void prepareForWriting(in int frameSize, in int framesCount, out android.hardware.common.fmq.MQDescriptor<byte,android.hardware.common.fmq.SynchronizedReadWrite> dataMQ, out android.hardware.common.fmq.MQDescriptor<device.google.atv.audio_proxy.WriteStatus,android.hardware.common.fmq.SynchronizedReadWrite> statusMQ);
+ void setVolume(float left, float right);
+ long getBufferSizeBytes();
+ int getLatencyMs();
+ void start();
+ void stop();
+ device.google.atv.audio_proxy.MmapBufferInfo createMmapBuffer(int minSizeFrames);
+ device.google.atv.audio_proxy.PresentationPosition getMmapPosition();
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/IStreamProvider.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/IStreamProvider.aidl
new file mode 100644
index 0000000..4d05698
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/IStreamProvider.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@VintfStability
+interface IStreamProvider {
+ device.google.atv.audio_proxy.IOutputStream openOutputStream(in String address, in device.google.atv.audio_proxy.AudioConfig config, in int flags);
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/MessageQueueFlag.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/MessageQueueFlag.aidl
new file mode 100644
index 0000000..736bfc3
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/MessageQueueFlag.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@Backing(type="int") @VintfStability
+enum MessageQueueFlag {
+ NOT_EMPTY = 1,
+ NOT_FULL = 2,
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/MmapBufferInfo.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/MmapBufferInfo.aidl
new file mode 100644
index 0000000..c205c97
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/MmapBufferInfo.aidl
@@ -0,0 +1,26 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@VintfStability
+parcelable MmapBufferInfo {
+ ParcelFileDescriptor sharedMemoryFd;
+ int bufferSizeFrames;
+ int burstSizeFrames;
+ int flags;
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/PresentationPosition.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/PresentationPosition.aidl
new file mode 100644
index 0000000..4fa7cb9
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/PresentationPosition.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@FixedSize @VintfStability
+parcelable PresentationPosition {
+ long frames;
+ device.google.atv.audio_proxy.TimeSpec timestamp;
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/TimeSpec.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/TimeSpec.aidl
new file mode 100644
index 0000000..daed6ac
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/TimeSpec.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@FixedSize @VintfStability
+parcelable TimeSpec {
+ long tvSec;
+ long tvNSec;
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/WriteStatus.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/WriteStatus.aidl
new file mode 100644
index 0000000..169163a
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/WriteStatus.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@FixedSize @VintfStability
+parcelable WriteStatus {
+ long written;
+ device.google.atv.audio_proxy.PresentationPosition position;
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/current/device/google/atv/audio_proxy/AudioConfig.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/current/device/google/atv/audio_proxy/AudioConfig.aidl
index 2c32b79..b75f906 100644
--- a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/current/device/google/atv/audio_proxy/AudioConfig.aidl
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/current/device/google/atv/audio_proxy/AudioConfig.aidl
@@ -22,4 +22,6 @@ parcelable AudioConfig {
device.google.atv.audio_proxy.AudioFormat format;
int sampleRateHz;
device.google.atv.audio_proxy.AudioChannelMask channelMask;
+ long bufferSizeBytes;
+ int latencyMs;
}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/current/device/google/atv/audio_proxy/IOutputStream.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/current/device/google/atv/audio_proxy/IOutputStream.aidl
index 226ebd4..7b5f9ce 100644
--- a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/current/device/google/atv/audio_proxy/IOutputStream.aidl
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/current/device/google/atv/audio_proxy/IOutputStream.aidl
@@ -27,4 +27,10 @@ interface IOutputStream {
void flush();
void prepareForWriting(in int frameSize, in int framesCount, out android.hardware.common.fmq.MQDescriptor<byte,android.hardware.common.fmq.SynchronizedReadWrite> dataMQ, out android.hardware.common.fmq.MQDescriptor<device.google.atv.audio_proxy.WriteStatus,android.hardware.common.fmq.SynchronizedReadWrite> statusMQ);
void setVolume(float left, float right);
+ long getBufferSizeBytes();
+ int getLatencyMs();
+ void start();
+ void stop();
+ device.google.atv.audio_proxy.MmapBufferInfo createMmapBuffer(int minSizeFrames);
+ device.google.atv.audio_proxy.PresentationPosition getMmapPosition();
}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/current/device/google/atv/audio_proxy/MmapBufferInfo.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/current/device/google/atv/audio_proxy/MmapBufferInfo.aidl
new file mode 100644
index 0000000..c205c97
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/current/device/google/atv/audio_proxy/MmapBufferInfo.aidl
@@ -0,0 +1,26 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@VintfStability
+parcelable MmapBufferInfo {
+ ParcelFileDescriptor sharedMemoryFd;
+ int bufferSizeFrames;
+ int burstSizeFrames;
+ int flags;
+}
diff --git a/audio_proxy/interfaces/aidl/device/google/atv/audio_proxy/AudioConfig.aidl b/audio_proxy/interfaces/aidl/device/google/atv/audio_proxy/AudioConfig.aidl
index 4e7a85f..97b5e17 100644
--- a/audio_proxy/interfaces/aidl/device/google/atv/audio_proxy/AudioConfig.aidl
+++ b/audio_proxy/interfaces/aidl/device/google/atv/audio_proxy/AudioConfig.aidl
@@ -11,4 +11,10 @@ parcelable AudioConfig {
AudioFormat format;
int sampleRateHz;
AudioChannelMask channelMask;
-} \ No newline at end of file
+
+ // Expected buffer size and latency for the stream. If 0, the impl should
+ // provide their own value.
+ long bufferSizeBytes;
+ int latencyMs;
+}
+
diff --git a/audio_proxy/interfaces/aidl/device/google/atv/audio_proxy/IOutputStream.aidl b/audio_proxy/interfaces/aidl/device/google/atv/audio_proxy/IOutputStream.aidl
index cffc255..955d369 100644
--- a/audio_proxy/interfaces/aidl/device/google/atv/audio_proxy/IOutputStream.aidl
+++ b/audio_proxy/interfaces/aidl/device/google/atv/audio_proxy/IOutputStream.aidl
@@ -4,6 +4,8 @@ import android.hardware.common.fmq.MQDescriptor;
import android.hardware.common.fmq.SynchronizedReadWrite;
import device.google.atv.audio_proxy.AudioDrain;
+import device.google.atv.audio_proxy.MmapBufferInfo;
+import device.google.atv.audio_proxy.PresentationPosition;
import device.google.atv.audio_proxy.WriteStatus;
/**
@@ -42,4 +44,26 @@ interface IOutputStream {
* Volume control.
*/
void setVolume(float left, float right);
+
+ /**
+ * Get the buffer size and latency of the stream. They're called before starting the playback.
+ */
+ long getBufferSizeBytes();
+ int getLatencyMs();
+
+ /**
+ * Start/Stop playback for MMAP_NOIRQ stream.
+ */
+ void start();
+ void stop();
+
+ /**
+ * Create a share memory for MMAP_NOIRQ stream.
+ */
+ MmapBufferInfo createMmapBuffer(int minSizeFrames);
+
+ /**
+ * Query the presentation position for MMAP_NOIRQ stream.
+ */
+ PresentationPosition getMmapPosition();
}
diff --git a/audio_proxy/interfaces/aidl/device/google/atv/audio_proxy/MmapBufferInfo.aidl b/audio_proxy/interfaces/aidl/device/google/atv/audio_proxy/MmapBufferInfo.aidl
new file mode 100644
index 0000000..dbc7f70
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/device/google/atv/audio_proxy/MmapBufferInfo.aidl
@@ -0,0 +1,15 @@
+package device.google.atv.audio_proxy;
+
+import android.os.ParcelFileDescriptor;
+
+/**
+ * Shared memory and the associated info for the playback.
+ * This is the corresponding structure of audio HAL MmapBufferInfo.
+ */
+@VintfStability
+parcelable MmapBufferInfo {
+ ParcelFileDescriptor sharedMemoryFd;
+ int bufferSizeFrames;
+ int burstSizeFrames;
+ int flags;
+}
diff --git a/audio_proxy/public/audio_proxy.h b/audio_proxy/public/audio_proxy.h
index 097d33b..f6ea632 100644
--- a/audio_proxy/public/audio_proxy.h
+++ b/audio_proxy/public/audio_proxy.h
@@ -46,7 +46,10 @@ enum {
AUDIO_PROXY_FORMAT_INVALID = 0xFFFFFFFFu,
AUDIO_PROXY_FORMAT_PCM_16_BIT = 0x1u,
AUDIO_PROXY_FORMAT_PCM_8_BIT = 0x2u,
+ AUDIO_PROXY_FORMAT_PCM_32_BIT = 0x3u,
+ AUDIO_PROXY_FORMAT_PCM_8_24_BIT = 0x4u,
AUDIO_PROXY_FORMAT_PCM_FLOAT = 0x5u,
+ AUDIO_PROXY_FORMAT_PCM_24_BIT_PACKED = 0x6u,
};
typedef uint32_t audio_proxy_format_t;
@@ -55,6 +58,35 @@ enum {
AUDIO_PROXY_CHANNEL_INVALID = 0xC0000000u,
AUDIO_PROXY_CHANNEL_OUT_MONO = 0x1u,
AUDIO_PROXY_CHANNEL_OUT_STEREO = 0x3u,
+ AUDIO_PROXY_CHANNEL_OUT_2POINT1 = 0xBu,
+ AUDIO_PROXY_CHANNEL_OUT_TRI = 0x7u,
+ AUDIO_PROXY_CHANNEL_OUT_TRI_BACK = 0x103u,
+ AUDIO_PROXY_CHANNEL_OUT_3POINT1 = 0xFu,
+ AUDIO_PROXY_CHANNEL_OUT_2POINT0POINT2 = 0xC0003u,
+ AUDIO_PROXY_CHANNEL_OUT_2POINT1POINT2 = 0xC000Bu,
+ AUDIO_PROXY_CHANNEL_OUT_3POINT0POINT2 = 0xC0007u,
+ AUDIO_PROXY_CHANNEL_OUT_3POINT1POINT2 = 0xC000Fu,
+ AUDIO_PROXY_CHANNEL_OUT_QUAD = 0x33u,
+ // AUDIO_PROXY_CHANNEL_OUT_QUAD_BACK = 0x33u,
+ AUDIO_PROXY_CHANNEL_OUT_QUAD_SIDE = 0x603u,
+ AUDIO_PROXY_CHANNEL_OUT_SURROUND = 0x107u,
+ AUDIO_PROXY_CHANNEL_OUT_PENTA = 0x37u,
+ AUDIO_PROXY_CHANNEL_OUT_5POINT1 = 0x3Fu,
+ // AUDIO_PROXY_CHANNEL_OUT_5POINT1_BACK = 0x3Fu,
+ AUDIO_PROXY_CHANNEL_OUT_5POINT1_SIDE = 0x60Fu,
+ AUDIO_PROXY_CHANNEL_OUT_5POINT1POINT2 = 0xC003Fu,
+ AUDIO_PROXY_CHANNEL_OUT_5POINT1POINT4 = 0x2D03Fu,
+ AUDIO_PROXY_CHANNEL_OUT_6POINT1 = 0x13Fu,
+ AUDIO_PROXY_CHANNEL_OUT_7POINT1 = 0x63Fu,
+ AUDIO_PROXY_CHANNEL_OUT_7POINT1POINT2 = 0xC063Fu,
+ AUDIO_PROXY_CHANNEL_OUT_7POINT1POINT4 = 0x2D63Fu,
+ AUDIO_PROXY_CHANNEL_OUT_13POINT_360RA = 0x72F607u,
+ AUDIO_PROXY_CHANNEL_OUT_22POINT2 = 0xFFFFFFu,
+ AUDIO_PROXY_CHANNEL_OUT_MONO_HAPTIC_A = 0x20000001u,
+ AUDIO_PROXY_CHANNEL_OUT_STEREO_HAPTIC_A = 0x20000003u,
+ AUDIO_PROXY_CHANNEL_OUT_HAPTIC_AB = 0x30000000u,
+ AUDIO_PROXY_CHANNEL_OUT_MONO_HAPTIC_AB = 0x30000001u,
+ AUDIO_PROXY_CHANNEL_OUT_STEREO_HAPTIC_AB = 0x30000003u,
};
typedef uint32_t audio_proxy_channel_mask_t;
@@ -75,13 +107,21 @@ typedef int32_t audio_proxy_output_flags_t;
// AudioConfig
typedef struct {
+ int64_t buffer_size_bytes;
+ int32_t latency_ms;
+
+ // Points to extra fields defined in the future versions.
+ void* extension;
+} audio_proxy_config_v2_t;
+
+typedef struct {
uint32_t sample_rate;
audio_proxy_channel_mask_t channel_mask;
audio_proxy_format_t format;
uint32_t frame_count;
- // Points to extra fields defined in the future versions.
- void* extension;
+ // Points to extra fields.
+ audio_proxy_config_v2_t* v2;
} audio_proxy_config_t;
// Util structure for key value pair.
@@ -93,10 +133,32 @@ typedef struct {
typedef void (*audio_proxy_get_parameters_callback_t)(
void*, const audio_proxy_key_val_t*);
-// The following struct/functions mirror those definitions in audio HAL. They
-// should have the same requirement as audio HAL interfaces, unless specified.
+enum {
+ AUDIO_PROXY_MMAP_BUFFER_FLAG_NONE = 0x0,
+ AUDIO_PROXY_MMAP_BUFFER_FLAG_APPLICATION_SHAREABLE = 0x1,
+};
+typedef int32_t audio_proxy_mmap_buffer_flag_t;
+
+typedef struct {
+ int shared_memory_fd;
+ int32_t buffer_size_frames;
+ int32_t burst_size_frames;
+ audio_proxy_mmap_buffer_flag_t flags;
+} audio_proxy_mmap_buffer_info_t;
// IStreamOut.
+struct audio_proxy_stream_out_v2 {
+ void (*start)(struct audio_proxy_stream_out_v2* stream);
+ void (*stop)(struct audio_proxy_stream_out_v2* stream);
+ audio_proxy_mmap_buffer_info_t (*create_mmap_buffer)(
+ struct audio_proxy_stream_out_v2* stream, int32_t min_buffer_size_frames);
+ void (*get_mmap_position)(struct audio_proxy_stream_out_v2* stream,
+ int64_t* frames, struct timespec* timestamp);
+ // Pointer to the next version structure, for compatibility.
+ void* extension;
+};
+typedef struct audio_proxy_stream_out_v2 audio_proxy_stream_out_v2_t;
+
struct audio_proxy_stream_out {
size_t (*get_buffer_size)(const struct audio_proxy_stream_out* stream);
uint64_t (*get_frame_count)(const struct audio_proxy_stream_out* stream);
@@ -191,8 +253,8 @@ struct audio_proxy_stream_out {
// optional.
int (*dump)(const struct audio_proxy_stream_out* stream, int fd);
- // Pointer to the next version structure, for compatibility.
- void* extension;
+ // Pointer to the next version structure.
+ audio_proxy_stream_out_v2_t* v2;
};
typedef struct audio_proxy_stream_out audio_proxy_stream_out_t;
diff --git a/audio_proxy/sepolicy/OWNERS b/audio_proxy/sepolicy/OWNERS
new file mode 100644
index 0000000..508598e
--- /dev/null
+++ b/audio_proxy/sepolicy/OWNERS
@@ -0,0 +1,2 @@
+include platform/system/sepolicy:/OWNERS
+
diff --git a/audio_proxy/sepolicy/public/hal_audio_proxy.te b/audio_proxy/sepolicy/public/hal_audio_proxy.te
index 2ebdbfe..8b643b9 100644
--- a/audio_proxy/sepolicy/public/hal_audio_proxy.te
+++ b/audio_proxy/sepolicy/public/hal_audio_proxy.te
@@ -4,4 +4,3 @@ hal_attribute(audio_proxy);
binder_call(hal_audio_proxy_client, hal_audio_proxy_server);
binder_call(hal_audio_proxy_server, hal_audio_proxy_client);
binder_call(hal_audio_proxy_server, servicemanager);
-
diff --git a/audio_proxy/sepolicy/vendor/dumpstate.te b/audio_proxy/sepolicy/vendor/dumpstate.te
new file mode 100644
index 0000000..4fc2358
--- /dev/null
+++ b/audio_proxy/sepolicy/vendor/dumpstate.te
@@ -0,0 +1 @@
+binder_call(dumpstate, hal_audio_proxy_default);
diff --git a/audio_proxy/sepolicy/vendor/hal_audio_proxy_default.te b/audio_proxy/sepolicy/vendor/hal_audio_proxy_default.te
index 5534200..e53bfc5 100644
--- a/audio_proxy/sepolicy/vendor/hal_audio_proxy_default.te
+++ b/audio_proxy/sepolicy/vendor/hal_audio_proxy_default.te
@@ -8,16 +8,5 @@ init_daemon_domain(hal_audio_proxy_default);
hal_server_domain(hal_audio_proxy_default, hal_audio);
hal_server_domain(hal_audio_proxy_default, hal_audio_proxy);
-# allows audio proxy service access audio HAL interfaces.
-hal_client_domain(hal_audio_proxy_default, hal_audio);
-
# allow audioserver to call hal_audio dump with its own fd to retrieve status
allow hal_audio_proxy_default audioserver:fifo_file write;
-
-allow dumpstate hal_audio_proxy_default:binder call;
-
-type hal_audio_proxy_service, vendor_service, protected_service, service_manager_type;
-hal_attribute_service(hal_audio_proxy, hal_audio_proxy_service);
-
-add_service(hal_audio_proxy_default, hal_audio_proxy_service);
-
diff --git a/audio_proxy/sepolicy/vendor/service.te b/audio_proxy/sepolicy/vendor/service.te
new file mode 100644
index 0000000..f96c68d
--- /dev/null
+++ b/audio_proxy/sepolicy/vendor/service.te
@@ -0,0 +1,2 @@
+type hal_audio_proxy_service, hal_service_type, protected_service, service_manager_type;
+hal_attribute_service(hal_audio_proxy, hal_audio_proxy_service);
diff --git a/audio_proxy/service/AidlTypes.h b/audio_proxy/service/AidlTypes.h
index 5e3fc9b..022098b 100644
--- a/audio_proxy/service/AidlTypes.h
+++ b/audio_proxy/service/AidlTypes.h
@@ -18,6 +18,8 @@
#include <aidl/device/google/atv/audio_proxy/AudioConfig.h>
#include <aidl/device/google/atv/audio_proxy/AudioDrain.h>
#include <aidl/device/google/atv/audio_proxy/AudioFormat.h>
+#include <aidl/device/google/atv/audio_proxy/MmapBufferInfo.h>
+#include <aidl/device/google/atv/audio_proxy/PresentationPosition.h>
#include <aidl/device/google/atv/audio_proxy/WriteStatus.h>
namespace audio_proxy::service {
@@ -28,6 +30,10 @@ using AidlAudioChannelMask =
using AidlAudioConfig = aidl::device::google::atv::audio_proxy::AudioConfig;
using AidlAudioDrain = aidl::device::google::atv::audio_proxy::AudioDrain;
using AidlAudioFormat = aidl::device::google::atv::audio_proxy::AudioFormat;
+using AidlMmapBufferInfo =
+ aidl::device::google::atv::audio_proxy::MmapBufferInfo;
+using AidlPresentationPosition =
+ aidl::device::google::atv::audio_proxy::PresentationPosition;
using AidlWriteStatus = aidl::device::google::atv::audio_proxy::WriteStatus;
} // namespace audio_proxy::service \ No newline at end of file
diff --git a/audio_proxy/service/Android.bp b/audio_proxy/service/Android.bp
index 711add8..6dd2cc1 100644
--- a/audio_proxy/service/Android.bp
+++ b/audio_proxy/service/Android.bp
@@ -41,6 +41,7 @@ cc_defaults {
srcs: [
"AudioProxyImpl.cpp",
+ "AudioUtil.cpp",
"BusOutputStream.cpp",
"BusStreamProvider.cpp",
"DeviceImpl.cpp",
@@ -53,7 +54,7 @@ cc_defaults {
],
shared_libs: [
- "device.google.atv.audio_proxy-aidl-V1-ndk",
+ "device.google.atv.audio_proxy-aidl-V3-ndk",
"libbase",
"libbinder_ndk",
"libhidlbase",
diff --git a/audio_proxy/service/AudioUtil.cpp b/audio_proxy/service/AudioUtil.cpp
new file mode 100644
index 0000000..b51c63d
--- /dev/null
+++ b/audio_proxy/service/AudioUtil.cpp
@@ -0,0 +1,38 @@
+// Copyright (C) 2022 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.
+
+#include "AudioUtil.h"
+
+#include <system/audio.h>
+
+namespace audio_proxy::service {
+int computeFrameSize(const AidlAudioConfig& config) {
+ audio_format_t format = static_cast<audio_format_t>(config.format);
+
+ if (!audio_has_proportional_frames(format)) {
+ return sizeof(int8_t);
+ }
+
+ size_t channelSampleSize = audio_bytes_per_sample(format);
+ return audio_channel_count_from_out_mask(
+ static_cast<audio_channel_mask_t>(config.channelMask)) *
+ channelSampleSize;
+}
+
+int64_t computeBufferSizeBytes(const AidlAudioConfig& config,
+ int32_t bufferSizeMs) {
+ return static_cast<int64_t>(bufferSizeMs) * config.sampleRateHz *
+ computeFrameSize(config) / 1000;
+}
+} // namespace audio_proxy::service \ No newline at end of file
diff --git a/audio_proxy/service/AudioUtil.h b/audio_proxy/service/AudioUtil.h
new file mode 100644
index 0000000..5430472
--- /dev/null
+++ b/audio_proxy/service/AudioUtil.h
@@ -0,0 +1,25 @@
+// Copyright (C) 2022 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.
+
+#pragma once
+
+#include <cstdint>
+
+#include "AidlTypes.h"
+
+namespace audio_proxy::service {
+int computeFrameSize(const AidlAudioConfig& config);
+int64_t computeBufferSizeBytes(const AidlAudioConfig& config,
+ int32_t bufferSizeMs);
+} // namespace audio_proxy::service \ No newline at end of file
diff --git a/audio_proxy/service/BusOutputStream.cpp b/audio_proxy/service/BusOutputStream.cpp
index 40ac8d4..e456faf 100644
--- a/audio_proxy/service/BusOutputStream.cpp
+++ b/audio_proxy/service/BusOutputStream.cpp
@@ -15,7 +15,8 @@
#include "BusOutputStream.h"
#include <android-base/logging.h>
-#include <system/audio.h>
+
+#include "AudioUtil.h"
namespace audio_proxy::service {
@@ -28,18 +29,7 @@ const std::string& BusOutputStream::getAddress() const { return mAddress; }
const AidlAudioConfig& BusOutputStream::getConfig() const { return mConfig; }
int32_t BusOutputStream::getFlags() const { return mFlags; }
-int BusOutputStream::getFrameSize() const {
- audio_format_t format = static_cast<audio_format_t>(mConfig.format);
-
- if (!audio_has_proportional_frames(format)) {
- return sizeof(int8_t);
- }
-
- size_t channelSampleSize = audio_bytes_per_sample(format);
- return audio_channel_count_from_out_mask(
- static_cast<audio_channel_mask_t>(mConfig.channelMask)) *
- channelSampleSize;
-}
+int BusOutputStream::getFrameSize() const { return computeFrameSize(mConfig); }
bool BusOutputStream::prepareForWriting(uint32_t frameSize,
uint32_t frameCount) {
diff --git a/audio_proxy/service/BusOutputStream.h b/audio_proxy/service/BusOutputStream.h
index f9fb930..2116bca 100644
--- a/audio_proxy/service/BusOutputStream.h
+++ b/audio_proxy/service/BusOutputStream.h
@@ -50,6 +50,11 @@ class BusOutputStream {
const uint8_t* secondMem,
size_t secondLength) = 0;
+ virtual bool start() = 0;
+ virtual bool stop() = 0;
+ virtual AidlMmapBufferInfo createMmapBuffer(int32_t minBufferSizeFrames) = 0;
+ virtual AidlPresentationPosition getMmapPosition() = 0;
+
protected:
virtual bool prepareForWritingImpl(uint32_t frameSize,
uint32_t frameCount) = 0;
diff --git a/audio_proxy/service/BusStreamProvider.cpp b/audio_proxy/service/BusStreamProvider.cpp
index 42e6dda..af2c5b8 100644
--- a/audio_proxy/service/BusStreamProvider.cpp
+++ b/audio_proxy/service/BusStreamProvider.cpp
@@ -34,9 +34,11 @@ void BusStreamProvider::setStreamProvider(
for (auto& weakStream : mStreamOutList) {
if (sp<StreamOutImpl> stream = weakStream.promote()) {
auto oldOutputStream = stream->getOutputStream();
- auto outputStream = openOutputStream_Locked(oldOutputStream->getAddress(),
- oldOutputStream->getConfig(),
- oldOutputStream->getFlags());
+ auto outputStream = openOutputStream_Locked(
+ oldOutputStream->getAddress(), oldOutputStream->getConfig(),
+ oldOutputStream->getFlags(),
+ oldOutputStream->getConfig().bufferSizeBytes,
+ oldOutputStream->getConfig().latencyMs);
stream->updateOutputStream(std::move(outputStream));
}
}
@@ -48,9 +50,11 @@ std::shared_ptr<IStreamProvider> BusStreamProvider::getStreamProvider() {
}
std::shared_ptr<BusOutputStream> BusStreamProvider::openOutputStream(
- const std::string& address, const AidlAudioConfig& config, int32_t flags) {
+ const std::string& address, const AidlAudioConfig& config, int32_t flags,
+ int64_t bufferSizeBytes, int32_t latencyMs) {
std::lock_guard<std::mutex> lock(mLock);
- return openOutputStream_Locked(address, config, flags);
+ return openOutputStream_Locked(address, config, flags, bufferSizeBytes,
+ latencyMs);
}
void BusStreamProvider::onStreamOutCreated(wp<StreamOutImpl> stream) {
@@ -60,9 +64,14 @@ void BusStreamProvider::onStreamOutCreated(wp<StreamOutImpl> stream) {
}
std::shared_ptr<BusOutputStream> BusStreamProvider::openOutputStream_Locked(
- const std::string& address, const AidlAudioConfig& config, int32_t flags) {
+ const std::string& address, const AidlAudioConfig& config, int32_t flags,
+ int64_t bufferSizeBytes, int32_t latencyMs) {
+ AidlAudioConfig newConfig = config;
+ newConfig.bufferSizeBytes = bufferSizeBytes;
+ newConfig.latencyMs = latencyMs;
+
if (!mStreamProvider) {
- return std::make_shared<DummyBusOutputStream>(address, config, flags);
+ return std::make_shared<DummyBusOutputStream>(address, newConfig, flags);
}
std::shared_ptr<IOutputStream> stream;
@@ -70,11 +79,22 @@ std::shared_ptr<BusOutputStream> BusStreamProvider::openOutputStream_Locked(
mStreamProvider->openOutputStream(address, config, flags, &stream);
if (!status.isOk() || !stream) {
LOG(ERROR) << "Failed to open output stream, status " << status.getStatus();
- return std::make_shared<DummyBusOutputStream>(address, config, flags);
+ return std::make_shared<DummyBusOutputStream>(address, newConfig, flags);
+ }
+
+ int64_t aidlBufferSizeInBytes = -1;
+ if (stream->getBufferSizeBytes(&aidlBufferSizeInBytes).isOk() &&
+ aidlBufferSizeInBytes > 0) {
+ newConfig.bufferSizeBytes = aidlBufferSizeInBytes;
+ }
+
+ int32_t aidlLatencyMs = -1;
+ if (stream->getLatencyMs(&aidlLatencyMs).isOk() && aidlLatencyMs > 0) {
+ newConfig.latencyMs = aidlLatencyMs;
}
return std::make_shared<RemoteBusOutputStream>(std::move(stream), address,
- config, flags);
+ newConfig, flags);
}
size_t BusStreamProvider::cleanAndCountStreamOuts() {
diff --git a/audio_proxy/service/BusStreamProvider.h b/audio_proxy/service/BusStreamProvider.h
index abcca8b..8d3ebbb 100644
--- a/audio_proxy/service/BusStreamProvider.h
+++ b/audio_proxy/service/BusStreamProvider.h
@@ -52,15 +52,16 @@ class BusStreamProvider {
// 2. Returns DummyBusOutputStream otherwise.
// This function always return a non null BusOutputStream.
std::shared_ptr<BusOutputStream> openOutputStream(
- const std::string& address, const AidlAudioConfig& config, int32_t flags);
+ const std::string& address, const AidlAudioConfig& config, int32_t flags,
+ int64_t bufferSizeBytes, int32_t latencyMs);
// Clear closed StreamOut and return number of opened StreamOut.
size_t cleanAndCountStreamOuts();
private:
std::shared_ptr<BusOutputStream> openOutputStream_Locked(
- const std::string& address, const AidlAudioConfig& config, int32_t flags)
- REQUIRES(mLock);
+ const std::string& address, const AidlAudioConfig& config, int32_t flags,
+ int64_t bufferSizeBytes, int32_t latencyMs) REQUIRES(mLock);
// Remove the dead dead from mStreamOutList.
void cleanStreamOutList_Locked() REQUIRES(mLock);
diff --git a/audio_proxy/service/DeviceImpl.cpp b/audio_proxy/service/DeviceImpl.cpp
index 15637d5..1cbae5a 100644
--- a/audio_proxy/service/DeviceImpl.cpp
+++ b/audio_proxy/service/DeviceImpl.cpp
@@ -22,6 +22,7 @@
#include <optional>
#include "AidlTypes.h"
+#include "AudioUtil.h"
#include "BusOutputStream.h"
#include "BusStreamProvider.h"
#include "ServiceConfig.h"
@@ -54,7 +55,9 @@ std::optional<AidlAudioConfig> toAidlAudioConfig(
AidlAudioConfig aidlConfig = {
.format = static_cast<AidlAudioFormat>(format),
.sampleRateHz = static_cast<int32_t>(hidl_config.sampleRateHz),
- .channelMask = static_cast<AidlAudioChannelMask>(channelMask)};
+ .channelMask = static_cast<AidlAudioChannelMask>(channelMask),
+ .bufferSizeBytes = 0,
+ .latencyMs = 0};
return aidlConfig;
}
@@ -183,8 +186,9 @@ AidlAudioConfig toAidlAudioConfig(const AudioConfig& hidl_config) {
AidlAudioConfig aidlConfig = {
.format = static_cast<AidlAudioFormat>(hidl_config.format),
.sampleRateHz = static_cast<int32_t>(hidl_config.sampleRateHz),
- .channelMask =
- static_cast<AidlAudioChannelMask>(hidl_config.channelMask)};
+ .channelMask = static_cast<AidlAudioChannelMask>(hidl_config.channelMask),
+ .bufferSizeBytes = 0,
+ .latencyMs = 0};
return aidlConfig;
}
@@ -278,11 +282,13 @@ Return<void> DeviceImpl::openOutputStreamImpl(
}
std::shared_ptr<BusOutputStream> busOutputStream =
- mBusStreamProvider.openOutputStream(address, *aidlConfig, *outputFlags);
+ mBusStreamProvider.openOutputStream(
+ address, *aidlConfig, *outputFlags,
+ computeBufferSizeBytes(*aidlConfig, configIt->second.bufferSizeMs),
+ configIt->second.latencyMs);
DCHECK(busOutputStream);
- auto streamOut = sp<StreamOutImpl>::make(
- std::move(busOutputStream), config.base, configIt->second.bufferSizeMs,
- configIt->second.latencyMs);
+ auto streamOut =
+ sp<StreamOutImpl>::make(std::move(busOutputStream), config.base);
mBusStreamProvider.onStreamOutCreated(streamOut);
_hidl_cb(Result::OK, streamOut, config);
return Void();
@@ -330,14 +336,14 @@ Return<void> DeviceImpl::openOutputStream(int32_t ioHandle,
return Void();
}
+ auto aidlConfig = toAidlAudioConfig(config);
std::shared_ptr<BusOutputStream> busOutputStream =
- mBusStreamProvider.openOutputStream(address,
- toAidlAudioConfig(config),
- static_cast<int32_t>(flags));
+ mBusStreamProvider.openOutputStream(
+ address, aidlConfig, static_cast<int32_t>(flags),
+ computeBufferSizeBytes(aidlConfig, configIt->second.bufferSizeMs),
+ configIt->second.latencyMs);
DCHECK(busOutputStream);
- auto streamOut = sp<StreamOutImpl>::make(std::move(busOutputStream), config,
- configIt->second.bufferSizeMs,
- configIt->second.latencyMs);
+ auto streamOut = sp<StreamOutImpl>::make(std::move(busOutputStream), config);
mBusStreamProvider.onStreamOutCreated(streamOut);
_hidl_cb(Result::OK, streamOut, config);
return Void();
diff --git a/audio_proxy/service/DummyBusOutputStream.cpp b/audio_proxy/service/DummyBusOutputStream.cpp
index b18728f..3b9b56e 100644
--- a/audio_proxy/service/DummyBusOutputStream.cpp
+++ b/audio_proxy/service/DummyBusOutputStream.cpp
@@ -115,4 +115,17 @@ bool DummyBusOutputStream::prepareForWritingImpl(uint32_t frameSize,
return true;
}
+bool DummyBusOutputStream::start() { return false; }
+
+bool DummyBusOutputStream::stop() { return false; };
+
+AidlMmapBufferInfo DummyBusOutputStream::createMmapBuffer(
+ int32_t minBufferSizeFrames) {
+ return AidlMmapBufferInfo();
+}
+
+AidlPresentationPosition DummyBusOutputStream::getMmapPosition() {
+ return AidlPresentationPosition();
+}
+
} // namespace audio_proxy::service
diff --git a/audio_proxy/service/DummyBusOutputStream.h b/audio_proxy/service/DummyBusOutputStream.h
index c5ca47e..0cb8e44 100644
--- a/audio_proxy/service/DummyBusOutputStream.h
+++ b/audio_proxy/service/DummyBusOutputStream.h
@@ -41,6 +41,11 @@ class DummyBusOutputStream : public BusOutputStream {
const uint8_t* secondMem,
size_t secondLength) override;
+ bool start() override;
+ bool stop() override;
+ AidlMmapBufferInfo createMmapBuffer(int32_t minBufferSizeFrames) override;
+ AidlPresentationPosition getMmapPosition() override;
+
protected:
bool prepareForWritingImpl(uint32_t frameSize, uint32_t frameCount) override;
diff --git a/audio_proxy/service/RemoteBusOutputStream.cpp b/audio_proxy/service/RemoteBusOutputStream.cpp
index a5edc76..177cb55 100644
--- a/audio_proxy/service/RemoteBusOutputStream.cpp
+++ b/audio_proxy/service/RemoteBusOutputStream.cpp
@@ -158,5 +158,22 @@ bool RemoteBusOutputStream::prepareForWritingImpl(uint32_t frameSize,
return true;
}
+bool RemoteBusOutputStream::start() { return mStream->start().isOk(); }
+
+bool RemoteBusOutputStream::stop() { return mStream->stop().isOk(); };
+
+AidlMmapBufferInfo RemoteBusOutputStream::createMmapBuffer(
+ int32_t minBufferSizeFrames) {
+ AidlMmapBufferInfo info;
+ mStream->createMmapBuffer(minBufferSizeFrames, &info);
+ return info;
+}
+
+AidlPresentationPosition RemoteBusOutputStream::getMmapPosition() {
+ AidlPresentationPosition position;
+ mStream->getMmapPosition(&position);
+ return position;
+}
+
} // namespace service
} // namespace audio_proxy \ No newline at end of file
diff --git a/audio_proxy/service/RemoteBusOutputStream.h b/audio_proxy/service/RemoteBusOutputStream.h
index 6f663cb..cb7de19 100644
--- a/audio_proxy/service/RemoteBusOutputStream.h
+++ b/audio_proxy/service/RemoteBusOutputStream.h
@@ -49,6 +49,11 @@ class RemoteBusOutputStream : public BusOutputStream {
const uint8_t* secondMem,
size_t secondLength) override;
+ bool start() override;
+ bool stop() override;
+ AidlMmapBufferInfo createMmapBuffer(int32_t minBufferSizeFrames) override;
+ AidlPresentationPosition getMmapPosition() override;
+
protected:
bool prepareForWritingImpl(uint32_t frameSize, uint32_t frameCount) override;
diff --git a/audio_proxy/service/StreamOutImpl.cpp b/audio_proxy/service/StreamOutImpl.cpp
index baf086f..5e4a772 100644
--- a/audio_proxy/service/StreamOutImpl.cpp
+++ b/audio_proxy/service/StreamOutImpl.cpp
@@ -28,6 +28,7 @@
#include "WriteThread.h"
using android::status_t;
+using android::hardware::hidl_memory;
namespace audio_proxy::service {
@@ -73,12 +74,11 @@ uint64_t estimatePlayedFramesSince(const TimeSpec& timestamp,
} // namespace
StreamOutImpl::StreamOutImpl(std::shared_ptr<BusOutputStream> stream,
- const StreamOutConfig& config,
- uint32_t bufferSizeMs, uint32_t latencyMs)
+ const StreamOutConfig& config)
: mStream(std::move(stream)),
mConfig(config),
- mBufferSizeMs(bufferSizeMs),
- mLatencyMs(latencyMs),
+ mBufferSizeBytes(mStream->getConfig().bufferSizeBytes),
+ mLatencyMs(mStream->getConfig().latencyMs),
mEventFlag(nullptr, deleteEventFlag) {}
StreamOutImpl::~StreamOutImpl() {
@@ -98,12 +98,10 @@ Return<uint64_t> StreamOutImpl::getFrameSize() {
}
Return<uint64_t> StreamOutImpl::getFrameCount() {
- return mBufferSizeMs * mConfig.sampleRateHz / 1000;
+ return mBufferSizeBytes / mStream->getFrameSize();
}
-Return<uint64_t> StreamOutImpl::getBufferSize() {
- return mBufferSizeMs * mConfig.sampleRateHz * mStream->getFrameSize() / 1000;
-}
+Return<uint64_t> StreamOutImpl::getBufferSize() { return mBufferSizeBytes; }
#if MAJOR_VERSION >= 7
Return<void> StreamOutImpl::getSupportedProfiles(
@@ -441,18 +439,51 @@ Return<void> StreamOutImpl::getPresentationPosition(
return Void();
}
-Return<Result> StreamOutImpl::start() { return Result::NOT_SUPPORTED; }
+Return<Result> StreamOutImpl::start() {
+ return mStream->start() ? Result::OK : Result::NOT_SUPPORTED;
+}
-Return<Result> StreamOutImpl::stop() { return Result::NOT_SUPPORTED; }
+Return<Result> StreamOutImpl::stop() {
+ return mStream->stop() ? Result::OK : Result::NOT_SUPPORTED;
+}
Return<void> StreamOutImpl::createMmapBuffer(int32_t minSizeFrames,
createMmapBuffer_cb _hidl_cb) {
- _hidl_cb(Result::NOT_SUPPORTED, MmapBufferInfo());
+ MmapBufferInfo hidlInfo;
+ AidlMmapBufferInfo info = mStream->createMmapBuffer(minSizeFrames);
+ int sharedMemoryFd = info.sharedMemoryFd.get();
+ if (sharedMemoryFd == -1) {
+ _hidl_cb(Result::NOT_SUPPORTED, hidlInfo);
+ return Void();
+ }
+
+ native_handle_t* hidlHandle = nullptr;
+ hidlHandle = native_handle_create(1, 0);
+ hidlHandle->data[0] = sharedMemoryFd;
+
+ hidlInfo.sharedMemory =
+ hidl_memory("audio_proxy_mmap_buffer", hidlHandle,
+ mStream->getFrameSize() * info.bufferSizeFrames);
+ hidlInfo.bufferSizeFrames = info.bufferSizeFrames;
+ hidlInfo.burstSizeFrames = info.burstSizeFrames;
+ hidlInfo.flags = static_cast<hidl_bitfield<MmapBufferFlag>>(info.flags);
+ _hidl_cb(Result::OK, hidlInfo);
return Void();
}
Return<void> StreamOutImpl::getMmapPosition(getMmapPosition_cb _hidl_cb) {
- _hidl_cb(Result::NOT_SUPPORTED, MmapPosition());
+ MmapPosition hidlPosition;
+
+ AidlPresentationPosition position = mStream->getMmapPosition();
+ if (position.timestamp.tvSec == 0 && position.timestamp.tvNSec == 0) {
+ _hidl_cb(Result::NOT_SUPPORTED, hidlPosition);
+ return Void();
+ }
+
+ hidlPosition.timeNanoseconds =
+ position.timestamp.tvSec * kOneSecInNs + position.timestamp.tvNSec;
+ hidlPosition.positionFrames = position.frames;
+ _hidl_cb(Result::OK, hidlPosition);
return Void();
}
@@ -505,6 +536,10 @@ uint64_t StreamOutImpl::estimateTotalPlayedFrames() const {
}
auto [frames, timestamp] = mWriteThread->getPresentationPosition();
+ if (frames == 0) {
+ return 0;
+ }
+
return frames + estimatePlayedFramesSince(timestamp, mConfig.sampleRateHz);
}
diff --git a/audio_proxy/service/StreamOutImpl.h b/audio_proxy/service/StreamOutImpl.h
index 80884d9..c1b557d 100644
--- a/audio_proxy/service/StreamOutImpl.h
+++ b/audio_proxy/service/StreamOutImpl.h
@@ -61,8 +61,7 @@ class StreamOutImpl : public IStreamOut {
#endif
StreamOutImpl(std::shared_ptr<BusOutputStream> stream,
- const StreamOutConfig& config, uint32_t bufferSizeMs,
- uint32_t latencyMs);
+ const StreamOutConfig& config);
~StreamOutImpl() override;
std::shared_ptr<BusOutputStream> getOutputStream();
@@ -171,7 +170,7 @@ class StreamOutImpl : public IStreamOut {
std::shared_ptr<BusOutputStream> mStream;
const StreamOutConfig mConfig;
- const uint32_t mBufferSizeMs;
+ const uint64_t mBufferSizeBytes;
const uint32_t mLatencyMs;
std::unique_ptr<CommandMQ> mCommandMQ;
diff --git a/audio_proxy/service/audio_proxy_policy_configuration.xml b/audio_proxy/service/audio_proxy_policy_configuration.xml
index b44ce98..6fddee2 100644
--- a/audio_proxy/service/audio_proxy_policy_configuration.xml
+++ b/audio_proxy/service/audio_proxy_policy_configuration.xml
@@ -10,22 +10,22 @@
flags="AUDIO_OUTPUT_FLAG_DIRECT" maxOpenCount="0">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="16000 22050 44100 48000 96000"
- channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+ channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
<profile name="" format="AUDIO_FORMAT_PCM_8_BIT"
samplingRates="16000 22050 44100 48000 96000"
- channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+ channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
<profile name="" format="AUDIO_FORMAT_PCM_32_BIT"
samplingRates="16000 22050 44100 48000 96000"
- channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+ channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
<profile name="" format="AUDIO_FORMAT_PCM_8_24_BIT"
samplingRates="16000 22050 44100 48000 96000"
- channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+ channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
<profile name="" format="AUDIO_FORMAT_PCM_FLOAT"
samplingRates="16000 22050 44100 48000 96000"
- channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+ channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
<profile name="" format="AUDIO_FORMAT_PCM_24_BIT_PACKED"
samplingRates="16000 22050 44100 48000 96000"
- channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+ channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
</mixPort>
<mixPort name="mixer_mix_port" role="source">
diff --git a/audio_proxy/service/audio_proxy_policy_configuration_hw_av_sync.xml b/audio_proxy/service/audio_proxy_policy_configuration_hw_av_sync.xml
index e73a6e7..b86a897 100644
--- a/audio_proxy/service/audio_proxy_policy_configuration_hw_av_sync.xml
+++ b/audio_proxy/service/audio_proxy_policy_configuration_hw_av_sync.xml
@@ -9,45 +9,52 @@
<mixPort name="direct_mix_port" role="source"
flags="AUDIO_OUTPUT_FLAG_DIRECT" maxOpenCount="0">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
- samplingRates="16000 22050 44100 48000 96000"
- channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+ samplingRates="16000 22050 24000 32000 44100 48000 96000"
+ channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
<profile name="" format="AUDIO_FORMAT_PCM_8_BIT"
- samplingRates="16000 22050 44100 48000 96000"
- channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+ samplingRates="16000 22050 24000 32000 44100 48000 96000"
+ channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
<profile name="" format="AUDIO_FORMAT_PCM_32_BIT"
- samplingRates="16000 22050 44100 48000 96000"
- channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+ samplingRates="16000 22050 24000 32000 44100 48000 96000"
+ channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
<profile name="" format="AUDIO_FORMAT_PCM_8_24_BIT"
- samplingRates="16000 22050 44100 48000 96000"
- channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+ samplingRates="16000 22050 24000 32000 44100 48000 96000"
+ channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
<profile name="" format="AUDIO_FORMAT_PCM_FLOAT"
- samplingRates="16000 22050 44100 48000 96000"
- channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+ samplingRates="16000 22050 24000 32000 44100 48000 96000"
+ channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
<profile name="" format="AUDIO_FORMAT_PCM_24_BIT_PACKED"
- samplingRates="16000 22050 44100 48000 96000"
- channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+ samplingRates="16000 22050 24000 32000 44100 48000 96000"
+ channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
</mixPort>
<mixPort name="tunneling_mix_port" role="source"
flags="AUDIO_OUTPUT_FLAG_DIRECT AUDIO_OUTPUT_FLAG_HW_AV_SYNC" maxOpenCount="0">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
- samplingRates="16000 22050 44100 48000 96000"
- channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+ samplingRates="16000 22050 24000 32000 44100 48000 96000"
+ channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
<profile name="" format="AUDIO_FORMAT_PCM_8_BIT"
- samplingRates="16000 22050 44100 48000 96000"
- channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+ samplingRates="16000 22050 24000 32000 44100 48000 96000"
+ channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
<profile name="" format="AUDIO_FORMAT_PCM_32_BIT"
- samplingRates="16000 22050 44100 48000 96000"
- channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+ samplingRates="16000 22050 24000 32000 44100 48000 96000"
+ channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
<profile name="" format="AUDIO_FORMAT_PCM_8_24_BIT"
- samplingRates="16000 22050 44100 48000 96000"
- channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+ samplingRates="16000 22050 24000 32000 44100 48000 96000"
+ channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
<profile name="" format="AUDIO_FORMAT_PCM_FLOAT"
- samplingRates="16000 22050 44100 48000 96000"
- channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+ samplingRates="16000 22050 24000 32000 44100 48000 96000"
+ channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
<profile name="" format="AUDIO_FORMAT_PCM_24_BIT_PACKED"
- samplingRates="16000 22050 44100 48000 96000"
- channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+ samplingRates="16000 22050 24000 32000 44100 48000 96000"
+ channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
+ </mixPort>
+
+ <mixPort name="mmap_noirq_mix_port" role="source"
+ flags="AUDIO_OUTPUT_FLAG_DIRECT AUDIO_OUTPUT_FLAG_MMAP_NOIRQ" maxOpenCount="0">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="16000 22050 24000 32000 44100 48000 96000"
+ channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>
<mixPort name="mixer_mix_port" role="source">
@@ -71,7 +78,7 @@
<routes>
<route type="mix" sink="MediaShell Direct Audio Device"
- sources="direct_mix_port,tunneling_mix_port"/>
+ sources="direct_mix_port,tunneling_mix_port,mmap_noirq_mix_port"/>
<route type="mix" sink="MediaShell Mixer Audio Device"
sources="mixer_mix_port"/>
diff --git a/audio_proxy/service/device.google.atv.audio_proxy@5.1-service.rc b/audio_proxy/service/device.google.atv.audio_proxy@5.1-service.rc
index 5ce6140..3b1d713 100644
--- a/audio_proxy/service/device.google.atv.audio_proxy@5.1-service.rc
+++ b/audio_proxy/service/device.google.atv.audio_proxy@5.1-service.rc
@@ -1,6 +1,6 @@
service audio_proxy_service /vendor/bin/hw/device.google.atv.audio_proxy@5.1-service \
--name mediashell \
- --stream MEDIASHELL_AUDIO_DEVICE_ADDR:40:200 \
+ --stream MEDIASHELL_AUDIO_DEVICE_ADDR:40:600 \
--stream MEDIASHELL_MIXER_DEVICE_ADDR:40:40
class hal
user system
diff --git a/audio_proxy/service/device.google.atv.audio_proxy@6.0-service.rc b/audio_proxy/service/device.google.atv.audio_proxy@6.0-service.rc
index e0a9371..fa6ed77 100644
--- a/audio_proxy/service/device.google.atv.audio_proxy@6.0-service.rc
+++ b/audio_proxy/service/device.google.atv.audio_proxy@6.0-service.rc
@@ -1,6 +1,6 @@
service audio_proxy_service /vendor/bin/hw/device.google.atv.audio_proxy@6.0-service \
--name mediashell \
- --stream MEDIASHELL_AUDIO_DEVICE_ADDR:40:200 \
+ --stream MEDIASHELL_AUDIO_DEVICE_ADDR:40:600 \
--stream MEDIASHELL_MIXER_DEVICE_ADDR:40:40
class hal
user system
diff --git a/audio_proxy/service/device.google.atv.audio_proxy@7.0-service.rc b/audio_proxy/service/device.google.atv.audio_proxy@7.0-service.rc
index d8d2298..e8477fb 100644
--- a/audio_proxy/service/device.google.atv.audio_proxy@7.0-service.rc
+++ b/audio_proxy/service/device.google.atv.audio_proxy@7.0-service.rc
@@ -1,6 +1,6 @@
service audio_proxy_service /vendor/bin/hw/device.google.atv.audio_proxy@7.0-service \
--name mediashell \
- --stream MEDIASHELL_AUDIO_DEVICE_ADDR:40:200 \
+ --stream MEDIASHELL_AUDIO_DEVICE_ADDR:40:600 \
--stream MEDIASHELL_MIXER_DEVICE_ADDR:40:40
class hal
user system
diff --git a/audio_proxy/service/device.google.atv.audio_proxy@7.1-service.rc b/audio_proxy/service/device.google.atv.audio_proxy@7.1-service.rc
index 6490f71..37cab1e 100644
--- a/audio_proxy/service/device.google.atv.audio_proxy@7.1-service.rc
+++ b/audio_proxy/service/device.google.atv.audio_proxy@7.1-service.rc
@@ -1,6 +1,6 @@
service audio_proxy_service /vendor/bin/hw/device.google.atv.audio_proxy@7.1-service \
--name mediashell \
- --stream MEDIASHELL_AUDIO_DEVICE_ADDR:40:200 \
+ --stream MEDIASHELL_AUDIO_DEVICE_ADDR:40:600 \
--stream MEDIASHELL_MIXER_DEVICE_ADDR:40:40
class hal
user system
diff --git a/audio_proxy/service/manifest_audio_proxy_5_0.xml b/audio_proxy/service/manifest_audio_proxy_5_0.xml
index 51d3292..176b388 100644
--- a/audio_proxy/service/manifest_audio_proxy_5_0.xml
+++ b/audio_proxy/service/manifest_audio_proxy_5_0.xml
@@ -1,7 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>device.google.atv.audio_proxy</name>
- <version>1</version>
+ <version>3</version>
<fqname>IAudioProxy/mediashell</fqname>
</hal>
diff --git a/audio_proxy/service/manifest_audio_proxy_6_0.xml b/audio_proxy/service/manifest_audio_proxy_6_0.xml
index 0d4009a..fdb5eb1 100644
--- a/audio_proxy/service/manifest_audio_proxy_6_0.xml
+++ b/audio_proxy/service/manifest_audio_proxy_6_0.xml
@@ -1,7 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>device.google.atv.audio_proxy</name>
- <version>1</version>
+ <version>3</version>
<fqname>IAudioProxy/mediashell</fqname>
</hal>
diff --git a/audio_proxy/service/manifest_audio_proxy_7_0.xml b/audio_proxy/service/manifest_audio_proxy_7_0.xml
index 26f1cd9..3adb4b5 100644
--- a/audio_proxy/service/manifest_audio_proxy_7_0.xml
+++ b/audio_proxy/service/manifest_audio_proxy_7_0.xml
@@ -1,7 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>device.google.atv.audio_proxy</name>
- <version>1</version>
+ <version>3</version>
<fqname>IAudioProxy/mediashell</fqname>
</hal>
diff --git a/audio_proxy/service/manifest_audio_proxy_7_1.xml b/audio_proxy/service/manifest_audio_proxy_7_1.xml
index 1b3bc8b..51b93d6 100644
--- a/audio_proxy/service/manifest_audio_proxy_7_1.xml
+++ b/audio_proxy/service/manifest_audio_proxy_7_1.xml
@@ -1,7 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>device.google.atv.audio_proxy</name>
- <version>1</version>
+ <version>3</version>
<fqname>IAudioProxy/mediashell</fqname>
</hal>
diff --git a/libraries/BluetoothServices/Android.bp b/libraries/BluetoothServices/Android.bp
index 4631c4c..0d9ffc1 100644
--- a/libraries/BluetoothServices/Android.bp
+++ b/libraries/BluetoothServices/Android.bp
@@ -20,7 +20,6 @@ android_library {
"TwoPanelSettingsLib",
],
- min_sdk_version: "32",
resource_dirs: ["res"],
manifest: "AndroidManifest.xml",
sdk_version: "system_current",
diff --git a/libraries/BluetoothServices/lint-baseline.xml b/libraries/BluetoothServices/lint-baseline.xml
new file mode 100644
index 0000000..3b31d66
--- /dev/null
+++ b/libraries/BluetoothServices/lint-baseline.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev">
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 33 (current min is 32): `android.bluetooth.BluetoothDevice#getBatteryLevel`"
+ errorLine1=" int battery = mDevice.getBatteryLevel();"
+ errorLine2=" ~~~~~~~~~~~~~~~">
+ <location
+ file="device/google/atv/libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/DefaultProxy.java"
+ line="103"
+ column="35"/>
+ </issue>
+
+</issues> \ No newline at end of file
diff --git a/libraries/BluetoothServices/src/com/google/android/tv/btservices/pairing/profiles/PairingProfileWrapperA2dp.java b/libraries/BluetoothServices/src/com/google/android/tv/btservices/pairing/profiles/PairingProfileWrapperA2dp.java
index 1284e41..9ce679f 100644
--- a/libraries/BluetoothServices/src/com/google/android/tv/btservices/pairing/profiles/PairingProfileWrapperA2dp.java
+++ b/libraries/BluetoothServices/src/com/google/android/tv/btservices/pairing/profiles/PairingProfileWrapperA2dp.java
@@ -17,7 +17,7 @@
package com.google.android.tv.btservices.pairing.profiles;
import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothDevice;;
+import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import java.util.List;
diff --git a/libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/DefaultProxy.java b/libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/DefaultProxy.java
index e4c3899..d57b3dd 100644
--- a/libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/DefaultProxy.java
+++ b/libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/DefaultProxy.java
@@ -34,11 +34,13 @@ public class DefaultProxy extends RemoteProxy {
private BleConnection bleConnection;
private final boolean isBleSupported;
private BluetoothGattCharacteristic batteryLevelCharacteristic;
+ private BluetoothGattCharacteristic versionCharacteristic;
// return value: whether the subscription was successfully created
private final CompletableFuture<Boolean> subscribeBatteryLevelRes;
private BatteryResult lastKnownBatteryLevel = BatteryResult.RESULT_NOT_IMPLEMENTED;
private Runnable batteryLevelCallback;
+ private Version lastKnownVersion = Version.BAD_VERSION;
public DefaultProxy(Context context, BluetoothDevice device) {
super(context, device);
@@ -129,12 +131,55 @@ public class DefaultProxy extends RemoteProxy {
@Override
public CompletableFuture<Boolean> refreshVersion(){
- return CompletableFuture.completedFuture(true);
+ CompletableFuture<Boolean> ret = new CompletableFuture<>();
+ if (!isBleSupported || versionCharacteristic == null) {
+ ret.complete(false);
+ return ret;
+ }
+
+ bleConnection.readCharacteristic(
+ versionCharacteristic,
+ (BluetoothGattCharacteristic characteristic, int status) -> {
+ if (status != BluetoothGatt.GATT_SUCCESS) {
+ Log.w(TAG, "GATT failure while reading version: " + status);
+ ret.complete(false);
+ }
+ String versionString = characteristic.getStringValue(0);
+ Version version = stringToVersion(versionString);
+ if (version == Version.BAD_VERSION) {
+ if (DEBUG) {
+ Log.d(TAG, "Disabling Gatt version reading for device " + mDevice
+ + " due to malformed version string: " + versionString);
+ }
+ // disable gatt version reading for this device
+ versionCharacteristic = null;
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "Received valid version via Gatt: " + version);
+ }
+ lastKnownVersion = version;
+ ret.complete(true);
+ }
+ });
+ return ret;
+ }
+
+ private Version stringToVersion(String s) {
+ if (s == null || !(s.startsWith("V") || s.startsWith("v"))) return Version.BAD_VERSION;
+ try {
+ String[] versionParts = s.split("\\.");
+ int majorVersion = Integer.parseInt(versionParts[0].substring(1));
+ int minorVersion = Integer.parseInt(versionParts[1]);
+ return new Version(majorVersion, minorVersion, 0 /*vendorId*/, 0 /*productId*/);
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to parse version" + e);
+ return Version.BAD_VERSION;
+ }
}
@Override
public Version getLastKnownVersion(){
- return Version.BAD_VERSION;
+ return lastKnownVersion;
}
@Override
@@ -156,6 +201,20 @@ public class DefaultProxy extends RemoteProxy {
private class Callback implements BleConnection.Callback {
@Override
public void onGattReady(BluetoothGatt gatt) {
+ // Get characteristic for firmware version
+ final BluetoothGattService deviceInfoService = gatt.getService(
+ UUID_DEVICE_INFO_SERVICE);
+ if (deviceInfoService != null) {
+ versionCharacteristic =
+ deviceInfoService.getCharacteristic(UUID_VERSION_CHARACTERISTIC);
+ }
+ if (versionCharacteristic != null) {
+ // Read firmware version
+ refreshVersion();
+ } else {
+ Log.w(TAG, "versionCharacteristic is null for device " + mDevice);
+ }
+
// Get characteristic for battery level
final BluetoothGattService battService = gatt.getService(UUID_BATTERY_SERVICE);
if (battService != null) {
diff --git a/libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/RemoteProxy.java b/libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/RemoteProxy.java
index ec0cab6..cf5f932 100644
--- a/libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/RemoteProxy.java
+++ b/libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/RemoteProxy.java
@@ -37,6 +37,11 @@ public abstract class RemoteProxy {
UUID.fromString("00002a19-0000-1000-8000-00805f9b34fb");
public static final UUID CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID =
UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
+ // Version info
+ public static final UUID UUID_DEVICE_INFO_SERVICE =
+ UUID.fromString("0000180a-0000-1000-8000-00805f9b34fb");
+ public static final UUID UUID_VERSION_CHARACTERISTIC =
+ UUID.fromString("00002A28-0000-1000-8000-00805f9b34fb");
public static class Result {
diff --git a/libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/Version.java b/libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/Version.java
index 0bb491b..da94570 100644
--- a/libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/Version.java
+++ b/libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/Version.java
@@ -88,8 +88,8 @@ public class Version implements Comparable<Version> {
}
public String toVersionString() {
- String major = String.valueOf(mMajorVersion);
- String minor = String.valueOf(mMinorVersion);
+ String major = String.format("%01d", mMajorVersion);
+ String minor = String.format("%02d", mMinorVersion);
return major + "." + minor;
}
diff --git a/libraries/BluetoothServices/src/com/google/android/tv/btservices/settings/ConnectedDevicesSliceProvider.java b/libraries/BluetoothServices/src/com/google/android/tv/btservices/settings/ConnectedDevicesSliceProvider.java
index 03a66f1..56afc5d 100644
--- a/libraries/BluetoothServices/src/com/google/android/tv/btservices/settings/ConnectedDevicesSliceProvider.java
+++ b/libraries/BluetoothServices/src/com/google/android/tv/btservices/settings/ConnectedDevicesSliceProvider.java
@@ -41,6 +41,8 @@ import static com.google.android.tv.btservices.settings.SlicesUtil.GENERAL_SLICE
import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothClass.Device;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -93,6 +95,7 @@ public class ConnectedDevicesSliceProvider extends SliceProvider implements
"com.google.android.settings.usage.TOGGLE_CHANGED";
private static final String TAG = "Atv.ConDevsSliceProvider";
private static final boolean DEBUG = false;
+ private static final boolean DISCONNECT_PREFERENCE_ENABLED = false;
private boolean mBtDeviceServiceBound;
private final Map<String, Version> mVersionsMap = new ConcurrentHashMap<>();
private BluetoothDeviceService.LocalBinder mBtDeviceServiceBinder;
@@ -473,39 +476,40 @@ public class ConnectedDevicesSliceProvider extends SliceProvider implements
}
// Update "connect/disconnect preference"
- if (showConnectDisconnectButtons(device) && cachedDevice != null
- && !cachedDevice.isBusy()) {
+ if (cachedDevice != null && !cachedDevice.isBusy()) {
// Whether the device is actually connected from CachedBluetoothDevice's perceptive.
boolean isConnected = BluetoothUtils.isConnected(device) && cachedDevice.isConnected();
- RowBuilder disconnectPref = new RowBuilder()
- .setKey(isConnected ? KEY_DISCONNECT : KEY_CONNECT)
- .setTitle(getString(isConnected
- ? R.string.bluetooth_disconnect : R.string.bluetooth_connect));
- extras = new Bundle();
- i = new Intent(context, ResponseActivity.class);
- ResponseFragment.prepareArgs(
- extras,
- isConnected ? KEY_DISCONNECT : KEY_CONNECT,
- isConnected ? R.string.settings_bt_disconnect : R.string.settings_bt_connect,
- 0,
- R.drawable.ic_baseline_bluetooth_searching_large,
- YES_NO_ARGS,
- deviceName,
- isConnected ? 1 /* default to NO (index 1) */ : 0 /* default to YES */
- );
- i.putExtras(extras)
- .putExtra(KEY_EXTRAS_DEVICE, device)
- .setData(Uri.parse(SCHEME_CONTENT + device.getAddress()));
- List<String> updatedUris = Arrays.asList(GENERAL_SLICE_URI.toString(),
- sliceUri.toString());
- PendingIntent updateSliceIntent = backAndUpdateSliceIntent(getContext(), 1,
- new ArrayList<>(updatedUris), sliceUri.toString());
- i.putExtra(EXTRA_SLICE_FOLLOWUP, updateSliceIntent);
- PendingIntent pendingIntent = PendingIntent
- .getActivity(context, 1, i,
- PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
- disconnectPref.setPendingIntent(pendingIntent);
- psb.addPreference(disconnectPref);
+ if (!isConnected || showDisconnectButton(device, context)) {
+ RowBuilder disconnectPref = new RowBuilder()
+ .setKey(isConnected ? KEY_DISCONNECT : KEY_CONNECT)
+ .setTitle(getString(isConnected
+ ? R.string.bluetooth_disconnect : R.string.bluetooth_connect));
+ extras = new Bundle();
+ i = new Intent(context, ResponseActivity.class);
+ ResponseFragment.prepareArgs(
+ extras,
+ isConnected ? KEY_DISCONNECT : KEY_CONNECT,
+ isConnected ? R.string.settings_bt_disconnect : R.string.settings_bt_connect,
+ 0,
+ R.drawable.ic_baseline_bluetooth_searching_large,
+ YES_NO_ARGS,
+ deviceName,
+ isConnected ? 1 /* default to NO (index 1) */ : 0 /* default to YES */
+ );
+ i.putExtras(extras)
+ .putExtra(KEY_EXTRAS_DEVICE, device)
+ .setData(Uri.parse(SCHEME_CONTENT + device.getAddress()));
+ List<String> updatedUris = Arrays.asList(GENERAL_SLICE_URI.toString(),
+ sliceUri.toString());
+ PendingIntent updateSliceIntent = backAndUpdateSliceIntent(getContext(), 1,
+ new ArrayList<>(updatedUris), sliceUri.toString());
+ i.putExtra(EXTRA_SLICE_FOLLOWUP, updateSliceIntent);
+ PendingIntent pendingIntent = PendingIntent
+ .getActivity(context, 1, i,
+ PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
+ disconnectPref.setPendingIntent(pendingIntent);
+ psb.addPreference(disconnectPref);
+ }
}
// Update "rename preference".
@@ -709,8 +713,13 @@ public class ConnectedDevicesSliceProvider extends SliceProvider implements
return Configuration.get(getContext()).isEnabled(R.bool.show_cec_in_connected_settings);
}
- private boolean showConnectDisconnectButtons(BluetoothDevice device) {
- return !BluetoothUtils.isBluetoothDeviceMetadataInList(
+ private boolean showDisconnectButton(BluetoothDevice device, Context context) {
+ if (DISCONNECT_PREFERENCE_ENABLED) {
+ return true;
+ }
+ return !BluetoothUtils.isRemoteClass(device)
+ && !BluetoothUtils.isRemote(context, device)
+ && !BluetoothUtils.isBluetoothDeviceMetadataInList(
getContext(),
device,
BluetoothDevice.METADATA_MODEL_NAME,
diff --git a/overlay/TvDisableLowPowerStandbyOverlay/Android.bp b/overlay/TvDisableLowPowerStandbyOverlay/Android.bp
new file mode 100644
index 0000000..c08c2d3
--- /dev/null
+++ b/overlay/TvDisableLowPowerStandbyOverlay/Android.bp
@@ -0,0 +1,16 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "device_google_atv_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["device_google_atv_license"],
+}
+
+runtime_resource_overlay {
+ name: "TvDisableLowPowerStandbyOverlay",
+ certificate: "platform",
+ resource_dirs: ["res"],
+ product_specific: true,
+ sdk_version: "current",
+}
diff --git a/overlay/TvDisableLowPowerStandbyOverlay/AndroidManifest.xml b/overlay/TvDisableLowPowerStandbyOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..33700c3
--- /dev/null
+++ b/overlay/TvDisableLowPowerStandbyOverlay/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<!--
+ 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tv.overlay.lps.disabled"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <application android:hasCode="false" />
+
+ <overlay
+ android:targetPackage="android"
+ android:priority="1"
+ android:isStatic="true" />
+
+</manifest>
diff --git a/overlay/TvDisableLowPowerStandbyOverlay/res/values/config.xml b/overlay/TvDisableLowPowerStandbyOverlay/res/values/config.xml
new file mode 100644
index 0000000..0f30d7c
--- /dev/null
+++ b/overlay/TvDisableLowPowerStandbyOverlay/res/values/config.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<resources>
+ <!-- Whether Low Power Standby is supported and can be enabled. -->
+ <bool name="config_lowPowerStandbySupported">false</bool>
+</resources>
+
diff --git a/overlay/TvFrameworkOverlay/res/values-de/strings.xml b/overlay/TvFrameworkOverlay/res/values-de/strings.xml
index 7ab517e..8931686 100644
--- a/overlay/TvFrameworkOverlay/res/values-de/strings.xml
+++ b/overlay/TvFrameworkOverlay/res/values-de/strings.xml
@@ -18,8 +18,8 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="accessibility_shortcut_multiple_service_warning" msgid="7133244216041378936">"Du hast die Zurück-Taste und die Taste „Leiser“ drei Sekunden lang gedrückt gehalten, um <xliff:g id="SERVICE_0">%1$s</xliff:g> zu verwenden.\n\n Wenn du <xliff:g id="SERVICE_1">%1$s</xliff:g> jetzt aktivieren möchtest, halte die Zurück-Taste und die Taste „Leiser“ noch einmal drei Sekunden lang gedrückt. Du kannst diese Tastenkombination jederzeit verwenden, um <xliff:g id="SERVICE_2">%1$s</xliff:g> zu aktivieren oder zu deaktivieren.\n\n Unter Einstellungen &gt; System &gt; Bedienungshilfen kannst du die Einstellungen anpassen."</string>
<string name="accessibility_shortcut_toogle_warning" msgid="6107141001991769734">"Wenn die Tastenkombination aktiviert ist, kannst du die Zurück-Taste und die Taste „Leiser“ drei Sekunden lang gedrückt halten, um eine Bedienungshilfe zu starten.\n\n Aktuelle Bedienungshilfe:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Du kannst die Bedienungshilfe unter „Einstellungen“ &gt; „Bedienungshilfen“ ändern."</string>
- <string name="accessibility_shortcut_enabling_service" msgid="955379455142747901">"Du hast die Zurück-Taste und die Taste „Leiser“ gedrückt. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ist aktiviert."</string>
- <string name="accessibility_shortcut_disabling_service" msgid="1407311966343470931">"Du hast die Zurück-Taste und die Taste „Leiser“ gedrückt. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ist deaktiviert."</string>
+ <string name="accessibility_shortcut_enabling_service" msgid="955379455142747901">"Zurück- und Nach-Unten-Taste gleichzeitig gedrückt. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> wurde aktiviert."</string>
+ <string name="accessibility_shortcut_disabling_service" msgid="1407311966343470931">"Zurück- und Nach-Unten-Taste gleichzeitig gedrückt. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> wurde deaktiviert."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="7263788823743141556">"Du hast die Zurück-Taste und die Taste „Leiser“ drei Sekunden lang gedrückt gehalten, um <xliff:g id="SERVICE_0">%1$s</xliff:g> zu verwenden. Wenn du <xliff:g id="SERVICE_1">%1$s</xliff:g> jetzt aktivieren möchtest, halte die Zurück-Taste und die Taste „Leiser“ noch einmal drei Sekunden lang gedrückt. Du kannst diese Tastenkombination jederzeit verwenden, um <xliff:g id="SERVICE_2">%1$s</xliff:g> zu aktivieren oder zu deaktivieren."</string>
<string name="accessibility_shortcut_single_service_warning" msgid="7941823324711523679">"Du hast die Zurück-Taste und die Taste „Leiser“ drei Sekunden lang gedrückt gehalten, um <xliff:g id="SERVICE_0">%1$s</xliff:g> zu verwenden.\n\n Wenn du <xliff:g id="SERVICE_1">%1$s</xliff:g> jetzt aktivieren möchtest, halte die Zurück-Taste und die Taste „Leiser“ noch einmal drei Sekunden lang gedrückt.\n Du kannst diese Tastenkombination jederzeit verwenden, um <xliff:g id="SERVICE_2">%1$s</xliff:g> zu aktivieren oder zu deaktivieren.\n\n Unter Einstellungen &gt; System &gt; Bedienungshilfen kannst du die Einstellungen anpassen."</string>
<string name="disable_accessibility_shortcut" msgid="4559312586447750126">"Später"</string>
diff --git a/overlay/TvFrameworkOverlay/res/values-en-rCA/strings.xml b/overlay/TvFrameworkOverlay/res/values-en-rCA/strings.xml
index 9f7cbab..6ab3006 100644
--- a/overlay/TvFrameworkOverlay/res/values-en-rCA/strings.xml
+++ b/overlay/TvFrameworkOverlay/res/values-en-rCA/strings.xml
@@ -16,12 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_shortcut_multiple_service_warning" msgid="7133244216041378936">"You have held both the back and down buttons for three seconds to use <xliff:g id="SERVICE_0">%1$s</xliff:g>.\n\n To enable <xliff:g id="SERVICE_1">%1$s</xliff:g> now, hold the back and down buttons for three seconds again. Use this shortcut at any time to enable or disable <xliff:g id="SERVICE_2">%1$s</xliff:g>.\n\n You can adjust your preferences in Settings &gt; System &gt; Accessibility."</string>
- <string name="accessibility_shortcut_toogle_warning" msgid="6107141001991769734">"When the shortcut is on, pressing both the back and down buttons for three seconds will start an accessibility feature.\n\n Current accessibility feature:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n You can change the feature in Settings &gt; Accessibility."</string>
+ <string name="accessibility_shortcut_multiple_service_warning" msgid="7133244216041378936">"You have held both the back and down buttons for three seconds to use <xliff:g id="SERVICE_0">%1$s</xliff:g>.\n\n To enable <xliff:g id="SERVICE_1">%1$s</xliff:g> now, hold the back and down buttons for three seconds again. Use this shortcut anytime to enable or disable <xliff:g id="SERVICE_2">%1$s</xliff:g>.\n\n You can adjust your preferences in Settings &gt; System &gt; Accessibility."</string>
+ <string name="accessibility_shortcut_toogle_warning" msgid="6107141001991769734">"When the shortcut is on, pressing both the back and down buttons for 3 seconds will start an accessibility feature.\n\n Current accessibility feature:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n You can change the feature in Settings &gt; Accessibility."</string>
<string name="accessibility_shortcut_enabling_service" msgid="955379455142747901">"Held back and down buttons. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
<string name="accessibility_shortcut_disabling_service" msgid="1407311966343470931">"Held back and down buttons. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
- <string name="accessibility_shortcut_spoken_feedback" msgid="7263788823743141556">"You have held both the back and down buttons for three seconds to use <xliff:g id="SERVICE_0">%1$s</xliff:g>. To enable <xliff:g id="SERVICE_1">%1$s</xliff:g> now, hold the back and down buttons for three seconds again. Use this shortcut at any time to enable or disable <xliff:g id="SERVICE_2">%1$s</xliff:g>."</string>
- <string name="accessibility_shortcut_single_service_warning" msgid="7941823324711523679">"You have held both the back and down buttons for three seconds to use <xliff:g id="SERVICE_0">%1$s</xliff:g>.\n\n To enable <xliff:g id="SERVICE_1">%1$s</xliff:g> now, hold the back and down buttons for three seconds again.\n Use this shortcut at any time to enable or disable <xliff:g id="SERVICE_2">%1$s</xliff:g>.\n\n You can adjust your preferences in Settings &gt; System &gt; Accessibility."</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="7263788823743141556">"You have held both the back and down buttons for three seconds to use <xliff:g id="SERVICE_0">%1$s</xliff:g>. To enable <xliff:g id="SERVICE_1">%1$s</xliff:g> now, hold the back and down buttons for three seconds again. Use this shortcut anytime to enable or disable <xliff:g id="SERVICE_2">%1$s</xliff:g>."</string>
+ <string name="accessibility_shortcut_single_service_warning" msgid="7941823324711523679">"You have held both the back and down buttons for three seconds to use <xliff:g id="SERVICE_0">%1$s</xliff:g>.\n\n To enable <xliff:g id="SERVICE_1">%1$s</xliff:g> now, hold the back and down buttons for three seconds again.\n Use this shortcut anytime to enable or disable <xliff:g id="SERVICE_2">%1$s</xliff:g>.\n\n You can adjust your preferences in Settings &gt; System &gt; Accessibility."</string>
<string name="disable_accessibility_shortcut" msgid="4559312586447750126">"Not now"</string>
<string name="leave_accessibility_shortcut_on" msgid="6807632291651241490">"Turn on now"</string>
</resources>
diff --git a/overlay/TvFrameworkOverlay/res/values-fa/strings.xml b/overlay/TvFrameworkOverlay/res/values-fa/strings.xml
index da10234..949f7cb 100644
--- a/overlay/TvFrameworkOverlay/res/values-fa/strings.xml
+++ b/overlay/TvFrameworkOverlay/res/values-fa/strings.xml
@@ -22,6 +22,6 @@
<string name="accessibility_shortcut_disabling_service" msgid="1407311966343470931">"دکمه‌های برگشت و پایین را نگه داشتید. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> خاموش شد."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="7263788823743141556">"برای استفاده از <xliff:g id="SERVICE_0">%1$s</xliff:g>، دکمه‌های برگشت و پایین را با هم به‌مدت سه ثانیه نگه داشته‌اید. برای اینکه <xliff:g id="SERVICE_1">%1$s</xliff:g> اکنون فعال شود، دکمه‌های برگشت و پایین را دوباره به‌مدت سه ثانیه نگه دارید. هر زمان خواستید برای فعال یا غیرفعال کردن <xliff:g id="SERVICE_2">%1$s</xliff:g>، از این میان‌بر استفاده کنید."</string>
<string name="accessibility_shortcut_single_service_warning" msgid="7941823324711523679">"برای استفاده از <xliff:g id="SERVICE_0">%1$s</xliff:g>، دکمه‌های برگشت و پایین را با هم به‌مدت سه ثانیه نگه داشته‌اید.\n\n برای اینکه <xliff:g id="SERVICE_1">%1$s</xliff:g> اکنون فعال شود، دکمه‌های برگشت و پایین را دوباره به‌مدت سه ثانیه نگه دارید.\n هر زمان خواستید برای فعال یا غیرفعال کردن <xliff:g id="SERVICE_2">%1$s</xliff:g>، از این میان‌بر استفاده کنید.\n\n می‌توانید اولویت‌ها را در «تنظیمات &gt; سیستم &gt; دسترس‌پذیری» تنظیم کنید."</string>
- <string name="disable_accessibility_shortcut" msgid="4559312586447750126">"اکنون نه"</string>
+ <string name="disable_accessibility_shortcut" msgid="4559312586447750126">"حالا نه"</string>
<string name="leave_accessibility_shortcut_on" msgid="6807632291651241490">"اکنون روشن شود"</string>
</resources>
diff --git a/overlay/TvFrameworkOverlay/res/values-ky/strings.xml b/overlay/TvFrameworkOverlay/res/values-ky/strings.xml
index 1b5d5ec..278b60d 100644
--- a/overlay/TvFrameworkOverlay/res/values-ky/strings.xml
+++ b/overlay/TvFrameworkOverlay/res/values-ky/strings.xml
@@ -16,12 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_shortcut_multiple_service_warning" msgid="7133244216041378936">"<xliff:g id="SERVICE_0">%1$s</xliff:g> кызматын колдонуу үчүн артка жана ылдый баскычтарын үч секунддай коё бербей басып турдуңуз.\n\n <xliff:g id="SERVICE_1">%1$s</xliff:g> кызматын азыр иштетүү үчүн артка жана ылдый баскычтарын үч секунддай коё бербей басып туруңуз. <xliff:g id="SERVICE_2">%1$s</xliff:g> кызматын каалаган убакта ушул ыкчам баскыч менен иштетип же өчүрө аласыз.\n\n Параметрлерди тууралоо үчүн Жөндөөлөр &gt; Тутум &gt; Атайын мүмкүнчүлүктөргө өтүңүз."</string>
- <string name="accessibility_shortcut_toogle_warning" msgid="6107141001991769734">"Атайын мүмкүнчүлүктөр функциясын пайдалануу үчүн ал күйгүзүлгөндө, үндү катуулатып/акырындаткан эки баскычты тең 3 секунддай коё бербей басып туруңуз.\n\n Учурдагы атайын мүмкүнчүлүктөрдүн жөндөөлөрү:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Жөндөөлөр &gt; Атайын мүмкүнчүлүктөр бөлүмүнөн өзгөртө аласыз."</string>
+ <string name="accessibility_shortcut_multiple_service_warning" msgid="7133244216041378936">"<xliff:g id="SERVICE_0">%1$s</xliff:g> кызматын колдонуу үчүн артка жана ылдый баскычтарын үч секунддай коё бербей басып турдуңуз.\n\n <xliff:g id="SERVICE_1">%1$s</xliff:g> кызматын азыр иштетүү үчүн артка жана ылдый баскычтарын үч секунддай коё бербей басып туруңуз. <xliff:g id="SERVICE_2">%1$s</xliff:g> кызматын каалаган убакта ушул ыкчам баскыч менен иштетип же өчүрө аласыз.\n\n Параметрлерди тууралоо үчүн Параметрлер &gt; Тутум &gt; Атайын мүмкүнчүлүктөргө өтүңүз."</string>
+ <string name="accessibility_shortcut_toogle_warning" msgid="6107141001991769734">"Атайын мүмкүнчүлүктөр функциясын пайдалануу үчүн ал күйгүзүлгөндө, үндү катуулатып/акырындаткан эки баскычты тең 3 секунддай коё бербей басып туруңуз.\n\n Учурдагы атайын мүмкүнчүлүктөрдүн параметрлери:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Параметрлер &gt; Атайын мүмкүнчүлүктөр бөлүмүнөн өзгөртө аласыз."</string>
<string name="accessibility_shortcut_enabling_service" msgid="955379455142747901">"Артка жана ылдый баскычтарын коё бербей басып турдуңуз. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> күйгүзүлдү."</string>
<string name="accessibility_shortcut_disabling_service" msgid="1407311966343470931">"Артка жана ылдый баскычтарын коё бербей басып турдуңуз. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> өчүрүлдү."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="7263788823743141556">"<xliff:g id="SERVICE_0">%1$s</xliff:g> кызматын колдонуу үчүн артка жана ылдый баскычтарын үч секунддай коё бербей басып турдуңуз. <xliff:g id="SERVICE_1">%1$s</xliff:g> кызматын азыр иштетүү үчүн артка жана ылдый баскычтарын үч секунддай коё бербей басып туруңуз. <xliff:g id="SERVICE_2">%1$s</xliff:g> кызматын каалаган убакта ушул ыкчам баскыч менен иштетип же өчүрө аласыз."</string>
- <string name="accessibility_shortcut_single_service_warning" msgid="7941823324711523679">"<xliff:g id="SERVICE_0">%1$s</xliff:g> кызматын колдонуу үчүн артка жана ылдый баскычтарын үч секунддай коё бербей басып турдуңуз.\n\n <xliff:g id="SERVICE_1">%1$s</xliff:g> кызматын азыр иштетүү үчүн артка жана ылдый баскычтарын үч секунддай коё бербей басып туруңуз.\n <xliff:g id="SERVICE_2">%1$s</xliff:g> кызматын каалаган убакта ушул ыкчам баскыч менен иштетип же өчүрө аласыз.\n\n Параметрлерди тууралоо үчүн Жөндөөлөр &gt; Тутум &gt; Атайын мүмкүнчүлүктөргө өтүңүз."</string>
+ <string name="accessibility_shortcut_single_service_warning" msgid="7941823324711523679">"<xliff:g id="SERVICE_0">%1$s</xliff:g> кызматын колдонуу үчүн артка жана ылдый баскычтарын үч секунддай коё бербей басып турдуңуз.\n\n <xliff:g id="SERVICE_1">%1$s</xliff:g> кызматын азыр иштетүү үчүн артка жана ылдый баскычтарын үч секунддай коё бербей басып туруңуз.\n <xliff:g id="SERVICE_2">%1$s</xliff:g> кызматын каалаган убакта ушул ыкчам баскыч менен иштетип же өчүрө аласыз.\n\n Параметрлерди тууралоо үчүн Параметрлер &gt; Тутум &gt; Атайын мүмкүнчүлүктөргө өтүңүз."</string>
<string name="disable_accessibility_shortcut" msgid="4559312586447750126">"Азыр эмес"</string>
<string name="leave_accessibility_shortcut_on" msgid="6807632291651241490">"Күйгүзүү"</string>
</resources>
diff --git a/overlay/TvFrameworkOverlay/res/values-or/strings.xml b/overlay/TvFrameworkOverlay/res/values-or/strings.xml
index fa73c76..06045f2 100644
--- a/overlay/TvFrameworkOverlay/res/values-or/strings.xml
+++ b/overlay/TvFrameworkOverlay/res/values-or/strings.xml
@@ -16,8 +16,8 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_shortcut_multiple_service_warning" msgid="7133244216041378936">"<xliff:g id="SERVICE_0">%1$s</xliff:g> ବ୍ୟବହାର କରିବାକୁ ଆପଣ ତିନି ସେକେଣ୍ଡ ପାଇଁ ଉଭୟ ବ୍ୟାକ୍ ଓ ଡାଉନ୍ ବଟନକୁ ଧରି ରଖିଛନ୍ତି।\n\n ବର୍ତ୍ତମାନ <xliff:g id="SERVICE_1">%1$s</xliff:g>କୁ ସକ୍ଷମ କରିବା ପାଇଁ, ପୁଣି ତିନି ସେକେଣ୍ଡ ପାଇଁ ବ୍ୟାକ୍ ଓ ଡାଉନ୍ ବଟନକୁ ଧରି ରଖନ୍ତୁ। <xliff:g id="SERVICE_2">%1$s</xliff:g>କୁ ସକ୍ଷମ କିମ୍ବା ଅକ୍ଷମ କରିବା ପାଇଁ ଯେ କୌଣସି ସମୟରେ ଏହି ସର୍ଟକଟ୍ ବ୍ୟବହାର କରନ୍ତୁ।\n\n ଆପଣ ସେଟିଂସ୍ &gt; ସିଷ୍ଟମ୍ &gt; ଆକ୍ସେସିବିଲିଟୀରେ ଆପଣଙ୍କ ପସନ୍ଦଗୁଡ଼ିକୁ ଆଡଜଷ୍ଟ କରିପାରିବେ।"</string>
- <string name="accessibility_shortcut_toogle_warning" msgid="6107141001991769734">"ସର୍ଟକଟ୍ ଚାଲୁ ଥିବା ବେଳେ, ଉଭୟ ବ୍ୟାକ୍ ଓ ଡାଉନ୍ ବଟନକୁ 3 ସେକେଣ୍ଡ ପାଇଁ ଦବାଇବା ଫଳରେ ଏକ ଆକ୍ସେସିବିଲିଟୀ ଫିଚର୍ ଆରମ୍ଭ ହେବ।\n\n ବର୍ତ୍ତମାନର ଆକ୍ସେସିବିଲିଟୀ ଫିଚର୍:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n ଆପଣ ସେଟିଂସ୍ &gt; ଆକ୍ସେସିବିଲିଟୀରେ ଫିଚରକୁ ପରିବର୍ତ୍ତନ କରିପାରିବେ।"</string>
+ <string name="accessibility_shortcut_multiple_service_warning" msgid="7133244216041378936">"<xliff:g id="SERVICE_0">%1$s</xliff:g> ବ୍ୟବହାର କରିବାକୁ ଆପଣ ତିନି ସେକେଣ୍ଡ ପାଇଁ ଉଭୟ ବ୍ୟାକ ଓ ଡାଉନ ବଟନକୁ ଧରି ରଖିଛନ୍ତି।\n\n ବର୍ତ୍ତମାନ <xliff:g id="SERVICE_1">%1$s</xliff:g>କୁ ସକ୍ଷମ କରିବା ପାଇଁ, ପୁଣି ତିନି ସେକେଣ୍ଡ ପାଇଁ ବ୍ୟାକ ଓ ଡାଉନ ବଟନକୁ ଧରି ରଖନ୍ତୁ। <xliff:g id="SERVICE_2">%1$s</xliff:g>କୁ ସକ୍ଷମ କିମ୍ବା ଅକ୍ଷମ କରିବା ପାଇଁ ଯେ କୌଣସି ସମୟରେ ଏହି ସର୍ଟକଟ ବ୍ୟବହାର କରନ୍ତୁ।\n\n ଆପଣ ସେଟିଂସ &gt; ସିଷ୍ଟମ &gt; ଆକ୍ସେସିବିଲିଟୀରେ ଆପଣଙ୍କ ପସନ୍ଦଗୁଡ଼ିକୁ ଆଡଜଷ୍ଟ କରିପାରିବେ।"</string>
+ <string name="accessibility_shortcut_toogle_warning" msgid="6107141001991769734">"ସର୍ଟକଟ ଚାଲୁ ଥିବା ବେଳେ, ଉଭୟ ବ୍ୟାକ ଓ ଡାଉନ ବଟନକୁ 3 ସେକେଣ୍ଡ ପାଇଁ ଦବାଇବା ଫଳରେ ଏକ ଆକ୍ସେସିବିଲିଟୀ ଫିଚର ଆରମ୍ଭ ହେବ।\n\n ବର୍ତ୍ତମାନର ଆକ୍ସେସିବିଲିଟୀ ଫିଚର:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n ଆପଣ ସେଟିଂସ &gt; ଆକ୍ସେସିବିଲିଟୀରେ ଫିଚରକୁ ପରିବର୍ତ୍ତନ କରିପାରିବେ।"</string>
<string name="accessibility_shortcut_enabling_service" msgid="955379455142747901">"ବ୍ୟାକ୍ ଓ ଡାଉନ୍ ବଟନକୁ ଧରି ରଖନ୍ତୁ। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ଚାଲୁ କରାଯାଇଛି।"</string>
<string name="accessibility_shortcut_disabling_service" msgid="1407311966343470931">"ବ୍ୟାକ୍ ଓ ଡାଉନ୍ ବଟନକୁ ଧରି ରଖନ୍ତୁ। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ବନ୍ଦ କରାଯାଇଛି।"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="7263788823743141556">"<xliff:g id="SERVICE_0">%1$s</xliff:g> ବ୍ୟବହାର କରିବାକୁ ଆପଣ ତିନି ସେକେଣ୍ଡ ପାଇଁ ଉଭୟ ବ୍ୟାକ୍ ଓ ଡାଉନ୍ ବଟନକୁ ଧରି ରଖିଛନ୍ତି। ବର୍ତ୍ତମାନ <xliff:g id="SERVICE_1">%1$s</xliff:g>କୁ ସକ୍ଷମ କରିବା ପାଇଁ, ପୁଣି ତିନି ସେକେଣ୍ଡ ପାଇଁ ବ୍ୟାକ୍ ଓ ଡାଉନ୍ ବଟନକୁ ଧରି ରଖନ୍ତୁ। <xliff:g id="SERVICE_2">%1$s</xliff:g>କୁ ସକ୍ଷମ କିମ୍ବା ଅକ୍ଷମ କରିବା ପାଇଁ ଯେ କୌଣସି ସମୟରେ ଏହି ସର୍ଟକଟ୍ ବ୍ୟବହାର କରନ୍ତୁ।"</string>
diff --git a/overlay/TvFrameworkOverlay/res/values-ro/strings.xml b/overlay/TvFrameworkOverlay/res/values-ro/strings.xml
index cdc79c6..2df2bfa 100644
--- a/overlay/TvFrameworkOverlay/res/values-ro/strings.xml
+++ b/overlay/TvFrameworkOverlay/res/values-ro/strings.xml
@@ -16,12 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_shortcut_multiple_service_warning" msgid="7133244216041378936">"Ați apăsat lung butoanele înapoi și în jos timp de trei secunde pentru a folosi <xliff:g id="SERVICE_0">%1$s</xliff:g>.\n\n Pentru a activa <xliff:g id="SERVICE_1">%1$s</xliff:g> acum, apăsați din nou butoanele înapoi și în jos timp de trei secunde. Folosiți oricând această comandă rapidă pentru a activa sau a dezactiva <xliff:g id="SERVICE_2">%1$s</xliff:g>.\n\n Vă puteți ajusta preferințele în Setări &gt; Sistem &gt; Accesibilitate."</string>
- <string name="accessibility_shortcut_toogle_warning" msgid="6107141001991769734">"Când comanda rapidă este activată, dacă apăsați simultan butoanele înapoi și în jos timp de trei secunde, veți lansa o funcție de accesibilitate.\n\n Funcția de accesibilitate actuală:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Puteți modifica funcția în Setări și accesibilitate."</string>
- <string name="accessibility_shortcut_enabling_service" msgid="955379455142747901">"Ați apăsat lung butoanele înapoi și în jos. S-a activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
- <string name="accessibility_shortcut_disabling_service" msgid="1407311966343470931">"Ați apăsat lung butoanele înapoi și în jos. S-a dezactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
- <string name="accessibility_shortcut_spoken_feedback" msgid="7263788823743141556">"Ați apăsat lung butoanele înapoi și în jos timp de trei secunde pentru a folosi <xliff:g id="SERVICE_0">%1$s</xliff:g>. Pentru a activa <xliff:g id="SERVICE_1">%1$s</xliff:g> acum, apăsați din nou butoanele înapoi și în jos timp de trei secunde. Folosiți oricând această comandă rapidă pentru a activa sau a dezactiva <xliff:g id="SERVICE_2">%1$s</xliff:g>."</string>
- <string name="accessibility_shortcut_single_service_warning" msgid="7941823324711523679">"Ați apăsat lung butoanele înapoi și în jos timp de trei secunde pentru a folosi <xliff:g id="SERVICE_0">%1$s</xliff:g>.\n\n Pentru a activa <xliff:g id="SERVICE_1">%1$s</xliff:g> acum, apăsați din nou butoanele înapoi și în jos timp de trei secunde.\n Folosiți oricând această comandă rapidă pentru a activa sau a dezactiva <xliff:g id="SERVICE_2">%1$s</xliff:g>.\n\n Vă puteți ajusta preferințele în Setări &gt; Sistem &gt; Accesibilitate."</string>
+ <string name="accessibility_shortcut_multiple_service_warning" msgid="7133244216041378936">"Ai apăsat lung butoanele înapoi și în jos timp de trei secunde pentru a folosi <xliff:g id="SERVICE_0">%1$s</xliff:g>.\n\n Pentru a activa <xliff:g id="SERVICE_1">%1$s</xliff:g> acum, apasă din nou butoanele înapoi și în jos timp de trei secunde. Folosește oricând această comandă rapidă pentru a activa sau a dezactiva <xliff:g id="SERVICE_2">%1$s</xliff:g>.\n\n Îți poți ajusta preferințele în Setări &gt; Sistem &gt; Accesibilitate."</string>
+ <string name="accessibility_shortcut_toogle_warning" msgid="6107141001991769734">"Când comanda rapidă este activată, dacă apeși simultan butoanele înapoi și în jos timp de trei secunde, vei lansa o funcție de accesibilitate.\n\n Funcția de accesibilitate actuală:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Poți modifica funcția în Setări și accesibilitate."</string>
+ <string name="accessibility_shortcut_enabling_service" msgid="955379455142747901">"Ai apăsat lung butoanele înapoi și în jos. S-a activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_shortcut_disabling_service" msgid="1407311966343470931">"Ai apăsat lung butoanele înapoi și în jos. S-a dezactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="7263788823743141556">"Ai apăsat lung butoanele înapoi și în jos timp de trei secunde pentru a folosi <xliff:g id="SERVICE_0">%1$s</xliff:g>. Pentru a activa <xliff:g id="SERVICE_1">%1$s</xliff:g> acum, apasă din nou butoanele înapoi și în jos timp de trei secunde. Folosește oricând această comandă rapidă pentru a activa sau a dezactiva <xliff:g id="SERVICE_2">%1$s</xliff:g>."</string>
+ <string name="accessibility_shortcut_single_service_warning" msgid="7941823324711523679">"Ai apăsat lung butoanele înapoi și în jos timp de trei secunde pentru a folosi <xliff:g id="SERVICE_0">%1$s</xliff:g>.\n\n Pentru a activa <xliff:g id="SERVICE_1">%1$s</xliff:g> acum, apasă din nou butoanele înapoi și în jos timp de trei secunde.\n Folosește oricând această comandă rapidă pentru a activa sau a dezactiva <xliff:g id="SERVICE_2">%1$s</xliff:g>.\n\n Îți poți ajusta preferințele în Setări &gt; Sistem &gt; Accesibilitate."</string>
<string name="disable_accessibility_shortcut" msgid="4559312586447750126">"Nu acum"</string>
- <string name="leave_accessibility_shortcut_on" msgid="6807632291651241490">"Activați acum"</string>
+ <string name="leave_accessibility_shortcut_on" msgid="6807632291651241490">"Activează acum"</string>
</resources>
diff --git a/overlay/TvFrameworkOverlay/res/values/config.xml b/overlay/TvFrameworkOverlay/res/values/config.xml
index eba2806..1cbfc11 100644
--- a/overlay/TvFrameworkOverlay/res/values/config.xml
+++ b/overlay/TvFrameworkOverlay/res/values/config.xml
@@ -20,23 +20,31 @@
is disableable for products for which it is irrelevant. -->
<bool name="config_useVolumeKeySounds">false</bool>
- <!-- Disable AUDIO_BECOMING_NOISY notifications. -->
+ <!-- Flag indicating whether the AUDIO_BECOMING_NOISY notification should
+ be sent during a change to the audio output device. -->
<bool name="config_sendAudioBecomingNoisy">false</bool>
- <!-- This device is data-only. -->
- <bool name="config_voice_capable">false</bool>
-
- <!-- This device does not allow sms service. -->
- <bool name="config_sms_capable">false</bool>
+ <!-- Set this to true to enable the platform's auto-power-save modes like doze and
+ app standby. These are not enabled by default because they require a standard
+ cloud-to-device messaging service for apps to interact correctly with the modes
+ (such as to be able to deliver an instant message to the device even when it is
+ dozing). This should be enabled if you have such services and expect apps to
+ correctly use them when installed on your device. Otherwise, keep this disabled
+ so that applications can still use their own mechanisms. -->
+ <bool name="config_enableAutoPowerModes">true</bool>
- <!-- This device does not support mobile data. -->
- <bool name="config_mobile_data_capable">false</bool>
+ <!-- Whether (if true) this is a kind of device that can be moved around (eg. phone/laptop),
+ or (if false) something for which movement is either not measurable or should not count
+ toward power states (eg. tv/soundbar). -->
+ <bool name="config_autoPowerModeUseMotionSensor">false</bool>
<!-- Control the default UI mode type to use when there is no other type override
happening. One of the following values (See Configuration.java):
1 UI_MODE_TYPE_NORMAL
4 UI_MODE_TYPE_TELEVISION
5 UI_MODE_TYPE_APPLIANCE
+ 6 UI_MODE_TYPE_WATCH
+ 7 UI_MODE_TYPE_VR_HEADSET
Any other values will have surprising consequences. -->
<integer name="config_defaultUiModeType">4</integer>
@@ -52,8 +60,20 @@
-->
<integer name="config_defaultNightMode">2</integer>
- <!-- default device has recents property -->
- <bool name="config_hasRecents">false</bool>
+ <!-- Control the behavior when the user long presses the power button.
+ 0 - Nothing
+ 1 - Global actions menu
+ 2 - Power off (with confirmation)
+ 3 - Power off (without confirmation)
+ 4 - Go to voice assist
+ 5 - Go to assistant (Settings.Secure.ASSISTANT)
+ -->
+ <integer name="config_longPressOnPowerBehavior">3</integer>
+
+ <!-- If true, enables verification of the lockscreen credential in the factory reset protection
+ flow. This should be true if gatekeeper / weaver credentials can still be checked after a
+ factory reset. -->
+ <bool name="config_enableCredentialFactoryResetProtection">false</bool>
<!-- Control the behavior when the user long presses the home button.
0 - Nothing
@@ -74,76 +94,117 @@
-->
<integer name="config_doubleTapOnHomeBehavior">2</integer>
- <!-- Override configuration check for dpad so that we always appear to have one -->
+ <!-- Whether the geolocation time zone detection feature is enabled. Setting this to false means
+ the feature cannot be used. Setting this to true means system server components can be
+ tested and location time zone detection may be used if other configuration allows (see
+ location time zone provider configuration settings below). -->
+ <bool name="config_enableGeolocationTimeZoneDetection" translatable="false">false</bool>
+
+ <!-- Flag indicating whether the current device is "voice capable".
+ If true, this means that the device supports circuit-switched
+ (i.e. voice) phone calls over the telephony network, and is
+ allowed to display the in-call UI while a cellular voice call is
+ active. This can be overridden to false for "data only" devices
+ which can't make voice calls and don't support any in-call UI.
+
+ Note: this flag is subtly different from the
+ PackageManager.FEATURE_TELEPHONY system feature, which is
+ available on *any* device with a telephony radio, even if the
+ device is data-only. -->
+ <bool name="config_voice_capable">false</bool>
+
+ <!-- Flag indicating whether the current device allows sms service.
+ If true, this means that the device supports both sending and
+ receiving sms via the telephony network.
+ This can be overridden to false for "data only" devices
+ which can't send and receive sms message.
+
+ Note: Disable SMS also disable voicemail waiting sms,
+ cell broadcasting sms, and MMS. -->
+ <bool name="config_sms_capable">false</bool>
+
+ <!-- Flag indicating whether the current device allows data.
+ If true, this means that the device supports data connectivity through
+ the telephony network.
+ This can be overridden to false for devices that support voice and/or sms . -->
+ <bool name="config_mobile_data_capable">false</bool>
+
+ <!-- The name of the package that will hold the system contacts role. -->
+ <string name="config_systemContacts" translatable="false">com.android.tv.frameworkpackagestubs</string>
+
+ <!-- ComponentName of the default dream (Settings.Secure.DEFAULT_SCREENSAVER_COMPONENT) -->
+ <string name="config_dreamsDefaultComponent" translatable="false">com.android.dreams.basic/com.android.dreams.basic.Colors</string>
+
+ <!-- Override the DPad detection behavior for configuration purposes -->
<bool name="config_hasPermanentDpad">true</bool>
- <bool name="config_defaultInTouchMode">false</bool>
+ <!-- Determines whether recent tasks are provided to the user. Default device has recents
+ property. If this is false, then the following recents config flags are ignored. -->
+ <bool name="config_hasRecents">false</bool>
- <!-- Launcher customization requires AppWidgetService, but otherwise
- home screen widgets are not supported on TV -->
- <bool name="config_enableAppWidgetService">true</bool>
+ <!-- Whether to start in touch mode -->
+ <bool name="config_defaultInTouchMode">false</bool>
- <!-- Whether to keep background restricted profiles running after exiting. If set to false,
- restricted profiles may be put into stopped state as soon as the switch back to primary
- happens.
- Can be overridden with android.provider.Settings.Global.KEEP_PROFILE_IN_BACKGROUND. -->
- <bool name="config_keepRestrictedProfilesInBackground">false</bool>
+ <!-- Allow the gesture to double tap the power button twice to start the camera while the device
+ is non-interactive. -->
+ <bool name="config_cameraDoubleTapPowerGestureEnabled">false</bool>
- <!-- Enable doze mode -->
- <bool name="config_enableAutoPowerModes">true</bool>
- <bool name="config_autoPowerModeUseMotionSensor">false</bool>
+ <!-- Inactivity threshold (in milliseconds) used in JobScheduler. JobScheduler will consider
+ the device to be "idle" after being inactive for this long. -->
+ <integer name="config_jobSchedulerInactivityIdleThreshold">300000</integer>
<!-- True if the device supports split screen as a form of multi-window. -->
<bool name="config_supportsSplitScreenMultiWindow">false</bool>
- <!-- Whether the device supports quick settings and its associated APIs -->
- <bool name="config_quickSettingsSupported">false</bool>
-
<!-- True if the device supports system decorations on secondary displays. -->
<bool name="config_supportsSystemDecorsOnSecondaryDisplays">false</bool>
- <!-- Enable assistant to show in front of the dream/screensaver. -->
- <bool name="config_assistantOnTopOfDream">true</bool>
+ <!-- True if the device requires AppWidgetService even if it does not have
+ the PackageManager.FEATURE_APP_WIDGETS feature.
+ TV launcher customization requires AppWidgetService, but otherwise
+ home screen widgets are not supported on TV -->
+ <bool name="config_enableAppWidgetService">true</bool>
<!-- Maximum size, specified in pixels, to restrain the display space width to. Height and
density will be scaled accordingly to maintain aspect ratio. A value of 0 indicates no
constraint will be enforced.
- We limit the UI graphics width to 1920 because higher resolution is unnecessary and causes
+ ATV limits the UI graphics width to 1920 because higher resolution is unnecessary and causes
too much overhead on the GPU for Android TV devices. -->
<integer name="config_maxUiWidth">1920</integer>
- <!-- If true, enables verification of the lockscreen credential in the factory reset protection
- flow. This should be true if gatekeeper / weaver credentials can still be checked after a
- factory reset. -->
- <bool name="config_enableCredentialFactoryResetProtection">false</bool>
+ <!-- Whether the device supports quick settings and its associated APIs -->
+ <bool name="config_quickSettingsSupported">false</bool>
- <!-- Control the behavior when the user long presses the power button.
- 0 - Nothing
- 1 - Global actions menu
- 2 - Power off (with confirmation)
- 3 - Power off (without confirmation)
- 4 - Go to voice assist
- 5 - Go to assistant (Settings.Secure.ASSISTANT -->
- <integer name="config_longPressOnPowerBehavior">3</integer>
+ <!-- Whether to keep background restricted profiles running after exiting. If set to false,
+ restricted profiles may be put into stopped state as soon as the switch back to primary
+ happens.
+ Can be overridden with android.provider.Settings.Global.KEEP_PROFILE_IN_BACKGROUND. -->
+ <bool name="config_keepRestrictedProfilesInBackground">false</bool>
- <!-- Whether the time zone detection feature is enabled. Disabled for ATV devices. -->
- <bool name="config_enableGeolocationTimeZoneDetection" translatable="false">false</bool>
+ <!-- Set to true to make assistant show in front of the dream/screensaver. -->
+ <bool name="config_assistantOnTopOfDream">true</bool>
- <!-- Whether this device is supporting the microphone toggle -->
+ <!-- If true, Views will declare they prefer to be kept clear from overlays when focused. -->
+ <bool name="config_preferKeepClearForFocus">true</bool>
+
+ <!-- Whether this device is supporting the software microphone toggle -->
<bool name="config_supportsMicToggle">true</bool>
<!-- Whether this device is supporting the camera toggle -->
<bool name="config_supportsCamToggle">true</bool>
- <!-- ComponentName of the default dream (Settings.Secure.DEFAULT_SCREENSAVER_COMPONENT) -->
- <string name="config_dreamsDefaultComponent" translatable="false">com.android.dreams.basic/com.android.dreams.basic.Colors</string>
-
- <!-- If true, Views will declare they prefer to be kept clear from overlays when focused. -->
- <bool name="config_preferKeepClearForFocus">true</bool>
-
<!-- Determines whether SafetyCenter feature is enabled.
SafetyCenter is not yet supported on TV. -->
<bool name="config_enableSafetyCenter">false</bool>
- <!-- The name of the package that will hold the system contacts role. -->
- <string name="config_systemContacts" translatable="false">com.android.tv.frameworkpackagestubs</string>
+ <!-- Flag indicating whether seamless refresh rate switching is supported by a device. -->
+ <bool name="config_supportsSeamlessRefreshRateSwitching">false</bool>
+
+ <!-- If a location should be pre-fetched when going into device idle. -->
+ <bool name="config_autoPowerModePrefetchLocation">false</bool>
+
+ <!-- Whether Low Power Standby is supported and can be enabled. -->
+ <bool name="config_lowPowerStandbySupported">true</bool>
+
+ <!-- If supported, whether Low Power Standby is enabled by default. -->
+ <bool name="config_lowPowerStandbyEnabledByDefault">true</bool>
</resources>
diff --git a/overlay/TvFrameworkOverlay/res/values/config_device_idle.xml b/overlay/TvFrameworkOverlay/res/values/config_device_idle.xml
new file mode 100644
index 0000000..98d5a37
--- /dev/null
+++ b/overlay/TvFrameworkOverlay/res/values/config_device_idle.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2022, 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". Some legacy
+ entries do not follow the convention, but all new entries should. -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET -->
+ <integer name="device_idle_light_idle_maintenance_min_budget_ms">30000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET -->
+ <integer name="device_idle_light_idle_maintenance_max_budget_ms">180000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.INACTIVE_TIMEOUT -->
+ <integer name="device_idle_inactive_to_ms">600000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.LOCATING_TIMEOUT -->
+ <integer name="device_idle_locating_to_ms">100</integer>
+
+ <!-- Default for DeviceIdleController.Constants.IDLE_AFTER_INACTIVE_TIMEOUT -->
+ <integer name="device_idle_idle_after_inactive_to_ms">0</integer>
+
+ <!-- Default for DeviceIdleController.Constants.IDLE_TIMEOUT -->
+ <integer name="device_idle_idle_to_ms">5400000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.MIN_TIME_TO_ALARM -->
+ <integer name="device_idle_min_time_to_alarm_ms">3600</integer>
+
+ <!-- Default for DeviceIdleController.Constants.MAX_TEMP_APP_ALLOWLIST_DURATION_MS -->
+ <integer name="device_idle_max_temp_app_allowlist_duration_ms">120000</integer>
+</resources>
diff --git a/overlay/TvNetworkStackOverlay/Android.bp b/overlay/TvNetworkStackOverlay/Android.bp
new file mode 100644
index 0000000..0a30622
--- /dev/null
+++ b/overlay/TvNetworkStackOverlay/Android.bp
@@ -0,0 +1,18 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+runtime_resource_overlay {
+ name: "TvNetworkStackOverlay",
+ certificate: "platform",
+ resource_dirs: ["res"],
+ product_specific: true,
+ sdk_version: "current",
+}
+
+override_runtime_resource_overlay {
+ name: "TvInProcessNetworkStackOverlay",
+ base: "TvNetworkStackOverlay",
+ package_name: "com.android.tv.overlay.networkstack.inprocess",
+ target_package_name: "com.android.networkstack.inprocess",
+}
diff --git a/overlay/TvNetworkStackOverlay/AndroidManifest.xml b/overlay/TvNetworkStackOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..c518f56
--- /dev/null
+++ b/overlay/TvNetworkStackOverlay/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<!--
+ 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tv.overlay.networkstack"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <application android:hasCode="false" />
+
+ <overlay
+ android:targetPackage="com.android.networkstack"
+ android:targetName="NetworkStackConfig"
+ android:priority="0"
+ android:isStatic="true" />
+</manifest>
diff --git a/overlay/TvNetworkStackOverlay/res/values/config.xml b/overlay/TvNetworkStackOverlay/res/values/config.xml
new file mode 100644
index 0000000..eb90d88
--- /dev/null
+++ b/overlay/TvNetworkStackOverlay/res/values/config.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+
+<resources>
+ <!-- Increase the tolerance towards unresponsive neighbors on a Wi-Fi network. -->
+ <integer name="config_nud_steadystate_solicit_num">15</integer>
+ <integer name="config_nud_steadystate_solicit_interval">1000</integer>
+ <integer name="config_nud_postroaming_solicit_num">15</integer>
+ <integer name="config_nud_postroaming_solicit_interval">1000</integer>
+</resources>
diff --git a/overlay/TvWifiOverlay/Android.bp b/overlay/TvWifiOverlay/Android.bp
index 200b695..7499087 100644
--- a/overlay/TvWifiOverlay/Android.bp
+++ b/overlay/TvWifiOverlay/Android.bp
@@ -14,3 +14,10 @@ runtime_resource_overlay {
product_specific: true,
sdk_version: "current",
}
+
+override_runtime_resource_overlay {
+ name: "TvWifiOverlayGoogle",
+ base: "TvWifiOverlay",
+ package_name: "com.google.android.tv.overlay.wifi.resources",
+ target_package_name: "com.google.android.wifi.resources",
+} \ No newline at end of file
diff --git a/overlay/TvWifiOverlay/AndroidManifest.xml b/overlay/TvWifiOverlay/AndroidManifest.xml
index f0372be..9ea1edf 100644
--- a/overlay/TvWifiOverlay/AndroidManifest.xml
+++ b/overlay/TvWifiOverlay/AndroidManifest.xml
@@ -23,7 +23,7 @@
<overlay
android:targetPackage="com.android.wifi.resources"
android:targetName="WifiCustomization"
- android:priority="-1"
+ android:priority="0"
android:isStatic="true" />
</manifest>
diff --git a/overlay/TvWifiOverlay/res/values/config.xml b/overlay/TvWifiOverlay/res/values/config.xml
index d800492..679995b 100644
--- a/overlay/TvWifiOverlay/res/values/config.xml
+++ b/overlay/TvWifiOverlay/res/values/config.xml
@@ -20,4 +20,22 @@
believe it's rare to have multiple Wifi networks configured whose
quality changes over time. -->
<bool name="config_wifi_framework_enable_associated_network_selection">false</bool>
+
+ <!-- Enable the Software PNO feature. If Hardware PNO is not enabled, the device is in
+ standby and Wifi is disconnected, periodic scans are performed according to the
+ following schedule:
+ 1) config_wifiSwPnoMobilityStateTimerIterations iterations with initial interval provided by
+ the mobility status (see config_wifiMovingPnoScanIntervalMillis and
+ config_wifiStationaryPnoScanIntervalMillis for the default values respectively for moving and
+ stationary devices). At each iteration the interval is increased proportionally to the elapsed
+ iterations. The device is awakened even if currently in doze/idle mode.
+ 2) config_wifiSwPnoFastTimerIterations iterations with initial interval provided by
+ config_wifiSwPnoFastTimerMs. The device is awakened even if currently in doze/idle mode.
+ 3) config_wifiSwPnoSlowTimerIterations iterations with initial provided by
+ config_wifiSwPnoSlowTimerMs and a window of config_wifiSwPnoSlowTimerMargin.
+ Inside such time window, the device is not awakened to perform the scan. If a wakeup happens for
+ other reasons, the scan might be performed as well in order to avoid subsequent awakening.
+ If no spontaneous awakening happens at the end of the time window, the device is awakened to
+ perform the scan.-->
+ <bool translatable="false" name="config_wifiSwPnoEnabled">true</bool>
</resources>
diff --git a/permissions/tv_core_hardware.xml b/permissions/tv_core_hardware.xml
index 565f72c..b914510 100644
--- a/permissions/tv_core_hardware.xml
+++ b/permissions/tv_core_hardware.xml
@@ -31,7 +31,7 @@
<feature name="android.software.leanback_only" />
<feature name="android.software.live_tv" />
<feature name="android.software.picture_in_picture" notLowRam="true" />
- <feature name="android.software.activities_on_secondary_displays" notLowRam="true" />
+ <feature name="android.software.expanded_picture_in_picture" notLowRam="true" />
<feature name="android.software.voice_recognizers" />
<feature name="android.software.input_methods" />
<feature name="android.software.autofill" />
diff --git a/products/AndroidProducts.mk b/products/AndroidProducts.mk
index 110cf2f..390ed4f 100644
--- a/products/AndroidProducts.mk
+++ b/products/AndroidProducts.mk
@@ -32,10 +32,8 @@
#
PRODUCT_MAKEFILES := \
- $(LOCAL_DIR)/aosp_tv_arm.mk \
$(LOCAL_DIR)/aosp_tv_arm64.mk \
$(LOCAL_DIR)/aosp_tv_x86.mk \
$(LOCAL_DIR)/gsi_tv_arm.mk \
$(LOCAL_DIR)/gsi_tv_arm64.mk \
- $(LOCAL_DIR)/sdk_atv_armv7.mk \
$(LOCAL_DIR)/sdk_atv_x86.mk
diff --git a/products/aosp_tv_arm.mk b/products/aosp_tv_arm.mk
deleted file mode 100644
index ca26028..0000000
--- a/products/aosp_tv_arm.mk
+++ /dev/null
@@ -1,86 +0,0 @@
-#
-# Copyright (C) 2020 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# Generate source.properties for image target
-PRODUCT_SDK_ADDON_SYS_IMG_SOURCE_PROP := \
- device/google/atv/sdk/images_source.prop_template
-
-PRODUCT_USE_DYNAMIC_PARTITIONS := true
-
-# The system image of aosp_tv_arm-userdebug is a GSI for the devices with:
-# - ARM 32 bits user space
-# - 64 bits binder interface
-# - VNDK enforcement
-# - compatible property override enabled
-
-#
-# All components inherited here go to system image
-#
-$(call inherit-product, device/google/atv/products/atv_generic_system.mk)
-
-# Enable mainline checking for excat this product name
-ifeq (aosp_tv_arm,$(TARGET_PRODUCT))
-PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed
-endif
-
-#
-# All components inherited here go to system_ext image
-#
-$(call inherit-product, device/google/atv/products/atv_system_ext.mk)
-# Packages required for ATV GSI
-PRODUCT_PACKAGES += \
- TvProvision
-
-#
-# All components inherited here go to product image
-#
-$(call inherit-product, device/google/atv/products/atv_product.mk)
-# Packages required for ATV GSI
-PRODUCT_PACKAGES += \
- LeanbackIME \
- TvSampleLeanbackLauncher
-
-#
-# All components inherited here go to vendor image
-#
-$(call inherit-product, device/google/atv/products/atv_emulator_vendor.mk)
-$(call inherit-product-if-exists, device/generic/goldfish/arm32-vendor.mk)
-$(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86/device.mk)
-
-#
-# Special settings for GSI releasing
-#
-ifeq (aosp_tv_arm,$(TARGET_PRODUCT))
-$(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk)
-PRODUCT_BUILD_SYSTEM_IMAGE := true
-PRODUCT_BUILD_SYSTEM_OTHER_IMAGE := false
-PRODUCT_BUILD_VENDOR_IMAGE := false
-PRODUCT_BUILD_PRODUCT_IMAGE := false
-PRODUCT_BUILD_PRODUCT_SERVICES_IMAGE := false
-PRODUCT_BUILD_ODM_IMAGE := false
-PRODUCT_BUILD_CACHE_IMAGE := false
-PRODUCT_BUILD_RAMDISK_IMAGE := true
-PRODUCT_BUILD_USERDATA_IMAGE := false
-PRODUCT_BUILD_BOOT_IMAGE := false
-PRODUCT_BUILD_DEBUG_BOOT_IMAGE := false
-PRODUCT_BUILD_VENDOR_BOOT_IMAGE := false
-PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE := false
-endif
-
-PRODUCT_NAME := aosp_tv_arm
-PRODUCT_DEVICE := generic
-PRODUCT_BRAND := Android
-PRODUCT_MODEL := AOSP TV on ARM
diff --git a/products/atv_emulator_vendor.mk b/products/atv_emulator_vendor.mk
index 4b12dd3..4659e0b 100644
--- a/products/atv_emulator_vendor.mk
+++ b/products/atv_emulator_vendor.mk
@@ -18,11 +18,15 @@
# TV emulator-related modules to PRODUCT_PACKAGES.
#
-# ATV SDK is not designed to have a GNSS-receiver by default
EMULATOR_VENDOR_NO_GNSS := true
+EMULATOR_VENDOR_NO_BIOMETRICS := true
+EMULATOR_VENDOR_NO_SENSORS := true
$(call inherit-product, device/google/atv/products/atv_vendor.mk)
+# Sets HDMI CEC as Playback Device.
+PRODUCT_PROPERTY_OVERRIDES += ro.hdmi.device_type=4
+
# need this for gles libraries to load properly
# after moving to /vendor/lib/
PRODUCT_PACKAGES += \
@@ -42,12 +46,8 @@ PRODUCT_COPY_FILES += \
PRODUCT_COPY_FILES += \
device/generic/goldfish/data/etc/apns-conf.xml:$(TARGET_COPY_OUT_VENDOR)/etc/apns-conf.xml \
- device/generic/goldfish/camera/media_codecs.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs.xml \
- frameworks/av/media/libstagefright/data/media_codecs_google_audio.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_google_audio.xml \
+ device/generic/goldfish/camera/media/media_codecs_google_tv.xml:${TARGET_COPY_OUT_VENDOR}/etc/media_codecs_google_tv.xml \
frameworks/native/data/etc/android.hardware.ethernet.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.ethernet.xml \
- frameworks/av/media/libstagefright/data/media_codecs_google_telephony.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_google_telephony.xml \
- frameworks/av/media/libstagefright/data/media_codecs_google_tv.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_google_tv.xml \
- frameworks/av/media/libstagefright/data/media_codecs_google_video.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_google_video.xml \
hardware/libhardware_legacy/audio/audio_policy.conf:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy.conf
# Exclude all non-default hardware features on ATV SDK.
@@ -56,7 +56,7 @@ PRODUCT_COPY_FILES += \
device/google/atv/permissions/tv_sdk_excluded_core_hardware.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/tv_sdk_excluded_core_hardware.xml
# goldfish vendor partition configurations
-$(call inherit-product-if-exists, device/generic/goldfish/vendor.mk)
+$(call inherit-product-if-exists, device/generic/goldfish/64bitonly/product/vendor.mk)
#watchdog tiggers reboot because location service is not
#responding, disble it for now.
diff --git a/products/atv_generic_system.mk b/products/atv_generic_system.mk
index 7985ef8..d14f2a1 100644
--- a/products/atv_generic_system.mk
+++ b/products/atv_generic_system.mk
@@ -40,7 +40,7 @@ PRODUCT_PACKAGES += \
# System libraries commonly depended on by things on the system_ext or product partitions.
# These lists will be pruned periodically.
PRODUCT_PACKAGES += \
- android.hardware.wifi@1.0 \
+ android.hardware.wifi \
libaudio-resampler \
libaudiohal \
libdrm \
@@ -60,10 +60,11 @@ PRODUCT_PACKAGES += \
PRODUCT_PACKAGES_DEBUG += \
avbctl \
bootctl \
- tinyplay \
tinycap \
+ tinyhostless \
tinymix \
tinypcminfo \
+ tinyplay \
update_engine_client
PRODUCT_HOST_PACKAGES += \
diff --git a/products/atv_lowram_defaults.mk b/products/atv_lowram_defaults.mk
index 182e129..7ce3557 100644
--- a/products/atv_lowram_defaults.mk
+++ b/products/atv_lowram_defaults.mk
@@ -34,13 +34,6 @@ PRODUCT_DEX_PREOPT_BOOT_IMAGE_PROFILE_LOCATION := frameworks/base/config/boot-im
# Do not generate libartd.
PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD := false
-# Do not spin up a separate process for the network stack on low ram devices, use an in-process APK.
-ifneq ($(PRODUCT_IS_ATV_SDK),true)
- PRODUCT_PACKAGES += InProcessNetworkStack
- PRODUCT_PACKAGES += PlatformCaptivePortalLogin
- PRODUCT_PACKAGES += com.android.tethering.inprocess
-endif
-
# Strip the local variable table and the local variable type table to reduce
# the size of the system image. This has no bearing on stack traces, but will
# leave less information available via JDWP.
diff --git a/products/atv_product.mk b/products/atv_product.mk
index 40e80fa..4c38e84 100644
--- a/products/atv_product.mk
+++ b/products/atv_product.mk
@@ -17,14 +17,20 @@
# a generic TV device.
$(call inherit-product, $(SRC_TARGET_DIR)/product/media_product.mk)
-$(call inherit-product-if-exists, frameworks/base/data/sounds/AudioTv.mk)
-
PRODUCT_PUBLIC_SEPOLICY_DIRS += device/google/atv/audio_proxy/sepolicy/public
PRODUCT_PACKAGES += \
+ TvNetworkStackOverlay \
TvFrameworkOverlay \
TvSettingsProviderOverlay \
- TvWifiOverlay
+ TvWifiOverlay \
+ SettingsIntelligence
+
+# Override com.android.* overlays with com.google.android.* overlays for mainline
+ifeq ($(PRODUCT_IS_ATV_MAINLINE), true)
+PRODUCT_PACKAGES += \
+ TvWifiOverlayGoogle
+endif
PRODUCT_COPY_FILES += \
device/google/atv/atv-component-overrides.xml:$(TARGET_COPY_OUT_PRODUCT)/etc/sysconfig/atv-component-overrides.xml
diff --git a/products/atv_system.mk b/products/atv_system.mk
index 5515f3e..84bfa68 100644
--- a/products/atv_system.mk
+++ b/products/atv_system.mk
@@ -47,7 +47,6 @@ endif
# From build/target/product/core.mk
PRODUCT_PACKAGES += \
BasicDreams \
- Bluetooth \
CalendarProvider \
CaptivePortalLogin \
CertInstaller \
@@ -65,6 +64,10 @@ PRODUCT_PACKAGES += \
VpnDialogs \
com.android.media.tv.remoteprovider
+# Use TV PackageInstaller
+PRODUCT_PACKAGES += \
+ PackageInstaller_tv
+
# Device owner provisioning for devices defining device_admin
PRODUCT_PACKAGES += \
ManagedProvisioning
@@ -79,6 +82,7 @@ PRODUCT_PACKAGES += \
PRODUCT_SUPPORTS_CAMERA ?= true
ifeq ($(PRODUCT_SUPPORTS_CAMERA),true)
PRODUCT_PACKAGES += cameraserver
+ PRODUCT_PACKAGES += CameraExtensionsProxy
else
# When cameraserver is not included, we need to configure Camera API to not
# connect to it.
@@ -114,4 +118,4 @@ PRODUCT_COPY_FILES += \
device/google/atv/permissions/tv_core_hardware.xml:system/etc/permissions/tv_core_hardware.xml
PRODUCT_COPY_FILES += \
- frameworks/av/media/libeffects/data/audio_effects.conf:system/etc/audio_effects.conf
+ frameworks/av/media/libeffects/data/audio_effects.xml:system/etc/audio_effects.xml
diff --git a/products/gsi_tv_base.mk b/products/gsi_tv_base.mk
index acda9fb..d9576ab 100644
--- a/products/gsi_tv_base.mk
+++ b/products/gsi_tv_base.mk
@@ -45,6 +45,8 @@ $(call inherit-product, device/google/atv/products/atv_product.mk)
PRODUCT_PACKAGES += \
LeanbackIME \
TvSampleLeanbackLauncher
+# Specify product type
+PRODUCT_CHARACTERISTICS := tv
#
# Special settings for GSI releasing
diff --git a/products/sdk_atv_armv7.mk b/products/sdk_atv_arm64.mk
index e161a3b..f887f8b 100644
--- a/products/sdk_atv_armv7.mk
+++ b/products/sdk_atv_arm64.mk
@@ -1,5 +1,5 @@
#
-# Copyright (C) 2014 The Android Open Source Project
+# 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.
@@ -13,21 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-
PRODUCT_IS_ATV_SDK := true
-QEMU_USE_SYSTEM_EXT_PARTITIONS := true
-
-$(call inherit-product, device/google/atv/products/aosp_tv_arm.mk)
-
-# keep this apk for sdk targets for now
-PRODUCT_PACKAGES += \
- EmulatorSmokeTests
-
# Overrides
PRODUCT_BRAND := Android
-PRODUCT_NAME := sdk_atv_armv7
-PRODUCT_DEVICE := generic
-
-PRODUCT_PRODUCT_PROPERTIES += \
- ro.oem.key1=ATV00100020
+PRODUCT_NAME := sdk_atv_arm64
+PRODUCT_DEVICE := generic_arm64
diff --git a/products/sdk_atv_x86.mk b/products/sdk_atv_x86.mk
index 57555ca..dde3edb 100644
--- a/products/sdk_atv_x86.mk
+++ b/products/sdk_atv_x86.mk
@@ -15,8 +15,6 @@
#
PRODUCT_IS_ATV_SDK := true
-QEMU_USE_SYSTEM_EXT_PARTITIONS := true
-
$(call inherit-product, device/google/atv/products/aosp_tv_x86.mk)
# keep this apk for sdk targets for now
diff --git a/sdk/images_armeabi-v7a_source.prop_template b/sdk/images_armeabi-v7a_source.prop_template
index 3c6a41e..d38fe89 100644
--- a/sdk/images_armeabi-v7a_source.prop_template
+++ b/sdk/images_armeabi-v7a_source.prop_template
@@ -1,6 +1,6 @@
Pkg.Desc=Android SDK Platform ${PLATFORM_VERSION}
Pkg.UserSrc=false
-Pkg.Revision=3
+Pkg.Revision=1
AndroidVersion.ApiLevel=${PLATFORM_SDK_VERSION}
AndroidVersion.CodeName=${PLATFORM_VERSION_CODENAME}
SystemImage.Abi=armeabi-v7a
diff --git a/sdk/images_source.prop_template b/sdk/images_source.prop_template
index a05bc28..8972e78 100644
--- a/sdk/images_source.prop_template
+++ b/sdk/images_source.prop_template
@@ -1,6 +1,6 @@
Pkg.Desc=Android SDK Platform ${PLATFORM_VERSION}
Pkg.UserSrc=false
-Pkg.Revision=3
+Pkg.Revision=1
Pkg.Dependencies=emulator#28.1.6
AndroidVersion.ApiLevel=${PLATFORM_SDK_VERSION}
AndroidVersion.CodeName=${PLATFORM_VERSION_CODENAME}