/*
* This file is part of Popcorn Time.
*
* Popcorn Time 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 3 of the License, or
* (at your option) any later version.
*
* Popcorn Time 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 Popcorn Time. If not, see <http://www.gnu.org/licenses/>.
*/
package pct.droid.base.torrent;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import android.os.PowerManager;
import android.support.annotation.NonNull;
import android.support.v4.app.NotificationCompat;
import com.github.sv244.torrentstream.StreamStatus;
import com.github.sv244.torrentstream.Torrent;
import com.github.sv244.torrentstream.TorrentOptions;
import com.github.sv244.torrentstream.TorrentStream;
import com.github.sv244.torrentstream.listeners.TorrentListener;
import com.sjl.foreground.Foreground;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import pct.droid.base.PopcornApplication;
import pct.droid.base.R;
import pct.droid.base.activities.TorrentActivity;
import pct.droid.base.content.preferences.Prefs;
import pct.droid.base.utils.PrefUtils;
import timber.log.Timber;
public class TorrentService extends Service implements TorrentListener {
public static final Integer NOTIFICATION_ID = 3423423;
private static String WAKE_LOCK = "TorrentService_WakeLock";
private static TorrentService sThis;
private TorrentStream mTorrentStream;
private Torrent mCurrentTorrent;
private StreamStatus mStreamStatus;
private boolean mInForeground = false, mIsReady = false, mStopped = false;
private IBinder mBinder = new ServiceBinder();
private List<TorrentListener> mListener = new ArrayList<>();
private PowerManager.WakeLock mWakeLock;
private Class mCurrentActivityClass;
private Timer mUpdateTimer;
public class ServiceBinder extends Binder {
public TorrentService getService() {
return TorrentService.this;
}
}
@Override
public void onCreate() {
super.onCreate();
sThis = this;
Foreground.get().addListener(mForegroundListener);
TorrentOptions options = new TorrentOptions();
options.setRemoveFilesAfterStop(true);
options.setMaxConnections(PrefUtils.get(this, Prefs.LIBTORRENT_CONNECTION_LIMIT, 200));
options.setMaxDownloadSpeed(PrefUtils.get(this, Prefs.LIBTORRENT_DOWNLOAD_LIMIT, 0));
options.setMaxUploadSpeed(PrefUtils.get(this, Prefs.LIBTORRENT_UPLOAD_LIMIT, 0));
options.setSaveLocation(PrefUtils.get(this, Prefs.STORAGE_LOCATION, PopcornApplication.getStreamDir()));
mTorrentStream = TorrentStream.init(options);
}
@Override
public void onDestroy() {
super.onDestroy();
Timber.d("onDestroy");
if (mWakeLock != null && mWakeLock.isHeld())
mWakeLock.release();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Timber.d("onStartCommand");
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
Timber.d("onBind");
if(mInForeground) {
stopForeground();
}
return mBinder;
}
@Override
public void onRebind(Intent intent) {
super.onRebind(intent);
Timber.d("onRebind");
if(mInForeground) {
stopForeground();
}
}
public void setCurrentActivity(TorrentActivity activity) {
mCurrentActivityClass = activity.getClass();
if(mInForeground) {
stopForeground();
startForeground();
}
}
public void startForeground() {
if (Foreground.get().isForeground()) return;
if (mCurrentActivityClass == null) return;
Intent notificationIntent = new Intent(this, mCurrentActivityClass);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Intent stopIntent = new Intent();
stopIntent.setAction(TorrentBroadcastReceiver.STOP);
PendingIntent pendingStopIntent = PendingIntent.getBroadcast(this, TorrentBroadcastReceiver.REQUEST_CODE, stopIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Action stopAction = new NotificationCompat.Action.Builder(R.drawable.abc_ic_clear_mtrl_alpha, getString(R.string.stop), pendingStopIntent).build();
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_notif_logo)
.setContentTitle("Popcorn Time - " + getString(pct.droid.base.R.string.running))
.setContentText(getString(R.string.tap_to_resume))
.setOngoing(true)
.setOnlyAlertOnce(true)
.setPriority(Notification.PRIORITY_LOW)
.setContentIntent(pendingIntent)
.addAction(stopAction)
.setCategory(NotificationCompat.CATEGORY_SERVICE);
if(mStreamStatus != null && mIsReady) {
String downloadSpeed;
DecimalFormat df = new DecimalFormat("#############0.00");
if (mStreamStatus.downloadSpeed / 1024 < 1000) {
downloadSpeed = df.format(mStreamStatus.downloadSpeed / 1024) + " KB/s";
} else {
downloadSpeed = df.format(mStreamStatus.downloadSpeed / (1024 * 1024)) + " MB/s";
}
String progress = df.format(mStreamStatus.progress);
builder.setContentText(progress + "%, ↓" + downloadSpeed);
}
Notification notification = builder.build();
NotificationManager notifManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notifManager.notify(NOTIFICATION_ID, notification);
startForeground(NOTIFICATION_ID, notification);
if(mUpdateTimer == null) {
mUpdateTimer = new Timer();
mUpdateTimer.scheduleAtFixedRate(new UpdateTask(), 5000, 5000);
}
}
public void stopForeground() {
stopForeground(true);
if(mUpdateTimer != null) {
mUpdateTimer.cancel();
mUpdateTimer.purge();
mUpdateTimer = null;
}
}
public void streamTorrent(@NonNull final String torrentUrl) {
Timber.d("streamTorrent");
mStopped = false;
if (mTorrentStream.isStreaming()) return;
Timber.d("Starting streaming");
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
if(mWakeLock != null && mWakeLock.isHeld()) {
mWakeLock.release();
mWakeLock = null;
}
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKE_LOCK);
mWakeLock.acquire();
TorrentOptions options = mTorrentStream.getOptions();
options.setRemoveFilesAfterStop(true);
options.setMaxConnections(PrefUtils.get(this, Prefs.LIBTORRENT_CONNECTION_LIMIT, 200));
options.setMaxDownloadSpeed(PrefUtils.get(this, Prefs.LIBTORRENT_DOWNLOAD_LIMIT, 0));
options.setMaxUploadSpeed(PrefUtils.get(this, Prefs.LIBTORRENT_UPLOAD_LIMIT, 0));
options.setSaveLocation(PrefUtils.get(this, Prefs.STORAGE_LOCATION, PopcornApplication.getStreamDir()));
mTorrentStream.setOptions(options);
mIsReady = false;
mTorrentStream.addListener(this);
mTorrentStream.startStream(torrentUrl);
}
public void stopStreaming() {
mStopped = true;
mTorrentStream.removeListener(this);
if (mWakeLock != null && mWakeLock.isHeld())
mWakeLock.release();
if(!mTorrentStream.isStreaming())
return;
stopForeground();
mTorrentStream.stopStream();
mIsReady = false;
Timber.d("Stopped torrent and removed files if possible");
}
public boolean isStreaming() {
return mTorrentStream.isStreaming();
}
public boolean isReady() {
return mIsReady;
}
public boolean checkStopped() {
if(mStopped) {
mStopped = false;
return true;
}
return false;
}
public void addListener(@NonNull TorrentListener listener) {
mListener.add(listener);
}
public void removeListener(@NonNull TorrentListener listener) {
mListener.remove(listener);
}
public static void bindHere(Context context, ServiceConnection serviceConnection) {
Intent torrentServiceIntent = new Intent(context, TorrentService.class);
context.bindService(torrentServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}
public static void start(Context context) {
Intent torrentServiceIntent = new Intent(context, TorrentService.class);
context.startService(torrentServiceIntent);
}
protected static void stop() {
sThis.stopStreaming();
}
private Foreground.Listener mForegroundListener = new Foreground.Listener() {
@Override
public void onBecameForeground() {
if (!mTorrentStream.isStreaming()) {
mTorrentStream.resumeSession();
} else {
mInForeground = false;
stopForeground();
}
}
@Override
public void onBecameBackground() {
if (!mTorrentStream.isStreaming()) {
mTorrentStream.pauseSession();
} else {
mInForeground = true;
startForeground();
}
}
};
public Torrent getCurrentTorrent() {
return mCurrentTorrent;
}
public String getCurrentTorrentUrl() {
return mTorrentStream.getCurrentTorrentUrl();
}
@Override
public void onStreamPrepared(Torrent torrent) {
mCurrentTorrent = torrent;
for(TorrentListener listener : mListener) {
listener.onStreamPrepared(torrent);
}
}
@Override
public void onStreamStarted(Torrent torrent) {
for(TorrentListener listener : mListener) {
listener.onStreamStarted(torrent);
}
}
@Override
public void onStreamError(Torrent torrent, Exception e) {
for(TorrentListener listener : mListener) {
listener.onStreamError(torrent, e);
}
}
@Override
public void onStreamReady(Torrent torrent) {
mCurrentTorrent = torrent;
mIsReady = true;
for(TorrentListener listener : mListener) {
listener.onStreamReady(torrent);
}
}
@Override
public void onStreamProgress(Torrent torrent, StreamStatus streamStatus) {
for(TorrentListener listener : mListener) {
if (null != listener) {
listener.onStreamProgress(torrent, streamStatus);
}
}
if(mInForeground) {
mStreamStatus = streamStatus;
}
}
@Override
public void onStreamStopped() {
for(TorrentListener listener : mListener) {
if (listener != null) {
listener.onStreamStopped();
}
}
}
private class UpdateTask extends TimerTask {
@Override
public void run() {
if(mInForeground) {
startForeground();
} else {
stopForeground();
}
}
};
}