/*****************************************************************************
* VideoPlayerActivity.java
*****************************************************************************
* Copyright © 2011-2013 VLC authors and VideoLAN
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
package org.videolan.vlc.gui.video;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.StreamCorruptedException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import org.tribler.triblersvod.gui.FilePriority;
import org.tribler.triblersvod.gui.R;
import org.tribler.triblersvod.gui.StorageModes;
import org.tribler.triblersvod.gui.TorrentState;
import org.videolan.libvlc.EventHandler;
import org.videolan.libvlc.IVideoPlayer;
import org.videolan.libvlc.LibVLC;
import org.videolan.libvlc.LibVlcException;
import org.videolan.libvlc.Media;
import org.videolan.vlc.AudioServiceController;
import org.videolan.vlc.MediaDatabase;
import org.videolan.vlc.Util;
import org.videolan.vlc.VLCApplication;
import org.videolan.vlc.WeakHandler;
import org.videolan.vlc.gui.CommonDialogs;
import org.videolan.vlc.gui.CommonDialogs.MenuType;
import org.videolan.vlc.gui.PreferencesActivity;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.KeyguardManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.Color;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.media.AudioManager;
import android.media.AudioManager.OnAudioFocusChangeListener;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.provider.MediaStore;
import android.provider.Settings.SettingNotFoundException;
import android.text.format.DateFormat;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnSystemUiVisibilityChangeListener;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;
import com.softwarrior.libtorrent.LibTorrent;
public class VideoPlayerActivity extends Activity implements IVideoPlayer {
public final static String TAG = "VLC/VideoPlayerActivity";
// Internal intent identifier to distinguish between internal launch and
// external intent.
private final static String PLAY_FROM_VIDEOGRID = "org.videolan.vlc.gui.video.PLAY_FROM_VIDEOGRID";
private SurfaceView mSurface;
private SurfaceView mSubtitlesSurface;
private SurfaceHolder mSurfaceHolder;
private SurfaceHolder mSubtitlesSurfaceHolder;
private FrameLayout mSurfaceFrame;
private LibVLC mLibVLC;
private String mLocation;
private static final int SURFACE_BEST_FIT = 0;
private static final int SURFACE_FIT_HORIZONTAL = 1;
private static final int SURFACE_FIT_VERTICAL = 2;
private static final int SURFACE_FILL = 3;
private static final int SURFACE_16_9 = 4;
private static final int SURFACE_4_3 = 5;
private static final int SURFACE_ORIGINAL = 6;
private int mCurrentSize = SURFACE_BEST_FIT;
/** Overlay */
private View mOverlayHeader;
private View mOverlayOption;
private View mOverlayProgress;
private static final int OVERLAY_TIMEOUT = 4000;
private static final int OVERLAY_INFINITE = 3600000;
private static final int FADE_OUT = 1;
private static final int SHOW_PROGRESS = 2;
private static final int SURFACE_SIZE = 3;
private static final int FADE_OUT_INFO = 4;
private boolean mDragging;
private boolean mShowing;
private int mUiVisibility = -1;
private SeekBar mSeekbar;
private TextView mTitle;
private TextView mSysTime;
private TextView mBattery;
private TextView mTime;
private TextView mLength;
private TextView mInfo;
private ImageButton mPlayPause;
private ImageButton mBackward;
private ImageButton mForward;
private boolean mEnableJumpButtons;
private boolean mEnableBrightnessGesture;
private boolean mDisplayRemainingTime = false;
private int mScreenOrientation;
private ImageButton mAudioTrack;
private ImageButton mSubtitle;
private ImageButton mLock;
private ImageButton mSize;
private ImageButton mMenu;
private boolean mIsLocked = false;
private int mLastAudioTrack = -1;
private int mLastSpuTrack = -2;
/**
* For uninterrupted switching between audio and video mode
*/
private boolean mSwitchingView;
private boolean mEndReached;
private boolean mCanSeek;
// Playlist
private int savedIndexPosition = -1;
// size of the video
private int mVideoHeight;
private int mVideoWidth;
private int mVideoVisibleHeight;
private int mVideoVisibleWidth;
private int mSarNum;
private int mSarDen;
// Volume
private AudioManager mAudioManager;
private int mAudioMax;
private OnAudioFocusChangeListener mAudioFocusListener;
// Touch Events
private static final int TOUCH_NONE = 0;
private static final int TOUCH_VOLUME = 1;
private static final int TOUCH_BRIGHTNESS = 2;
private static final int TOUCH_SEEK = 3;
private int mTouchAction;
private int mSurfaceYDisplayRange;
private float mTouchY, mTouchX, mVol;
// Brightness
private boolean mIsFirstBrightnessGesture = true;
// Tracks & Subtitles
private Map<Integer, String> mAudioTracksList;
private Map<Integer, String> mSubtitleTracksList;
/**
* Used to store a selected subtitle; see onActivityResult. It is possible
* to have multiple custom subs in one session (just like desktop VLC allows
* you as well.)
*/
private ArrayList<String> mSubtitleSelectedFiles = new ArrayList<String>();
// TRIBLER
public final static String KEY_LATEST_CONTENT = "key_latest_content";
public final static String KEY_LATEST_CONTENT_DIR = "key_latest_content_dir";
public final static int waitTime = 30000; // in ms
private static boolean torrentFile; // does it have a torrent loaded in libt
private static String contentName = "";
private static long fileLength;
private static int[] piecePriorities;
private static int firstPieceIndex = -1;
private static int lastPieceIndex = -1;
private static boolean initCompleted;
private static boolean seekBufferCompleted;
private static int CHECKSIZE = 30000000; // TODO resolution and bandwidth
// dependent
private static int CHECKSIZEMB = CHECKSIZE / (1024 * 1024);
private static int REQUESTSIZE = 2 * CHECKSIZE;
private static boolean inMetaDataWait = false;
private static boolean metaDataDone = false;
private static boolean magnet = false;
private static ArrayList<String> asyncTaskRunning;
private static InitAsyncTask initAsyncTask;
private static WaitForMetaDataTask waitDataTask;
private static SeekTask seekTask;
private LibTorrent libTorrent(int listenPort, int uploadLimit,
int downloadLimit, boolean encryption) {
return ((VLCApplication) getApplication()).getLibTorrent(listenPort,
uploadLimit, downloadLimit, encryption);
}
private LibTorrent libTorrent() {
return ((VLCApplication) getApplication()).getLibTorrent();
}
private void deleteLibTorrent() {
((VLCApplication) getApplication()).deleteLibTorrent();
}
@Override
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.player);
// TRIBLER INIT
SharedPreferences pref = PreferenceManager
.getDefaultSharedPreferences(this);
findViewById(R.id.libtorrent_debug).setVisibility(
(pref.getBoolean("libtorrent_debug", true) ? View.VISIBLE
: View.GONE));
int listenPort = Integer
.parseInt(pref.getString("listenport", "54321"));
int uploadLimit = Integer.parseInt(pref.getString("upoadlim", "0"));
int downloadLimit = Integer
.parseInt(pref.getString("downloadlim", "0"));
boolean encryption = pref.getBoolean("encryption", false);
Log.d(TAG, "onCreate");
libTorrent(listenPort, uploadLimit, downloadLimit, encryption);
asyncTaskRunning = new ArrayList<String>();
asyncTaskRunning.add("main");
if (Util.isICSOrLater())
getWindow()
.getDecorView()
.findViewById(android.R.id.content)
.setOnSystemUiVisibilityChangeListener(
new OnSystemUiVisibilityChangeListener() {
@Override
public void onSystemUiVisibilityChange(
int visibility) {
if (visibility == mUiVisibility)
return;
setSurfaceSize(mVideoWidth, mVideoHeight,
mVideoVisibleWidth,
mVideoVisibleHeight, mSarNum,
mSarDen);
if (visibility == View.SYSTEM_UI_FLAG_VISIBLE
&& !mShowing && !isFinishing()) {
showOverlay();
}
mUiVisibility = visibility;
}
});
/** initialize Views an their Events */
mOverlayHeader = findViewById(R.id.player_overlay_header);
mOverlayOption = findViewById(R.id.option_overlay);
mOverlayProgress = findViewById(R.id.progress_overlay);
/* header */
mTitle = (TextView) findViewById(R.id.player_overlay_title);
mSysTime = (TextView) findViewById(R.id.player_overlay_systime);
mBattery = (TextView) findViewById(R.id.player_overlay_battery);
// Position and remaining time
mTime = (TextView) findViewById(R.id.player_overlay_time);
mTime.setOnClickListener(mRemainingTimeListener);
mLength = (TextView) findViewById(R.id.player_overlay_length);
mLength.setOnClickListener(mRemainingTimeListener);
// the info textView is not on the overlay
mInfo = (TextView) findViewById(R.id.player_overlay_info);
mEnableBrightnessGesture = pref.getBoolean("enable_brightness_gesture",
true);
mScreenOrientation = Integer
.valueOf(pref
.getString("screen_orientation_value", "4" /* SCREEN_ORIENTATION_SENSOR */));
mEnableJumpButtons = pref.getBoolean("enable_jump_buttons", false);
mPlayPause = (ImageButton) findViewById(R.id.player_overlay_play);
mPlayPause.setOnClickListener(mPlayPauseListener);
mBackward = (ImageButton) findViewById(R.id.player_overlay_backward);
mBackward.setOnClickListener(mBackwardListener);
mForward = (ImageButton) findViewById(R.id.player_overlay_forward);
mForward.setOnClickListener(mForwardListener);
mAudioTrack = (ImageButton) findViewById(R.id.player_overlay_audio);
mAudioTrack.setVisibility(View.GONE);
mSubtitle = (ImageButton) findViewById(R.id.player_overlay_subtitle);
mSubtitle.setVisibility(View.GONE);
mLock = (ImageButton) findViewById(R.id.lock_overlay_button);
mLock.setOnClickListener(mLockListener);
mSize = (ImageButton) findViewById(R.id.player_overlay_size);
mSize.setOnClickListener(mSizeListener);
mMenu = (ImageButton) findViewById(R.id.player_overlay_adv_function);
mSurface = (SurfaceView) findViewById(R.id.player_surface);
mSurfaceHolder = mSurface.getHolder();
mSurfaceFrame = (FrameLayout) findViewById(R.id.player_surface_frame);
String chroma = pref.getString("chroma_format", "");
if (Util.isGingerbreadOrLater() && chroma.equals("YV12")) {
mSurfaceHolder.setFormat(ImageFormat.YV12);
} else if (chroma.equals("RV16")) {
mSurfaceHolder.setFormat(PixelFormat.RGB_565);
} else {
mSurfaceHolder.setFormat(PixelFormat.RGBX_8888);
}
mSurfaceHolder.addCallback(mSurfaceCallback);
mSubtitlesSurface = (SurfaceView) findViewById(R.id.subtitles_surface);
mSubtitlesSurfaceHolder = mSubtitlesSurface.getHolder();
mSubtitlesSurfaceHolder.setFormat(PixelFormat.RGBA_8888);
mSubtitlesSurface.setZOrderMediaOverlay(true);
mSubtitlesSurfaceHolder.addCallback(mSubtitlesSurfaceCallback);
mSeekbar = (SeekBar) findViewById(R.id.player_overlay_seekbar);
mSeekbar.setOnSeekBarChangeListener(mSeekListener);
mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
mAudioMax = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
mSwitchingView = false;
mEndReached = false;
// Clear the resume time, since it is only used for resumes in external
// videos.
SharedPreferences preferences = getSharedPreferences(
PreferencesActivity.NAME, MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putLong(PreferencesActivity.VIDEO_RESUME_TIME, -1);
// Also clear the subs list, because it is supposed to be per session
// only (like desktop VLC). We don't want the customs subtitle file
// to persist forever with this video.
editor.putString(PreferencesActivity.VIDEO_SUBTITLE_FILES, null);
editor.commit();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
filter.addAction(VLCApplication.SLEEP_INTENT);
registerReceiver(mReceiver, filter);
try {
mLibVLC = Util.getLibVlcInstance();
} catch (LibVlcException e) {
Log.d(TAG, "LibVLC initialisation failed");
return;
}
/* Only show the subtitles surface when using "Full Acceleration" mode */
if (mLibVLC.getHardwareAcceleration() == 2)
mSubtitlesSurface.setVisibility(View.VISIBLE);
EventHandler em = EventHandler.getInstance();
em.addHandler(eventHandler);
this.setVolumeControlStream(AudioManager.STREAM_MUSIC);
// 100 is the value for screen_orientation_start_lock
setRequestedOrientation(mScreenOrientation != 100 ? mScreenOrientation
: getScreenOrientation());
}
@Override
protected void onPause() {
Log.d("lifeCycleVPA", "onPause");
super.onPause();
if (mSwitchingView) {
Log.d(TAG, "mLocation = \"" + mLocation + "\"");
AudioServiceController.getInstance().showWithoutParse(
savedIndexPosition);
AudioServiceController.getInstance().unbindAudioService(this);
return;
}
long time = mLibVLC.getTime();
long length = mLibVLC.getLength();
// remove saved position if in the last 5 seconds
if (length - time < 5000)
time = 0;
else
time -= 5000; // go back 5 seconds, to compensate loading time
/*
* Pausing here generates errors because the vout is constantly trying
* to refresh itself every 80ms while the surface is not accessible
* anymore. To workaround that, we keep the last known position in the
* playlist in savedIndexPosition to be able to restore it during
* onResume().
*/
mLibVLC.stop();
mSurface.setKeepScreenOn(false);
SharedPreferences preferences = getSharedPreferences(
PreferencesActivity.NAME, MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
// Save position
if (time >= 0 && mCanSeek) {
if (MediaDatabase.getInstance(this).mediaItemExists(mLocation)) {
MediaDatabase.getInstance(this).updateMedia(mLocation,
MediaDatabase.mediaColumn.MEDIA_TIME, time);
} else {
// Video file not in media library, store time just for
// onResume()
editor.putLong(PreferencesActivity.VIDEO_RESUME_TIME, time);
}
}
// Save selected subtitles
String subtitleList_serialized = null;
if (mSubtitleSelectedFiles.size() > 0) {
Log.d(TAG, "Saving selected subtitle files");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(mSubtitleSelectedFiles);
subtitleList_serialized = bos.toString();
} catch (IOException e) {
}
}
editor.putString(PreferencesActivity.VIDEO_SUBTITLE_FILES,
subtitleList_serialized);
editor.commit();
AudioServiceController.getInstance().unbindAudioService(this);
}
@Override
protected void onStop() {
Log.d("lifeCycleVPA", "onStop");
super.onStop();
// TRIBLER
if (!contentName.isEmpty()) {
libTorrent().PauseTorrent(contentName);
}
// don't pause the session!
// cancel asynctasks
if (asyncTaskRunning.contains("load()")) {
initAsyncTask.cancel(true);
}
if (asyncTaskRunning.contains("waitForMetaData()")) {
waitDataTask.cancel(true);
}
if (asyncTaskRunning.contains("setDownloadPrioritySeekTo()")) {
seekTask.cancel(true);
}
}
@Override
protected void onDestroy() {
Log.d("lifeCycleVPA", "onDestroy");
super.onDestroy();
unregisterReceiver(mReceiver);
EventHandler em = EventHandler.getInstance();
em.removeHandler(eventHandler);
mAudioManager = null;
// TRIBLER
initCompleted = false;
metaDataDone = false;
seekBufferCompleted = false;
inMetaDataWait = false;
magnet = false;
torrentFile = false;
if (!contentName.isEmpty()) {
libTorrent().RemoveTorrent(contentName);
}
libTorrent().AbortSession();
asyncTaskRunning.remove("main");
// delete libTorrent instance
deleteLibTorrent();
}
@Override
protected void onResume() {
Log.d("lifeCycleVPA", "onResume");
super.onResume();
if (!inMetaDataWait) {
onTriblerResume();
}
}
private void logStates() {
Log.d("STATES", "initCompleted: " + initCompleted);
Log.d("STATES", "metaDataDone: " + metaDataDone);
Log.d("STATES", "seekBufferCompleted: " + seekBufferCompleted);
Log.d("STATES", "inMetaDataWait: " + inMetaDataWait);
Log.d("STATES", "magnet: " + magnet);
Log.d("STATES", "torrentFile: " + torrentFile);
Log.d("STATES", "Number of torrents in liBT: "
+ libTorrent().GetNumberOfTorrents());
Log.d("STATES", "session active: " + libTorrent().GetSessionState());
}
// TRIBLER
private void onTriblerResume() {
mSwitchingView = false;
AudioServiceController.getInstance().bindAudioService(this);
// for debugging:
logStates();
Log.d("check", asyncTaskRunning.toString());
if (!contentName.isEmpty()) {
Log.d(TAG, "contentName isn't empty, resume done: "
+ libTorrent().ResumeTorrent(contentName));
}
load();
// for debugging:
logStates();
/*
* if the activity has been paused by pressing the power button,
* pressing it again will show the lock screen. But onResume will also
* be called, even if vlc-android is still in the background. To
* workaround that, pause playback if the lockscreen is displayed
*/
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (mLibVLC != null && mLibVLC.isPlaying()) {
KeyguardManager km = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
if (km.inKeyguardRestrictedInputMode())
mLibVLC.pause();
}
}
}, 500);
// showOverlay();
// Add any selected subtitle file from the file picker
if (mSubtitleSelectedFiles.size() > 0) {
for (String file : mSubtitleSelectedFiles) {
Log.i(TAG, "Adding user-selected subtitle " + file);
mLibVLC.addSubtitleTrack(file);
}
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (data == null)
return;
if (data.getDataString() == null) {
Log.d(TAG, "Subtitle selection dialog was cancelled");
}
if (data.getData() == null)
return;
String uri = data.getData().getPath();
if (requestCode == CommonDialogs.INTENT_SPECIFIC) {
Log.d(TAG, "Specific subtitle file: " + uri);
} else if (requestCode == CommonDialogs.INTENT_GENERIC) {
Log.d(TAG, "Generic subtitle file: " + uri);
}
mSubtitleSelectedFiles.add(data.getData().getPath());
}
public static void start(Context context, String location) {
start(context, location, null, -1, false, false);
}
public static void start(Context context, String location, Boolean fromStart) {
start(context, location, null, -1, false, fromStart);
}
public static void start(Context context, String location, String title,
Boolean dontParse) {
start(context, location, title, -1, dontParse, false);
}
public static void start(Context context, String location, String title,
int position, Boolean dontParse) {
start(context, location, title, position, dontParse, false);
}
public static void start(Context context, String location, String title,
int position, Boolean dontParse, Boolean fromStart) {
Intent intent = new Intent(context, VideoPlayerActivity.class);
intent.setAction(VideoPlayerActivity.PLAY_FROM_VIDEOGRID);
intent.putExtra("itemLocation", location);
intent.putExtra("itemTitle", title);
intent.putExtra("dontParse", dontParse);
intent.putExtra("fromStart", fromStart);
intent.putExtra("itemPosition", position);
if (dontParse)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
else {
// Stop the currently running audio
AudioServiceController asc = AudioServiceController.getInstance();
asc.stop();
}
context.startActivity(intent);
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equalsIgnoreCase(Intent.ACTION_BATTERY_CHANGED)) {
int batteryLevel = intent.getIntExtra("level", 0);
if (batteryLevel >= 50)
mBattery.setTextColor(Color.GREEN);
else if (batteryLevel >= 30)
mBattery.setTextColor(Color.YELLOW);
else
mBattery.setTextColor(Color.RED);
mBattery.setText(String.format("%d%%", batteryLevel));
} else if (action.equalsIgnoreCase(VLCApplication.SLEEP_INTENT)) {
finish();
}
}
};
@Override
public boolean onTrackballEvent(MotionEvent event) {
showOverlay();
return true;
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
setSurfaceSize(mVideoWidth, mVideoHeight, mVideoVisibleWidth,
mVideoVisibleHeight, mSarNum, mSarDen);
super.onConfigurationChanged(newConfig);
}
@Override
public void setSurfaceSize(int width, int height, int visible_width,
int visible_height, int sar_num, int sar_den) {
if (width * height == 0)
return;
// store video size
mVideoHeight = height;
mVideoWidth = width;
mVideoVisibleHeight = visible_height;
mVideoVisibleWidth = visible_width;
mSarNum = sar_num;
mSarDen = sar_den;
Message msg = mHandler.obtainMessage(SURFACE_SIZE);
mHandler.sendMessage(msg);
}
/**
* Lock screen rotation
*/
private void lockScreen() {
if (mScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
setRequestedOrientation(14 /* SCREEN_ORIENTATION_LOCKED */);
else
setRequestedOrientation(getScreenOrientation());
}
showInfo(R.string.locked, 1000);
mLock.setBackgroundResource(R.drawable.locked);
mTime.setEnabled(false);
mSeekbar.setEnabled(false);
mLength.setEnabled(false);
mMenu.setEnabled(false);
hideOverlay(true);
}
/**
* Remove screen lock
*/
private void unlockScreen() {
if (mScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR)
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
showInfo(R.string.unlocked, 1000);
mLock.setBackgroundResource(R.drawable.lock);
mTime.setEnabled(true);
mSeekbar.setEnabled(true);
mLength.setEnabled(true);
mMenu.setEnabled(true);
mShowing = false;
showOverlay();
}
/**
* Show text in the info view for "duration" milliseconds
*
* @param text
* @param duration
*/
private void showInfo(String text, int duration) {
mInfo.setVisibility(View.VISIBLE);
mInfo.setText(text);
mHandler.removeMessages(FADE_OUT_INFO);
mHandler.sendEmptyMessageDelayed(FADE_OUT_INFO, duration);
}
private void showInfo(int textid, int duration) {
mInfo.setVisibility(View.VISIBLE);
mInfo.setText(textid);
mHandler.removeMessages(FADE_OUT_INFO);
mHandler.sendEmptyMessageDelayed(FADE_OUT_INFO, duration);
}
/**
* Show text in the info view
*
* @param text
*/
private void showInfo(String text) {
mInfo.setVisibility(View.VISIBLE);
mInfo.setText(text);
mHandler.removeMessages(FADE_OUT_INFO);
}
/**
* hide the info view with "delay" milliseconds delay
*
* @param delay
*/
private void hideInfo(int delay) {
mHandler.sendEmptyMessageDelayed(FADE_OUT_INFO, delay);
}
/**
* hide the info view
*/
private void hideInfo() {
hideInfo(0);
}
private void fadeOutInfo() {
if (mInfo.getVisibility() == View.VISIBLE)
mInfo.startAnimation(AnimationUtils.loadAnimation(
VideoPlayerActivity.this, android.R.anim.fade_out));
mInfo.setVisibility(View.INVISIBLE);
}
@TargetApi(Build.VERSION_CODES.FROYO)
private void changeAudioFocus(boolean gain) {
if (!Util.isFroyoOrLater()) // NOP if not supported
return;
if (mAudioFocusListener == null) {
mAudioFocusListener = new OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int focusChange) {
/*
* Pause playback during alerts and notifications
*/
switch (focusChange) {
case AudioManager.AUDIOFOCUS_LOSS:
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
if (mLibVLC.isPlaying())
mLibVLC.pause();
break;
case AudioManager.AUDIOFOCUS_GAIN:
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
if (!mLibVLC.isPlaying())
mLibVLC.play();
break;
}
}
};
}
AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE);
if (gain)
am.requestAudioFocus(mAudioFocusListener,
AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
else
am.abandonAudioFocus(mAudioFocusListener);
}
/**
* Handle libvlc asynchronous events
*/
private final Handler eventHandler = new VideoPlayerEventHandler(this);
private static class VideoPlayerEventHandler extends
WeakHandler<VideoPlayerActivity> {
public VideoPlayerEventHandler(VideoPlayerActivity owner) {
super(owner);
}
@Override
public void handleMessage(Message msg) {
VideoPlayerActivity activity = getOwner();
if (activity == null)
return;
switch (msg.getData().getInt("event")) {
case EventHandler.MediaPlayerPlaying:
Log.i(TAG, "MediaPlayerPlaying");
activity.showOverlay();
/**
* FIXME: update the track list when it changes during the
* playback. (#7540)
*/
// TRIBLER
if (VideoPlayerActivity.initCompleted
&& VideoPlayerActivity.seekBufferCompleted) {
activity.setESTrackLists(true);
activity.setESTracks();
activity.changeAudioFocus(true);
}
break;
case EventHandler.MediaPlayerPaused:
Log.i(TAG, "MediaPlayerPaused");
break;
case EventHandler.MediaPlayerStopped:
Log.i(TAG, "MediaPlayerStopped");
activity.changeAudioFocus(false);
break;
case EventHandler.MediaPlayerEndReached:
Log.i(TAG, "MediaPlayerEndReached");
activity.changeAudioFocus(false);
activity.endReached();
break;
case EventHandler.MediaPlayerVout:
activity.handleVout(msg);
break;
case EventHandler.MediaPlayerPositionChanged:
if (!activity.mCanSeek)
activity.mCanSeek = true;
// don't spam the logs
break;
case EventHandler.MediaPlayerEncounteredError:
Log.i(TAG, "MediaPlayerEncounteredError");
activity.encounteredError();
break;
default:
Log.e(TAG, String.format("Event not handled (0x%x)", msg
.getData().getInt("event")));
break;
}
activity.updateOverlayPausePlay();
}
};
/**
* Handle resize of the surface and the overlay
*/
private final Handler mHandler = new VideoPlayerHandler(this);
private static class VideoPlayerHandler extends
WeakHandler<VideoPlayerActivity> {
public VideoPlayerHandler(VideoPlayerActivity owner) {
super(owner);
}
@Override
public void handleMessage(Message msg) {
VideoPlayerActivity activity = getOwner();
if (activity == null) // WeakReference could be GC'ed early
return;
switch (msg.what) {
case FADE_OUT:
activity.hideOverlay(false);
break;
case SHOW_PROGRESS:
int pos = activity.setOverlayProgress();
if (activity.canShowProgress()) {
msg = obtainMessage(SHOW_PROGRESS);
sendMessageDelayed(msg, 1000 - (pos % 1000));
}
break;
case SURFACE_SIZE:
activity.changeSurfaceSize();
break;
case FADE_OUT_INFO:
activity.fadeOutInfo();
break;
}
}
};
private boolean canShowProgress() {
// TRIBLER
return !mDragging && mShowing && mLibVLC.isPlaying() && metaDataDone;
}
private void endReached() {
if (mLibVLC.getMediaList().expandMedia(savedIndexPosition) == 0) {
Log.d(TAG, "Found a video playlist, expanding it");
eventHandler.postDelayed(new Runnable() {
@Override
public void run() {
load();
}
}, 1000);
} else if (torrentFile) {
// TODO (maybe delete this stuff?)
mEndReached = false;
initCompleted = false;
seekBufferCompleted = false;
// TRIBLER
} else {
/* Exit player when reaching the end */
mEndReached = true;
finish();
}
}
private void encounteredError() {
/* Encountered Error, exit player with a message */
AlertDialog dialog = new AlertDialog.Builder(VideoPlayerActivity.this)
.setPositiveButton(R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
finish();
}
}).setTitle(R.string.encountered_error_title)
.setMessage(R.string.encountered_error_message).create();
dialog.show();
}
private void handleVout(Message msg) {
if (msg.getData().getInt("data") == 0 && !mEndReached) {
/* Video track lost, open in audio mode */
Log.i(TAG, "Video track lost, switching to audio");
mSwitchingView = true;
finish();
}
}
private void changeSurfaceSize() {
// get screen size
int sw = getWindow().getDecorView().getWidth();
int sh = getWindow().getDecorView().getHeight();
double dw = sw, dh = sh;
// getWindow().getDecorView() doesn't always take orientation into
// account, we have to correct the values
boolean isPortrait = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
if (sw > sh && isPortrait || sw < sh && !isPortrait) {
dw = sh;
dh = sw;
}
// sanity check
if (dw * dh == 0 || mVideoWidth * mVideoHeight == 0) {
Log.e(TAG, "Invalid surface size");
return;
}
// compute the aspect ratio
double ar, vw;
double density = (double) mSarNum / (double) mSarDen;
if (density == 1.0) {
/* No indication about the density, assuming 1:1 */
vw = mVideoVisibleWidth;
ar = (double) mVideoVisibleWidth / (double) mVideoVisibleHeight;
} else {
/* Use the specified aspect ratio */
vw = mVideoVisibleWidth * density;
ar = vw / mVideoVisibleHeight;
}
// compute the display aspect ratio
double dar = dw / dh;
switch (mCurrentSize) {
case SURFACE_BEST_FIT:
if (dar < ar)
dh = dw / ar;
else
dw = dh * ar;
break;
case SURFACE_FIT_HORIZONTAL:
dh = dw / ar;
break;
case SURFACE_FIT_VERTICAL:
dw = dh * ar;
break;
case SURFACE_FILL:
break;
case SURFACE_16_9:
ar = 16.0 / 9.0;
if (dar < ar)
dh = dw / ar;
else
dw = dh * ar;
break;
case SURFACE_4_3:
ar = 4.0 / 3.0;
if (dar < ar)
dh = dw / ar;
else
dw = dh * ar;
break;
case SURFACE_ORIGINAL:
dh = mVideoVisibleHeight;
dw = vw;
break;
}
// force surface buffer size
mSurfaceHolder.setFixedSize(mVideoWidth, mVideoHeight);
mSubtitlesSurfaceHolder.setFixedSize(mVideoWidth, mVideoHeight);
// set display size
LayoutParams lp = mSurface.getLayoutParams();
lp.width = (int) Math.ceil(dw * mVideoWidth / mVideoVisibleWidth);
lp.height = (int) Math.ceil(dh * mVideoHeight / mVideoVisibleHeight);
mSurface.setLayoutParams(lp);
mSubtitlesSurface.setLayoutParams(lp);
// set frame size (crop if necessary)
lp = mSurfaceFrame.getLayoutParams();
lp.width = (int) Math.floor(dw);
lp.height = (int) Math.floor(dh);
mSurfaceFrame.setLayoutParams(lp);
mSurface.invalidate();
mSubtitlesSurface.invalidate();
}
/**
* show/hide the overlay
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mIsLocked) {
// locked, only handle show/hide & ignore all actions
if (event.getAction() == MotionEvent.ACTION_UP) {
if (!mShowing) {
showOverlay();
} else {
hideOverlay(true);
}
}
return false;
}
DisplayMetrics screen = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(screen);
if (mSurfaceYDisplayRange == 0)
mSurfaceYDisplayRange = Math.min(screen.widthPixels,
screen.heightPixels);
float y_changed = event.getRawY() - mTouchY;
float x_changed = event.getRawX() - mTouchX;
// coef is the gradient's move to determine a neutral zone
float coef = Math.abs(y_changed / x_changed);
float xgesturesize = ((x_changed / screen.xdpi) * 2.54f);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// Audio
mTouchY = event.getRawY();
mVol = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
mTouchAction = TOUCH_NONE;
// Seek
mTouchX = event.getRawX();
break;
case MotionEvent.ACTION_MOVE:
// No volume/brightness action if coef < 2
if (coef > 2) {
// Volume (Up or Down - Right side)
if (!mEnableBrightnessGesture
|| mTouchX > (screen.widthPixels / 2)) {
doVolumeTouch(y_changed);
}
// Brightness (Up or Down - Left side)
if (mEnableBrightnessGesture
&& mTouchX < (screen.widthPixels / 2)) {
doBrightnessTouch(y_changed);
}
// Extend the overlay for a little while, so that it doesn't
// disappear on the user if more adjustment is needed. This
// is because on devices with soft navigation (e.g. Galaxy
// Nexus), gestures can't be made without activating the UI.
if (Util.hasNavBar())
showOverlay();
}
// Seek (Right or Left move)
doSeekTouch(coef, xgesturesize, false);
break;
case MotionEvent.ACTION_UP:
// Audio or Brightness
if (mTouchAction == TOUCH_NONE) {
if (!mShowing) {
showOverlay();
} else {
hideOverlay(true);
}
}
// Seek
doSeekTouch(coef, xgesturesize, true);
break;
}
return mTouchAction != TOUCH_NONE;
}
private void doSeekTouch(float coef, float gesturesize, boolean seek) {
// No seek action if coef > 0.5 and gesturesize < 1cm
if (coef > 0.5 || Math.abs(gesturesize) < 1 || !mCanSeek)
return;
if (mTouchAction != TOUCH_NONE && mTouchAction != TOUCH_SEEK)
return;
mTouchAction = TOUCH_SEEK;
// Always show seekbar when searching
if (!mShowing)
showOverlay();
long length = mLibVLC.getLength();
long time = mLibVLC.getTime();
// Size of the jump, 10 minutes max (600000), with a bi-cubic
// progression, for a 8cm gesture
int jump = (int) (Math.signum(gesturesize) * ((600000 * Math.pow(
(gesturesize / 8), 4)) + 3000));
// Adjust the jump
if ((jump > 0) && ((time + jump) > length))
jump = (int) (length - time);
if ((jump < 0) && ((time + jump) < 0))
jump = (int) -time;
// Jump !
if (seek && length > 0)
mLibVLC.setTime(time + jump);
if (length > 0)
// Show the jump's size
showInfo(
String.format("%s%s (%s)", jump >= 0 ? "+" : "",
Util.millisToString(jump),
Util.millisToString(time + jump)), 1000);
else
showInfo(R.string.unseekable_stream, 1000);
}
private void doVolumeTouch(float y_changed) {
if (mTouchAction != TOUCH_NONE && mTouchAction != TOUCH_VOLUME)
return;
int delta = -(int) ((y_changed / mSurfaceYDisplayRange) * mAudioMax);
int vol = (int) Math.min(Math.max(mVol + delta, 0), mAudioMax);
if (delta != 0) {
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, vol, 0);
mTouchAction = TOUCH_VOLUME;
showInfo(
getString(R.string.volume) + '\u00A0'
+ Integer.toString(vol), 1000);
}
}
private void initBrightnessTouch() {
float brightnesstemp = 0.01f;
// Initialize the layoutParams screen brightness
try {
brightnesstemp = android.provider.Settings.System.getInt(
getContentResolver(),
android.provider.Settings.System.SCREEN_BRIGHTNESS) / 255.0f;
} catch (SettingNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.screenBrightness = brightnesstemp;
getWindow().setAttributes(lp);
mIsFirstBrightnessGesture = false;
}
private void doBrightnessTouch(float y_changed) {
if (mTouchAction != TOUCH_NONE && mTouchAction != TOUCH_BRIGHTNESS)
return;
if (mIsFirstBrightnessGesture)
initBrightnessTouch();
mTouchAction = TOUCH_BRIGHTNESS;
// Set delta : 0.07f is arbitrary for now, it possibly will change in
// the future
float delta = -y_changed / mSurfaceYDisplayRange * 0.07f;
// Estimate and adjust Brightness
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.screenBrightness = Math.min(
Math.max(lp.screenBrightness + delta, 0.01f), 1);
// Set Brightness
getWindow().setAttributes(lp);
showInfo(
getString(R.string.brightness) + '\u00A0'
+ Math.round(lp.screenBrightness * 15), 1000);
}
/**
* handle changes of the seekbar (slicer)
*/
private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
private int lastRecordedProgress;
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
mDragging = true;
showOverlay(OVERLAY_INFINITE);
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
mDragging = false;
onProgressChanged(seekBar, lastRecordedProgress, true);
//showOverlay();
//hideInfo();
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
// TRIBLER
lastRecordedProgress = progress;
if (fromUser && mCanSeek && !mDragging && initCompleted) {
float seekToPosition = (progress * 1.0f) / mLibVLC.getLength();
if (seekBar.getSecondaryProgress() != 0) {
float secondaryPosition = (seekBar.getSecondaryProgress() * 1.0f)
/ mSeekbar.getMax();
// only seek in already downloaded part:
if (seekToPosition < secondaryPosition) {
prepareSeek(progress, seekToPosition);
}
} else {
prepareSeek(progress, seekToPosition);
}
}
}
private void prepareSeek(int progress, float seekToPosition) {
mTime.setText(Util.millisToString(progress));
showInfo(Util.millisToString(progress));
mLibVLC.pause();
seekBufferCompleted = false;
setDownloadPrioritySeekTo(seekToPosition);
}
};
/**
*
*/
private final OnClickListener mAudioTrackListener = new OnClickListener() {
@Override
public void onClick(View v) {
final String[] arrList = new String[mAudioTracksList.size()];
int i = 0;
int listPosition = 0;
for (Map.Entry<Integer, String> entry : mAudioTracksList.entrySet()) {
arrList[i] = entry.getValue();
// map the track position to the list position
if (entry.getKey() == mLibVLC.getAudioTrack())
listPosition = i;
i++;
}
AlertDialog dialog = new AlertDialog.Builder(
VideoPlayerActivity.this)
.setTitle(R.string.track_audio)
.setSingleChoiceItems(arrList, listPosition,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int listPosition) {
int trackID = -1;
// Reverse map search...
for (Map.Entry<Integer, String> entry : mAudioTracksList
.entrySet()) {
if (arrList[listPosition].equals(entry
.getValue())) {
trackID = entry.getKey();
break;
}
}
if (trackID < 0)
return;
MediaDatabase
.getInstance(
VideoPlayerActivity.this)
.updateMedia(
mLocation,
MediaDatabase.mediaColumn.MEDIA_AUDIOTRACK,
trackID);
mLibVLC.setAudioTrack(trackID);
dialog.dismiss();
}
}).create();
dialog.setCanceledOnTouchOutside(true);
dialog.setOwnerActivity(VideoPlayerActivity.this);
dialog.show();
}
};
/**
*
*/
private final OnClickListener mSubtitlesListener = new OnClickListener() {
@Override
public void onClick(View v) {
final String[] arrList = new String[mSubtitleTracksList.size()];
int i = 0;
int listPosition = 0;
for (Map.Entry<Integer, String> entry : mSubtitleTracksList
.entrySet()) {
arrList[i] = entry.getValue();
// map the track position to the list position
if (entry.getKey() == mLibVLC.getSpuTrack())
listPosition = i;
i++;
}
AlertDialog dialog = new AlertDialog.Builder(
VideoPlayerActivity.this)
.setTitle(R.string.track_text)
.setSingleChoiceItems(arrList, listPosition,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int listPosition) {
int trackID = -2;
// Reverse map search...
for (Map.Entry<Integer, String> entry : mSubtitleTracksList
.entrySet()) {
if (arrList[listPosition].equals(entry
.getValue())) {
trackID = entry.getKey();
break;
}
}
if (trackID < -1)
return;
MediaDatabase
.getInstance(
VideoPlayerActivity.this)
.updateMedia(
mLocation,
MediaDatabase.mediaColumn.MEDIA_SPUTRACK,
trackID);
mLibVLC.setSpuTrack(trackID);
dialog.dismiss();
}
}).create();
dialog.setCanceledOnTouchOutside(true);
dialog.setOwnerActivity(VideoPlayerActivity.this);
dialog.show();
}
};
/**
*
*/
private final OnClickListener mPlayPauseListener = new OnClickListener() {
@Override
public void onClick(View v) {
if (mLibVLC.isPlaying())
pause();
else
play();
showOverlay();
}
};
/**
*
*/
private final OnClickListener mBackwardListener = new OnClickListener() {
@Override
public void onClick(View v) {
seek(-10000);
}
};
/**
*
*/
private final OnClickListener mForwardListener = new OnClickListener() {
@Override
public void onClick(View v) {
seek(10000);
}
};
public void seek(int delta) {
// unseekable stream
if (mLibVLC.getLength() <= 0 || !mCanSeek)
return;
long position = mLibVLC.getTime() + delta;
if (position < 0)
position = 0;
mLibVLC.setTime(position);
showOverlay();
}
/**
*
*/
private final OnClickListener mLockListener = new OnClickListener() {
@Override
public void onClick(View v) {
if (mIsLocked) {
mIsLocked = false;
unlockScreen();
} else {
mIsLocked = true;
lockScreen();
}
}
};
/**
*
*/
private final OnClickListener mSizeListener = new OnClickListener() {
@Override
public void onClick(View v) {
if (mCurrentSize < SURFACE_ORIGINAL) {
mCurrentSize++;
} else {
mCurrentSize = 0;
}
changeSurfaceSize();
switch (mCurrentSize) {
case SURFACE_BEST_FIT:
showInfo(R.string.surface_best_fit, 1000);
break;
case SURFACE_FIT_HORIZONTAL:
showInfo(R.string.surface_fit_horizontal, 1000);
break;
case SURFACE_FIT_VERTICAL:
showInfo(R.string.surface_fit_vertical, 1000);
break;
case SURFACE_FILL:
showInfo(R.string.surface_fill, 1000);
break;
case SURFACE_16_9:
showInfo("16:9", 1000);
break;
case SURFACE_4_3:
showInfo("4:3", 1000);
break;
case SURFACE_ORIGINAL:
showInfo(R.string.surface_original, 1000);
break;
}
showOverlay();
}
};
private final OnClickListener mRemainingTimeListener = new OnClickListener() {
@Override
public void onClick(View v) {
mDisplayRemainingTime = !mDisplayRemainingTime;
showOverlay();
}
};
/**
* attach and disattach surface to the lib
*/
private final SurfaceHolder.Callback mSurfaceCallback = new Callback() {
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
if (format == PixelFormat.RGBX_8888)
Log.d(TAG, "Pixel format is RGBX_8888");
else if (format == PixelFormat.RGB_565)
Log.d(TAG, "Pixel format is RGB_565");
else if (format == ImageFormat.YV12)
Log.d(TAG, "Pixel format is YV12");
else
Log.d(TAG, "Pixel format is other/unknown");
mLibVLC.attachSurface(holder.getSurface(), VideoPlayerActivity.this);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mLibVLC.detachSurface();
}
};
private final SurfaceHolder.Callback mSubtitlesSurfaceCallback = new Callback() {
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
mLibVLC.attachSubtitlesSurface(holder.getSurface());
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mLibVLC.detachSubtitlesSurface();
}
};
/**
* show overlay the the default timeout
*/
private void showOverlay() {
showOverlay(OVERLAY_TIMEOUT);
}
/**
* show overlay
*/
private void showOverlay(int timeout) {
mHandler.sendEmptyMessage(SHOW_PROGRESS);
if (!mShowing) {
mShowing = true;
if (!mIsLocked) {
mOverlayHeader.setVisibility(View.VISIBLE);
mOverlayOption.setVisibility(View.VISIBLE);
mPlayPause.setVisibility(View.VISIBLE);
dimStatusBar(false);
// TRIBLER
// mSeekbar.setVisibility(View.VISIBLE);
// mTime.setVisibility(View.VISIBLE);
// mLength.setVisibility(View.VISIBLE);
SharedPreferences pref = PreferenceManager
.getDefaultSharedPreferences(this);
findViewById(R.id.libtorrent_debug)
.setVisibility(
(pref.getBoolean("libtorrent_debug", true) ? View.VISIBLE
: View.GONE));
}
mOverlayProgress.setVisibility(View.VISIBLE);
}
Message msg = mHandler.obtainMessage(FADE_OUT);
if (timeout != 0) {
mHandler.removeMessages(FADE_OUT);
mHandler.sendMessageDelayed(msg, timeout);
}
updateOverlayPausePlay();
}
/**
* hider overlay
*/
private void hideOverlay(boolean fromUser) {
if (mShowing) {
mHandler.removeMessages(SHOW_PROGRESS);
Log.i(TAG, "remove View!");
if (!fromUser && !mIsLocked) {
mOverlayHeader.startAnimation(AnimationUtils.loadAnimation(
this, android.R.anim.fade_out));
mOverlayOption.startAnimation(AnimationUtils.loadAnimation(
this, android.R.anim.fade_out));
mOverlayProgress.startAnimation(AnimationUtils.loadAnimation(
this, android.R.anim.fade_out));
mPlayPause.startAnimation(AnimationUtils.loadAnimation(this,
android.R.anim.fade_out));
}
mOverlayHeader.setVisibility(View.INVISIBLE);
mOverlayOption.setVisibility(View.INVISIBLE);
mOverlayProgress.setVisibility(View.INVISIBLE);
mPlayPause.setVisibility(View.INVISIBLE);
mShowing = false;
dimStatusBar(true);
// TRIBLER
// mSeekbar.setVisibility(View.INVISIBLE);
// mTime.setVisibility(View.INVISIBLE);
// mLength.setVisibility(View.INVISIBLE);
findViewById(R.id.libtorrent_debug).setVisibility(View.GONE);
}
}
/**
* Dim the status bar and/or navigation icons when needed on Android 3.x.
* Hide it on Android 4.0 and later
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void dimStatusBar(boolean dim) {
if (!Util.isHoneycombOrLater() || !Util.hasNavBar())
return;
int layout = 0;
if (!Util.hasCombBar() && Util.isJellyBeanOrLater())
layout = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
int visibility = (dim ? (Util.hasCombBar() ? View.SYSTEM_UI_FLAG_LOW_PROFILE
: View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
: View.SYSTEM_UI_FLAG_VISIBLE)
| layout;
mSurface.setSystemUiVisibility(visibility);
mSubtitlesSurface.setSystemUiVisibility(visibility);
}
private void updateOverlayPausePlay() {
if (mLibVLC == null) {
return;
}
// TRIBLER
// if (initCompleted && seekBufferCompleted) {
// mControls.setState(mLibVLC.isPlaying());
// // mLibVLC.play();
// } else {
// mControls.setState(false);
// }
mPlayPause
.setBackgroundResource(mLibVLC.isPlaying() ? R.drawable.pause_circle
: R.drawable.play_circle);
}
/**
* update the overlay
*/
private int setOverlayProgress() {
if (mLibVLC == null) {
return 0;
}
int time = (int) mLibVLC.getTime();
int length = (int) mLibVLC.getLength();
if (length == 0) {
Media media = MediaDatabase.getInstance(this).getMedia(mLocation);
if (media != null)
length = (int) media.getLength();
}
// Update all view elements
boolean isSeekable = mEnableJumpButtons && length > 0;
mBackward.setVisibility(isSeekable ? View.VISIBLE : View.GONE);
mForward.setVisibility(isSeekable ? View.VISIBLE : View.GONE);
// TRIBLER
mSeekbar.setMax(length == 0 ? (int) fileLength : length);
mSeekbar.setProgress(time);
mSysTime.setText(DateFormat.getTimeFormat(this).format(
new Date(System.currentTimeMillis())));
if (time >= 0)
mTime.setText(Util.millisToString(time));
if (length >= 0)
mLength.setText(mDisplayRemainingTime && length > 0 ? "- "
+ Util.millisToString(length - time) : Util
.millisToString(length));
// TRIBLER
if (torrentFile || magnet) {
int max = mSeekbar.getMax();
int sizeMB = (int) (fileLength / (1024 * 1024));
// TRIBLER HACK pretend you are not as fast as you are.
long progressSize = libTorrent()
.GetTorrentProgressSize(contentName);
int hackMb = sizeMB < max && progressSize > 5 ? 5 : 0;
int progress = (int) (sizeMB == 0 ? 0
: (max * progressSize - hackMb) / sizeMB);
mSeekbar.setSecondaryProgress(progress);
if (findViewById(R.id.libtorrent_debug).getVisibility() == View.VISIBLE
&& !inMetaDataWait) {
((TextView) findViewById(R.id.libtorrent_debug))
.setText(setLibtorrentDebugText(sizeMB, progressSize));
}
}
return time;
}
private String setLibtorrentDebugText(int sizeMB, long progressSize) {
String result = "";
String statusText = libTorrent().GetTorrentStatusText(contentName);
if (statusText.split("\n").length <= 1) {
result += TorrentState.values()[libTorrent().GetTorrentState(
contentName)]
+ "\n" + "progress: " + progressSize + "/" + sizeMB + "MB";
} else {
result += statusText + sizeMB + "MB";
}
return result;
}
private void setESTracks() {
if (mLastAudioTrack >= 0) {
mLibVLC.setAudioTrack(mLastAudioTrack);
mLastAudioTrack = -1;
}
if (mLastSpuTrack >= -1) {
mLibVLC.setSpuTrack(mLastSpuTrack);
mLastSpuTrack = -2;
}
}
private void setESTrackLists(boolean force) {
if (mAudioTracksList == null || force) {
if (mLibVLC.getAudioTracksCount() > 2) {
mAudioTracksList = mLibVLC.getAudioTrackDescription();
mAudioTrack.setOnClickListener(mAudioTrackListener);
mAudioTrack.setVisibility(View.VISIBLE);
} else {
mAudioTrack.setVisibility(View.GONE);
mAudioTrack.setOnClickListener(null);
}
}
if (mSubtitleTracksList == null || force) {
if (mLibVLC.getSpuTracksCount() > 0) {
mSubtitleTracksList = mLibVLC.getSpuTrackDescription();
mSubtitle.setOnClickListener(mSubtitlesListener);
mSubtitle.setVisibility(View.VISIBLE);
} else {
mSubtitle.setVisibility(View.GONE);
mSubtitle.setOnClickListener(null);
}
}
}
/**
*
*/
private void play() {
mLibVLC.play();
mSurface.setKeepScreenOn(true);
}
/**
*
*/
private void pause() {
mLibVLC.pause();
mSurface.setKeepScreenOn(false);
}
// TRIBLER
private static File getTorrentDownloadDir() {
// return new File(new File(Environment.getExternalStorageDirectory(),
// "Download"), "TriblerStreamingCache");
return new File(Environment.getExternalStorageDirectory(),
"TriblerStreamingCache");
}
// TRIBLER
public String getPiecePrioritiesString(int[] priorities) {
String result = "";
for (int i : priorities) {
result += "" + i;
}
return result;
}
// TRIBLER
public static boolean deleteRecursive(File path, GenericExtFilter filter)
throws FileNotFoundException {
if (!path.exists())
throw new FileNotFoundException(path.getAbsolutePath());
boolean ret = true;
if (path.isDirectory()) {
if (filter != null) {
for (File f : path.listFiles(filter)) {
ret = ret && deleteRecursive(f, null);
}
} else {
for (File f : path.listFiles()) {
ret = ret && deleteRecursive(f, null);
}
}
}
return ret && path.delete();
}
// TRIBLER
private void setDownloadPrioritySeekTo(float position) {
long totalTime = mLibVLC.getLength();
if (totalTime != 0) {
float ratio = position;
int numberOfPieces = lastPieceIndex - firstPieceIndex;
int startNewDownloadHere = firstPieceIndex
+ ((int) Math.floor(numberOfPieces * ratio));
// // then go back a bit to make up for the fact of metadata at
// start
// // or end
// long pieceSize = libTorrent().GetPieceSize(contentName, 0);
// int goBack = (int) ((1024 * 1024) / pieceSize);
// if (startNewDownloadHere - goBack >= firstPieceIndex) {
// startNewDownloadHere -= ((goBack < 1) ? 1 : goBack);
// // go back one mb
// }
Log.d("SEEK TO", "startNewDownloadHere: " + startNewDownloadHere
+ "lastPieceIndex: " + lastPieceIndex + "firstPieceIndex: "
+ firstPieceIndex);
Log.d("SEEK TO", "piecePrios: "
+ getPiecePrioritiesString(piecePriorities));
for (int i = firstPieceIndex; i <= lastPieceIndex; i++) {
piecePriorities[i] = i < startNewDownloadHere ? FilePriority.DONTDOWNLOAD
.ordinal() : FilePriority.NORMAL.ordinal();
}
libTorrent().SetPiecePriorities(contentName, piecePriorities);
Log.d("SEEK TO", "set piece priorities");
seekTask = new SeekTask(position, startNewDownloadHere);
seekTask.execute();
} else {
Log.d(TAG, "No end time specified");
}
}
/**
* delete the previous torrent cache, so the application holds only one
* file/movie at a time
*
* @param savePath
* , where the torrent cache is
*/
private void deletePreviousTorrentCache(File savePath, boolean magnet) {
Log.d("Deleting cache",
"deleting previous torrent cache if != lastContent");
contentName = magnet ? libTorrent().GetMagnetContentFileName(mLocation)
: libTorrent().GetTorrentName(mLocation);
if (contentName == null) {
showError("couldn't get content file name, please check internet connectivity and link");
}
Log.d("Deleting cache", "current content: " + contentName);
String contentNameDir = new File(savePath, contentName)
.getAbsolutePath();
SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(this);
String latestContent = prefs.getString(KEY_LATEST_CONTENT, "");
String latestContentDir = prefs.getString(KEY_LATEST_CONTENT_DIR, "");
Editor e = prefs.edit();
Log.d("Deleting cache", "previous content: " + latestContent);
if (!latestContent.equals(contentName)) {
Log.d("Deleting cache", "previous content: " + latestContent);
e.putString(KEY_LATEST_CONTENT, contentName);
e.putString(KEY_LATEST_CONTENT_DIR, contentNameDir);
try {
boolean deleteSucces = deleteRecursive(new File(
latestContentDir), null);
GenericExtFilter filter = new GenericExtFilter(".resume");
deleteRecursive(getTorrentDownloadDir(), filter);
Log.d(TAG, "deleted: " + new File(latestContentDir).toString()
+ " ?: " + deleteSucces);
} catch (FileNotFoundException fnfe) {
fnfe.printStackTrace();
Log.d(TAG,
"File not found: "
+ new File(latestContentDir).toString());
}
}
e.commit();
}
// inner class, generic extension filter
public class GenericExtFilter implements FilenameFilter {
private String ext;
public GenericExtFilter(String ext) {
this.ext = ext;
}
public boolean accept(File dir, String name) {
return (name.endsWith(ext));
}
}
/**
* Find biggest file, and set priorities, so you can play it.
*
* @param savePath
* , where is the location of the to be downloaded file
* @return location of biggest file
*/
private File setFilesPriorities(File savePath) {
Log.d("setPrios", "setting prios for: " + contentName);
String[] files = libTorrent().GetTorrentFiles(contentName).split(
"\\r?\\n");
long[] sizes = new long[files.length];
byte[] priorities = libTorrent().GetTorrentFilesPriority(contentName);
fileLength = 0;
int biggestFileIndex = 0;
for (int i = 0; i < files.length; i++) {
String file = files[i];
int spaceIndex = file.lastIndexOf(" ");
files[i] = file.substring(0, spaceIndex);
String sizeString = file.substring(spaceIndex + 1);
sizes[i] = getSize(sizeString);
Log.v(TAG, "File: " + files[i] + " | " + sizes[i] + " | "
+ FilePriority.values()[priorities[i]]);
if (sizes[i] > fileLength) {
priorities[biggestFileIndex] = FilePriority.DONTDOWNLOAD
.getByte();
fileLength = sizes[i];
biggestFileIndex = i;
priorities[biggestFileIndex] = FilePriority.NORMAL.getByte();
} else {
priorities[i] = FilePriority.DONTDOWNLOAD.getByte();
}
}
// Set priorities to only biggest file gets downloaded
Log.v(TAG, "=== RESULT ===");
for (int i = 0; i < files.length; i++) {
Log.v(TAG, "File: " + files[i] + " | " + sizes[i] + " | "
+ FilePriority.values()[priorities[i]]);
}
libTorrent().SetTorrentFilesPriority(priorities, contentName);
return new File(savePath, files[biggestFileIndex]);
}
/**
* check if (part of) movie is already downloaded in a previous app lifetime
*/
private boolean isAlreadyDownloaded() {
if (libTorrent().GetTorrentState(contentName) == TorrentState.CHECKING_FILES
.ordinal()) {
Log.d(TAG,
"checking files? progressSize?"
+ (libTorrent().GetTorrentState(contentName) == TorrentState.CHECKING_FILES
.ordinal())
+ libTorrent().GetTorrentProgressSize(contentName));
mLibVLC.stop();
findViewById(R.id.libtorrent_loading).setVisibility(View.VISIBLE);
int counter = 0;
int sleeptime = 300;
while (libTorrent().GetTorrentProgressSize(contentName) < CHECKSIZEMB
&& counter <= 1500) {
Log.d(TAG,
"while "
+ libTorrent().GetTorrentProgressSize(
contentName) + " < CZ");
if (libTorrent().GetTorrentState(contentName) != TorrentState.CHECKING_FILES
.ordinal()) {
Log.d(TAG, "breaking...it's not checking files anymore");
break;
}
try {
Thread.sleep(sleeptime);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
counter += sleeptime;
}
if (libTorrent().GetTorrentProgressSize(contentName) >= CHECKSIZEMB) {
Log.d("isAlreadyDownloaded", "returns true");
return true;
} else {
Log.d("isAlreadyDownloaded", "returns false");
return false;
}
} else if (libTorrent().GetTorrentProgressSize(contentName) >= CHECKSIZEMB) {
Log.d("isAlreadyDownloaded", "returns true");
return true;
} else {
Log.d("isAlreadyDownloaded", "returns false");
return false;
}
}
private File addTorrent() {
torrentFile = true;
Log.d(TAG, "GOT TORRENT FILE: " + mLocation);
mLocation = mLocation.substring(7);
Log.d(TAG, "GOT TORRENT FILE: " + mLocation);
File savePath = getTorrentDownloadDir();
Log.d(TAG, "SAVEPATH: " + savePath.getAbsolutePath());
libTorrent().AddTorrent(savePath.getAbsolutePath(), mLocation,
StorageModes.ALLOCATE.ordinal(), false);
metaDataDone = true;
return savePath;
}
private File addMagnet() {
magnet = true;
torrentFile = true;
Log.d(TAG, "GOT MAGNET: " + mLocation);
File savePath = getTorrentDownloadDir();
Log.d(TAG, "SAVEPATH: " + savePath.getAbsolutePath());
boolean success = libTorrent().AddMagnet(savePath.getAbsolutePath(),
StorageModes.ALLOCATE.ordinal(), mLocation);
if (success) {
return savePath;
} else {
return null;
}
}
// TRIBLER
private void waitForMetaData(File savePath) {
inMetaDataWait = true;
((TextView) findViewById(R.id.libtorrent_debug))
.setText("Fetching Metadata...");
waitDataTask = new WaitForMetaDataTask(savePath.getAbsolutePath());
waitDataTask.execute();
}
private void showError(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
finish();
}
/**
* External extras: - position (long) - position of the video to start with
* (in ms)
*/
@SuppressWarnings({ "deprecation" })
private void load() {
mLocation = null;
boolean dontParse = false;
boolean fromStart = false;
String itemTitle = null;
int itemPosition = -1; // Index in the media list as passed by
// AudioServer (used only for vout transition
// internally)
long intentPosition = -1; // position passed in by intent (ms)
if (getIntent().getAction() != null
&& getIntent().getAction().equals(Intent.ACTION_VIEW)) {
/* Started from external application */
if (getIntent().getData() != null
&& getIntent().getData().getScheme() != null
&& getIntent().getData().getScheme().equals("content")) {
if (getIntent().getData().getHost().equals("media")) {
// Media URI
Cursor cursor = managedQuery(getIntent().getData(),
new String[] { MediaStore.Video.Media.DATA }, null,
null, null);
int column_index = cursor
.getColumnIndexOrThrow(MediaStore.Video.Media.DATA);
if (cursor.moveToFirst())
mLocation = LibVLC.PathToURI(cursor
.getString(column_index));
} else if (getIntent().getData().getHost()
.equals("com.fsck.k9.attachmentprovider")
|| getIntent().getData().getHost().equals("gmail-ls")) {
// Mail-based apps - download the stream to a temporary file
// and play it
try {
Cursor cursor = getContentResolver()
.query(getIntent().getData(),
new String[] { MediaStore.MediaColumns.DISPLAY_NAME },
null, null, null);
cursor.moveToFirst();
String filename = cursor
.getString(cursor
.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME));
Log.i(TAG, "Getting file " + filename
+ " from content:// URI");
InputStream is = getContentResolver().openInputStream(
getIntent().getData());
OutputStream os = new FileOutputStream(Environment
.getExternalStorageDirectory().getPath()
+ "/Download/" + filename);
byte[] buffer = new byte[1024];
int bytesRead = 0;
while ((bytesRead = is.read(buffer)) >= 0) {
os.write(buffer, 0, bytesRead);
}
os.close();
is.close();
mLocation = LibVLC.PathToURI(Environment
.getExternalStorageDirectory().getPath()
+ "/Download/" + filename);
} catch (Exception e) {
Log.e(TAG, "Couldn't download file from mail URI");
encounteredError();
}
} else {
// other content-based URI (probably file pickers)
mLocation = getIntent().getData().getPath();
}
} else {
// Plain URI
mLocation = getIntent().getDataString();
// Remove VLC prefix if needed
if (mLocation.startsWith("vlc://")) {
mLocation = mLocation.substring(6);
}
mLocation = Uri.decode(getIntent().getDataString());
Log.d(TAG, "datastring: " + mLocation);
// Decode URI
if (!mLocation.contains("/")) {
try {
mLocation = URLDecoder.decode(mLocation, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
if (getIntent().getExtras() != null)
intentPosition = getIntent().getExtras()
.getLong("position", -1);
} else if (getIntent().getAction() != null
&& getIntent().getAction().equals(PLAY_FROM_VIDEOGRID)
&& getIntent().getExtras() != null) {
/* Started from VideoListActivity */
mLocation = getIntent().getExtras().getString("itemLocation");
itemTitle = getIntent().getExtras().getString("itemTitle");
dontParse = getIntent().getExtras().getBoolean("dontParse");
fromStart = getIntent().getExtras().getBoolean("fromStart");
itemPosition = getIntent().getExtras().getInt("itemPosition", -1);
}
mSurface.setKeepScreenOn(true);
// TRIBLER
if (mLocation != null
&& (mLocation.endsWith(".torrent") || mLocation
.startsWith("magnet"))) {
File savePath;
Log.d("CHECK", "number of torrents in libtorrent: "
+ libTorrent().GetNumberOfTorrents());
if (mLocation.endsWith(".torrent")) {
savePath = addTorrent();
} else { // mLocation.startsWith("magnet")
savePath = addMagnet();
logStates();
if (savePath != null) {
if (!libTorrent().HasMetaData(savePath.getAbsolutePath(),
mLocation)) {
waitForMetaData(savePath);
} else {
metaDataDone = true;
}
Log.d("LOAD", "metadatadone: " + metaDataDone);
} else {
String errorMsg = "Adding unsuccesfull, check link and internet connection and try again";
Log.e("MAGNET ADD ERROR", errorMsg);
showError(errorMsg);
}
}
if (metaDataDone) {
libTorrent().SetSavePath(savePath.getAbsolutePath());
deletePreviousTorrentCache(savePath, magnet);
File location = setFilesPriorities(savePath);
mLocation = "file://" + location.getAbsolutePath();
while (!location.exists()) {
try {
Thread.sleep(300);
} catch (InterruptedException ie) {
Log.d(TAG, "================ Doesnt Exist Yet");
}
}
Log.d(TAG, "!!!!!!!!!! Exists !!!!!!!!!!!!");
if (isAlreadyDownloaded()) {
Log.d(TAG, "contentsize >= checksize");
findViewById(R.id.libtorrent_loading).setVisibility(
View.GONE);
initCompleted = true;
seekBufferCompleted = true;
} else {
Log.d(TAG, "contentsize not bigger than checksize");
initCompleted = false;
seekBufferCompleted = false;
}
setPiecePrioritesInit();
}
}
if (metaDataDone) {
Log.d("TRIBLER_DEBUG", "metaDataDone load");
if (initCompleted) {
/* Start / resume playback */
if (dontParse && itemPosition >= 0) {
// Provided externally from AudioService
Log.d(TAG,
"Continuing playback from AudioService at index "
+ itemPosition);
savedIndexPosition = itemPosition;
if (!mLibVLC.isPlaying()) {
// AudioService-transitioned playback for item after
// sleep and resume
mLibVLC.playIndex(savedIndexPosition);
dontParse = false;
}
} else if (savedIndexPosition > -1) {
mLibVLC.setMediaList();
mLibVLC.playIndex(savedIndexPosition);
} else if (mLocation != null && mLocation.length() > 0
&& !dontParse) {
mLibVLC.setMediaList();
mLibVLC.getMediaList().add(new Media(mLibVLC, mLocation));
Log.d("CHECK", "mediaplayerlistsize: "
+ mLibVLC.getMediaList().size());
savedIndexPosition = mLibVLC.getMediaList().size() - 1;
mLibVLC.playIndex(savedIndexPosition);
}
mCanSeek = false;
setMediaPostLoad(fromStart, itemTitle, intentPosition);
} else {
Log.v("TRIBLER_DEBUG", "progress size = "
+ libTorrent().GetTorrentProgressSize(contentName));
initAsyncTask = new InitAsyncTask();
initAsyncTask.execute();
}
}
Log.d(TAG, "set overlay progress in load()");
setOverlayProgress();
}
// TRIBLER pulled from load
@SuppressWarnings("unchecked")
private void setMediaPostLoad(boolean fromStart, String itemTitle,
long intentPosition) {
Log.v("TRIBLER_DEBUG", "SETTING MEDIA");
String title = getResources().getString(R.string.title);
// restore last position
SharedPreferences preferences = getSharedPreferences(
PreferencesActivity.NAME, MODE_PRIVATE);
Media media = MediaDatabase.getInstance(this).getMedia(mLocation);
if (media != null) {
// in media library
if (media.getTime() > 0 && !fromStart) {
mLibVLC.setTime(media.getTime());
}
mLastAudioTrack = media.getAudioTrack();
mLastSpuTrack = media.getSpuTrack();
} else {
Log.v("TRIBLER_DEBUG", "media == null");
// not in media library
long rTime = preferences.getLong(
PreferencesActivity.VIDEO_RESUME_TIME, -1);
SharedPreferences.Editor editor = preferences.edit();
editor.putLong(PreferencesActivity.VIDEO_RESUME_TIME, -1);
editor.commit();
if (rTime > 0) {
Log.v("TRIBLER_DEBUG", "rTime > 0");
mLibVLC.setTime(rTime);
}
if (intentPosition > 0) {
Log.v("TRIBLER_DEBUG", "intentPosition > 0");
mLibVLC.setTime(intentPosition);
}
}
// Subtitles
String subtitleList_serialized = preferences.getString(
PreferencesActivity.VIDEO_SUBTITLE_FILES, null);
ArrayList<String> prefsList = new ArrayList<String>();
if (subtitleList_serialized != null) {
ByteArrayInputStream bis = new ByteArrayInputStream(
subtitleList_serialized.getBytes());
try {
ObjectInputStream ois = new ObjectInputStream(bis);
prefsList = (ArrayList<String>) ois.readObject();
} catch (ClassNotFoundException e) {
} catch (StreamCorruptedException e) {
} catch (IOException e) {
}
}
for (String x : prefsList) {
if (!mSubtitleSelectedFiles.contains(x))
mSubtitleSelectedFiles.add(x);
}
try {
title = URLDecoder.decode(mLocation, "UTF-8");
} catch (UnsupportedEncodingException e) {
} catch (IllegalArgumentException e) {
}
if (title.startsWith("file:")) {
title = new File(title).getName();
int dotIndex = title.lastIndexOf('.');
if (dotIndex != -1)
title = title.substring(0, dotIndex);
}
if (itemTitle != null) {
title = itemTitle;
}
mTitle.setText(title);
}
private class SeekTask extends AsyncTask<Void, Integer, Void> {
boolean running = true;
TextView loadingInfo = (TextView) findViewById(R.id.libtorrent_progress_text);
ProgressBar loadingBar = (ProgressBar) findViewById(R.id.libtorrent_progressbar);
float setDownloadPosition;
int startNewDownloadHere;
public SeekTask(float position, int startNewDownloadHere) {
setDownloadPosition = position;
this.startNewDownloadHere = startNewDownloadHere;
}
@Override
protected void onCancelled() {
asyncTaskRunning.remove("setDownloadPrioritySeekTo()");
running = false;
findViewById(R.id.libtorrent_loading).setVisibility(View.GONE);
}
@Override
protected void onPreExecute() {
asyncTaskRunning.add("setDownloadPrioritySeekTo()");
Log.d("SEEK TO", "PreExecute of seek..."
+ getPiecePrioritiesString(piecePriorities));
findViewById(R.id.libtorrent_loading).setVisibility(View.VISIBLE);
mPlayPause.setVisibility(View.INVISIBLE);
loadingInfo.setText("Seeking...");
loadingBar.setProgress(0);
}
@Override
protected void onProgressUpdate(Integer... progress) {
int progressPercentage = progress[0];
loadingBar.setProgress(progressPercentage);
setOverlayProgress();
// progressPercentageText.setText(progressPercentage + "%");
}
@Override
protected Void doInBackground(Void... params) {
Thread.currentThread().setName("AsyncTask seekTo");
// if the torrent is in seeding state, it is already done,
// so we can skip the following parts and go to the
// postExecute
if (libTorrent().GetTorrentState(contentName) == TorrentState.SEEDING
.ordinal()) {
return null;
}
long pieceSize = libTorrent().GetPieceSize(contentName, 0);
int rightIndex = startNewDownloadHere
+ ((int) ((CHECKSIZE * 1.5) / pieceSize));
int piecesToCheck = (int) ((CHECKSIZE * 1.5) / pieceSize);
long newCheckSize = CHECKSIZE;
int progressPercentage;
if (rightIndex >= lastPieceIndex) {
rightIndex = lastPieceIndex;
// the minus one is because the last piece is often
// smaller than standard, this way it won't hang on it
// too much.
piecesToCheck = rightIndex - startNewDownloadHere - 1;
newCheckSize = piecesToCheck * pieceSize;
}
Log.d("SEEK TO", "running in Async, newCheckSize: " + newCheckSize
+ " piecesToCheck: " + piecesToCheck + " startPiece: "
+ startNewDownloadHere);
while (running) {
long sum = 0;
if (isCancelled()) {
running = false;
return null;
}
for (int i = startNewDownloadHere; i <= rightIndex; i++) {
if (libTorrent().HavePiece(contentName, i)) {
// Log.d("SEEK TO", "have piece " + i);
sum += pieceSize;
}
if (sum >= newCheckSize) {
// wait a bit longer
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
running = false;
return null;
}
return null;
}
}
progressPercentage = (int) (((sum * 1.0) / (newCheckSize * 1.0)) * 100);
publishProgress(progressPercentage);
Log.d("SEEK TO", "sum: " + sum);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
running = false;
return null;
}
}
return null;
}
@Override
protected void onPostExecute(Void result) {
if (running) {
Log.d("SEEK TO", "postExecute seek..."
+ getPiecePrioritiesString(piecePriorities));
findViewById(R.id.libtorrent_loading).setVisibility(View.GONE);
seekBufferCompleted = true;
mEndReached = false;
Log.d("SEEK TO", "setting position and overlay to: "
+ setDownloadPosition);
mLibVLC.setPosition(setDownloadPosition);
Log.d("SEEK TO", "setting text and showing overlay");
hideInfo();
showOverlay(0);
Log.d("SEEK TO", "starting to play");
pause();
play();
}
asyncTaskRunning.remove("setDownloadPrioritySeekTo()");
}
}
private class WaitForMetaDataTask extends AsyncTask<Void, String, Void> {
boolean running = true;
int counter = 0;
String savePathString;
TextView loadingInfo = (TextView) findViewById(R.id.libtorrent_progress_text);
ProgressBar loadingBar = (ProgressBar) findViewById(R.id.libtorrent_progressbar);
public WaitForMetaDataTask(String savePath) {
savePathString = savePath;
}
@Override
protected void onCancelled() {
asyncTaskRunning.remove("waitForMetaData()");
running = false;
findViewById(R.id.libtorrent_loading).setVisibility(View.GONE);
}
@Override
protected void onPreExecute() {
asyncTaskRunning.add("waitForMetaData()");
mPlayPause.setVisibility(View.INVISIBLE);
findViewById(R.id.libtorrent_loading).setVisibility(View.VISIBLE);
loadingInfo.setText("Locating Video...");
loadingBar.setVisibility(View.INVISIBLE);
}
@Override
protected Void doInBackground(Void... params) {
while (running) {
if (libTorrent().HasMetaData(savePathString, mLocation)) {
return null;
} else if (counter > waitTime) {
Log.e("TIMEOUT",
"waiting too long for metaData, check link and internet connection and try again");
counter = -1;
return null;
} else {
Log.d(TAG, "waiting for metaData: " + mLocation);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
counter += 100;
if (isCancelled()) {
return null;
}
}
return null;
}
@Override
protected void onPostExecute(Void result) {
if (running) {
loadingBar.setVisibility(View.VISIBLE);
findViewById(R.id.libtorrent_loading).setVisibility(View.GONE);
if (counter == -1) {
showError("waiting too long for metaData, check link and internet connection and try again");
} else {
Log.d("metaDataWait", "MetaData done");
inMetaDataWait = false;
metaDataDone = true;
onTriblerResume();
}
}
asyncTaskRunning.remove("waitForMetaData()");
}
}
private class InitAsyncTask extends AsyncTask<Void, Integer, Void> {
boolean running = true;
TextView loadingInfo = (TextView) findViewById(R.id.libtorrent_progress_text);
ProgressBar loadingBar = (ProgressBar) findViewById(R.id.libtorrent_progressbar);
@Override
protected void onCancelled() {
running = false;
asyncTaskRunning.remove("load()");
findViewById(R.id.libtorrent_loading).setVisibility(View.GONE);
}
@Override
protected void onPreExecute() {
asyncTaskRunning.add("load()");
findViewById(R.id.libtorrent_loading).setVisibility(View.VISIBLE);
loadingInfo.setText("Loading Video...");
mPlayPause.setVisibility(View.INVISIBLE);
}
@Override
protected void onProgressUpdate(Integer... progress) {
int progressPercentage = progress[0];
// progressPercentageText.setText(progressPercentage + "%");
loadingBar.setProgress(progressPercentage);
setOverlayProgress();
}
@Override
protected Void doInBackground(Void... params) {
int progressSize = 0;
int progressPercentage = 0;
int progressPercentageOld = -1;
while (running) {
progressSize = (int) libTorrent().GetTorrentProgressSize(
contentName);
// for loading screen:
progressPercentage = (int) (((double) progressSize / (double) CHECKSIZEMB) * 100);
if (progressPercentage > progressPercentageOld) {
progressPercentageOld = progressPercentage;
// for debug:
Log.d(TAG,
"ICCT: progress:"
+ progressPercentage
+ "%, have first: "
+ libTorrent().HavePiece(contentName,
firstPieceIndex)
+ ", last-1: "
+ libTorrent().HavePiece(contentName,
lastPieceIndex - 1)
+ ", last: "
+ libTorrent().HavePiece(contentName,
lastPieceIndex));
if (progressPercentage <= 90) {
publishProgress(progressPercentage);
} else {
// cheat to make people think it's not done yet, while
// progresspercentage is above 100%
publishProgress(((int) (90 + (0.03 * progressPercentage))));
}
}
if (isCancelled()) {
running = false;
return null;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
running = false;
return null;
}
if (firstPieceIndex != -1 && lastPieceIndex != -1) {
// if you have enough to buffer and the first piece plus one
// of the two last two pieces must be downloaded:
if ((progressSize >= CHECKSIZEMB
&& libTorrent().HavePiece(contentName,
firstPieceIndex) && (libTorrent()
.HavePiece(contentName, lastPieceIndex) || libTorrent()
.HavePiece(contentName, lastPieceIndex - 1)))
|| progressSize >= (1.7 * CHECKSIZEMB)) {
mLibVLC.play();
mLibVLC.stop();
Log.d(TAG, "lets play");
Log.d(TAG,
"ICCT, length: "
+ mLibVLC.getLength()
+ ", have first: "
+ libTorrent().HavePiece(contentName,
firstPieceIndex)
+ ", last-1: "
+ libTorrent().HavePiece(contentName,
lastPieceIndex - 1)
+ ", last: "
+ libTorrent().HavePiece(contentName,
lastPieceIndex));
mEndReached = false;
return null;
}
}
}
return null;
}
@Override
protected void onPostExecute(Void result) {
Log.v(TAG, "onPostExecute");
findViewById(R.id.libtorrent_loading).setVisibility(View.GONE);
if (running) {
for (int i = 0; i < piecePriorities.length; i++) {
piecePriorities[i] = FilePriority.NORMAL.ordinal();
}
String prios = "firstPiece: " + firstPieceIndex + "\n";
for (int i : piecePriorities) {
prios += "" + i;
}
Log.v(TAG, "PRIOSAFTER: " + prios + "\nLastpieceIndex "
+ lastPieceIndex);
libTorrent().SetPiecePriorities(contentName, piecePriorities);
initCompleted = true;
seekBufferCompleted = true;
startPlayingAfterInit();
}
asyncTaskRunning.remove("load()");
}
}
// TRIBLER
private void startPlayingAfterInit() {
mLibVLC.stop();
// onPause();
// onStop();
// onStart();
Log.d(TAG, "frameskipped enabled: " + mLibVLC.frameSkipEnabled());
onTriblerResume();
}
// TRIBLER
private long getSize(String sizeString) {
sizeString = sizeString.toLowerCase(Locale.ENGLISH);
Log.v(TAG, "getSize(" + sizeString + ")");
long res = 0;
int factor = 1;
if (sizeString.endsWith("kb")) {
sizeString = sizeString.substring(0, sizeString.length() - 2);
factor = 1000;
} else if (sizeString.endsWith("mb")) {
sizeString = sizeString.substring(0, sizeString.length() - 2);
factor = 1000000;
} else if (sizeString.endsWith("gb")) {
sizeString = sizeString.substring(0, sizeString.length() - 2);
factor = 1000000000;
} else if (sizeString.endsWith("b")) {
sizeString = sizeString.substring(0, sizeString.length() - 1);
}
Log.v(TAG, "getSize(" + sizeString + " * " + factor + ")");
try {
res = (long) (Double.parseDouble(sizeString) * factor);
} catch (NumberFormatException e) {
Log.e(TAG, e.getLocalizedMessage());
}
return res;
}
@SuppressWarnings("deprecation")
private int getScreenRotation() {
WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO /*
* Android 2.2
* has
* getRotation
*/) {
try {
Method m = display.getClass().getDeclaredMethod("getRotation");
return (Integer) m.invoke(display);
} catch (Exception e) {
return Surface.ROTATION_0;
}
} else {
return display.getOrientation();
}
}
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
private int getScreenOrientation() {
WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
int rot = getScreenRotation();
/*
* Since getRotation() returns the screen's "natural" orientation, which
* is not guaranteed to be SCREEN_ORIENTATION_PORTRAIT, we have to
* invert the SCREEN_ORIENTATION value if it is "naturally" landscape.
*/
@SuppressWarnings("deprecation")
boolean defaultWide = display.getWidth() > display.getHeight();
if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270)
defaultWide = !defaultWide;
if (defaultWide) {
switch (rot) {
case Surface.ROTATION_0:
return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
case Surface.ROTATION_90:
return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
case Surface.ROTATION_180:
// SCREEN_ORIENTATION_REVERSE_PORTRAIT only available since API
// Level 9+
return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO ? ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
: ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
case Surface.ROTATION_270:
// SCREEN_ORIENTATION_REVERSE_LANDSCAPE only available since API
// Level 9+
return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO ? ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT
: ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
default:
return 0;
}
} else {
switch (rot) {
case Surface.ROTATION_0:
return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
case Surface.ROTATION_90:
return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
case Surface.ROTATION_180:
// SCREEN_ORIENTATION_REVERSE_PORTRAIT only available since API
// Level 9+
return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO ? ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT
: ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
case Surface.ROTATION_270:
// SCREEN_ORIENTATION_REVERSE_LANDSCAPE only available since API
// Level 9+
return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO ? ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
: ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
default:
return 0;
}
}
}
private void setPiecePrioritesInit() {
piecePriorities = libTorrent().GetPiecePriorities(contentName);
firstPieceIndex = -1;
lastPieceIndex = -1;
long firstPieceSize = libTorrent().GetPieceSize(contentName, 0);
int firstNumberOfPieces = 0;
// minimal 18 pieces or 20MB
firstNumberOfPieces = Math
.max(18, (int) (REQUESTSIZE / firstPieceSize));
Log.d(TAG, "firstPieceSize " + firstPieceSize);
Log.d(TAG, "firstNumberOfPieces " + firstNumberOfPieces);
Log.v(TAG, "PRIOSBEFORE: " + getPiecePrioritiesString(piecePriorities));
for (int i = 0; i < piecePriorities.length; i++) {
if (piecePriorities[i] != 0) {
// We're in the file to download
if (firstPieceIndex == -1)
// Found the first piece
firstPieceIndex = i;
if (!initCompleted) {
// only dl the first few pieces if the initial
// stage isn't completed yet
if (i < firstPieceIndex + firstNumberOfPieces) {
piecePriorities[i] = FilePriority.NORMAL.ordinal();
} else {
piecePriorities[i] = FilePriority.DONTDOWNLOAD
.ordinal();
}
} else {
piecePriorities[i] = FilePriority.NORMAL.ordinal();
}
} else {
// We're either before or after the file to download
if (firstPieceIndex != -1)
// After firstPiece
if (lastPieceIndex == -1)
lastPieceIndex = i - 1;
piecePriorities[i] = FilePriority.DONTDOWNLOAD.ordinal();
}
}
if (lastPieceIndex == -1)
lastPieceIndex = piecePriorities.length - 1;
if (!initCompleted
&& (!libTorrent().HavePiece(contentName, lastPieceIndex) && !libTorrent()
.HavePiece(contentName, (lastPieceIndex - 1)))) {
if (lastPieceIndex > -1)
piecePriorities[lastPieceIndex] = FilePriority.MAXPRIORITY
.ordinal();
if (lastPieceIndex - 1 > -1)
piecePriorities[lastPieceIndex - 1] = FilePriority.MAXPRIORITY
.ordinal();
}
Log.d(TAG, "LastPiece Index: " + lastPieceIndex + " / "
+ (piecePriorities.length - 1));
String prios = "firstPiece: " + firstPieceIndex + "\n";
prios += getPiecePrioritiesString(piecePriorities);
Log.v(TAG, "PRIOSAFTER: " + prios + "\nLastpieceIndex "
+ lastPieceIndex);
libTorrent().SetPiecePriorities(contentName, piecePriorities);
}
public void showAdvancedOptions(View v) {
CommonDialogs.advancedOptions(this, v, MenuType.Video);
}
// Tribler
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK)) {
finish();
}
return super.onKeyDown(keyCode, event);
}
}