diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-07-07 04:47:16 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-07-07 04:47:16 +0000 |
commit | 2df05e4505990f438e928ae5c597b5383d57832f (patch) | |
tree | 0ebc0230124cdaa344d382be5ca21b0d3618c09e | |
parent | e3602e4250f7d950fab50be45574fdba80b37819 (diff) | |
parent | 4f1d7f1fcc787d6fb54e2255405e30477c0d00c4 (diff) | |
download | atv-android14-mainline-media-release.tar.gz |
Snap for 10453563 from 4f1d7f1fcc787d6fb54e2255405e30477c0d00c4 to mainline-media-releaseaml_med_341619000aml_med_341513600aml_med_341312300aml_med_341312020aml_med_341111000aml_med_341011000aml_med_340922010android14-mainline-media-release
Change-Id: Ie7b8d11784f2429d797fe482c600b3f43647d256
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 {} @@ -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 Binary files differdeleted file mode 100644 index 6afbf52..0000000 --- a/TvSampleLeanbackLauncher/TvSampleLeanbackLauncher.apk +++ /dev/null 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 Binary files differnew file mode 100644 index 0000000..71ccedd --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_0_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..62983e6 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_1_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..83f5f60 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_2_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..b9a1257 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_3_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..83eec97 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_4_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..c40f219 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_0_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..ced92d4 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_1_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..8411980 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_2_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..71bc360 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_3_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..98b9177 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_4_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..3a956e6 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_sim_launcher.png diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_null_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_null_launcher.png Binary files differnew file mode 100644 index 0000000..ee0ec27 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_null_launcher.png diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_off_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_off_launcher.png Binary files differnew file mode 100644 index 0000000..17bc81a --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_off_launcher.png diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ethernet_active_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ethernet_active_launcher.png Binary files differnew file mode 100644 index 0000000..ef43441 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ethernet_active_launcher.png 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 Binary files differnew file mode 100644 index 0000000..1ce500a --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ethernet_no_internet_launcher.png diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ic_notifications.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ic_notifications.png Binary files differnew file mode 100644 index 0000000..cbd0c1e --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ic_notifications.png 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 Binary files differnew file mode 100644 index 0000000..9879210 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_0_launcher.png 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 Binary files differnew file mode 100644 index 0000000..709d421 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_1_launcher.png 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 Binary files differnew file mode 100644 index 0000000..98a35c8 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_2_launcher.png 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 Binary files differnew file mode 100644 index 0000000..6036042 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_3_launcher.png 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 Binary files differnew file mode 100644 index 0000000..383c2ba --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_4_launcher.png 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 Binary files differnew file mode 100644 index 0000000..eda95d9 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_no_internet_launcher.png 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 Binary files differnew file mode 100644 index 0000000..d95a67d --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_not_connected_launcher.png 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 Binary files differnew file mode 100644 index 0000000..2ae8ade --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_0_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..709e05e --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_1_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..9f4a7de --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_2_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..64d2479 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_3_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..d3c6afd --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_4_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..8dc8813 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_0_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..a43642a --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_1_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..88747c3 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_2_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..bce337b --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_3_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..6a4fb75 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_4_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..23035f4 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_sim_launcher.png diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_null_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_null_launcher.png Binary files differnew file mode 100644 index 0000000..ee23581 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_null_launcher.png diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_off_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_off_launcher.png Binary files differnew file mode 100644 index 0000000..2b666da --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_off_launcher.png diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ethernet_active_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ethernet_active_launcher.png Binary files differnew file mode 100644 index 0000000..08a16e3 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ethernet_active_launcher.png 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 Binary files differnew file mode 100644 index 0000000..1184777 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ethernet_no_internet_launcher.png diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ic_notifications.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ic_notifications.png Binary files differnew file mode 100644 index 0000000..9718ccf --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ic_notifications.png 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 Binary files differnew file mode 100644 index 0000000..cac8e26 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_0_launcher.png 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 Binary files differnew file mode 100644 index 0000000..8b69f9b --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_1_launcher.png 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 Binary files differnew file mode 100644 index 0000000..178aa8a --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_2_launcher.png 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 Binary files differnew file mode 100644 index 0000000..ccd5f0b --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_3_launcher.png 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 Binary files differnew file mode 100644 index 0000000..a694fd8 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_4_launcher.png 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 Binary files differnew file mode 100644 index 0000000..eb57262 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_no_internet_launcher.png 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 Binary files differnew file mode 100644 index 0000000..e270196 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_not_connected_launcher.png 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 Binary files differnew file mode 100644 index 0000000..55e4423 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_0_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..8085ee6 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_1_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..4543409 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_2_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..8e0d081 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_3_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..87abeb1 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_4_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..6a1c11c --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_0_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..82f9a06 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_1_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..89e8be7 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_2_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..2bf5e9d --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_3_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..e6e16ff --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_4_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..73cd783 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_sim_launcher.png diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_null_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_null_launcher.png Binary files differnew file mode 100644 index 0000000..b140005 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_null_launcher.png diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_off_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_off_launcher.png Binary files differnew file mode 100644 index 0000000..3246266 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_off_launcher.png diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ethernet_active_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ethernet_active_launcher.png Binary files differnew file mode 100644 index 0000000..ee00c2b --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ethernet_active_launcher.png 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 Binary files differnew file mode 100644 index 0000000..bcf6645 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ethernet_no_internet_launcher.png diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ic_notifications.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ic_notifications.png Binary files differnew file mode 100644 index 0000000..1e70b63 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ic_notifications.png 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 Binary files differnew file mode 100644 index 0000000..e1a1b28 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_0_launcher.png 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 Binary files differnew file mode 100644 index 0000000..8efbf08 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_1_launcher.png 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 Binary files differnew file mode 100644 index 0000000..bb64d06 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_2_launcher.png 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 Binary files differnew file mode 100644 index 0000000..ed91075 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_3_launcher.png 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 Binary files differnew file mode 100644 index 0000000..d1dce01 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_4_launcher.png 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 Binary files differnew file mode 100644 index 0000000..c2f34d5 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_no_internet_launcher.png 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 Binary files differnew file mode 100644 index 0000000..3ea207e --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_not_connected_launcher.png 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 Binary files differnew file mode 100644 index 0000000..e89adac --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_0_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..b9b5c05 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_1_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..33c3f0c --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_2_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..bc9f70a --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_3_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..6e55476 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_4_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..ce014ca --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_0_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..efc8f83 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_1_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..c77aeb8 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_2_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..e563533 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_3_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..d0318c6 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_4_bar_launcher.png 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 Binary files differnew file mode 100644 index 0000000..0884e3c --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_sim_launcher.png diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_null_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_null_launcher.png Binary files differnew file mode 100644 index 0000000..72d13cb --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_null_launcher.png diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_off_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_off_launcher.png Binary files differnew file mode 100644 index 0000000..0177906 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_off_launcher.png diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ethernet_active_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ethernet_active_launcher.png Binary files differnew file mode 100644 index 0000000..06dc98b --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ethernet_active_launcher.png 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 Binary files differnew file mode 100644 index 0000000..d71f28b --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ethernet_no_internet_launcher.png diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ic_notifications.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ic_notifications.png Binary files differnew file mode 100644 index 0000000..c5fb9ef --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ic_notifications.png 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 Binary files differnew file mode 100755 index 0000000..988c426 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_0_launcher.png 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 Binary files differnew file mode 100755 index 0000000..c8a42b7 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_1_launcher.png 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 Binary files differnew file mode 100755 index 0000000..133ca1d --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_2_launcher.png 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 Binary files differnew file mode 100755 index 0000000..1752147 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_3_launcher.png 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 Binary files differnew file mode 100755 index 0000000..a9cf1da --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_4_launcher.png 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 Binary files differnew file mode 100644 index 0000000..25f1cdc --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_no_internet_launcher.png 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 Binary files differnew file mode 100644 index 0000000..781e512 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_not_connected_launcher.png 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 Binary files differnew file mode 100644 index 0000000..cde69bc --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/TvSampleLeanbackLauncher/src/main/res/mipmap-mdpi/ic_launcher.png b/TvSampleLeanbackLauncher/src/main/res/mipmap-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..c133a0c --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/TvSampleLeanbackLauncher/src/main/res/mipmap-xhdpi/ic_launcher.png b/TvSampleLeanbackLauncher/src/main/res/mipmap-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..bfa42f0 --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/TvSampleLeanbackLauncher/src/main/res/mipmap-xxhdpi/ic_launcher.png b/TvSampleLeanbackLauncher/src/main/res/mipmap-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..324e72c --- /dev/null +++ b/TvSampleLeanbackLauncher/src/main/res/mipmap-xxhdpi/ic_launcher.png 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 > System > 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“ > „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 > System > 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 > System > 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 > 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 > System > 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 > 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 > System > 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 > System > 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 میتوانید اولویتها را در «تنظیمات > سیستم > دسترسپذیری» تنظیم کنید."</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 Параметрлерди тууралоо үчүн Жөндөөлөр > Тутум > Атайын мүмкүнчүлүктөргө өтүңүз."</string> - <string name="accessibility_shortcut_toogle_warning" msgid="6107141001991769734">"Атайын мүмкүнчүлүктөр функциясын пайдалануу үчүн ал күйгүзүлгөндө, үндү катуулатып/акырындаткан эки баскычты тең 3 секунддай коё бербей басып туруңуз.\n\n Учурдагы атайын мүмкүнчүлүктөрдүн жөндөөлөрү:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Жөндөөлөр > Атайын мүмкүнчүлүктөр бөлүмүнөн өзгөртө аласыз."</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 Параметрлерди тууралоо үчүн Параметрлер > Тутум > Атайын мүмкүнчүлүктөргө өтүңүз."</string> + <string name="accessibility_shortcut_toogle_warning" msgid="6107141001991769734">"Атайын мүмкүнчүлүктөр функциясын пайдалануу үчүн ал күйгүзүлгөндө, үндү катуулатып/акырындаткан эки баскычты тең 3 секунддай коё бербей басып туруңуз.\n\n Учурдагы атайын мүмкүнчүлүктөрдүн параметрлери:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Параметрлер > Атайын мүмкүнчүлүктөр бөлүмүнөн өзгөртө аласыз."</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 Параметрлерди тууралоо үчүн Жөндөөлөр > Тутум > Атайын мүмкүнчүлүктөргө өтүңүз."</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 Параметрлерди тууралоо үчүн Параметрлер > Тутум > Атайын мүмкүнчүлүктөргө өтүңүз."</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 ଆପଣ ସେଟିଂସ୍ > ସିଷ୍ଟମ୍ > ଆକ୍ସେସିବିଲିଟୀରେ ଆପଣଙ୍କ ପସନ୍ଦଗୁଡ଼ିକୁ ଆଡଜଷ୍ଟ କରିପାରିବେ।"</string> - <string name="accessibility_shortcut_toogle_warning" msgid="6107141001991769734">"ସର୍ଟକଟ୍ ଚାଲୁ ଥିବା ବେଳେ, ଉଭୟ ବ୍ୟାକ୍ ଓ ଡାଉନ୍ ବଟନକୁ 3 ସେକେଣ୍ଡ ପାଇଁ ଦବାଇବା ଫଳରେ ଏକ ଆକ୍ସେସିବିଲିଟୀ ଫିଚର୍ ଆରମ୍ଭ ହେବ।\n\n ବର୍ତ୍ତମାନର ଆକ୍ସେସିବିଲିଟୀ ଫିଚର୍:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n ଆପଣ ସେଟିଂସ୍ > ଆକ୍ସେସିବିଲିଟୀରେ ଫିଚରକୁ ପରିବର୍ତ୍ତନ କରିପାରିବେ।"</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 ଆପଣ ସେଟିଂସ > ସିଷ୍ଟମ > ଆକ୍ସେସିବିଲିଟୀରେ ଆପଣଙ୍କ ପସନ୍ଦଗୁଡ଼ିକୁ ଆଡଜଷ୍ଟ କରିପାରିବେ।"</string> + <string name="accessibility_shortcut_toogle_warning" msgid="6107141001991769734">"ସର୍ଟକଟ ଚାଲୁ ଥିବା ବେଳେ, ଉଭୟ ବ୍ୟାକ ଓ ଡାଉନ ବଟନକୁ 3 ସେକେଣ୍ଡ ପାଇଁ ଦବାଇବା ଫଳରେ ଏକ ଆକ୍ସେସିବିଲିଟୀ ଫିଚର ଆରମ୍ଭ ହେବ।\n\n ବର୍ତ୍ତମାନର ଆକ୍ସେସିବିଲିଟୀ ଫିଚର:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n ଆପଣ ସେଟିଂସ > ଆକ୍ସେସିବିଲିଟୀରେ ଫିଚରକୁ ପରିବର୍ତ୍ତନ କରିପାରିବେ।"</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 > Sistem > 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 > Sistem > 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 > Sistem > 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 > Sistem > 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} |