aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build.gradle2
-rw-r--r--libs/fullscreenvideoview/.gitignore1
-rw-r--r--libs/fullscreenvideoview/build.gradle27
-rw-r--r--libs/fullscreenvideoview/gradle.properties3
-rw-r--r--libs/fullscreenvideoview/proguard-rules.pro17
-rw-r--r--libs/fullscreenvideoview/src/main/AndroidManifest.xml4
-rw-r--r--libs/fullscreenvideoview/src/main/java/com/github/rtoshiro/view/video/FullscreenVideoLayout.java336
-rw-r--r--libs/fullscreenvideoview/src/main/java/com/github/rtoshiro/view/video/FullscreenVideoView.java1014
-rw-r--r--libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_control_disabled.pngbin0 -> 1406 bytes
-rw-r--r--libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_control_focused.pngbin0 -> 2085 bytes
-rw-r--r--libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_control_normal.pngbin0 -> 2537 bytes
-rw-r--r--libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_control_pressed.pngbin0 -> 2667 bytes
-rw-r--r--libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_fullscreen_reader.pngbin0 -> 5035 bytes
-rw-r--r--libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_fullscreen_reader_white.pngbin0 -> 4355 bytes
-rw-r--r--libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_pause_reader.pngbin0 -> 4363 bytes
-rw-r--r--libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_pause_reader_white.pngbin0 -> 3673 bytes
-rw-r--r--libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_play_reader.pngbin0 -> 5354 bytes
-rw-r--r--libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_play_reader_white.pngbin0 -> 4552 bytes
-rw-r--r--libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_primary.9.pngbin0 -> 153 bytes
-rw-r--r--libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_secondary.9.pngbin0 -> 154 bytes
-rw-r--r--libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_track.9.pngbin0 -> 1094 bytes
-rw-r--r--libs/fullscreenvideoview/src/main/res/drawable/fvl_progress.xml27
-rw-r--r--libs/fullscreenvideoview/src/main/res/drawable/fvl_selector_fullscreen.xml5
-rw-r--r--libs/fullscreenvideoview/src/main/res/drawable/fvl_selector_pause.xml5
-rw-r--r--libs/fullscreenvideoview/src/main/res/drawable/fvl_selector_play.xml5
-rw-r--r--libs/fullscreenvideoview/src/main/res/layout/view_videocontrols.xml82
-rw-r--r--libs/fullscreenvideoview/src/main/res/values/strings.xml6
-rw-r--r--settings.gradle1
-rw-r--r--src/main/java/de/pixart/messenger/ui/MediaViewerActivity.java31
29 files changed, 1565 insertions, 1 deletions
diff --git a/build.gradle b/build.gradle
index 99dd1d732..908fa4b8b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -70,7 +70,7 @@ dependencies {
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support:cardview-v7:28.0.0'
implementation 'com.davemorrissey.labs:subsampling-scale-image-view:3.10.0'
- implementation 'com.github.rtoshiro.fullscreenvideoview:fullscreenvideoview:1.1.3'
+ implementation project(':libs:fullscreenvideoview')
implementation 'pub.devrel:easypermissions:2.0.0'
implementation 'com.wefika:flowlayout:0.4.1'
implementation 'com.googlecode.ez-vcard:ez-vcard:0.10.5'
diff --git a/libs/fullscreenvideoview/.gitignore b/libs/fullscreenvideoview/.gitignore
new file mode 100644
index 000000000..796b96d1c
--- /dev/null
+++ b/libs/fullscreenvideoview/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/libs/fullscreenvideoview/build.gradle b/libs/fullscreenvideoview/build.gradle
new file mode 100644
index 000000000..1201cea57
--- /dev/null
+++ b/libs/fullscreenvideoview/build.gradle
@@ -0,0 +1,27 @@
+apply plugin: 'com.android.library'
+
+android {
+ lintOptions {
+ abortOnError false
+ }
+ compileSdkVersion 28
+
+ defaultConfig {
+ minSdkVersion 14
+ targetSdkVersion 28
+ versionCode 11
+ versionName "1.1.3"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+}
+
+//apply from: '../maven_push.gradle' \ No newline at end of file
diff --git a/libs/fullscreenvideoview/gradle.properties b/libs/fullscreenvideoview/gradle.properties
new file mode 100644
index 000000000..878a8d0ed
--- /dev/null
+++ b/libs/fullscreenvideoview/gradle.properties
@@ -0,0 +1,3 @@
+POM_NAME=FullscreenVideoView Library
+POM_ARTIFACT_ID=fullscreenvideoview
+POM_PACKAGING=aar \ No newline at end of file
diff --git a/libs/fullscreenvideoview/proguard-rules.pro b/libs/fullscreenvideoview/proguard-rules.pro
new file mode 100644
index 000000000..c7928bcb5
--- /dev/null
+++ b/libs/fullscreenvideoview/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Applications/ADT/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/libs/fullscreenvideoview/src/main/AndroidManifest.xml b/libs/fullscreenvideoview/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..5b5b2ace5
--- /dev/null
+++ b/libs/fullscreenvideoview/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.github.rtoshiro.view.video">
+
+</manifest>
diff --git a/libs/fullscreenvideoview/src/main/java/com/github/rtoshiro/view/video/FullscreenVideoLayout.java b/libs/fullscreenvideoview/src/main/java/com/github/rtoshiro/view/video/FullscreenVideoLayout.java
new file mode 100644
index 000000000..f4113ce48
--- /dev/null
+++ b/libs/fullscreenvideoview/src/main/java/com/github/rtoshiro/view/video/FullscreenVideoLayout.java
@@ -0,0 +1,336 @@
+/**
+ * Copyright (C) 2016 Toshiro Sugii
+ * <p/>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.github.rtoshiro.view.video;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.media.MediaPlayer;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.RelativeLayout;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import java.util.Locale;
+
+public class FullscreenVideoLayout extends FullscreenVideoView implements View.OnClickListener, SeekBar.OnSeekBarChangeListener, MediaPlayer.OnPreparedListener {
+
+ /**
+ * Log cat TAG name
+ */
+ private final static String TAG = "FullscreenVideoLayout";
+
+ /**
+ * RelativeLayout that contains all control related views
+ */
+ public View videoControlsView;
+
+ /**
+ * SeekBar reference (from videoControlsView)
+ */
+ protected SeekBar seekBar;
+
+ /**
+ * Reference to ImageButton play
+ */
+ protected ImageButton imgplay;
+
+ /**
+ * Reference to ImageButton fullscreen
+ */
+ protected ImageButton imgfullscreen;
+
+ /**
+ * Reference to TextView for elapsed time and total time
+ */
+ protected TextView textTotal, textElapsed;
+
+ /**
+ * Handler and Runnable to keep tracking on elapsed time
+ */
+ protected static final Handler TIME_THREAD = new Handler();
+ protected Runnable updateTimeRunnable = new Runnable() {
+ public void run() {
+ updateCounter();
+
+ TIME_THREAD.postDelayed(this, 200);
+ }
+ };
+
+ public FullscreenVideoLayout(Context context) {
+ super(context);
+ }
+
+ public FullscreenVideoLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public FullscreenVideoLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ protected void release() {
+ super.release();
+ }
+
+ @Override
+ protected void initObjects() {
+ super.initObjects();
+
+ if (this.videoControlsView == null) {
+ LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ this.videoControlsView = inflater.inflate(R.layout.view_videocontrols, this, false);
+ }
+
+ if (videoControlsView != null) {
+ RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.FILL_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
+ params.addRule(ALIGN_PARENT_BOTTOM);
+ addView(videoControlsView, params);
+
+ this.seekBar = this.videoControlsView.findViewById(R.id.vcv_seekbar);
+ this.imgfullscreen = this.videoControlsView.findViewById(R.id.vcv_img_fullscreen);
+ this.imgplay = this.videoControlsView.findViewById(R.id.vcv_img_play);
+ this.textTotal = this.videoControlsView.findViewById(R.id.vcv_txt_total);
+ this.textElapsed = this.videoControlsView.findViewById(R.id.vcv_txt_elapsed);
+ }
+
+ if (this.imgplay != null)
+ this.imgplay.setOnClickListener(this);
+ if (this.imgfullscreen != null)
+ this.imgfullscreen.setOnClickListener(this);
+ if (this.seekBar != null)
+ this.seekBar.setOnSeekBarChangeListener(this);
+
+ // Start controls invisible. Make it visible when it is prepared
+ if (this.videoControlsView != null)
+ this.videoControlsView.setVisibility(View.INVISIBLE);
+ }
+
+ @Override
+ protected void releaseObjects() {
+ super.releaseObjects();
+
+ if (this.videoControlsView != null)
+ removeView(this.videoControlsView);
+ }
+
+ protected void startCounter() {
+ Log.d(TAG, "startCounter");
+
+ TIME_THREAD.postDelayed(updateTimeRunnable, 200);
+ }
+
+ protected void stopCounter() {
+ Log.d(TAG, "stopCounter");
+
+ TIME_THREAD.removeCallbacks(updateTimeRunnable);
+ }
+
+ protected void updateCounter() {
+ if (this.textElapsed == null)
+ return;
+
+ int elapsed = getCurrentPosition();
+ // getCurrentPosition is a little bit buggy :(
+ if (elapsed > 0 && elapsed < getDuration()) {
+ seekBar.setProgress(elapsed);
+
+ elapsed = Math.round(elapsed / 1000.f);
+ long s = elapsed % 60;
+ long m = (elapsed / 60) % 60;
+ long h = (elapsed / (60 * 60)) % 24;
+
+ if (h > 0)
+ textElapsed.setText(String.format(Locale.US, "%d:%02d:%02d", h, m, s));
+ else
+ textElapsed.setText(String.format(Locale.US, "%02d:%02d", m, s));
+ }
+ }
+
+ @Override
+ public void onCompletion(MediaPlayer mp) {
+ Log.d(TAG, "onCompletion");
+
+ super.onCompletion(mp);
+ stopCounter();
+ updateControls();
+ if (currentState != State.ERROR)
+ updateCounter();
+ }
+
+ @Override
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ boolean result = super.onError(mp, what, extra);
+ stopCounter();
+ updateControls();
+ return result;
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (getCurrentState() == State.END) {
+ Log.d(TAG, "onDetachedFromWindow END");
+ stopCounter();
+ }
+ }
+
+ @Override
+ protected void tryToPrepare() {
+ Log.d(TAG, "tryToPrepare");
+ super.tryToPrepare();
+
+ if (getCurrentState() == State.PREPARED || getCurrentState() == State.STARTED) {
+ if (textElapsed != null && textTotal != null) {
+ int total = getDuration();
+ if (total > 0) {
+ seekBar.setMax(total);
+ seekBar.setProgress(0);
+
+ total = total / 1000;
+ long s = total % 60;
+ long m = (total / 60) % 60;
+ long h = (total / (60 * 60)) % 24;
+ if (h > 0) {
+ textElapsed.setText("00:00:00");
+ textTotal.setText(String.format(Locale.US, "%d:%02d:%02d", h, m, s));
+ } else {
+ textElapsed.setText("00:00");
+ textTotal.setText(String.format(Locale.US, "%02d:%02d", m, s));
+ }
+ }
+ }
+
+ if (videoControlsView != null)
+ videoControlsView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ public void start() throws IllegalStateException {
+ Log.d(TAG, "start");
+
+ if (!isPlaying()) {
+ super.start();
+ startCounter();
+ updateControls();
+ }
+ }
+
+ @Override
+ public void pause() throws IllegalStateException {
+ Log.d(TAG, "pause");
+
+ if (isPlaying()) {
+ stopCounter();
+ super.pause();
+ updateControls();
+ }
+ }
+
+ @Override
+ public void reset() {
+ Log.d(TAG, "reset");
+
+ super.reset();
+
+ stopCounter();
+ updateControls();
+ }
+
+ @Override
+ public void stop() throws IllegalStateException {
+ Log.d(TAG, "stop");
+
+ super.stop();
+ stopCounter();
+ updateControls();
+ }
+
+ protected void updateControls() {
+ if (imgplay == null) return;
+
+ Drawable icon;
+ if (getCurrentState() == State.STARTED) {
+ icon = context.getResources().getDrawable(R.drawable.fvl_selector_pause);
+ } else {
+ icon = context.getResources().getDrawable(R.drawable.fvl_selector_play);
+ }
+ imgplay.setBackgroundDrawable(icon);
+ }
+
+ public void hideControls() {
+ Log.d(TAG, "hideControls");
+ if (videoControlsView != null) {
+ videoControlsView.setVisibility(View.INVISIBLE);
+ }
+ }
+
+ public void showControls() {
+ Log.d(TAG, "showControls");
+ if (videoControlsView != null) {
+ videoControlsView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ /**
+ * Onclick action
+ * Controls play button and fullscreen button.
+ *
+ * @param v View defined in XML
+ */
+ @Override
+ public void onClick(View v) {
+ if (v.getId() == R.id.vcv_img_play) {
+ if (isPlaying()) {
+ pause();
+ } else {
+ start();
+ }
+ } else {
+ setFullscreen(!isFullscreen());
+ }
+ }
+
+ /**
+ * SeekBar Listener
+ */
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ Log.d(TAG, "onProgressChanged " + progress);
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ stopCounter();
+ Log.d(TAG, "onStartTrackingTouch");
+
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ int progress = seekBar.getProgress();
+ seekTo(progress);
+ Log.d(TAG, "onStopTrackingTouch");
+
+ }
+}
diff --git a/libs/fullscreenvideoview/src/main/java/com/github/rtoshiro/view/video/FullscreenVideoView.java b/libs/fullscreenvideoview/src/main/java/com/github/rtoshiro/view/video/FullscreenVideoView.java
new file mode 100644
index 000000000..f068a468b
--- /dev/null
+++ b/libs/fullscreenvideoview/src/main/java/com/github/rtoshiro/view/video/FullscreenVideoView.java
@@ -0,0 +1,1014 @@
+/**
+ * Copyright (C) 2016 Toshiro Sugii
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.github.rtoshiro.view.video;
+
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.graphics.Color;
+import android.graphics.SurfaceTexture;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnBufferingUpdateListener;
+import android.media.MediaPlayer.OnCompletionListener;
+import android.media.MediaPlayer.OnErrorListener;
+import android.media.MediaPlayer.OnInfoListener;
+import android.media.MediaPlayer.OnPreparedListener;
+import android.media.MediaPlayer.OnSeekCompleteListener;
+import android.media.MediaPlayer.OnVideoSizeChangedListener;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.TextureView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.widget.ProgressBar;
+import android.widget.RelativeLayout;
+
+import java.io.IOException;
+
+/**
+ * Acts like a android.widget.VideoView with fullscreen functionality
+ *
+ * @author rtoshiro
+ * @version 2015.0527
+ * @since 1.0
+ */
+@SuppressLint("NewApi")
+public class FullscreenVideoView extends RelativeLayout implements SurfaceHolder.Callback, OnPreparedListener, OnErrorListener, OnSeekCompleteListener, OnCompletionListener, OnInfoListener, OnVideoSizeChangedListener, OnBufferingUpdateListener, TextureView.SurfaceTextureListener {
+
+ /**
+ * Debug Tag for use logging debug output to LogCat
+ */
+ private final static String TAG = "FullscreenVideoView";
+
+ protected Context context;
+ protected Activity activity; // Used when orientation changes is not static
+
+ protected MediaPlayer mediaPlayer;
+ protected SurfaceHolder surfaceHolder;
+ protected SurfaceView surfaceView;
+ protected boolean videoIsReady, surfaceIsReady;
+ protected boolean detachedByFullscreen;
+ protected State currentState;
+ protected State lastState; // Tells onSeekCompletion what to do
+
+ protected View onProgressView;
+
+ protected ViewGroup parentView; // Controls fullscreen container
+ protected ViewGroup.LayoutParams currentLayoutParams;
+
+ protected boolean fullscreen;
+ protected boolean shouldAutoplay;
+ protected int initialConfigOrientation;
+ protected int initialMovieWidth, initialMovieHeight;
+ protected String videoPath;
+ protected Uri videoUri;
+
+ protected OnBufferingUpdateListener bufferingUpdateListener;
+ protected OnCompletionListener completionListener;
+ protected OnErrorListener errorListener;
+ protected OnInfoListener infoListener;
+ protected OnPreparedListener preparedListener;
+ protected OnSeekCompleteListener seekCompleteListener;
+ protected OnVideoSizeChangedListener videoSizeChangedListener;
+
+ protected TextureView textureView;
+
+ /**
+ * States of MediaPlayer
+ *
+ * @see <a href="http://developer.android.com/reference/android/media/MediaPlayer.html#StateDiagram">MediaPlayer</a>
+ */
+ public enum State {
+ IDLE,
+ INITIALIZED,
+ PREPARED,
+ PREPARING,
+ STARTED,
+ STOPPED,
+ PAUSED,
+ PLAYBACKCOMPLETED,
+ ERROR,
+ END
+ }
+
+ public FullscreenVideoView(Context context) {
+ super(context);
+ this.context = context;
+
+ init();
+ }
+
+ public FullscreenVideoView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ this.context = context;
+
+ init();
+ }
+
+ public FullscreenVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ this.context = context;
+
+ init();
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ Log.d(TAG, "onSizeChanged - w = " + w + " - h: " + h + " - oldw: " + oldw + " - oldh:" + oldw);
+ super.onSizeChanged(w, h, oldw, oldh);
+ resize();
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ Log.d(TAG, "onSaveInstanceState");
+ Parcelable p = super.onSaveInstanceState();
+ return p;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ Log.d(TAG, "onRestoreInstanceState");
+ super.onRestoreInstanceState(state);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ Log.d(TAG, "onDetachedFromWindow - detachedByFullscreen: " + detachedByFullscreen);
+
+ super.onDetachedFromWindow();
+
+ if (!this.detachedByFullscreen) {
+ release();
+ }
+
+ this.detachedByFullscreen = false;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ Log.d(TAG, "onAttachedToWindow");
+
+ // If Object still exists, reload the video
+ if (this.mediaPlayer == null &&
+ this.currentState == State.END) {
+ initObjects();
+ try {
+ if (this.videoPath != null)
+ setVideoPath(this.videoPath);
+ else if (this.videoUri != null)
+ setVideoURI(this.videoUri);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ // TextureView
+ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ @Override
+ public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
+ Log.d(TAG, "onSurfaceTextureAvailable - state: " + this.currentState);
+
+ Surface surface = new Surface(surfaceTexture);
+ if (this.mediaPlayer != null) {
+ this.mediaPlayer.setSurface(surface);
+
+ // If is not prepared yet - tryToPrepare()
+ if (!this.surfaceIsReady) {
+ this.surfaceIsReady = true;
+ if (this.currentState == State.INITIALIZED ||
+ this.currentState == State.PREPARING)
+ tryToPrepare();
+ }
+ }
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+ Log.d(TAG, "onSurfaceTextureSizeChanged - width: " + width + " - height: " + height);
+ resize();
+ }
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+ Log.d(TAG, "onSurfaceTextureDestroyed");
+ if (mediaPlayer != null && mediaPlayer.isPlaying())
+ mediaPlayer.pause();
+
+ surfaceIsReady = false;
+
+ return true;
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+ Log.d(TAG, "onSurfaceTextureUpdated");
+ }
+
+ // SurfaceView methods
+ @Override
+ synchronized public void surfaceCreated(SurfaceHolder holder) {
+ Log.d(TAG, "surfaceCreated called = " + currentState);
+
+ if (this.mediaPlayer != null) {
+ this.mediaPlayer.setDisplay(surfaceHolder);
+
+ // If is not prepared yet - tryToPrepare()
+ if (!this.surfaceIsReady) {
+ this.surfaceIsReady = true;
+ if (this.currentState == State.INITIALIZED ||
+ this.currentState == State.PREPARING)
+ tryToPrepare();
+ }
+ }
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ Log.d(TAG, "surfaceChanged called");
+ resize();
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ Log.d(TAG, "surfaceDestroyed called");
+ if (mediaPlayer != null && mediaPlayer.isPlaying())
+ mediaPlayer.pause();
+
+ surfaceIsReady = false;
+ }
+
+ @Override
+ synchronized public void onPrepared(MediaPlayer mp) {
+ Log.d(TAG, "onPrepared called");
+ videoIsReady = true;
+ tryToPrepare();
+ }
+
+ /**
+ * Restore the last State before seekTo()
+ *
+ * @param mp the MediaPlayer that issued the seek operation
+ */
+ @Override
+ public void onSeekComplete(MediaPlayer mp) {
+ Log.d(TAG, "onSeekComplete");
+
+ stopLoading();
+ if (lastState != null) {
+ switch (lastState) {
+ case STARTED: {
+ start();
+ break;
+ }
+ case PLAYBACKCOMPLETED: {
+ currentState = State.PLAYBACKCOMPLETED;
+ break;
+ }
+ case PREPARED: {
+ currentState = State.PREPARED;
+ break;
+ }
+ }
+ }
+
+ if (this.seekCompleteListener != null)
+ this.seekCompleteListener.onSeekComplete(mp);
+ }
+
+ @Override
+ public void onCompletion(MediaPlayer mp) {
+ if (this.mediaPlayer != null) {
+ if (this.currentState != State.ERROR) {
+ Log.d(TAG, "onCompletion");
+ if (!this.mediaPlayer.isLooping())
+ this.currentState = State.PLAYBACKCOMPLETED;
+ else
+ start();
+ }
+ }
+
+ if (this.completionListener != null)
+ this.completionListener.onCompletion(mp);
+ }
+
+ @Override
+ public boolean onInfo(MediaPlayer mediaPlayer, int what, int extra) {
+ Log.d(TAG, "onInfo " + what);
+
+ if (this.infoListener != null)
+ return this.infoListener.onInfo(mediaPlayer, what, extra);
+
+ return false;
+ }
+
+ @Override
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ Log.d(TAG, "onError called - " + what + " - " + extra);
+
+ stopLoading();
+ this.currentState = State.ERROR;
+
+ if (this.errorListener != null)
+ return this.errorListener.onError(mp, what, extra);
+ return false;
+ }
+
+ @Override
+ public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
+ Log.d(TAG, "onVideoSizeChanged = " + width + " - " + height);
+
+ if (this.initialMovieWidth == -1 &&
+ this.initialMovieHeight == -1 &&
+ width != 0 &&
+ height != 0) {
+ initialMovieWidth = width;
+ initialMovieHeight = height;
+ resize();
+ }
+
+ if (this.videoSizeChangedListener != null)
+ this.videoSizeChangedListener.onVideoSizeChanged(mp, width, height);
+ }
+
+ @Override
+ public void onBufferingUpdate(MediaPlayer mp, int percent) {
+// Log.d(TAG, "onBufferingUpdate = " + percent);
+
+ if (this.bufferingUpdateListener != null)
+ this.bufferingUpdateListener.onBufferingUpdate(mp, percent);
+ }
+
+ /**
+ * Initializes the default configuration
+ */
+ protected void init() {
+ Log.d(TAG, "init");
+
+ if (isInEditMode())
+ return;
+
+ this.mediaPlayer = null;
+ this.shouldAutoplay = false;
+ this.fullscreen = false;
+ this.initialConfigOrientation = -1;
+ this.videoIsReady = false;
+ this.surfaceIsReady = false;
+ this.initialMovieHeight = -1;
+ this.initialMovieWidth = -1;
+ this.setBackgroundColor(Color.BLACK);
+
+ initObjects();
+ }
+
+ /**
+ * Releases and ends the current Object
+ */
+ protected void release() {
+ Log.d(TAG, "release");
+ releaseObjects();
+
+ if (this.mediaPlayer != null) {
+ this.mediaPlayer.setOnBufferingUpdateListener(null);
+ this.mediaPlayer.setOnPreparedListener(null);
+ this.mediaPlayer.setOnErrorListener(null);
+ this.mediaPlayer.setOnSeekCompleteListener(null);
+ this.mediaPlayer.setOnCompletionListener(null);
+ this.mediaPlayer.setOnInfoListener(null);
+ this.mediaPlayer.setOnVideoSizeChangedListener(null);
+ this.mediaPlayer.release();
+ this.mediaPlayer = null;
+ }
+
+ this.currentState = State.END;
+ }
+
+ /**
+ * Initializes all objects FullscreenVideoView depends on
+ * It does not interfere with configuration properties
+ * because it is supposed to be called when this Object
+ * still exists
+ */
+ protected void initObjects() {
+ Log.d(TAG, "initObjects");
+
+ if (this.mediaPlayer == null) {
+ this.mediaPlayer = new MediaPlayer();
+ this.mediaPlayer.setOnInfoListener(this);
+ this.mediaPlayer.setOnErrorListener(this);
+ this.mediaPlayer.setOnPreparedListener(this);
+ this.mediaPlayer.setOnCompletionListener(this);
+ this.mediaPlayer.setOnSeekCompleteListener(this);
+ this.mediaPlayer.setOnBufferingUpdateListener(this);
+ this.mediaPlayer.setOnVideoSizeChangedListener(this);
+ this.mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ }
+
+ RelativeLayout.LayoutParams layoutParams;
+ View view;
+
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ if (this.textureView == null) {
+ this.textureView = new TextureView(this.context);
+ this.textureView.setSurfaceTextureListener(this);
+ }
+ view = this.textureView;
+
+ } else {
+ if (this.surfaceView == null) {
+ this.surfaceView = new SurfaceView(context);
+ }
+ view = this.surfaceView;
+
+ if (this.surfaceHolder == null) {
+ this.surfaceHolder = this.surfaceView.getHolder();
+ //noinspection deprecation
+ this.surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
+ this.surfaceHolder.addCallback(this);
+ }
+ }
+
+ layoutParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+ layoutParams.addRule(CENTER_IN_PARENT);
+ view.setLayoutParams(layoutParams);
+ addView(view);
+
+ // Try not reset onProgressView
+ if (this.onProgressView == null)
+ this.onProgressView = new ProgressBar(context);
+
+ layoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+ layoutParams.addRule(CENTER_IN_PARENT);
+ this.onProgressView.setLayoutParams(layoutParams);
+ addView(this.onProgressView);
+
+ stopLoading();
+ this.currentState = State.IDLE;
+ }
+
+ /**
+ * Releases all objects FullscreenVideoView depends on
+ * It does not interfere with configuration properties
+ * because it is supposed to be called when this Object
+ * still exists
+ */
+ protected void releaseObjects() {
+ Log.d(TAG, "releaseObjects");
+ if (this.mediaPlayer != null) {
+ this.mediaPlayer.setSurface(null);
+ this.mediaPlayer.reset();
+ }
+
+ this.videoIsReady = false;
+ this.surfaceIsReady = false;
+ this.initialMovieHeight = -1;
+ this.initialMovieWidth = -1;
+
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ if (this.textureView != null) {
+ this.textureView.setSurfaceTextureListener(null);
+ removeView(this.textureView);
+ this.textureView = null;
+ }
+ } else {
+ if (this.surfaceHolder != null) {
+ this.surfaceHolder.removeCallback(this);
+ this.surfaceHolder = null;
+ }
+ if (this.surfaceView != null) {
+ removeView(this.surfaceView);
+ this.surfaceView = null;
+ }
+ }
+
+ if (this.onProgressView != null) {
+ removeView(this.onProgressView);
+ }
+ }
+
+ /**
+ * Calls prepare() method of MediaPlayer
+ */
+
+ protected void prepare() throws IllegalStateException {
+ Log.d(TAG, "prepare");
+ startLoading();
+
+ this.currentState = State.PREPARING;
+ this.mediaPlayer.prepareAsync();
+ }
+
+ /**
+ * Try to call state PREPARED
+ * Only if SurfaceView is already created and MediaPlayer is prepared
+ * Video is loaded and is ok to play.
+ */
+ protected void tryToPrepare() {
+ Log.d(TAG, "tryToPrepare");
+ if (this.surfaceIsReady && this.videoIsReady) {
+ if (this.mediaPlayer != null &&
+ this.mediaPlayer.getVideoWidth() != 0 &&
+ this.mediaPlayer.getVideoHeight() != 0) {
+ this.initialMovieWidth = this.mediaPlayer.getVideoWidth();
+ this.initialMovieHeight = this.mediaPlayer.getVideoHeight();
+ }
+
+ resize();
+ stopLoading();
+ currentState = State.PREPARED;
+
+ if (shouldAutoplay)
+ start();
+
+ if (this.preparedListener != null)
+ this.preparedListener.onPrepared(mediaPlayer);
+ }
+ }
+
+ protected void startLoading() {
+ if (this.onProgressView != null)
+ this.onProgressView.setVisibility(View.VISIBLE);
+ }
+
+ protected void stopLoading() {
+ if (this.onProgressView != null)
+ this.onProgressView.setVisibility(View.GONE);
+ }
+
+ /**
+ * Get the current {@link FullscreenVideoView.State}.
+ *
+ * @return Current {@link FullscreenVideoView.State}
+ */
+ synchronized public State getCurrentState() {
+ return currentState;
+ }
+
+ /**
+ * Returns if VideoView is in fullscreen mode
+ *
+ * @return true if is in fullscreen mode otherwise false
+ * @since 1.1
+ */
+ public boolean isFullscreen() {
+ return fullscreen;
+ }
+
+ /**
+ * Turn VideoView fulllscreen mode on or off.
+ *
+ * @param fullscreen true to turn on fullscreen mode or false to turn off
+ * @throws RuntimeException In case of mediaPlayer doesn't exist or illegal state exception
+ * @since 1.1
+ */
+ public void setFullscreen(final boolean fullscreen) throws RuntimeException {
+
+ if (mediaPlayer == null)
+ throw new RuntimeException("Media Player is not initialized");
+
+ if (this.currentState != State.ERROR) {
+ if (FullscreenVideoView.this.fullscreen == fullscreen) return;
+ FullscreenVideoView.this.fullscreen = fullscreen;
+
+ final boolean wasPlaying = mediaPlayer.isPlaying();
+ if (wasPlaying)
+ pause();
+
+ if (FullscreenVideoView.this.fullscreen) {
+ if (activity != null)
+ activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
+
+ View rootView = getRootView();
+ View v = rootView.findViewById(android.R.id.content);
+ ViewParent viewParent = getParent();
+ if (viewParent instanceof ViewGroup) {
+ if (parentView == null)
+ parentView = (ViewGroup) viewParent;
+
+ // Prevents MediaPlayer to became invalidated and released
+ detachedByFullscreen = true;
+
+ // Saves the last state (LayoutParams) of view to restore after
+ currentLayoutParams = FullscreenVideoView.this.getLayoutParams();
+
+ parentView.removeView(FullscreenVideoView.this);
+ } else
+ Log.e(TAG, "Parent View is not a ViewGroup");
+
+ if (v instanceof ViewGroup) {
+ ((ViewGroup) v).addView(FullscreenVideoView.this);
+ } else
+ Log.e(TAG, "RootView is not a ViewGroup");
+ } else {
+ if (activity != null)
+ activity.setRequestedOrientation(initialConfigOrientation);
+
+ ViewParent viewParent = getParent();
+ if (viewParent instanceof ViewGroup) {
+ // Check if parent view is still available
+ boolean parentHasParent = false;
+ if (parentView != null && parentView.getParent() != null) {
+ parentHasParent = true;
+ detachedByFullscreen = true;
+ }
+
+ ((ViewGroup) viewParent).removeView(FullscreenVideoView.this);
+ if (parentHasParent) {
+ parentView.addView(FullscreenVideoView.this);
+ FullscreenVideoView.this.setLayoutParams(currentLayoutParams);
+ }
+ }
+ }
+
+ resize();
+
+ Handler handler = new Handler(Looper.getMainLooper());
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (wasPlaying && mediaPlayer != null)
+ start();
+ }
+ });
+ }
+ }
+
+ /**
+ * Binds an Activity to VideoView. This is necessary to keep tracking on orientation changes
+ *
+ * @param activity The activity that VideoView is related to
+ */
+ public void setActivity(Activity activity) {
+ this.activity = activity;
+ this.initialConfigOrientation = activity.getRequestedOrientation();
+ }
+
+ public void resize() {
+ if (initialMovieHeight == -1 || initialMovieWidth == -1 || (surfaceView == null && this.textureView == null))
+ return;
+
+ Handler handler = new Handler(Looper.getMainLooper());
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+
+ View currentParent = (View) getParent();
+ if (currentParent != null) {
+ float videoProportion = (float) initialMovieWidth / (float) initialMovieHeight;
+
+ int screenWidth = currentParent.getWidth();
+ int screenHeight = currentParent.getHeight();
+ float screenProportion = (float) screenWidth / (float) screenHeight;
+
+ int newWidth, newHeight;
+ if (videoProportion > screenProportion) {
+ newWidth = screenWidth;
+ newHeight = (int) ((float) screenWidth / videoProportion);
+ } else {
+ newWidth = (int) (videoProportion * (float) screenHeight);
+ newHeight = screenHeight;
+ }
+
+ View currentView = null;
+ RelativeLayout.LayoutParams lp;
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ currentView = textureView;
+ } else {
+ currentView = surfaceView;
+ }
+
+ if (currentView != null) {
+ lp = (RelativeLayout.LayoutParams) currentView.getLayoutParams();
+ lp.addRule(CENTER_IN_PARENT);
+ lp.width = newWidth;
+ lp.height = newHeight;
+ currentView.setLayoutParams(lp);
+ }
+
+ Log.d(TAG, "Resizing: initialMovieWidth: " + initialMovieWidth + " - initialMovieHeight: " + initialMovieHeight);
+ Log.d(TAG, "Resizing: screenWidth: " + screenWidth + " - screenHeight: " + screenHeight);
+ Log.d(TAG, "Resizing To: newWidth: " + newWidth + " - newHeight: " + newHeight);
+ }
+ }
+ });
+ }
+
+ /**
+ * Tells if application should autoplay videos as soon as it is prepared
+ *
+ * @return true if application are going to play videos as soon as it is prepared
+ */
+ public boolean isShouldAutoplay() {
+ return shouldAutoplay;
+ }
+
+ /**
+ * Tells application that it should begin playing as soon as buffering
+ * is ok
+ *
+ * @param shouldAutoplay If true, call start() method when getCurrentState() == PREPARED. Default is false.
+ */
+ public void setShouldAutoplay(boolean shouldAutoplay) {
+ this.shouldAutoplay = shouldAutoplay;
+ }
+
+ /**
+ * Toggles view to fullscreen mode
+ * It saves currentState and calls pause() method.
+ * When fullscreen is finished, it calls the saved currentState before pause()
+ * In practice, it only affects STARTED state.
+ * If currenteState was STARTED when fullscreen() is called, it calls start() method
+ * after fullscreen() has ended.
+ *
+ * @deprecated As of release 1.1.0, replaced by {@link #setFullscreen(boolean)}
+ */
+ @Deprecated
+ public void fullscreen() throws IllegalStateException {
+ setFullscreen(!fullscreen);
+ }
+
+ /**
+ * MediaPlayer method (getCurrentPosition)
+ *
+ * @see <a href="http://developer.android.com/reference/android/media/MediaPlayer.html#getCurrentPosition%28%29">getCurrentPosition</a>
+ */
+ public int getCurrentPosition() {
+ if (mediaPlayer != null)
+ return mediaPlayer.getCurrentPosition();
+ else throw new RuntimeException("Media Player is not initialized");
+ }
+
+ /**
+ * MediaPlayer method (getDuration)
+ *
+ * @see <a href="http://developer.android.com/reference/android/media/MediaPlayer.html#getDuration%28%29">getDuration</a>
+ */
+ public int getDuration() {
+ if (mediaPlayer != null)
+ return mediaPlayer.getDuration();
+ else throw new RuntimeException("Media Player is not initialized");
+ }
+
+ /**
+ * MediaPlayer method (getVideoHeight)
+ *
+ * @see <a href="http://developer.android.com/reference/android/media/MediaPlayer.html#getVideoHeight%28%29">getVideoHeight</a>
+ */
+ public int getVideoHeight() {
+ if (mediaPlayer != null)
+ return mediaPlayer.getVideoHeight();
+ else throw new RuntimeException("Media Player is not initialized");
+ }
+
+ /**
+ * MediaPlayer method (getVideoWidth)
+ *
+ * @see <a href="http://developer.android.com/reference/android/media/MediaPlayer.html#getVideoWidth%28%29">getVideoWidth</a>
+ */
+ public int getVideoWidth() {
+ if (mediaPlayer != null)
+ return mediaPlayer.getVideoWidth();
+ else throw new RuntimeException("Media Player is not initialized");
+ }
+
+
+ /**
+ * MediaPlayer method (isLooping)
+ *
+ * @see <a href="http://developer.android.com/reference/android/media/MediaPlayer.html#isLooping%28%29">isLooping</a>
+ */
+ public boolean isLooping() {
+ if (mediaPlayer != null)
+ return mediaPlayer.isLooping();
+ else throw new RuntimeException("Media Player is not initialized");
+ }
+
+ /**
+ * MediaPlayer method (isPlaying)
+ *
+ * @see <a href="http://developer.android.com/reference/android/media/MediaPlayer.html#isPlaying%28%29">isPlaying</a>
+ */
+ public boolean isPlaying() throws IllegalStateException {
+ if (mediaPlayer != null)
+ return mediaPlayer.isPlaying();
+ else throw new RuntimeException("Media Player is not initialized");
+ }
+
+ /**
+ * MediaPlayer method (pause)
+ *
+ * @see <a href="http://developer.android.com/reference/android/media/MediaPlayer.html#pause%28%29">pause</a>
+ */
+ public void pause() throws IllegalStateException {
+ Log.d(TAG, "pause");
+ if (mediaPlayer != null) {
+ currentState = State.PAUSED;
+ mediaPlayer.pause();
+ } else throw new RuntimeException("Media Player is not initialized");
+ }
+
+ /**
+ * Due to a lack of access of SurfaceView, it rebuilds mediaPlayer and all
+ * views to update SurfaceView canvas
+ */
+ public void reset() {
+ Log.d(TAG, "reset");
+
+ releaseObjects();
+ initObjects();
+ }
+
+ /**
+ * MediaPlayer method (start)
+ *
+ * @see <a href="http://developer.android.com/reference/android/media/MediaPlayer.html#start%28%29">start</a>
+ */
+ public void start() throws IllegalStateException {
+ Log.d(TAG, "start");
+
+ if (mediaPlayer != null) {
+ currentState = State.STARTED;
+ mediaPlayer.setOnCompletionListener(this);
+ mediaPlayer.start();
+ } else throw new RuntimeException("Media Player is not initialized");
+ }
+
+ /**
+ * MediaPlayer method (stop)
+ *
+ * @see <a href="http://developer.android.com/reference/android/media/MediaPlayer.html#stop%28%29">stop</a>
+ */
+ public void stop() throws IllegalStateException {
+ Log.d(TAG, "stop");
+
+ if (mediaPlayer != null) {
+ currentState = State.STOPPED;
+ mediaPlayer.stop();
+ } else throw new RuntimeException("Media Player is not initialized");
+ }
+
+ /**
+ * MediaPlayer method (seekTo)
+ * It calls pause() method before calling MediaPlayer.seekTo()
+ *
+ * @param msec the offset in milliseconds from the start to seek to
+ * @throws IllegalStateException if the internal player engine has not been initialized
+ * @see <a href="http://developer.android.com/reference/android/media/MediaPlayer.html#seekTo%28%29">seekTo</a>
+ */
+ public void seekTo(int msec) throws IllegalStateException {
+ Log.d(TAG, "seekTo = " + msec);
+
+ if (mediaPlayer != null) {
+ // No live streaming
+ if (mediaPlayer.getDuration() > -1 && msec <= mediaPlayer.getDuration()) {
+ lastState = currentState;
+ pause();
+ mediaPlayer.seekTo(msec);
+
+ startLoading();
+ }
+ } else throw new RuntimeException("Media Player is not initialized");
+ }
+
+ public void setOnCompletionListener(OnCompletionListener l) {
+ if (mediaPlayer != null)
+ this.completionListener = l;
+ else throw new RuntimeException("Media Player is not initialized");
+ }
+
+ public void setOnErrorListener(OnErrorListener l) {
+ if (mediaPlayer != null)
+ errorListener = l;
+ else throw new RuntimeException("Media Player is not initialized");
+ }
+
+ public void setOnBufferingUpdateListener(OnBufferingUpdateListener l) {
+ if (mediaPlayer != null) {
+ this.bufferingUpdateListener = l;
+ this.mediaPlayer.setOnBufferingUpdateListener(this);
+ } else throw new RuntimeException("Media Player is not initialized");
+ }
+
+ public void setOnInfoListener(OnInfoListener l) {
+ if (mediaPlayer != null)
+ this.infoListener = l;
+ else throw new RuntimeException("Media Player is not initialized");
+ }
+
+ public void setOnSeekCompleteListener(OnSeekCompleteListener l) {
+ if (mediaPlayer != null)
+ this.seekCompleteListener = l;
+ else throw new RuntimeException("Media Player is not initialized");
+ }
+
+ public void setOnVideoSizeChangedListener(OnVideoSizeChangedListener l) {
+ if (mediaPlayer != null)
+ this.videoSizeChangedListener = l;
+ else throw new RuntimeException("Media Player is not initialized");
+ }
+
+ public void setOnPreparedListener(OnPreparedListener l) {
+ if (mediaPlayer != null)
+ this.preparedListener = l;
+ else throw new RuntimeException("Media Player is not initialized");
+ }
+
+ public void setLooping(boolean looping) {
+ if (mediaPlayer != null)
+ mediaPlayer.setLooping(looping);
+ else throw new RuntimeException("Media Player is not initialized");
+ }
+
+ public void setVolume(float leftVolume, float rightVolume) {
+ if (mediaPlayer != null)
+ mediaPlayer.setVolume(leftVolume, rightVolume);
+ else throw new RuntimeException("Media Player is not initialized");
+ }
+
+ /**
+ * VideoView method (setVideoPath)
+ */
+ public void setVideoPath(String path) throws IOException, IllegalStateException, SecurityException, IllegalArgumentException, RuntimeException {
+ Log.d(TAG, "setVideoPath");
+ if (mediaPlayer != null) {
+ if (currentState != State.IDLE)
+ throw new IllegalStateException("FullscreenVideoView Invalid State: " + currentState);
+
+ this.videoPath = path;
+ this.videoUri = null;
+ this.mediaPlayer.setDataSource(path);
+
+ this.currentState = State.INITIALIZED;
+ prepare();
+ } else throw new RuntimeException("Media Player is not initialized");
+ }
+
+ /**
+ * VideoView method (setVideoURI)
+ */
+ public void setVideoURI(Uri uri) throws IOException, IllegalStateException, SecurityException, IllegalArgumentException, RuntimeException {
+ Log.d(TAG, "setVideoURI");
+ if (mediaPlayer != null) {
+ if (currentState != State.IDLE)
+ throw new IllegalStateException("FullscreenVideoView Invalid State: " + currentState);
+
+ this.videoUri = uri;
+ this.videoPath = null;
+ this.mediaPlayer.setDataSource(context, uri);
+
+ this.currentState = State.INITIALIZED;
+ prepare();
+ } else throw new RuntimeException("Media Player is not initialized");
+ }
+
+ /**
+ * Overwrite the default ProgressView to represent loading progress state
+ * It is controlled by stopLoading and startLoading methods, that only sets it to VISIBLE and GONE
+ * <p>
+ * Remember to set RelativeLayout.LayoutParams before setting the view.
+ *
+ * @param v The custom View that will be used as progress view.
+ * Set it to null to remove the default one
+ */
+ public void setOnProgressView(View v) {
+ int progressViewVisibility = -1;
+ if (this.onProgressView != null) {
+ progressViewVisibility = this.onProgressView.getVisibility();
+ removeView(this.onProgressView);
+ }
+
+ this.onProgressView = v;
+ if (this.onProgressView != null) {
+ addView(this.onProgressView);
+ if (progressViewVisibility != -1)
+ this.onProgressView.setVisibility(progressViewVisibility);
+ }
+ }
+}
diff --git a/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_control_disabled.png b/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_control_disabled.png
new file mode 100644
index 000000000..df0552fdc
--- /dev/null
+++ b/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_control_disabled.png
Binary files differ
diff --git a/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_control_focused.png b/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_control_focused.png
new file mode 100644
index 000000000..59ebe18c7
--- /dev/null
+++ b/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_control_focused.png
Binary files differ
diff --git a/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_control_normal.png b/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_control_normal.png
new file mode 100644
index 000000000..f9c26fc5b
--- /dev/null
+++ b/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_control_normal.png
Binary files differ
diff --git a/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_control_pressed.png b/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_control_pressed.png
new file mode 100644
index 000000000..239ffc541
--- /dev/null
+++ b/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_control_pressed.png
Binary files differ
diff --git a/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_fullscreen_reader.png b/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_fullscreen_reader.png
new file mode 100644
index 000000000..2e9f6c3d0
--- /dev/null
+++ b/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_fullscreen_reader.png
Binary files differ
diff --git a/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_fullscreen_reader_white.png b/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_fullscreen_reader_white.png
new file mode 100644
index 000000000..969056d24
--- /dev/null
+++ b/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_fullscreen_reader_white.png
Binary files differ
diff --git a/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_pause_reader.png b/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_pause_reader.png
new file mode 100644
index 000000000..c508211bf
--- /dev/null
+++ b/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_pause_reader.png
Binary files differ
diff --git a/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_pause_reader_white.png b/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_pause_reader_white.png
new file mode 100644
index 000000000..2c386c24d
--- /dev/null
+++ b/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_pause_reader_white.png
Binary files differ
diff --git a/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_play_reader.png b/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_play_reader.png
new file mode 100644
index 000000000..9df16013b
--- /dev/null
+++ b/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_play_reader.png
Binary files differ
diff --git a/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_play_reader_white.png b/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_play_reader_white.png
new file mode 100644
index 000000000..b5bc9a96c
--- /dev/null
+++ b/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_play_reader_white.png
Binary files differ
diff --git a/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_primary.9.png b/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_primary.9.png
new file mode 100644
index 000000000..ed4171531
--- /dev/null
+++ b/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_primary.9.png
Binary files differ
diff --git a/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_secondary.9.png b/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_secondary.9.png
new file mode 100644
index 000000000..0f2de4d95
--- /dev/null
+++ b/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_secondary.9.png
Binary files differ
diff --git a/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_track.9.png b/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_track.9.png
new file mode 100644
index 000000000..4014860d6
--- /dev/null
+++ b/libs/fullscreenvideoview/src/main/res/drawable-xxhdpi/fvl_track.9.png
Binary files differ
diff --git a/libs/fullscreenvideoview/src/main/res/drawable/fvl_progress.xml b/libs/fullscreenvideoview/src/main/res/drawable/fvl_progress.xml
new file mode 100644
index 000000000..0a8173737
--- /dev/null
+++ b/libs/fullscreenvideoview/src/main/res/drawable/fvl_progress.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@android:id/background" android:drawable="@drawable/fvl_track" />
+ <item android:id="@android:id/secondaryProgress">
+ <scale android:scaleWidth="100%"
+ android:drawable="@drawable/fvl_secondary" />
+ </item>
+ <item android:id="@android:id/progress">
+ <scale android:scaleWidth="100%"
+ android:drawable="@drawable/fvl_primary" />
+ </item>
+</layer-list>
diff --git a/libs/fullscreenvideoview/src/main/res/drawable/fvl_selector_fullscreen.xml b/libs/fullscreenvideoview/src/main/res/drawable/fvl_selector_fullscreen.xml
new file mode 100644
index 000000000..9cce2d136
--- /dev/null
+++ b/libs/fullscreenvideoview/src/main/res/drawable/fvl_selector_fullscreen.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/fvl_fullscreen_reader" android:state_pressed="false" />
+ <item android:drawable="@drawable/fvl_fullscreen_reader_white" android:state_pressed="true" />
+</selector> \ No newline at end of file
diff --git a/libs/fullscreenvideoview/src/main/res/drawable/fvl_selector_pause.xml b/libs/fullscreenvideoview/src/main/res/drawable/fvl_selector_pause.xml
new file mode 100644
index 000000000..ed0d8a9a6
--- /dev/null
+++ b/libs/fullscreenvideoview/src/main/res/drawable/fvl_selector_pause.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/fvl_pause_reader" android:state_pressed="false" />
+ <item android:drawable="@drawable/fvl_pause_reader_white" android:state_pressed="true" />
+</selector> \ No newline at end of file
diff --git a/libs/fullscreenvideoview/src/main/res/drawable/fvl_selector_play.xml b/libs/fullscreenvideoview/src/main/res/drawable/fvl_selector_play.xml
new file mode 100644
index 000000000..9511e6538
--- /dev/null
+++ b/libs/fullscreenvideoview/src/main/res/drawable/fvl_selector_play.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/fvl_play_reader" android:state_pressed="false" />
+ <item android:drawable="@drawable/fvl_play_reader_white" android:state_pressed="true" />
+</selector> \ No newline at end of file
diff --git a/libs/fullscreenvideoview/src/main/res/layout/view_videocontrols.xml b/libs/fullscreenvideoview/src/main/res/layout/view_videocontrols.xml
new file mode 100644
index 000000000..8c8b53edf
--- /dev/null
+++ b/libs/fullscreenvideoview/src/main/res/layout/view_videocontrols.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout
+ android:id="@+id/rel_videocontrols"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="0dp"
+ android:background="#80cccccc">
+
+ <ImageButton
+ android:id="@+id/vcv_img_play"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_centerVertical="true"
+ android:layout_marginStart="0dp"
+ android:layout_marginLeft="0dp"
+ android:layout_marginTop="0dp"
+ android:layout_marginEnd="0dp"
+ android:layout_marginRight="0dp"
+ android:layout_marginBottom="0dp"
+ android:background="@drawable/fvl_selector_play" />
+
+ <TextView
+ android:id="@+id/vcv_txt_elapsed"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_toRightOf="@+id/vcv_img_play"
+ android:text="00:00"
+ android:textColor="@android:color/black" />
+
+ <ImageButton
+ android:id="@+id/vcv_img_fullscreen"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ android:layout_marginStart="0dp"
+ android:layout_marginLeft="0dp"
+ android:layout_marginTop="0dp"
+ android:layout_marginEnd="0dp"
+ android:layout_marginRight="0dp"
+ android:layout_marginBottom="0dp"
+ android:background="@drawable/fvl_selector_fullscreen" />
+
+ <TextView
+ android:id="@+id/vcv_txt_total"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_marginRight="5dp"
+ android:layout_toLeftOf="@+id/vcv_img_fullscreen"
+ android:text="00:00"
+ android:textColor="@android:color/black" />
+
+ <SeekBar
+ android:id="@+id/vcv_seekbar"
+ style="@android:style/Widget.ProgressBar.Horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_margin="2dp"
+ android:layout_marginStart="5dp"
+ android:layout_marginLeft="5dp"
+ android:layout_marginTop="5dp"
+ android:layout_marginEnd="5dp"
+ android:layout_marginRight="5dp"
+ android:layout_marginBottom="5dp"
+ android:layout_toLeftOf="@+id/vcv_txt_total"
+ android:layout_toRightOf="@+id/vcv_txt_elapsed"
+ android:indeterminateDrawable="@drawable/fvl_progress"
+ android:maxHeight="13dp"
+ android:minHeight="13dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:progressDrawable="@drawable/fvl_progress"
+ android:thumb="@drawable/fvl_control_normal"
+ android:thumbOffset="16dp" />
+
+
+</RelativeLayout> \ No newline at end of file
diff --git a/libs/fullscreenvideoview/src/main/res/values/strings.xml b/libs/fullscreenvideoview/src/main/res/values/strings.xml
new file mode 100644
index 000000000..92951c27a
--- /dev/null
+++ b/libs/fullscreenvideoview/src/main/res/values/strings.xml
@@ -0,0 +1,6 @@
+<resources>
+ <string name="app_name">VideoLayout</string>
+
+ <string name="hello_world">Hello world!</string>
+ <string name="action_settings">Settings</string>
+</resources>
diff --git a/settings.gradle b/settings.gradle
index 333bfa6bc..591d3e182 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,3 +1,4 @@
include ':libs:android-transcoder'
include ':libs:xmpp-addr'
+include ':libs:fullscreenvideoview'
rootProject.name = 'PixArtMessenger'
diff --git a/src/main/java/de/pixart/messenger/ui/MediaViewerActivity.java b/src/main/java/de/pixart/messenger/ui/MediaViewerActivity.java
index a51ef21ab..f9e5b1c2f 100644
--- a/src/main/java/de/pixart/messenger/ui/MediaViewerActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/MediaViewerActivity.java
@@ -10,13 +10,17 @@ import android.content.res.Configuration;
import android.databinding.DataBindingUtil;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.graphics.PointF;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.util.Log;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.webkit.MimeTypeMap;
@@ -52,6 +56,7 @@ public class MediaViewerActivity extends XmppActivity {
boolean isImage = false;
boolean isVideo = false;
private ActivityMediaViewerBinding binding;
+ private GestureDetector gestureDetector;
public static String getMimeType(String path) {
try {
@@ -73,6 +78,15 @@ public class MediaViewerActivity extends XmppActivity {
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_media_viewer);
this.mTheme = findTheme();
setTheme(this.mTheme);
+ gestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onDown(MotionEvent e) {
+ showFab();
+ return super.onDown(e);
+ }
+
+
+ });
ActionBar actionBar = getSupportActionBar();
if (actionBar != null && actionBar.isShowing()) {
@@ -110,6 +124,7 @@ public class MediaViewerActivity extends XmppActivity {
}
return false;
});
+ showFab();
}
private void share() {
@@ -226,9 +241,11 @@ public class MediaViewerActivity extends XmppActivity {
if (gif) {
binding.messageGifView.setVisibility(View.VISIBLE);
binding.messageGifView.setImageURI(FileUri);
+ binding.messageGifView.setOnTouchListener((view, motionEvent) -> gestureDetector.onTouchEvent(motionEvent));
} else {
binding.messageImageView.setVisibility(View.VISIBLE);
binding.messageImageView.setImage(ImageSource.uri(FileUri));
+ binding.messageImageView.setOnTouchListener((view, motionEvent) -> gestureDetector.onTouchEvent(motionEvent));
}
} catch (Exception e) {
Toast.makeText(this, getString(R.string.error_file_corrupt), Toast.LENGTH_LONG).show();
@@ -262,6 +279,7 @@ public class MediaViewerActivity extends XmppActivity {
binding.messageVideoView.setVideoURI(uri);
mFullscreenbutton.setVisibility(View.INVISIBLE);
binding.messageVideoView.setShouldAutoplay(true);
+ binding.messageVideoView.setOnTouchListener((view, motionEvent) -> gestureDetector.onTouchEvent(motionEvent));
} catch (IOException e) {
Toast.makeText(this, getString(R.string.error_file_corrupt), Toast.LENGTH_LONG).show();
@@ -356,4 +374,17 @@ public class MediaViewerActivity extends XmppActivity {
public SharedPreferences getPreferences() {
return PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
}
+
+ private void showFab() {
+ binding.speedDial.show();
+ hideFab();
+ }
+
+ private void hideFab() {
+ new Handler().postDelayed(() -> {
+ binding.speedDial.hide();
+ }, 3000);
+ }
+
+
} \ No newline at end of file