Skip to content
This repository has been archived by the owner on Jun 2, 2022. It is now read-only.

Commit

Permalink
Added whole thread's playlist playing in the exoplayer (exoplayer sup…
Browse files Browse the repository at this point in the history
…ports navigation between videos now). Added touch gestures in the exoplayer (left and right swipes for navigation and up swipe to close the player). Fixed appearing of the second app icon.
  • Loading branch information
f77 committed Jun 14, 2020
1 parent 884eac6 commit c7ded71
Show file tree
Hide file tree
Showing 7 changed files with 318 additions and 20 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Security


## [2.18.0] - 2020-06-14
### Added
- Added whole thread's playlist playing in the exoplayer (exoplayer supports navigation between videos now).
- Added touch gestures in the exoplayer (left and right swipes for navigation and up swipe to close the player).
### Fixed
- Fixed appearing of the second app icon.

## [2.17.0] - 2020-06-14
### Added
- Added test support of ExoPlayer. Enabled by default. You can disable it in "Preferences -> Contents -> Video Player -> Use ExoPlayer".
Expand Down Expand Up @@ -141,6 +148,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Added this changelog.
- Rebuilt to a new Android SDK (29).

[2.18.0]: https://github.com/f77/Dashchan/compare/2.17.0...2.18.0
[2.17.0]: https://github.com/f77/Dashchan/compare/2.16.0...2.17.0
[2.16.0]: https://github.com/f77/Dashchan/compare/2.15.0...2.16.0
[2.15.0]: https://github.com/f77/Dashchan/compare/2.14.0...2.15.0
Expand Down
1 change: 0 additions & 1 deletion player-lib/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ limitations under the License. -->
android:theme="@style/PlayerTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
Expand Down
125 changes: 125 additions & 0 deletions player-lib/src/main/java/com/mishiranu/exoplayer/OnSwipeListener.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package com.mishiranu.exoplayer;

import android.view.GestureDetector;
import android.view.MotionEvent;

/**
* Gestures listener.
*
* @link https://stackoverflow.com/a/26387629/10452175
*/
public class OnSwipeListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {

// Grab two events located on the plane at e1=(x1, y1) and e2=(x2, y2)
// Let e1 be the initial event
// e2 can be located at 4 different positions, consider the following diagram
// (Assume that lines are separated by 90 degrees.)
//
//
// \ A /
// \ /
// D e1 B
// / \
// / C \
//
// So if (x2,y2) falls in region:
// A => it's an UP swipe
// B => it's a RIGHT swipe
// C => it's a DOWN swipe
// D => it's a LEFT swipe
//

float x1 = e1.getX();
float y1 = e1.getY();

float x2 = e2.getX();
float y2 = e2.getY();

Direction direction = getDirection(x1, y1, x2, y2);
return onSwipe(direction);
}

/**
* Override this method. The Direction enum will tell you how the user swiped.
*/
public boolean onSwipe(Direction direction) {
return false;
}

/**
* Given two points in the plane p1=(x1, x2) and p2=(y1, y1), this method
* returns the direction that an arrow pointing from p1 to p2 would have.
*
* @param x1 the x position of the first point
* @param y1 the y position of the first point
* @param x2 the x position of the second point
* @param y2 the y position of the second point
* @return the direction
*/
public Direction getDirection(float x1, float y1, float x2, float y2) {
double angle = getAngle(x1, y1, x2, y2);
return Direction.fromAngle(angle);
}

/**
* Finds the angle between two points in the plane (x1,y1) and (x2, y2)
* The angle is measured with 0/360 being the X-axis to the right, angles
* increase counter clockwise.
*
* @param x1 the x position of the first point
* @param y1 the y position of the first point
* @param x2 the x position of the second point
* @param y2 the y position of the second point
* @return the angle between two points
*/
public double getAngle(float x1, float y1, float x2, float y2) {

double rad = Math.atan2(y1 - y2, x2 - x1) + Math.PI;
return (rad * 180 / Math.PI + 180) % 360;
}


public enum Direction {
up,
down,
left,
right;

/**
* Returns a direction given an angle.
* Directions are defined as follows:
* <p>
* Up: [45, 135]
* Right: [0,45] and [315, 360]
* Down: [225, 315]
* Left: [135, 225]
*
* @param angle an angle from 0 to 360 - e
* @return the direction of an angle
*/
public static Direction fromAngle(double angle) {
if (inRange(angle, 45, 135)) {
return Direction.up;
} else if (inRange(angle, 0, 45) || inRange(angle, 315, 360)) {
return Direction.right;
} else if (inRange(angle, 225, 315)) {
return Direction.down;
} else {
return Direction.left;
}

}

/**
* @param angle an angle
* @param init the initial bound
* @param end the final bound
* @return returns true if the given angle is in the interval [init, end).
*/
private static boolean inRange(double angle, float init, float end) {
return (angle >= init) && (angle < end);
}
}
}
115 changes: 103 additions & 12 deletions player-lib/src/main/java/com/mishiranu/exoplayer/PlayerActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,21 @@
package com.mishiranu.exoplayer;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.view.GestureDetectorCompat;

import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
Expand All @@ -39,7 +43,7 @@
* A fullscreen activity to play audio or video streams.
*/
public class PlayerActivity extends AppCompatActivity {
public static final String STATE_KEY_URL = "url";
public static final String STATE_PLAYLIST = "playlist";
public static final String STATE_KEY_HIDE_SYSTEM_UI = "hide_system_ui";
public static final String STATE_KEY_IS_REPEAT = "is_repeat";

Expand All @@ -48,27 +52,61 @@ public class PlayerActivity extends AppCompatActivity {

private PlayerView playerView;
private SimpleExoPlayer player;
private String url;
private boolean playWhenReady = true;
private int currentWindow = 0;
private int currentWindow;
private long playbackPosition = 0;
private boolean isControllerShowedNow = false;

private Playlist playlist;
private boolean isHideSystemUi;
private boolean isRepeat;

private GestureDetectorCompat gestureDetector;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_player);

gestureDetector = new GestureDetectorCompat(this, new MyGestureListener());
playerView = findViewById(R.id.video_view);
initPlayerView(playerView);

// https://github.com/google/ExoPlayer/issues/5725#issuecomment-493431401
playerView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent event) {
gestureDetector.onTouchEvent(event);
return false;
}
});

playbackStateListener = new PlaybackStateListener();

// Get state data.
url = getIntent().getStringExtra(STATE_KEY_URL);
isHideSystemUi = getIntent().getBooleanExtra(STATE_KEY_HIDE_SYSTEM_UI, false);
isRepeat = getIntent().getBooleanExtra(STATE_KEY_IS_REPEAT, true);
initState(getIntent());

// Set position.
currentWindow = playlist.getCurrentPosition();
}

/**
* Initialise activity state.
*
* @param intent Intent.
*/
protected void initState(Intent intent) {
playlist = intent.getParcelableExtra(STATE_PLAYLIST);
isHideSystemUi = intent.getBooleanExtra(STATE_KEY_HIDE_SYSTEM_UI, false);
isRepeat = intent.getBooleanExtra(STATE_KEY_IS_REPEAT, true);
}

/**
* Initialize PlayerView.
*
* @param playerView PlayerView.
*/
protected void initPlayerView(PlayerView playerView) {
playerView.setControllerHideOnTouch(false);
}

@Override
Expand Down Expand Up @@ -115,8 +153,7 @@ private void initializePlayer() {
}

playerView.setPlayer(player);
Uri uri = Uri.parse(url);
MediaSource mediaSource = buildMediaSource(uri);
MediaSource mediaSource = buildMediaSource(playlist);

player.setPlayWhenReady(playWhenReady);
player.seekTo(currentWindow, playbackPosition);
Expand All @@ -136,10 +173,22 @@ private void releasePlayer() {
}
}

private MediaSource buildMediaSource(Uri uri) {
private MediaSource buildMediaSource(Playlist playlist) {
// These factories are used to construct two media sources below.
DataSource.Factory dataSourceFactory =
new DefaultDataSourceFactory(this, "exoplayer-codelab");
return new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(uri);
new DefaultDataSourceFactory(this, "exoplayer-dashchan");
ProgressiveMediaSource.Factory mediaSourceFactory =
new ProgressiveMediaSource.Factory(dataSourceFactory);

// Result media source.
ConcatenatingMediaSource concatenatingMediaSource = new ConcatenatingMediaSource();

// Add single sources.
for (Uri uri : playlist.getURIs()) {
concatenatingMediaSource.addMediaSource(mediaSourceFactory.createMediaSource(uri));
}

return concatenatingMediaSource;
}

@SuppressLint("InlinedApi")
Expand All @@ -152,6 +201,18 @@ private void hideSystemUi() {
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
}

/**
* Reverse current status of the controller.
*/
protected void changeControllerVisibility() {
isControllerShowedNow = !isControllerShowedNow;
if (isControllerShowedNow) {
playerView.hideController();
} else {
playerView.showController();
}
}

private class PlaybackStateListener implements Player.EventListener {

@Override
Expand Down Expand Up @@ -180,4 +241,34 @@ public void onPlayerStateChanged(boolean playWhenReady,
}
}

class MyGestureListener extends OnSwipeListener {
private static final String DEBUG_TAG = "EXOPLAYER_GESTURES";

@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
Log.d(DEBUG_TAG, "SINGLE TAP");
changeControllerVisibility();
return super.onSingleTapConfirmed(e);
}

@Override
public boolean onSwipe(Direction direction) {
Log.d(DEBUG_TAG, "SWIPE TO: " + direction.name());
switch (direction) {
case up:
finish();
break;
case down:
break;
case left:
player.next();
break;
case right:
player.previous();
break;
}

return super.onSwipe(direction);
}
}
}
51 changes: 51 additions & 0 deletions player-lib/src/main/java/com/mishiranu/exoplayer/Playlist.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.mishiranu.exoplayer;

import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;

import java.util.ArrayList;

/**
* Simple playlist class for the player.
*/
public class Playlist implements Parcelable {
protected ArrayList<Uri> URIs;
protected int currentPosition;

public Playlist(ArrayList<Uri> URIs, int currentPosition) {
this.URIs = URIs;
this.currentPosition = currentPosition;
}

public ArrayList<Uri> getURIs() {
return URIs;
}

public int getCurrentPosition() {
return currentPosition;
}

@Override
public int describeContents() {
return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeList(URIs);
dest.writeInt(currentPosition);
}

// this is used to regenerate your object. All Parcelables must have a CREATOR that implements these two methods
public static final Parcelable.Creator<Playlist> CREATOR = new Parcelable.Creator<Playlist>() {
@SuppressWarnings("unchecked")
public Playlist createFromParcel(Parcel in) {
return new Playlist(in.readArrayList(getClass().getClassLoader()), in.readInt());
}

public Playlist[] newArray(int size) {
return new Playlist[size];
}
};
}
Loading

0 comments on commit c7ded71

Please sign in to comment.