### Enable Emulator Setup Wizard Source: https://developer.android.com/training/wearables/apps/test-bluetooth-audio Enable the setup wizard from the command line to pair with an emulated phone. ```bash -append-userspace-opt androidboot.setupwizard_mode=REQUIRED ``` -------------------------------- ### Install Splash Screen in Starting Activity Source: https://developer.android.com/training/wearables/apps/splash-screen Integrate the splash screen functionality into your starting activity by calling `installSplashScreen()` before `super.onCreate()`. ```kotlin class SplashScreenActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { installSplashScreen() super.onCreate(savedInstanceState) setContent { WearApp() } } } SplashScreenActivity.kt ``` -------------------------------- ### Run A2DP Sink Example (file output) Source: https://developer.android.com/training/wearables/apps/test-bluetooth-audio Run the A2DP sink example to redirect audio output to a file named 'output.sbc'. ```bash python3 examples/run_a2dp_sink.py examples/a2dp_sink1.json \ android-netsim output.sbc ``` -------------------------------- ### Example Gradle Build File (Kotlin) Source: https://developer.android.com/training/testing/espresso/setup A complete example of an app's build.gradle file including Espresso dependencies and the instrumentation runner. ```Kotlin plugins { id("com.android.application") } android { compileSdkVersion(36) defaultConfig { applicationId = "com.my.awesome.app" minSdkVersion(23) targetSdkVersion(36) versionCode = 1 versionName = "1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } } dependencies { androidTestImplementation('androidx.test:runner:1.6.1') androidTestImplementation('androidx.test.espresso:espresso-core:3.6.1') } ``` -------------------------------- ### Example Gradle Build File (Groovy) Source: https://developer.android.com/training/testing/espresso/setup A complete example of an app's build.gradle file including Espresso dependencies and the instrumentation runner. ```Groovy plugins { id 'com.android.application' } android { compileSdkVersion 36 defaultConfig { applicationId "com.my.awesome.app" minSdkVersion 23 targetSdkVersion 36 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } } dependencies { androidTestImplementation 'androidx.test:runner:1.6.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' } ``` -------------------------------- ### Run A2DP Sink Example (stdout) Source: https://developer.android.com/training/wearables/apps/test-bluetooth-audio Run the A2DP sink example from the bumble directory to discover and connect to the emulated speaker. Audio output is played through your computer's speakers. ```bash python3 examples/run_a2dp_sink.py examples/a2dp_sink1.json \ android-netsim stdout | ffplay -i ``` -------------------------------- ### Building a Preview Program Source: https://developer.android.com/training/tv/discovery/audio-programs Example of how to use `PreviewProgram.Builder` to construct an audio program object. ```APIDOC ## Building a Preview Program Use `PreviewProgram.Builder` to build a program. You can read more about possible values for each field in the Java documentation for each setter on the builder. The following example shows how to use `PreviewProgram.Builder`: ```java PreviewProgram program = new PreviewProgram.Builder() .setChannelId(channelId) .setTitle(clip.getTitle()) .setDescription(clip.getDescription()) .setType(TvContractCompat.PreviewPrograms.TYPE_ALBUM) // Set required attributes .build(); ``` ``` -------------------------------- ### Configure and start an exercise Source: https://developer.android.com/training/wearables/health-services/active-data Create an ExerciseConfig to specify exercise type, desired data types, goals, and milestones. Use this to start an exercise session. ```kotlin const val CALORIES_THRESHOLD = 250.0 const val DISTANCE_THRESHOLD = 1_000.0 // meters suspend fun startExercise() { // Types for which we want to receive metrics. val dataTypes = setOf( DataType.HEART_RATE_BPM, DataType.CALORIES_TOTAL, DataType.DISTANCE ) // Create a one-time goal. val calorieGoal = ExerciseGoal.createOneTimeGoal( DataTypeCondition( dataType = DataType.CALORIES_TOTAL, threshold = CALORIES_THRESHOLD, comparisonType = ComparisonType.GREATER_THAN_OR_EQUAL ) ) // Create a milestone goal. To make a milestone for every kilometer, set the initial // threshold to 1km and the period to 1km. val distanceGoal = ExerciseGoal.createMilestone( condition = DataTypeCondition( dataType = DataType.DISTANCE_TOTAL, threshold = DISTANCE_THRESHOLD, comparisonType = ComparisonType.GREATER_THAN_OR_EQUAL ), period = DISTANCE_THRESHOLD ) val config = ExerciseConfig( exerciseType = ExerciseType.RUNNING, dataTypes = dataTypes, isAutoPauseAndResumeEnabled = false, isGpsEnabled = true, exerciseGoals = mutableListOf>(calorieGoal, distanceGoal) ) exerciseClient.startExerciseAsync(config).await() } ``` -------------------------------- ### Setup LeanbackTabLayout and LeanbackViewPager in Java Source: https://developer.android.com/training/tv/playback/leanback/leanback-libraries Integrate LeanbackTabLayout with LeanbackViewPager to create tabbed interfaces, ensuring the adapter is set before setup. ```java LeanbackTabLayout leanbackTabLayout = findViewById(R.id.tab_layout); LeanbackViewPager leanbackViewPager = findViewById(R.id.view_pager); leanbackViewPager.setAdapter(adapter); leanbackTabLayout.setupWithViewPager(leanbackViewPager); ``` -------------------------------- ### Connect to Print Manager and Start Print Job (Kotlin) Source: https://developer.android.com/training/printing/custom-docs Get a PrintManager instance and start a print job by providing a name and a PrintDocumentAdapter implementation. The job name appears in the print queue. ```kotlin private fun doPrint() { activity?.also { context -> // Get a PrintManager instance val printManager = context.getSystemService(Context.PRINT_SERVICE) as PrintManager // Set job name, which will be displayed in the print queue val jobName = "${context.getString(R.string.app_name)} Document" // Start a print job, passing in a PrintDocumentAdapter implementation // to handle the generation of a print document printManager.print(jobName, MyPrintDocumentAdapter(context), null) } } ``` -------------------------------- ### Prepackage Database with Migrations (Java) Source: https://developer.android.com/training/data-storage/room/prepopulate This Java example demonstrates prepackaging a Room database using `createFromAsset()`, including custom migrations and destructive fallback behavior. ```java // Database class definition declaring version 4. @Database(version = 4) public abstract class AppDatabase extends RoomDatabase { ... } // Migration path definition from version 3 to version 4. static final Migration MIGRATION_3_4 = new Migration(3, 4) { @Override public void migrate(SupportSQLiteDatabase database) { ... } }; // Destructive migrations are enabled and a prepackaged database is // provided. Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db") .createFromAsset("database/myapp.db") .addMigrations(MIGRATION_3_4) .fallbackToDestructiveMigration() .build(); ``` -------------------------------- ### List installed watch faces Source: https://developer.android.com/training/wearables/watch-face-push/wear-os-app Use `listWatchFaces()` to get a list of installed watch faces and the remaining available slots. This helps determine if a new watch face can be added or if an existing one needs to be replaced. ```kotlin val response = watchFacePushManager.listWatchFaces() val installedList = response.installedWatchFaceDetails installedList.forEach { Log.i(TAG, "Installed watchface: ${it.packageName}") } val remainingSlots = response.remainingSlotCount Log.i(TAG, "Remaining slots: $remainingSlots") ``` -------------------------------- ### Start a MediaSession Source: https://developer.android.com/training/tv/playback/media-session Call setActive(true) when playback begins and request audio focus. This example shows how to activate the session and handle play requests. ```kotlin private fun handlePlayRequest() { tryToGetAudioFocus() if (!session.isActive) { session.isActive = true } ... } ``` ```java private void handlePlayRequest() { tryToGetAudioFocus(); if (!session.isActive()) { session.setActive(true); } ... } ``` -------------------------------- ### Prepackage Database with Migrations (Kotlin) Source: https://developer.android.com/training/data-storage/room/prepopulate Use `createFromAsset()` to load a prepackaged database. This example shows how to combine it with migrations and `fallbackToDestructiveMigration()` for handling version mismatches. ```kotlin // Database class definition declaring version 4. @Database(version = 4) abstract class AppDatabase : RoomDatabase() { ... } // Migration path definition from version 3 to version 4. val MIGRATION_3_4 = object : Migration(3, 4) { override fun migrate(database: SupportSQLiteDatabase) { ... } } // Destructive migrations are enabled and a prepackaged database is // provided. Room.databaseBuilder(appContext, AppDatabase::class.java, "Sample.db") .createFromAsset("database/myapp.db") .addMigrations(MIGRATION_3_4) .fallbackToDestructiveMigration() .build() ``` -------------------------------- ### Get Current Time-Shifting Position (Java) Source: https://developer.android.com/training/tv/tif/time-shifting Implement `onTimeShiftGetCurrentPosition()` to return the current playback position in milliseconds since the epoch. This is typically calculated by adding the player's current position to the program's start time. Ensure this value is never less than the start position. ```java @Override public long onTimeShiftGetCurrentPosition() { if (getTvPlayer() != null && currentProgram != null) { return getTvPlayer().getCurrentPosition() + currentProgram.getStartTimeUtcMillis(); } return TvInputManager.TIME_SHIFT_INVALID_TIME; } ``` -------------------------------- ### Get Current Time-Shifting Position (Kotlin) Source: https://developer.android.com/training/tv/tif/time-shifting Implement `onTimeShiftGetCurrentPosition()` to return the current playback position in milliseconds since the epoch. This is typically calculated by adding the player's current position to the program's start time. Ensure this value is never less than the start position. ```kotlin override fun onTimeShiftGetCurrentPosition(): Long = tvPlayer?.run { currentProgram?.let { program -> currentPosition + program.startTimeUtcMillis } } ?: TvInputManager.TIME_SHIFT_INVALID_TIME ``` -------------------------------- ### Query for SMS Apps Intent Source: https://developer.android.com/training/basics/intents/package-visibility-use-cases Include this intent to get information about installed SMS apps, such as the default SMS handler. Place it inside the element. ```xml ``` -------------------------------- ### Install and Run Android Test Orchestrator via ADB Source: https://developer.android.com/training/testing/instrumented-tests/androidx-test-libraries/runner Manually install the Android Test Orchestrator and test services using ADB, then run instrumentation tests with the orchestrator. This method is useful for command-line setups or when not using Gradle. Ensure to replace `com.example.test` with your test package name. ```bash DEVICE_API_LEVEL=$(adb shell getprop ro.build.version.sdk) FORCE_QUERYABLE_OPTION="" if [[ $DEVICE_API_LEVEL -ge 30 ]]; then FORCE_QUERYABLE_OPTION="--force-queryable" fi # uninstall old versions adb uninstall androidx.test.services adb uninstall androidx.test.orchestrator # Install the test orchestrator. adb install $FORCE_QUERYABLE_OPTION -r path/to/m2repository/androidx/test/orchestrator/1.4.2/orchestrator-1.4.2.apk # Install test services. adb install $FORCE_QUERYABLE_OPTION -r path/to/m2repository/androidx/test/services/test-services/1.4.2/test-services-1.4.2.apk # Replace "com.example.test" with the name of the package containing your tests. # Add "-e clearPackageData true" to clear your app's data in between runs. adb shell 'CLASSPATH=$(pm path androidx.test.services) app_process / \ androidx.test.services.shellexecutor.ShellMain am instrument -w -e \ targetInstrumentation com.example.test/androidx.test.runner.AndroidJUnitRunner \ androidx.test.orchestrator/.AndroidTestOrchestrator' ``` -------------------------------- ### Initialize BrowseSupportFragment (Kotlin) Source: https://developer.android.com/training/tv/playback/leanback/browse Basic setup for a BrowseSupportFragment in Kotlin, including lifecycle methods and helper function calls. ```kotlin class MainFragment : BrowseSupportFragment(), LoaderManager.LoaderCallbacks>> { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) loadVideoData() } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) prepareBackgroundManager() setupUIElements() setupEventListeners() } ... ``` -------------------------------- ### Prepopulate Room DB from File System (Java) Source: https://developer.android.com/training/data-storage/room/prepopulate Use `createFromFile()` to load a database from the device's file system. Ensure your app has read permissions for the file. ```java Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db") .createFromFile(new File("mypath")) .build(); ``` -------------------------------- ### UI Automator test scope Source: https://developer.android.com/training/testing/other-components/ui-automator Access UI Automator APIs within the `uiAutomator { ... }` block for a type-safe testing environment. This example starts an app and clicks an element with specific text. ```kotlin uiAutomator { // All your UI Automator actions go here startApp("com.example.targetapp") onElement { textAsString() == "Hello, World!" }.click() } ``` -------------------------------- ### Render Content Using VirtualDisplay and Presentation (Kotlin) Source: https://developer.android.com/training/cars/apps/library/draw-maps This example demonstrates how to render Views into the Surface using the VirtualDisplay and Presentation APIs. It includes setting up the virtual display and presentation, and handling surface availability and destruction. ```kotlin class HelloWorldSurfaceCallback(context: Context) : SurfaceCallback { lateinit var virtualDisplay: VirtualDisplay lateinit var presentation: Presentation override fun onSurfaceAvailable(surfaceContainer: SurfaceContainer) { virtualDisplay = context .getSystemService(DisplayManager::class.java) .createVirtualDisplay( VIRTUAL_DISPLAY_NAME , surfaceContainer.width, surfaceContainer.height, surfaceContainer.dpi, surfaceContainer.surface, 0 ) presentation = Presentation(context, virtualDisplay.display) // Instantiate the view to be used as the content view val view = ... presentation.setContentView(view) presentation.show() } override fun onSurfaceDestroyed(surfaceContainer: SurfaceContainer) { presentation.dismiss() // This handles releasing the Surface provided when creating the VirtualDisplay virtualDisplay.release() } } ``` -------------------------------- ### Variant with Animation Controls Source: https://developer.android.com/training/wearables/wff/common/variant/variant Example of using optional attributes like startOffset and duration to control the animation between interactive and ambient states. The animation starts halfway through the transition and uses the full available time. ```xml ``` -------------------------------- ### Initialize BrowseSupportFragment (Java) Source: https://developer.android.com/training/tv/playback/leanback/browse Basic setup for a BrowseSupportFragment in Java, including lifecycle methods and helper function calls. ```java public class MainFragment extends BrowseSupportFragment implements LoaderManager.LoaderCallbacks>> { } ... @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); loadVideoData(); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); prepareBackgroundManager(); setupUIElements(); setupEventListeners(); } ... ``` -------------------------------- ### Get Exercise Capabilities Source: https://developer.android.com/training/wearables/health-services/active-data Query the device capabilities at app startup to determine supported exercises, features, data types, and permissions. This example shows how to retrieve capabilities for a specific exercise type like RUNNING. ```kotlin val healthClient = HealthServices.getClient(this /*context*/) val exerciseClient = healthClient.exerciseClient lifecycleScope.launch { val capabilities = exerciseClient.getCapabilitiesAsync().await() if (ExerciseType.RUNNING in capabilities.supportedExerciseTypes) { runningCapabilities = capabilities.getExerciseTypeCapabilities(ExerciseType.RUNNING) } } ``` -------------------------------- ### Get Cipher Instance for AES CBC PKCS7 Padding in Kotlin Source: https://developer.android.com/training/sign-in/biometric-auth Obtain a Cipher instance configured for AES encryption with CBC block mode and PKCS7 padding. This is a common setup for secure data encryption. ```Kotlin private fun getCipher(): Cipher { return Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7) } ``` -------------------------------- ### Alle Laufzeitberechtigungen zu Testzwecken gewähren Source: https://developer.android.com/training/permissions/requesting?hl=de Verwenden Sie die Option `-g` mit dem Befehl `adb shell install`, um alle Laufzeitberechtigungen automatisch zu erteilen, wenn Sie eine App auf einem Emulator oder Testgerät installieren. ```bash adb shell install -g PATH_TO_APK_FILE ``` -------------------------------- ### Adapt Layout with Window Size Class in Compose Source: https://developer.android.com/training/multiscreen/screensizes Use the WindowManager library to get current window metrics and determine the appropriate window size class. This example shows how to decide whether to show a top app bar based on the window height. ```kotlin @Composable fun MyApp( windowSizeClass: WindowSizeClass = currentWindowAdaptiveInfo(supportLargeAndXLargeWidth = true).windowSizeClass ) { // Decide whether to show the top app bar based on window size class. val showTopAppBar = windowSizeClass.isHeightAtLeastBreakpoint(WindowSizeClass.HEIGHT_DP_MEDIUM_LOWER_BOUND) // MyScreen logic is based on the showTopAppBar boolean flag. MyScreen( showTopAppBar = showTopAppBar, /* ... */ ) } AdaptiveLayoutSnippets.kt ``` -------------------------------- ### App Lifecycle Management: Start, Clear, Intent Source: https://developer.android.com/training/testing/ui-automator APIs for controlling the app under test, including starting by package name, starting a specific activity, starting an intent, and clearing app data. ```kotlin // Start a specific app by package name. Used for benchmarking and other // self-instrumenting tests. startApp("com.example.targetapp") ``` ```kotlin // Start a specific activity within the target app startActivity(SomeActivity::class.java) ``` ```kotlin // Start an intent startIntent(myIntent) ``` ```kotlin // Clear the app's data (resets it to a fresh state) clearAppData("com.example.targetapp") ``` -------------------------------- ### Initialize BrowseSupportFragment in Java Source: https://developer.android.com/training/tv/playback/leanback/browse?hl=ar Sets up the BrowseSupportFragment by loading video data when the fragment is created. Requires activity context. ```java public class MainFragment extends BrowseSupportFragment implements LoaderManager.LoaderCallbacks>> { } ... @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); loadVideoData(); } ... private void loadVideoData() { VideoProvider.setContext(getActivity()); videosUrl = getString(R.string.catalog_url); getLoaderManager().initLoader(0, null, this); } ... ``` -------------------------------- ### Install Android Test Orchestrator Source: https://developer.android.com/training/testing/junit-runner Install the test services APK using adb. The FORCE_QUERYABLE_OPTION flag is used to force the installation of a queryable version of the APK. ```bash adb install $FORCE_QUERYABLE_OPTION -r path/to/m2repository/androidx/test/services/test-services/1.4.2/test-services-1.4.2.apk ``` -------------------------------- ### Start the Desktop Head Unit (DHU) Source: https://developer.android.com/training/cars/testing/dhu Navigate to the DHU directory in your SDK and run the executable. Use `desktop-head-unit.exe` on Windows or `./desktop-head-unit` on macOS/Linux. ```bash cd SDK_LOCATION/extras/google/auto desktop-head-unit.exe # Windows ``` ```bash ./desktop-head-unit # macOS or Linux ``` -------------------------------- ### Check if a watch face package is installed Source: https://developer.android.com/training/wearables/watch-face-push/wear-os-app A suspend function to check if a specific watch face package is currently installed by iterating through the details of installed watch faces. ```kotlin suspend fun isInstalled(packageName: String) = watchFacePushManager.listWatchFaces() .installedWatchFaceDetails.any { it.packageName == packageName } ``` -------------------------------- ### Prepopulate Room DB from File System (Kotlin) Source: https://developer.android.com/training/data-storage/room/prepopulate Use `createFromFile()` to load a database from the device's file system. Ensure your app has read permissions for the file. ```kotlin Room.databaseBuilder(appContext, AppDatabase::class.java, "Sample.db") .createFromFile(File("mypath")) .build() ``` -------------------------------- ### Initialize UiDevice and Launch App Source: https://developer.android.com/training/testing/other-components/ui-automator-legacy Initializes the UiDevice instance, navigates to the home screen, and launches a specified application. Ensures the test starts from a known state and waits for the app to become visible. Requires API level 18 or higher. ```kotlin import org.junit.Before import androidx.test.runner.AndroidJUnit4 import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.By import androidx.test.uiautomator.Until import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import android.content.Context import android.content.Intent import com.google.common.truth.Truth.assertThat import org.hamcrest.CoreMatchers.notNullValue import androidx.test.core.app.ApplicationProvider import androidx.test.filters.SdkSuppress private const val BASIC_SAMPLE_PACKAGE = "com.example.android.testing.uiautomator.BasicSample" private const val LAUNCH_TIMEOUT = 5000L private const val STRING_TO_BE_TYPED = "UiAutomator" @RunWith(AndroidJUnit4::class) @SdkSuppress(minSdkVersion = 18) class ChangeTextBehaviorTest2 { private lateinit var device: UiDevice @Before fun startMainActivityFromHomeScreen() { // Initialize UiDevice instance device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) // Start from the home screen device.pressHome() // Wait for launcher val launcherPackage: String = device.launcherPackageName assertThat(launcherPackage, notNullValue()) device.wait( Until.hasObject(By.pkg(launcherPackage).depth(0)), LAUNCH_TIMEOUT ) // Launch the app val context = ApplicationProvider.getApplicationContext() val intent = context.packageManager.getLaunchIntentForPackage( BASIC_SAMPLE_PACKAGE).apply { // Clear out any previous instances addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) } context.startActivity(intent) // Wait for the app to appear device.wait( Until.hasObject(By.pkg(BASIC_SAMPLE_PACKAGE).depth(0)), LAUNCH_TIMEOUT ) } } ``` ```java import org.junit.Before; import androidx.test.runner.AndroidJUnit4; import androidx.test.uiautomator.UiDevice; import androidx.test.uiautomator.By; import androidx.test.uiautomator.Until; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.platform.app.InstrumentationRegistry; import android.content.Context; import android.content.Intent; import com.google.common.truth.Truth.assertThat; import org.hamcrest.CoreMatchers.notNullValue; import androidx.test.core.app.ApplicationProvider; import androidx.test.filters.SdkSuppress; @RunWith(AndroidJUnit4.class) @SdkSuppress(minSdkVersion = 18) public class ChangeTextBehaviorTest { private static final String BASIC_SAMPLE_PACKAGE = "com.example.android.testing.uiautomator.BasicSample"; private static final int LAUNCH_TIMEOUT = 5000; private static final String STRING_TO_BE_TYPED = "UiAutomator"; private UiDevice device; @Before public void startMainActivityFromHomeScreen() { // Initialize UiDevice instance device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); // Start from the home screen device.pressHome(); // Wait for launcher final String launcherPackage = device.getLauncherPackageName(); assertThat(launcherPackage, notNullValue()); device.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), LAUNCH_TIMEOUT); // Launch the app Context context = ApplicationProvider.getApplicationContext(); final Intent intent = context.getPackageManager() .getLaunchIntentForPackage(BASIC_SAMPLE_SAMPLE_PACKAGE); // Clear out any previous instances intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); context.startActivity(intent); // Wait for the app to appear device.wait(Until.hasObject(By.pkg(BASIC_SAMPLE_PACKAGE).depth(0)), LAUNCH_TIMEOUT); } } ``` -------------------------------- ### Retry provider installation after dialog Source: https://developer.android.com/training/articles/security-gms-provider After a dialog is shown to the user for recovery, set a flag to retry the provider installation once the activity is resumed. This ensures the installation is attempted after the user has had a chance to interact with the dialog. ```java super.onActivityResult(requestCode, resultCode, data); if (requestCode == ERROR_DIALOG_REQUEST_CODE) { // Adding a fragment via GoogleApiAvailability.showErrorDialogFragment // before the instance state is restored throws an error. So instead, // set a flag here, which causes the fragment to delay until // onPostResume. retryProviderInstall = true; } ``` -------------------------------- ### Initialize BiometricPrompt and PromptInfo in Java Source: https://developer.android.com/training/sign-in/biometric-auth Set up the BiometricPrompt and its associated PromptInfo for user authentication. This includes defining the authentication callback and UI elements for the prompt. ```Java private Executor executor; private BiometricPrompt biometricPrompt; private BiometricPrompt.PromptInfo promptInfo; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); executor = ContextCompat.getMainExecutor(this); biometricPrompt = new BiometricPrompt(MainActivity.this, executor, new BiometricPrompt.AuthenticationCallback() { @Override public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) { super.onAuthenticationError(errorCode, errString); Toast.makeText(getApplicationContext(), "Authentication error: " + errString, Toast.LENGTH_SHORT) .show(); } @Override public void onAuthenticationSucceeded( @NonNull BiometricPrompt.AuthenticationResult result) { super.onAuthenticationSucceeded(result); Toast.makeText(getApplicationContext(), "Authentication succeeded!", Toast.LENGTH_SHORT).show(); } @Override public void onAuthenticationFailed() { super.onAuthenticationFailed(); Toast.makeText(getApplicationContext(), "Authentication failed", Toast.LENGTH_SHORT) .show(); } }); promptInfo = new BiometricPrompt.PromptInfo.Builder() .setTitle("Biometric login for my app") .setSubtitle("Log in using your biometric credential") .setNegativeButtonText("Use account password") .build(); // Prompt appears when user clicks "Log in". // Consider integrating with the keystore to unlock cryptographic operations, // if needed by your app. Button biometricLoginButton = findViewById(R.id.biometric_login); biometricLoginButton.setOnClickListener(view -> { biometricPrompt.authenticate(promptInfo); }); } ``` -------------------------------- ### Generate a GUID in Java Source: https://developer.android.com/training/articles/user-data-ids Use this snippet to generate a custom globally-unique ID (GUID) for your app instance when an FID is not practical. Ensure GUIDs are stored in internal storage to avoid cross-app linking concerns. ```java String uniqueID = UUID.randomUUID().toString(); ``` -------------------------------- ### Initialize BrowseSupportFragment in Kotlin Source: https://developer.android.com/training/tv/playback/leanback/browse?hl=ar Sets up the BrowseSupportFragment by loading video data when the fragment is created. Requires activity context. ```kotlin class MainFragment : BrowseSupportFragment(), LoaderManager.LoaderCallbacks>> { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) loadVideoData() } ... private fun loadVideoData() { VideoProvider.setContext(activity) videosUrl = getString(R.string.catalog_url) loaderManager.initLoader(0, null, this) } ... } ``` -------------------------------- ### Generate a GUID in Kotlin Source: https://developer.android.com/training/articles/user-data-ids Use this snippet to generate a custom globally-unique ID (GUID) for your app instance when an FID is not practical. Ensure GUIDs are stored in internal storage to avoid cross-app linking concerns. ```kotlin var uniqueID = UUID.randomUUID().toString() ``` -------------------------------- ### Initialize BrowseSupportFragment (Kotlin) Source: https://developer.android.com/training/tv/playback/browse This Kotlin code initializes the BrowseSupportFragment, loads video data, and sets up the UI elements and event listeners. ```kotlin class MainFragment : BrowseSupportFragment(), LoaderManager.LoaderCallbacks>> { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) loadVideoData() } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) prepareBackgroundManager() setupUIElements() setupEventListeners() } ... private fun prepareBackgroundManager() { backgroundManager = BackgroundManager.getInstance(activity).apply { attach(activity?.window) } defaultBackground = resources.getDrawable(R.drawable.default_background) metrics = DisplayMetrics() activity?.windowManager?.defaultDisplay?.getMetrics(metrics) } private fun setupUIElements() { badgeDrawable = resources.getDrawable(R.drawable.videos_by_google_banner) // Badge, when set, takes precedent over title title = getString(R.string.browse_title) headersState = BrowseSupportFragment.HEADERS_ENABLED isHeadersTransitionOnBackEnabled = true // Set header background color brandColor = ContextCompat.getColor(requireContext(), R.color.fastlane_background) // Set search icon color searchAffordanceColor = ContextCompat.getColor(requireContext(), R.color.search_opaque) } private fun loadVideoData() { VideoProvider.setContext(activity) videosUrl = getString(R.string.catalog_url) loaderManager.initLoader(0, null, this) } private fun setupEventListeners() { setOnSearchClickedListener { Intent(activity, SearchActivity::class.java).also { startActivity(intent) } } onItemViewClickedListener = ItemViewClickedListener() onItemViewSelectedListener = ItemViewSelectedListener() } ... ``` -------------------------------- ### Implement Preview Video Playback in a TV Input Service Source: https://developer.android.com/training/tv/discovery/preview-videos This Java code demonstrates how to create a TvInputService.Session to manage media playback for preview videos. It includes methods for tuning to a channel, setting the display surface, releasing resources, adjusting volume, and enabling/disabling captions. Ensure you have a mechanism to retrieve video URIs and handle media player preparation and playback. ```java import android.content.Context; import android.media.MediaPlayer; import android.media.tv.TvInputService; import android.net.Uri; import android.support.annotation.Nullable; import android.util.Log; import android.view.Surface; import java.io.IOException; public class PreviewVideoInputService extends TvInputService { private static final String TAG = "PreviewVideoInputService"; @Nullable @Override public Session onCreateSession(String inputId) { return new PreviewSession(this); } private class PreviewSession extends TvInputService.Session { private MediaPlayer mPlayer; PreviewSession(Context context) { super(context); mPlayer = new MediaPlayer(); } @Override public boolean onTune(Uri channelUri) { // Let the TvInputService know that the video is being loaded. notifyVideoUnavailable(VIDEO_UNAVAILABLE_REASON_TUNING); // Fetch the stream url from the TV Provider database // for content://android.media.tv/preview_program/ String id = uri.getLastPathSegment(); // Load your video in the background. retrieveYourVideoPreviewUrl(id, new MyCallback() { public void callback(Uri videoUri) { if (videoUri == null) { Log.d(TAG, "Could not find video" + id); notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN); } try { mPlayer.setDataSource(getApplicationContext(), videoUri); mPlayer.prepare(); mPlayer.start(); notifyVideoAvailable(); } catch (IOException e) { Log.e(TAG, "Could not prepare media player", e); notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN); } } }); return true; } @Override public boolean onSetSurface(@Nullable Surface surface) { if (mPlayer != null) { mPlayer.setSurface(surface); } return true; } @Override public void onRelease() { if (mPlayer != null) { mPlayer.release(); } mPlayer = null; } @Override public void onSetStreamVolume(float volume) { if (mPlayer != null) { // The home screen may fade in and out the video's volume. // Your player should be updated accordingly. mPlayer.setVolume(volume, volume); } } @Override public void onSetCaptionEnabled(boolean enabled) { // enable/disable captions here } } } ``` -------------------------------- ### Start Custom Synthetic Exercise Source: https://developer.android.com/training/wearables/health-services/synthetic-data Start a custom exercise with specific metric options for precise control over generated data. ```bash adb shell am broadcast \ -a "whs.synthetic.user.START_EXERCISE" \ --ei exercise_options_heart_rate 90 \ --ef exercise_options_average_speed 1.2 \ --ez exercise_options_use_location true \ com.google.android.wearable.healthservices ``` -------------------------------- ### Prepopulate Room DB from App Asset (Java) Source: https://developer.android.com/training/data-storage/room/prepopulate Use `createFromAsset()` to load a database from the `assets/` directory. The argument is the relative path to the database file within the assets folder. ```java Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db") .createFromAsset("database/myapp.db") .build(); ``` -------------------------------- ### Initialize BrowseSupportFragment (Kotlin) Source: https://developer.android.com/training/tv/playback/leanback/browse?hl=fr Initializes the BrowseSupportFragment and sets up essential UI elements and event listeners. ```kotlin class MainFragment : BrowseSupportFragment(), LoaderManager.LoaderCallbacks>> { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) loadVideoData() } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) prepareBackgroundManager() setupUIElements() setupEventListeners() } ... private fun prepareBackgroundManager() { backgroundManager = BackgroundManager.getInstance(activity).apply { attach(activity?.window) } defaultBackground = resources.getDrawable(R.drawable.default_background) metrics = DisplayMetrics() activity?.windowManager?.defaultDisplay?.getMetrics(metrics) } private fun setupUIElements() { badgeDrawable = resources.getDrawable(R.drawable.videos_by_google_banner) // Badge, when set, takes precedent over title title = getString(R.string.browse_title) headersState = BrowseSupportFragment.HEADERS_ENABLED isHeadersTransitionOnBackEnabled = true // Set header background color brandColor = ContextCompat.getColor(requireContext(), R.color.fastlane_background) // Set search icon color searchAffordanceColor = ContextCompat.getColor(requireContext(), R.color.search_opaque) } private fun loadVideoData() { VideoProvider.setContext(activity) videosUrl = getString(R.string.catalog_url) loaderManager.initLoader(0, null, this) } private fun setupEventListeners() { setOnSearchClickedListener { Intent(activity, SearchActivity::class.java).also { intent -> startActivity(intent) } } onItemViewClickedListener = ItemViewClickedListener() onItemViewSelectedListener = ItemViewSelectedListener() } ... ``` -------------------------------- ### Setup LeanbackTabLayout and LeanbackViewPager in Kotlin Source: https://developer.android.com/training/tv/playback/leanback/leanback-libraries Integrate LeanbackTabLayout with LeanbackViewPager to create tabbed interfaces, ensuring the adapter is set before setup. ```kotlin val leanbackTabLayout = findViewById(R.id.tab_layout) val leanbackViewPager = findViewById(R.id.view_pager) leanbackViewPager.setAdapter(adapter) leanbackTabLayout.setupWithViewPager(leanbackViewPager) ``` -------------------------------- ### Start Service with ServiceTestRule Source: https://developer.android.com/training/testing/instrumented-tests/androidx-test-libraries/rules Use ServiceTestRule to start a service before tests. This rule automatically stops or unbinds the service after the test completes. ```kotlin @RunWith(AndroidJUnit4::class.java) @MediumTest class MyServiceTest { @get:Rule val serviceRule = ServiceTestRule() @Test fun testWithStartedService() { serviceRule.startService( Intent(ApplicationProvider.getApplicationContext(), MyService::class.java)) // Add your test code here. } @Test fun testWithBoundService() { val binder = serviceRule.bindService( Intent(ApplicationProvider.getApplicationContext(), MyService::class.java)) val service = (binder as MyService.LocalBinder).service assertThat(service.doSomethingToReturnTrue()).isTrue() } } ``` ```java @RunWith(AndroidJUnit4.class) @MediumTest public class MyServiceTest { @Rule public final ServiceTestRule serviceRule = new ServiceTestRule(); @Test public void testWithStartedService() { serviceRule.startService( new Intent(ApplicationProvider.getApplicationContext(), MyService.class)); // Add your test code here. } @Test public void testWithBoundService() { IBinder binder = serviceRule.bindService( new Intent(ApplicationProvider.getApplicationContext(), MyService.class)); MyService service = ((MyService.LocalBinder) binder).getService(); assertThat(service.doSomethingToReturnTrue()).isTrue(); } } ``` -------------------------------- ### Install libc++ Libraries on Debian-based Linux Source: https://developer.android.com/training/cars/testing/dhu On Debian-derived Linux distributions, use this command to install the required libc++1 and libc++abi1 libraries. ```bash sudo apt-get install libc++1 libc++abi1 ``` -------------------------------- ### Start an activity with an intent (Kotlin) Source: https://developer.android.com/training/basics/intents/sending Call startActivity() with your created intent to send it to the system and launch the appropriate activity. ```kotlin startActivity(intent) ``` -------------------------------- ### Specify app installation location Source: https://developer.android.com/training/basics/data-storage/files Declare a preference in the manifest file to install your app on external storage if the APK size is very large. ```xml ... ``` -------------------------------- ### Initialize BrowseSupportFragment (Java) Source: https://developer.android.com/training/tv/playback/browse This Java code initializes the BrowseSupportFragment, loads video data, and sets up the UI elements and event listeners. ```java public class MainFragment extends BrowseSupportFragment implements LoaderManager.LoaderCallbacks>> { } ... @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); loadVideoData(); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); prepareBackgroundManager(); setupUIElements(); setupEventListeners(); } ... private void prepareBackgroundManager() { backgroundManager = BackgroundManager.getInstance(getActivity()); backgroundManager.attach(getActivity().getWindow()); defaultBackground = getResources() .getDrawable(R.drawable.default_background); metrics = new DisplayMetrics(); getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics); } private void setupUIElements() { setBadgeDrawable(getActivity().getResources() .getDrawable(R.drawable.videos_by_google_banner)); ``` -------------------------------- ### Implement Volume Slider with TalkBack Support (Java) Source: https://developer.android.com/training/tv/accessibility/talkback-support This example implements slider functionality for TalkBack users. Additional logic should be added for other users, which can reuse the increase and decrease methods. It sets up accessibility actions for scrolling forward and backward to adjust the volume. ```Java /** * This example only provides slider functionality for TalkBack users. Additional logic should * be added for other users. Such logic can reuse the increase and decrease methods. */ public class VolumeSlider extends LinearLayout { private final int min = 1; private final int max = 10; private int current = 5; private TextView textView; public VolumeSlider(Context context, AttributeSet attrs) { super(context, attrs); setFocusable(true); setFocusableInTouchMode(true); RangeInfo rangeInfo = new RangeInfo(RangeInfo.RANGE_TYPE_INT, min, max, current); setAccessibilityDelegate( new AccessibilityDelegate() { @Override public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(host, info); info.addAction(AccessibilityAction.ACTION_SET_PROGRESS); info.setRangeInfo(rangeInfo); } @Override public boolean performAccessibilityAction(View host, int action, Bundle args) { if (action == AccessibilityAction.ACTION_SCROLL_FORWARD.getId()) { increase(); return true; } if (action == AccessibilityAction.ACTION_SCROLL_BACKWARD.getId()) { decrease(); return true; } return super.performAccessibilityAction(host, action, args); } }); } @Override protected void onFinishInflate() { super.onFinishInflate(); textView = findViewById(R.id.text); textView.setText(getContext().getString(R.string.level, current)); } private void increase() { update(Math.min(current + 1, max)); } private void decrease() { update(Math.max(current - 1, min)); } private void update(int newLevel) { if (textView == null) { return; } String newText = getContext().getString(R.string.level, newLevel); // Update the user interface with the new volume. textView.setText(newText); // Announce the new volume. announceForAccessibility(newText); current = newLevel; } } ``` -------------------------------- ### Kotlin MainActivity Setup for Content Observer Source: https://developer.android.com/training/sync-adapters/running-sync-adapter?hl=ja Sets up the MainActivity to observe changes in a content provider. It constructs the content URI, creates a TableObserver, and registers it. ```kotlin override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... // Get the content resolver object for your app mResolver = contentResolver // Construct a URI that points to the content provider data table uri = Uri.Builder() .scheme(SCHEME) .authority(AUTHORITY) .path(TABLE_PATH) .build() /* * Create a content observer object. * Its code does not mutate the provider, so set * selfChange to "false" */ val observer = TableObserver(false) /* * Register the observer for the data table. The table's path * and any of its subpaths trigger the observer. */ mResolver.registerContentObserver(uri, true, observer) ... } ``` -------------------------------- ### Launch Navigation with Activity Check Source: https://developer.android.com/training/cars/platforms/automotive-os/android-intents-automotive?hl=de This example combines creating a navigation Intent with a check to ensure an app can handle it before launching. This is the recommended approach to prevent crashes. ```java Uri mapIntentUri = Uri.parse("google.navigation:q=Taronga+Zoo,+Sydney+Australia"); Intent mapIntent = new Intent(Intent.ACTION_VIEW, mapIntentUri); mapIntent.setPackage("com.google.android.apps.maps"); if (mapIntent.resolveActivity(getPackageManager()) != null) { startActivity(mapIntent); } ``` -------------------------------- ### Access Entry Point Dependencies in Content Provider Source: https://developer.android.com/training/dependency-injection/hilt-android Use `EntryPointAccessors.fromApplication` to retrieve dependencies from an entry point. Ensure the component and accessor method match the `@InstallIn` annotation. ```kotlin class ExampleContentProvider: ContentProvider() { ... override fun query(...): Cursor { val appContext = context?.applicationContext ?: throw IllegalStateException() val hiltEntryPoint = EntryPointAccessors.fromApplication(appContext, ExampleContentProviderEntryPoint::class.java) val analyticsService = hiltEntryPoint.analyticsService() ... } } ``` ```java public class ExampleContentProvider extends ContentProvider { @Override public Cursor query(...) { Context appContext = getContext().getApplicationContext(); ExampleContentProviderEntryPoint hiltEntryPoint = EntryPointAccessors.fromApplication(appContext, ExampleContentProviderEntryPoint.class); AnalyticsService analyticsService = hiltEntryPoint.analyticsService(); } } ``` -------------------------------- ### Resume provider installation on post-resume Source: https://developer.android.com/training/articles/security-gms-provider In `onPostResume`, check if a flag indicates that the provider needs to be reinstalled. If so, it's safe to retry the installation using `ProviderInstaller.installIfNeededAsync`. ```java super.onPostResume(); if (retryProviderInstall) { // It's safe to retry installation. ProviderInstaller.installIfNeededAsync(this, this); } retryProviderInstall = false; ``` -------------------------------- ### List Instrumentation Packages Source: https://developer.android.com/training/testing/instrumented-tests/androidx-test-libraries/runner Verify installed instrumentation packages on the device. This command can be used to check if Android Test Orchestrator or other instrumentation services are correctly installed. ```bash adb shell pm list instrumentation ``` -------------------------------- ### Build an Adapter for BrowseSupportFragment (Java) Source: https://developer.android.com/training/tv/playback/browse This Java code demonstrates how to build an adapter for `BrowseSupportFragment`, populating it with categories and media items using a `StringPresenter`. ```java private ArrayObjectAdapter rowsAdapter; private static final int NUM_ROWS = 4; @Override protected void onCreate(Bundle savedInstanceState) { ... buildRowsAdapter(); } private void buildRowsAdapter() { rowsAdapter = new ArrayObjectAdapter(new ListRowPresenter()); for (int i = 0; i < NUM_ROWS; ++i) { ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter( new StringPresenter()); listRowAdapter.add("Media Item 1"); listRowAdapter.add("Media Item 2"); listRowAdapter.add("Media Item 3"); HeaderItem header = new HeaderItem(i, "Category " + i); rowsAdapter.add(new ListRow(header, listRowAdapter)); } browseSupportFragment.setAdapter(rowsAdapter); } ```