[automerger skipped] Apply androidx.media2.* library/package separation to pump app am: 862df21538 am: 4e72d86717
am: ff85df5beb -s ours
am skip reason: change_id Id6dbfedb0ca3b39e992c53ad5a0903c8c082a8bf with SHA1 ec423bc6b3 is in history
Change-Id: I4498bb79617ea0b38659f0fc4358497021317c5b
diff --git a/Android.bp b/Android.bp
index 3c7f1e2..b12b53c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -25,6 +25,7 @@
static_libs: [
"androidx-constraintlayout_constraintlayout",
"androidx.media2_media2-widget",
+ "androidx.media2_media2-player",
"com.google.android.material_material"
],
optimize: {
diff --git a/build.gradle b/build.gradle
index cf219d4..2f87314 100644
--- a/build.gradle
+++ b/build.gradle
@@ -63,6 +63,7 @@
dependencies {
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
- implementation 'androidx.media2:media2-widget:1.0.0-alpha07'
+ implementation 'androidx.media2:media2-widget:+'
+ implementation 'androidx.media2:media2-player:+'
implementation 'com.google.android.material:material:1.0.0'
}
diff --git a/java/com/android/pump/activity/AudioPlayerActivity.java b/java/com/android/pump/activity/AudioPlayerActivity.java
index d55d286..cfdb651 100644
--- a/java/com/android/pump/activity/AudioPlayerActivity.java
+++ b/java/com/android/pump/activity/AudioPlayerActivity.java
@@ -27,7 +27,9 @@
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.appcompat.app.AppCompatActivity;
+import androidx.media.AudioAttributesCompat;
import androidx.media2.common.UriMediaItem;
+import androidx.media2.player.MediaPlayer;
import androidx.media2.widget.VideoView;
import com.android.pump.R;
@@ -44,6 +46,7 @@
private static final String TAG = Clog.tag(AudioPlayerActivity.class);
private VideoView mVideoView;
+ private MediaPlayer mMediaPlayer;
public static void start(@NonNull Context context, @NonNull Audio audio) {
// TODO(b/123702587) Find a better URI (audio.getUri()?)
@@ -118,6 +121,14 @@
setContentView(R.layout.activity_audio_player);
mVideoView = findViewById(R.id.video_view);
+ mMediaPlayer = new MediaPlayer(AudioPlayerActivity.this);
+ AudioAttributesCompat audioAttributes = new AudioAttributesCompat.Builder()
+ .setUsage(AudioAttributesCompat.USAGE_MEDIA)
+ .setContentType(AudioAttributesCompat.CONTENT_TYPE_MOVIE).build();
+
+ mMediaPlayer.setAudioAttributes(audioAttributes);
+ mVideoView.setPlayer(mMediaPlayer);
+
handleIntent();
}
@@ -138,6 +149,16 @@
return;
}
UriMediaItem mediaItem = new UriMediaItem.Builder(uri).build();
- mVideoView.setMediaItem(mediaItem);
+ mMediaPlayer.setMediaItem(mediaItem);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ try {
+ if (mMediaPlayer != null) {
+ mMediaPlayer.close();
+ }
+ } catch (Exception e) { }
}
}
diff --git a/java/com/android/pump/activity/VideoPlayerActivity.java b/java/com/android/pump/activity/VideoPlayerActivity.java
index 1e75c40..1ef7709 100644
--- a/java/com/android/pump/activity/VideoPlayerActivity.java
+++ b/java/com/android/pump/activity/VideoPlayerActivity.java
@@ -27,15 +27,14 @@
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.appcompat.app.AppCompatActivity;
-import androidx.media2.common.MediaItem;
+import androidx.core.content.ContextCompat;
+import androidx.media.AudioAttributesCompat;
import androidx.media2.common.SessionPlayer;
import androidx.media2.common.UriMediaItem;
-import androidx.media2.session.MediaController;
-import androidx.media2.session.SessionToken;
+import androidx.media2.player.MediaPlayer;
import androidx.media2.widget.VideoView;
import com.android.pump.R;
-import com.android.pump.concurrent.Executors;
import com.android.pump.db.Video;
import com.android.pump.util.Clog;
import com.android.pump.util.IntentUtils;
@@ -46,7 +45,7 @@
private static final String SAVED_POSITION_KEY = "SavedPosition";
private VideoView mVideoView;
- private MediaController mMediaController;
+ private MediaPlayer mMediaPlayer;
private long mSavedPosition = SessionPlayer.UNKNOWN_TIME;
public static void start(@NonNull Context context, @NonNull Video video) {
@@ -70,38 +69,24 @@
SessionPlayer.UNKNOWN_TIME);
}
+ mMediaPlayer = new MediaPlayer(VideoPlayerActivity.this);
+ AudioAttributesCompat audioAttributes = new AudioAttributesCompat.Builder()
+ .setUsage(AudioAttributesCompat.USAGE_MEDIA)
+ .setContentType(AudioAttributesCompat.CONTENT_TYPE_MOVIE).build();
+
+ mMediaPlayer.setAudioAttributes(audioAttributes);
+ mVideoView.setPlayer(mMediaPlayer);
+
handleIntent();
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
- if (mMediaController != null) {
- outState.putLong(SAVED_POSITION_KEY, mMediaController.getCurrentPosition());
- }
-
+ outState.putLong(SAVED_POSITION_KEY, mMediaPlayer.getCurrentPosition());
super.onSaveInstanceState(outState);
}
@Override
- public void onAttachedToWindow() {
- if (mMediaController == null) {
- SessionToken token = mVideoView.getSessionToken();
- mMediaController = new MediaController.Builder(this)
- .setSessionToken(token)
- .setControllerCallback(Executors.uiThreadExecutor(), new ControllerCallback())
- .build();
- }
- }
-
- @Override
- public void onDetachedFromWindow() {
- if (mMediaController != null) {
- mMediaController.close();
- mMediaController = null;
- }
- }
-
- @Override
protected void onNewIntent(@Nullable Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
@@ -118,18 +103,26 @@
return;
}
UriMediaItem mediaItem = new UriMediaItem.Builder(uri).build();
- mVideoView.setMediaItem(mediaItem);
+ mMediaPlayer.setMediaItem(mediaItem)
+ .addListener(new Runnable() {
+ @Override
+ public void run() {
+ if (mSavedPosition != SessionPlayer.UNKNOWN_TIME) {
+ mMediaPlayer.seekTo(mSavedPosition);
+ mSavedPosition = SessionPlayer.UNKNOWN_TIME;
+ }
+ mMediaPlayer.play();
+ }
+ }, ContextCompat.getMainExecutor(this));
}
- private class ControllerCallback extends MediaController.ControllerCallback {
- @Override
- public void onCurrentMediaItemChanged(@NonNull MediaController controller,
- @Nullable MediaItem item) {
- if (mSavedPosition != SessionPlayer.UNKNOWN_TIME) {
- controller.seekTo(mSavedPosition);
- mSavedPosition = SessionPlayer.UNKNOWN_TIME;
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ try {
+ if (mMediaPlayer != null) {
+ mMediaPlayer.close();
}
- controller.play();
- }
+ } catch (Exception e) { }
}
}
diff --git a/java/com/android/pump/app/GlobalsApplication.java b/java/com/android/pump/app/GlobalsApplication.java
index dc30372..27e1e43 100644
--- a/java/com/android/pump/app/GlobalsApplication.java
+++ b/java/com/android/pump/app/GlobalsApplication.java
@@ -32,7 +32,7 @@
@Override
public @NonNull ImageLoader getImageLoader() {
if (mImageLoader == null) {
- mImageLoader = new ImageLoader(getExecutor());
+ mImageLoader = new ImageLoader(getContentResolver(), getExecutor());
}
return mImageLoader;
}
diff --git a/java/com/android/pump/db/AudioStore.java b/java/com/android/pump/db/AudioStore.java
index 4cd569b..9058a20 100644
--- a/java/com/android/pump/db/AudioStore.java
+++ b/java/com/android/pump/db/AudioStore.java
@@ -31,7 +31,6 @@
import com.android.pump.util.Clog;
import com.android.pump.util.Collections;
-import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
@@ -317,7 +316,6 @@
Uri contentUri = MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI;
String[] projection = {
- MediaStore.Audio.Albums.ALBUM_ART,
MediaStore.Audio.Albums.ALBUM,
MediaStore.Audio.Media.ARTIST_ID // TODO MediaStore.Audio.Albums.ARTIST_ID
};
@@ -327,8 +325,6 @@
contentUri, projection, selection, selectionArgs, null);
if (cursor != null) {
try {
- int albumArtColumn = cursor.getColumnIndexOrThrow(
- MediaStore.Audio.Albums.ALBUM_ART);
int albumColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM);
int artistIdColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST_ID); // TODO MediaStore.Audio.Albums.ARTIST_ID
@@ -337,16 +333,20 @@
String albumTitle = cursor.getString(albumColumn);
updated |= album.setTitle(albumTitle);
}
- if (!cursor.isNull(albumArtColumn)) {
- Uri albumArtUri = Uri.fromFile(new File(cursor.getString(albumArtColumn)));
- updated |= album.setAlbumArtUri(albumArtUri);
- }
if (!cursor.isNull(artistIdColumn)) {
long artistId = cursor.getLong(artistIdColumn);
Artist artist = mMediaProvider.getArtistById(artistId);
updated |= album.setArtist(artist);
updated |= loadData(artist); // TODO(b/123707561) Load separate from album
}
+
+ // TODO(b/130363861) No need to store the URI -- generate when requested instead
+ Uri albumArtUri = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(MediaStore.AUTHORITY)
+ .appendPath("external").appendPath("audio").appendPath("albumart")
+ .appendPath(Long.toString(album.getId())).build();
+ updated |= album.setAlbumArtUri(albumArtUri);
}
} finally {
cursor.close();
diff --git a/java/com/android/pump/db/VideoStore.java b/java/com/android/pump/db/VideoStore.java
index 657c0c1..4f00b2a 100644
--- a/java/com/android/pump/db/VideoStore.java
+++ b/java/com/android/pump/db/VideoStore.java
@@ -17,6 +17,7 @@
package com.android.pump.db;
import android.content.ContentResolver;
+import android.content.ContentUris;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
@@ -43,7 +44,7 @@
private static final String RELATIVE_PATH = "relative_path";
// TODO Replace with Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q throughout the code.
- private static boolean isRunningQ() {
+ private static boolean isAtLeastRunningQ() {
return Build.VERSION.SDK_INT > Build.VERSION_CODES.P
|| (Build.VERSION.SDK_INT == Build.VERSION_CODES.P
&& Build.VERSION.PREVIEW_SDK_INT > 0);
@@ -101,7 +102,7 @@
{
Uri contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
String[] projection;
- if (isRunningQ()) {
+ if (isAtLeastRunningQ()) {
projection = new String[] {
MediaStore.Video.Media._ID,
MediaStore.Video.Media.MIME_TYPE,
@@ -126,7 +127,7 @@
int mimeTypeColumn = cursor.getColumnIndexOrThrow(
MediaStore.Video.Media.MIME_TYPE);
- if (isRunningQ()) {
+ if (isAtLeastRunningQ()) {
dataColumn = -1;
relativePathColumn = cursor.getColumnIndexOrThrow(RELATIVE_PATH);
displayNameColumn = cursor.getColumnIndexOrThrow(
@@ -142,7 +143,7 @@
String mimeType = cursor.getString(mimeTypeColumn);
File file;
- if (isRunningQ()) {
+ if (isAtLeastRunningQ()) {
String relativePath = cursor.getString(relativePathColumn);
String displayName = cursor.getString(displayNameColumn);
file = new File(relativePath, displayName);
@@ -276,35 +277,9 @@
}
private @Nullable Uri getThumbnailUri(long id) {
- int thumbKind = MediaStore.Video.Thumbnails.MINI_KIND;
-
- // TODO(b/123707512) The following line is required to generate thumbnails -- is there a better way?
- MediaStore.Video.Thumbnails.getThumbnail(mContentResolver, id, thumbKind, null);
-
- Uri thumbnailUri = null;
- Uri contentUri = MediaStore.Video.Thumbnails.EXTERNAL_CONTENT_URI;
- String[] projection = {
- MediaStore.Video.Thumbnails.DATA
- };
- String selection = MediaStore.Video.Thumbnails.KIND + " = " + thumbKind + " AND " +
- MediaStore.Video.Thumbnails.VIDEO_ID + " = ?";
- String[] selectionArgs = { Long.toString(id) };
- Cursor cursor = mContentResolver.query(
- contentUri, projection, selection, selectionArgs, null);
- if (cursor != null) {
- try {
- int dataColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Thumbnails.DATA);
-
- if (cursor.moveToFirst()) {
- String data = cursor.getString(dataColumn);
-
- thumbnailUri = Uri.fromFile(new File(data));
- }
- } finally {
- cursor.close();
- }
- }
- return thumbnailUri;
+ // TODO(b/130363861) No need to store the URI -- generate when requested instead
+ return ContentUris.withAppendedId(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id)
+ .buildUpon().appendPath("thumbnail").build();
}
@Override
diff --git a/java/com/android/pump/util/ImageLoader.java b/java/com/android/pump/util/ImageLoader.java
index ba9b89a..0a60e66 100644
--- a/java/com/android/pump/util/ImageLoader.java
+++ b/java/com/android/pump/util/ImageLoader.java
@@ -16,9 +16,14 @@
package com.android.pump.util;
+import android.content.ContentResolver;
+import android.content.UriMatcher;
+import android.content.res.AssetFileDescriptor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
+import android.os.Build;
+import android.provider.MediaStore;
import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
@@ -29,6 +34,7 @@
import com.android.pump.concurrent.Executors;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.AbstractMap.SimpleEntry;
import java.util.LinkedList;
@@ -41,8 +47,22 @@
public class ImageLoader {
private static final String TAG = Clog.tag(ImageLoader.class);
+ // TODO Replace with Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q throughout the code.
+ private static boolean isAtLeastRunningQ() {
+ return Build.VERSION.SDK_INT > Build.VERSION_CODES.P
+ || (Build.VERSION.SDK_INT == Build.VERSION_CODES.P
+ && Build.VERSION.PREVIEW_SDK_INT > 0);
+ }
+
+ private static final UriMatcher VIDEO_THUMBNAIL_URI_MATCHER =
+ new UriMatcher(UriMatcher.NO_MATCH);
+ static {
+ VIDEO_THUMBNAIL_URI_MATCHER.addURI("media", "*/video/media/#/thumbnail", 0);
+ }
+
private final BitmapCache mBitmapCache = new BitmapCache();
private final OrientationCache mOrientationCache = new OrientationCache();
+ private final ContentResolver mContentResolver;
private final Executor mExecutor;
private final Set<Map.Entry<Executor, Callback>> mCallbacks = new ArraySet<>();
private final Map<Uri, List<Map.Entry<Executor, Callback>>> mLoadCallbacks = new ArrayMap<>();
@@ -52,7 +72,8 @@
void onImageLoaded(@NonNull Uri uri, @Nullable Bitmap bitmap);
}
- public ImageLoader(@NonNull Executor executor) {
+ public ImageLoader(@NonNull ContentResolver contentResolver, @NonNull Executor executor) {
+ mContentResolver = contentResolver;
mExecutor = executor;
}
@@ -121,15 +142,26 @@
@Override
public void run() {
try {
- byte[] data;
- if (Scheme.isFile(mUri)) {
- data = IoUtils.readFromFile(new File(mUri.getPath()));
- } else if (Scheme.isHttp(mUri) || Scheme.isHttps(mUri)) {
- data = Http.get(mUri.toString());
+ Bitmap bitmap;
+ if (isAtLeastRunningQ() || !isVideoThumbnailUri(mUri)) {
+ byte[] data;
+ if (Scheme.isContent(mUri)) {
+ data = readFromContent(mUri);
+ } else if (Scheme.isFile(mUri)) {
+ data = IoUtils.readFromFile(new File(mUri.getPath()));
+ } else if (Scheme.isHttp(mUri) || Scheme.isHttps(mUri)) {
+ data = Http.get(mUri.toString());
+ } else {
+ throw new IllegalArgumentException(
+ "Unknown scheme '" + mUri.getScheme() + "'");
+ }
+ bitmap = decodeBitmapFromByteArray(data);
} else {
- throw new IllegalArgumentException("Unknown scheme '" + mUri.getScheme() + "'");
+ // TODO This will always return a bitmap which is inconsistent with Q.
+ bitmap = MediaStore.Video.Thumbnails.getThumbnail(mContentResolver,
+ Long.parseLong(mUri.getPathSegments().get(3)),
+ MediaStore.Video.Thumbnails.MINI_KIND, null);
}
- Bitmap bitmap = decodeBitmapFromByteArray(data);
Set<Map.Entry<Executor, Callback>> callbacks;
List<Map.Entry<Executor, Callback>> loadCallbacks;
synchronized (ImageLoader.this) { // TODO(b/123708613) proper lock
@@ -164,5 +196,23 @@
options.inSampleSize = 1; // TODO(b/123708796) add scaling
return BitmapFactory.decodeByteArray(data, 0, data.length, options);
}
+
+ private @NonNull byte[] readFromContent(@NonNull Uri uri) throws IOException {
+ // TODO(b/123708796) set EXTRA_SIZE in opts
+ AssetFileDescriptor assetFileDescriptor =
+ mContentResolver.openTypedAssetFileDescriptor(uri, "image/*", null);
+ if (assetFileDescriptor == null) {
+ throw new FileNotFoundException(uri.toString());
+ }
+ try {
+ return IoUtils.readFromAssetFileDescriptor(assetFileDescriptor);
+ } finally {
+ IoUtils.close(assetFileDescriptor);
+ }
+ }
+
+ private boolean isVideoThumbnailUri(@NonNull Uri uri) {
+ return VIDEO_THUMBNAIL_URI_MATCHER.match(uri) != UriMatcher.NO_MATCH;
+ }
}
}
diff --git a/java/com/android/pump/util/IoUtils.java b/java/com/android/pump/util/IoUtils.java
index c39fe35..aec3440 100644
--- a/java/com/android/pump/util/IoUtils.java
+++ b/java/com/android/pump/util/IoUtils.java
@@ -16,6 +16,8 @@
package com.android.pump.util;
+import android.content.res.AssetFileDescriptor;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
@@ -43,6 +45,16 @@
}
}
+ public static @NonNull byte[] readFromAssetFileDescriptor(
+ @NonNull AssetFileDescriptor assetFileDescriptor) throws IOException {
+ InputStream inputStream = assetFileDescriptor.createInputStream();
+ try {
+ return readFromStream(inputStream);
+ } finally {
+ close(inputStream);
+ }
+ }
+
public static @NonNull byte[] readFromStream(@NonNull InputStream inputStream)
throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
diff --git a/java/com/android/pump/util/Scheme.java b/java/com/android/pump/util/Scheme.java
index ad544e3..7c52ec4 100644
--- a/java/com/android/pump/util/Scheme.java
+++ b/java/com/android/pump/util/Scheme.java
@@ -26,10 +26,15 @@
public final class Scheme {
private Scheme() { }
+ private final static String CONTENT = ContentResolver.SCHEME_CONTENT;
private final static String FILE = ContentResolver.SCHEME_FILE;
private final static String HTTP = "http";
private final static String HTTPS = "https";
+ public static boolean isContent(@NonNull Uri uri) {
+ return CONTENT.equals(uri.getScheme());
+ }
+
public static boolean isFile(@NonNull Uri uri) {
return FILE.equals(uri.getScheme());
}
diff --git a/java/com/android/pump/widget/UriImageView.java b/java/com/android/pump/widget/UriImageView.java
index 7381a8e..f10425e 100644
--- a/java/com/android/pump/widget/UriImageView.java
+++ b/java/com/android/pump/widget/UriImageView.java
@@ -73,7 +73,8 @@
if (uri == null) {
return;
}
- if (Scheme.isFile(uri) || Scheme.isHttp(uri) || Scheme.isHttps(uri)) {
+ if (Scheme.isContent(uri) || Scheme.isFile(uri)
+ || Scheme.isHttp(uri) || Scheme.isHttps(uri)) {
mUri = uri;
loadImage();
} else {