Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.example.compose.snippets.components

import androidx.compose.runtime.Composable

object ButtonSnippets {
// [START android_compose_snippets_button_example]
@Composable
fun ButtonExample() {

}
Comment on lines +8 to +10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The ButtonExample function is currently empty. To make this snippet useful, it should contain a basic implementation of a Button. I've suggested an example using fully qualified names to avoid needing to add imports. You may need to add a dependency on androidx.compose.material3 if it's not already present.

Additionally, the file is missing a newline character at the end, which is a common convention.

    fun ButtonExample() {
        androidx.compose.material3.Button(onClick = { /* Do something */ }) {
            androidx.compose.material3.Text("Button")
        }
    }

// [END android_compose_snippets_button_example]
}
26 changes: 26 additions & 0 deletions media/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
plugins {
id("com.android.library")
kotlin("android")
}

android {
namespace = "com.example.media.snippets"
compileSdk = 35

defaultConfig {
minSdk = 21
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

kotlinOptions {
jvmTarget = "17"
}
}

dependencies {
implementation("androidx.core:core-ktx:1.15.0")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.example.media.snippets.mediaplayer

import android.media.MediaPlayer
import java.util.UUID

class DRMSnippets {
private var mediaPlayer: MediaPlayer? = null

fun syncDRM() {
// [START android_mediaplayer_drm_sync]
mediaPlayer?.apply {
setDataSource("https://example.com/video.mp4")
setOnDrmConfigHelper { _ -> /* configuration */ } // optional, for custom configuration
prepare()
drmInfo?.also {
prepareDrm(UUID.randomUUID())
getKeyRequest(byteArrayOf(), byteArrayOf(), "", 0, null)
provideKeyResponse(byteArrayOf(), byteArrayOf())
}

// MediaPlayer is now ready to use
start()
// ...play/pause/resume...
stop()
releaseDrm()
}
// [END android_mediaplayer_drm_sync]
}
Comment on lines +9 to +28
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The syncDRM function has several critical issues:

  1. The mediaPlayer property is never initialized, so the code inside mediaPlayer?.apply will never execute, making the snippet non-functional.
  2. Methods like setDataSource() and prepare() can throw exceptions which are not handled.
  3. The MediaPlayer is not released with release() after use, which will cause a resource leak. releaseDrm() is called, but release() is also necessary.

I suggest refactoring the function to be self-contained and robust by initializing the MediaPlayer locally and using try-catch-finally for exception handling and resource management.

    fun syncDRM() {
        // [START android_mediaplayer_drm_sync]
        val mediaPlayer = MediaPlayer()
        try {
            mediaPlayer.apply {
                setDataSource("https://example.com/video.mp4")
                setOnDrmConfigHelper { _ -> /* configuration */ } // optional, for custom configuration
                prepare()
                drmInfo?.also {
                    try {
                        prepareDrm(UUID.randomUUID())
                        getKeyRequest(byteArrayOf(), byteArrayOf(), "", 0, null)
                        provideKeyResponse(byteArrayOf(), byteArrayOf())
                    } catch (e: Exception) {
                        e.printStackTrace()
                    }
                }

                // MediaPlayer is now ready to use
                start()
                // ...play/pause/resume...
                stop()
            }
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            mediaPlayer.releaseDrm()
            mediaPlayer.release()
        }
        // [END android_mediaplayer_drm_sync]
    }


// [START android_mediaplayer_drm_async]
inner class MyDrmActivity : MediaPlayer.OnDrmInfoListener, MediaPlayer.OnPreparedListener {
fun setupAsync(mediaPlayer: MediaPlayer) {
mediaPlayer.apply {
setOnPreparedListener(this@MyDrmActivity)
setOnDrmInfoListener(this@MyDrmActivity)
setDataSource("https://example.com/video.mp4")
prepareAsync()
}
Comment on lines +33 to +38
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The setDataSource() method can throw an IOException among other exceptions. This is unhandled and could crash the application. The corresponding Java snippet includes a try-catch block, and this Kotlin version should as well for robustness.

Suggested change
mediaPlayer.apply {
setOnPreparedListener(this@MyDrmActivity)
setOnDrmInfoListener(this@MyDrmActivity)
setDataSource("https://example.com/video.mp4")
prepareAsync()
}
try {
mediaPlayer.apply {
setOnPreparedListener(this@MyDrmActivity)
setOnDrmInfoListener(this@MyDrmActivity)
setDataSource("https://example.com/video.mp4")
prepareAsync()
}
} catch (e: Exception) {
e.printStackTrace()
}

}

// If the data source content is protected you receive a call to the onDrmInfo() callback.
override fun onDrmInfo(mediaPlayer: MediaPlayer, drmInfo: MediaPlayer.DrmInfo) {
mediaPlayer.apply {
prepareDrm(UUID.randomUUID())
getKeyRequest(byteArrayOf(), byteArrayOf(), "", 0, null)
provideKeyResponse(byteArrayOf(), byteArrayOf())
}
Comment on lines +43 to +47
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The DRM methods prepareDrm(), getKeyRequest(), and provideKeyResponse() can all throw exceptions (e.g., UnsupportedSchemeException, NotProvisionedException). These should be handled in a try-catch block to prevent crashes.

            try {
                mediaPlayer.apply {
                    prepareDrm(UUID.randomUUID())
                    getKeyRequest(byteArrayOf(), byteArrayOf(), "", 0, null)
                    provideKeyResponse(byteArrayOf(), byteArrayOf())
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }

}

// When prepareAsync() finishes, you receive a call to the onPrepared() callback.
// If there is a DRM, onDrmInfo() sets it up before executing this callback,
// so you can start the player.
override fun onPrepared(mediaPlayer: MediaPlayer) {
mediaPlayer.start()
}
}
// [END android_mediaplayer_drm_async]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.example.media.snippets.mediaplayer;

import android.media.MediaPlayer;
import java.util.UUID;

public class DRMSnippetsJava {
private MediaPlayer mediaPlayer;

public void syncDRM() {
// [START android_mediaplayer_drm_sync_java]
try {
mediaPlayer.setDataSource("https://example.com/video.mp4");
mediaPlayer.setOnDrmConfigHelper(null); // optional, for custom configuration
mediaPlayer.prepare();
if (mediaPlayer.getDrmInfo() != null) {
mediaPlayer.prepareDrm(UUID.randomUUID());
mediaPlayer.getKeyRequest(null, null, null, 0, null);
mediaPlayer.provideKeyResponse(null, null);
}

// MediaPlayer is now ready to use
mediaPlayer.start();
// ...play/pause/resume...
mediaPlayer.stop();
mediaPlayer.releaseDrm();
} catch (Exception e) {
// Handle exceptions
}
// [END android_mediaplayer_drm_sync_java]
}
Comment on lines +9 to +30
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This function has two critical issues:

  1. The mediaPlayer field is used without being initialized, which will cause a NullPointerException as soon as mediaPlayer.setDataSource() is called.
  2. The MediaPlayer resource is not released with mediaPlayer.release(). This will lead to a resource leak. release() should be called in a finally block to ensure it executes even if exceptions occur.

I recommend initializing MediaPlayer within the method and using a finally block for cleanup.

    public void syncDRM() {
        // [START android_mediaplayer_drm_sync_java]
        MediaPlayer mediaPlayer = new MediaPlayer();
        try {
            mediaPlayer.setDataSource("https://example.com/video.mp4");
            mediaPlayer.setOnDrmConfigHelper(null); // optional, for custom configuration
            mediaPlayer.prepare();
            if (mediaPlayer.getDrmInfo() != null) {
                mediaPlayer.prepareDrm(UUID.randomUUID());
                mediaPlayer.getKeyRequest(null, null, null, 0, null);
                mediaPlayer.provideKeyResponse(null, null);
            }

            // MediaPlayer is now ready to use
            mediaPlayer.start();
            // ...play/pause/resume...
            mediaPlayer.stop();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            mediaPlayer.releaseDrm();
            mediaPlayer.release();
        }
        // [END android_mediaplayer_drm_sync_java]
    }


// [START android_mediaplayer_drm_async_java]
public class MyDrmActivity implements MediaPlayer.OnDrmInfoListener, MediaPlayer.OnPreparedListener {
public void setupAsync(MediaPlayer mediaPlayer) {
try {
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.setOnDrmInfoListener(this);
mediaPlayer.setDataSource("https://example.com/video.mp4");
mediaPlayer.prepareAsync();
} catch (Exception e) {
// Handle exceptions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This catch block is empty except for a comment. This can hide errors and make debugging difficult. At a minimum, the exception should be logged.

                e.printStackTrace();

}
}

// If the data source content is protected you receive a call to the onDrmInfo() callback.
@Override
public void onDrmInfo(MediaPlayer mediaPlayer, MediaPlayer.DrmInfo drmInfo) {
try {
mediaPlayer.prepareDrm(UUID.randomUUID());
mediaPlayer.getKeyRequest(null, null, null, 0, null);
mediaPlayer.provideKeyResponse(null, null);
} catch (Exception e) {
// Handle exceptions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This catch block is empty except for a comment. This can hide errors and make debugging difficult. At a minimum, the exception should be logged.

                e.printStackTrace();

}
}

// When prepareAsync() finishes, you receive a call to the onPrepared() callback.
// If there is a DRM, onDrmInfo() sets it up before executing this callback,
// so you can start the player.
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.start();
}
}
// [END android_mediaplayer_drm_async_java]
}
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ include(
":misc",
":identity:credentialmanager",
":xr",
":media",
":watchfacepush:validator",
":kmp:androidApp",
":kmp:shared"
Expand Down
Loading