Skip to content

Commit

Permalink
Feature/april changes 2 (jhomlala#450)
Browse files Browse the repository at this point in the history
* Refactored Android notification image selection.

* Refactored Android notification image selection.

* Updated example

* Added headers parameter in BetterPlayerSubtitlesSource

* Added headers parameter in BetterPlayerSubtitlesSource

* Added activityName, open application on notification click on Android

* Fixed playing only audio data sources in iOS

* Fixed playing only audio data sources in iOS

* Removed invalid files

* Updated changelog

* Updated Exo Player version

* Fixed notification not updating correctly for playlists in Android.

* Fixed notification not updating correctly for playlists in Android.

* Removed deprecated Android code. Better Player supports now only v2 embedding.

* Removed deprecated Android code. Better Player supports now only v2 embedding.

* Updated version
  • Loading branch information
jhomlala authored Apr 25, 2021
1 parent 368ad8a commit 8954f8f
Show file tree
Hide file tree
Showing 19 changed files with 439 additions and 228 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
## 0.0.65
* Refactored Android notification image selection.
* Added headers parameter in BetterPlayerSubtitlesSource. Headers is an optional parameter.
* Added activityName to BetterPlayerNotificationConfiguration.
* Android notification will open back application (by https://github.com/shashikantdurge).
* Fixed playing audio-only resources in iOS.
* Updated Exo Player version.
* Fixed notification not updating correctly for playlists in Android.
* [BREAKING_CHANGE] Removed deprecated Android code. Better Player supports now only v2 embedding.

## 0.0.64
* Added Turkish translations (by https://github.com/smurat).
* Video fit fixes (by https://github.com/themadmrj).
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ This plugin is based on [Chewie](https://github.com/brianegan/chewie). Chewie is

```yaml
dependencies:
better_player: ^0.0.64
better_player: ^0.0.65
```
2. Install it
Expand Down Expand Up @@ -889,15 +889,17 @@ BetterPlayerDataSource dataSource = BetterPlayerDataSource(
showNotification: true,
title: "Elephant dream",
author: "Some author",
imageUrl:"https://upload.wikimedia.org/wikipedia/commons/thumb/3/37/African_Bush_Elephant.jpg/1200px-African_Bush_Elephant.jpg",
imageUrl: "https://upload.wikimedia.org/wikipedia/commons/thumb/3/37/African_Bush_Elephant.jpg/1200px-African_Bush_Elephant.jpg",
activityName: "MainActivity",
),
);
```

There are 3 majors parameters here:
title - name of the resource, shown in first line
author - author of the resource, shown in second line
imageUrl - image of the resource (optional). Can be both link to external image or internal file.
imageUrl - image of the resource (optional). Can be both link to external image or internal file
activityName - name of activity used to open application back on notification click; used only for Activity

If showNotification is set as true and no title and author is provided, then empty notification will be
displayed.
Expand Down
12 changes: 6 additions & 6 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ android {
}

dependencies {
implementation 'com.google.android.exoplayer:exoplayer-core:2.13.1'
implementation 'com.google.android.exoplayer:exoplayer-hls:2.13.1'
implementation 'com.google.android.exoplayer:exoplayer-dash:2.13.1'
implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.13.1'
implementation 'com.google.android.exoplayer:exoplayer-ui:2.13.1'
implementation 'com.google.android.exoplayer:extension-mediasession:2.13.1'
implementation 'com.google.android.exoplayer:exoplayer-core:2.13.3'
implementation 'com.google.android.exoplayer:exoplayer-hls:2.13.3'
implementation 'com.google.android.exoplayer:exoplayer-dash:2.13.3'
implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.13.3'
implementation 'com.google.android.exoplayer:exoplayer-ui:2.13.3'
implementation 'com.google.android.exoplayer:extension-mediasession:2.13.3'
implementation "android.arch.lifecycle:runtime:1.1.1"
implementation "android.arch.lifecycle:common:1.1.1"
implementation "android.arch.lifecycle:common-java8:1.1.1"
Expand Down
170 changes: 74 additions & 96 deletions android/src/main/java/com/jhomlala/better_player/BetterPlayer.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
Expand Down Expand Up @@ -62,19 +61,18 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import androidx.lifecycle.Observer;
import androidx.media.session.MediaButtonReceiver;
import androidx.work.Data;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkInfo;
import androidx.work.WorkManager;

import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.view.TextureRegistry;

import java.io.File;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
Expand All @@ -92,7 +90,6 @@ final class BetterPlayer {
private static final String FORMAT_OTHER = "other";
private static final String DEFAULT_NOTIFICATION_CHANNEL = "BETTER_PLAYER_NOTIFICATION";
private static final int NOTIFICATION_ID = 20772077;
private static final int DEFAULT_NOTIFICATION_IMAGE_SIZE_PX = 256;

private final SimpleExoPlayer exoPlayer;
private final TextureRegistry.SurfaceTextureEntry textureEntry;
Expand All @@ -110,6 +107,8 @@ final class BetterPlayer {
private Bitmap bitmap;
private MediaSessionCompat mediaSession;
private DrmSessionManager drmSessionManager;
private WorkManager workManager;
private HashMap<UUID, Observer<WorkInfo>> workerObserverMap;


BetterPlayer(
Expand All @@ -121,6 +120,8 @@ final class BetterPlayer {
this.textureEntry = textureEntry;
trackSelector = new DefaultTrackSelector(context);
exoPlayer = new SimpleExoPlayer.Builder(context).setTrackSelector(trackSelector).build();
workManager = WorkManager.getInstance(context);
workerObserverMap = new HashMap<>();

setupVideoPlayer(eventChannel, textureEntry, result);
}
Expand Down Expand Up @@ -152,21 +153,23 @@ void setDataSource(
drmSessionManager = null;
} else {
UUID drmSchemeUuid = Util.getDrmUuid("widevine");
drmSessionManager =
new DefaultDrmSessionManager.Builder()
.setUuidAndExoMediaDrmProvider(drmSchemeUuid,
uuid -> {
try {
FrameworkMediaDrm mediaDrm = FrameworkMediaDrm.newInstance(uuid);
// Force L3.
mediaDrm.setPropertyString("securityLevel", "L3");
return mediaDrm;
} catch (UnsupportedDrmException e) {
return new DummyExoMediaDrm();
}
})
.setMultiSession(false)
.build(httpMediaDrmCallback);
if (drmSchemeUuid != null) {
drmSessionManager =
new DefaultDrmSessionManager.Builder()
.setUuidAndExoMediaDrmProvider(drmSchemeUuid,
uuid -> {
try {
FrameworkMediaDrm mediaDrm = FrameworkMediaDrm.newInstance(uuid);
// Force L3.
mediaDrm.setPropertyString("securityLevel", "L3");
return mediaDrm;
} catch (UnsupportedDrmException e) {
return new DummyExoMediaDrm();
}
})
.setMultiSession(false)
.build(httpMediaDrmCallback);
}
}
} else {
drmSessionManager = null;
Expand Down Expand Up @@ -195,7 +198,10 @@ void setDataSource(
result.success(null);
}

public void setupPlayerNotification(Context context, String title, String author, String imageUrl, String notificationChannelName) {

public void setupPlayerNotification(Context context, String title, String author,
String imageUrl, String notificationChannelName,
String activityName) {

PlayerNotificationManager.MediaDescriptionAdapter mediaDescriptionAdapter
= new PlayerNotificationManager.MediaDescriptionAdapter() {
Expand All @@ -208,10 +214,11 @@ public String getCurrentContentTitle(@NonNull Player player) {
@Nullable
@Override
public PendingIntent createCurrentContentIntent(@NonNull Player player) {

final String packageName = context.getApplicationContext().getPackageName();
Intent notificationIntent = new Intent();
notificationIntent.setClassName(packageName,
packageName + ".MainActivity");
packageName + "." + activityName);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
return PendingIntent.getActivity(context, 0,
Expand All @@ -234,18 +241,54 @@ public Bitmap getCurrentLargeIcon(@NonNull Player player,
if (bitmap != null) {
return bitmap;
}
new Thread(() -> {
bitmap = null;
if (imageUrl.contains("http")) {
bitmap = getBitmapFromExternalURL(imageUrl);
} else {
bitmap = getBitmapFromInternalURL(imageUrl);


OneTimeWorkRequest imageWorkRequest = new OneTimeWorkRequest.Builder(ImageWorker.class)
.addTag(imageUrl)
.setInputData(
new Data.Builder()
.putString(BetterPlayerPlugin.URL_PARAMETER, imageUrl)
.build())
.build();

workManager.enqueue(imageWorkRequest);

Observer<WorkInfo> workInfoObserver = workInfo -> {
try {
if (workInfo != null) {
WorkInfo.State state = workInfo.getState();
if (state == WorkInfo.State.SUCCEEDED) {

Data outputData = workInfo.getOutputData();
String filePath = outputData.getString(BetterPlayerPlugin.FILE_PATH_PARAMETER);
//Bitmap here is already processed and it's very small, so it won't
//break anything.
bitmap = BitmapFactory.decodeFile(filePath);
callback.onBitmap(bitmap);

}
if (state == WorkInfo.State.SUCCEEDED
|| state == WorkInfo.State.CANCELLED
|| state == WorkInfo.State.FAILED) {
final UUID uuid = imageWorkRequest.getId();
Observer<WorkInfo> observer = workerObserverMap.remove(uuid);
if (observer != null) {
workManager.getWorkInfoByIdLiveData(uuid).removeObserver(observer);
}
}
}


} catch (Exception exception) {
Log.e(TAG, "Image select error: " + exception);
}
};

Bitmap finalBitmap = bitmap;
new Handler(Looper.getMainLooper()).post(() -> callback.onBitmap(finalBitmap));
final UUID workerUuid = imageWorkRequest.getId();
workManager.getWorkInfoByIdLiveData(workerUuid)
.observeForever(workInfoObserver);
workerObserverMap.put(workerUuid, workInfoObserver);

}).start();
return null;
}
};
Expand Down Expand Up @@ -406,71 +449,6 @@ public void disposeRemoteNotifications() {
bitmap = null;
}

private Bitmap getBitmapFromInternalURL(String src) {
try {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inSampleSize = calculateBitmapInSmapleSize(options,
DEFAULT_NOTIFICATION_IMAGE_SIZE_PX,
DEFAULT_NOTIFICATION_IMAGE_SIZE_PX);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(src);
} catch (Exception exception) {
Log.e(TAG, "Failed to get bitmap from internal url: " + src);
return null;
}
}


private Bitmap getBitmapFromExternalURL(String src) {
InputStream inputStream = null;
try {
URL url = new URL(src);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
inputStream = connection.getInputStream();

final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(inputStream, null, options);
inputStream.close();
connection = (HttpURLConnection) url.openConnection();
inputStream = connection.getInputStream();
options.inSampleSize = calculateBitmapInSmapleSize(
options, DEFAULT_NOTIFICATION_IMAGE_SIZE_PX, DEFAULT_NOTIFICATION_IMAGE_SIZE_PX);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeStream(inputStream, null, options);

} catch (Exception exception) {
Log.e(TAG, "Failed to get bitmap from external url: " + src);
return null;
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (Exception exception) {
Log.e(TAG, "Failed to close bitmap input stream/");
}
}
}

private int calculateBitmapInSmapleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;

if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}


private MediaSource buildMediaSource(
Uri uri, DataSource.Factory mediaDataSourceFactory, String formatHint, Context context) {
Expand Down
Loading

0 comments on commit 8954f8f

Please sign in to comment.