diff --git a/README.md b/README.md
index 8659503..16e2a42 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,8 @@
-
+
+
## Features
@@ -15,6 +16,7 @@
- [X] Trending Videos
- [X] Endless scrolling
- [X] Pull to refresh
+- [X] Very Basic Torrent playback
- [ ] Video Playback via WebRTC
- [ ] Pick Server
- [ ] Login
diff --git a/Screenshot.png b/Screenshot.png
deleted file mode 100644
index 3b93c99..0000000
Binary files a/Screenshot.png and /dev/null differ
diff --git a/Screenshot1.png b/Screenshot1.png
new file mode 100644
index 0000000..a962100
Binary files /dev/null and b/Screenshot1.png differ
diff --git a/Screenshot2.png b/Screenshot2.png
new file mode 100644
index 0000000..180116c
Binary files /dev/null and b/Screenshot2.png differ
diff --git a/app/build.gradle b/app/build.gradle
index 7ae9a98..1162fec 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -38,11 +38,15 @@ dependencies {
implementation 'com.google.code.gson:gson:2.8.2'
- implementation 'org.webrtc:google-webrtc:1.0.+'
+// implementation 'org.webrtc:google-webrtc:1.0.+'
implementation 'com.android.support:design:27.1.0'
implementation 'com.blackboardtheory:android-iconify-fontawesome:3.0.1-SNAPSHOT'
+ implementation 'com.github.TorrentStream:TorrentStream-Android:2.5.0'
+ implementation 'com.google.android.exoplayer:exoplayer:2.7.0'
+// implementation "com.github.TorrentStream:TorrentStreamServer-Android:1.0.1"
+// implementation 'com.devbrackets.android:exomedia:4.1.0'
implementation 'com.android.support:support-v4:27.1.0'
testImplementation 'junit:junit:4.12'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 825e7d4..dc7caf1 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -8,6 +8,7 @@
+
+ android:label="@string/title_activity_login" />
+ android:name=".activity.TorrentVideoPlayActivity"
+ android:label="@string/title_activity_torrent_video_play"
+ android:theme="@style/AppTheme" />
+
\ No newline at end of file
diff --git a/app/src/main/java/net/schueller/peertube/activity/AppCompatPreferenceActivity.java b/app/src/main/java/net/schueller/peertube/activity/AppCompatPreferenceActivity.java
new file mode 100644
index 0000000..1ea9f48
--- /dev/null
+++ b/app/src/main/java/net/schueller/peertube/activity/AppCompatPreferenceActivity.java
@@ -0,0 +1,109 @@
+package net.schueller.peertube.activity;
+
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.Nullable;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatDelegate;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A {@link android.preference.PreferenceActivity} which implements and proxies the necessary calls
+ * to be used with AppCompat.
+ */
+public abstract class AppCompatPreferenceActivity extends PreferenceActivity {
+
+ private AppCompatDelegate mDelegate;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ getDelegate().installViewFactory();
+ getDelegate().onCreate(savedInstanceState);
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+ getDelegate().onPostCreate(savedInstanceState);
+ }
+
+ public ActionBar getSupportActionBar() {
+ return getDelegate().getSupportActionBar();
+ }
+
+ public void setSupportActionBar(@Nullable Toolbar toolbar) {
+ getDelegate().setSupportActionBar(toolbar);
+ }
+
+ @Override
+ public MenuInflater getMenuInflater() {
+ return getDelegate().getMenuInflater();
+ }
+
+ @Override
+ public void setContentView(@LayoutRes int layoutResID) {
+ getDelegate().setContentView(layoutResID);
+ }
+
+ @Override
+ public void setContentView(View view) {
+ getDelegate().setContentView(view);
+ }
+
+ @Override
+ public void setContentView(View view, ViewGroup.LayoutParams params) {
+ getDelegate().setContentView(view, params);
+ }
+
+ @Override
+ public void addContentView(View view, ViewGroup.LayoutParams params) {
+ getDelegate().addContentView(view, params);
+ }
+
+ @Override
+ protected void onPostResume() {
+ super.onPostResume();
+ getDelegate().onPostResume();
+ }
+
+ @Override
+ protected void onTitleChanged(CharSequence title, int color) {
+ super.onTitleChanged(title, color);
+ getDelegate().setTitle(title);
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ getDelegate().onConfigurationChanged(newConfig);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ getDelegate().onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ getDelegate().onDestroy();
+ }
+
+ public void invalidateOptionsMenu() {
+ getDelegate().invalidateOptionsMenu();
+ }
+
+ private AppCompatDelegate getDelegate() {
+ if (mDelegate == null) {
+ mDelegate = AppCompatDelegate.create(this, null);
+ }
+ return mDelegate;
+ }
+}
diff --git a/app/src/main/java/net/schueller/peertube/activity/SettingsActivity.java b/app/src/main/java/net/schueller/peertube/activity/SettingsActivity.java
new file mode 100644
index 0000000..c35773f
--- /dev/null
+++ b/app/src/main/java/net/schueller/peertube/activity/SettingsActivity.java
@@ -0,0 +1,152 @@
+package net.schueller.peertube.activity;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+
+import android.os.Build;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.support.v7.app.ActionBar;
+import android.preference.PreferenceFragment;
+import android.preference.PreferenceManager;
+
+import android.view.MenuItem;
+
+
+import net.schueller.peertube.R;
+
+import java.util.List;
+
+
+public class SettingsActivity extends AppCompatPreferenceActivity {
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ this.finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ /**
+ * A preference value change listener that updates the preference's summary
+ * to reflect its new value.
+ */
+ private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = (preference, value) -> {
+ String stringValue = value.toString();
+
+ preference.setSummary(stringValue);
+
+ return true;
+ };
+
+ /**
+ * Helper method to determine if the device has an extra-large screen. For
+ * example, 10" tablets are extra-large.
+ */
+ private static boolean isXLargeTablet(Context context) {
+ return (context.getResources().getConfiguration().screenLayout
+ & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE;
+ }
+
+ /**
+ * Binds a preference's summary to its value. More specifically, when the
+ * preference's value is changed, its summary (line of text below the
+ * preference title) is updated to reflect the value. The summary is also
+ * immediately updated upon calling this method. The exact display format is
+ * dependent on the type of preference.
+ *
+ * @see #sBindPreferenceSummaryToValueListener
+ */
+ private static void bindPreferenceSummaryToValue(Preference preference) {
+ // Set the listener to watch for value changes.
+ preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);
+
+ // Trigger the listener immediately with the preference's
+ // current value.
+ sBindPreferenceSummaryToValueListener.onPreferenceChange(preference,
+ PreferenceManager
+ .getDefaultSharedPreferences(preference.getContext())
+ .getString(preference.getKey(), ""));
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setupActionBar();
+ getFragmentManager().beginTransaction().replace(android.R.id.content, new GeneralPreferenceFragment()).commit();
+
+ }
+
+ /**
+ * Set up the {@link android.app.ActionBar}, if the API is available.
+ */
+ private void setupActionBar() {
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ // Show the Up button in the action bar.
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onIsMultiPane() {
+ return isXLargeTablet(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public void onBuildHeaders(List target) {
+ //loadHeadersFromResource(R.xml.pref_headers, target);
+ }
+
+ /**
+ * This method stops fragment injection in malicious applications.
+ * Make sure to deny any unknown fragments here.
+ */
+ protected boolean isValidFragment(String fragmentName) {
+ return PreferenceFragment.class.getName().equals(fragmentName)
+ || GeneralPreferenceFragment.class.getName().equals(fragmentName);
+ }
+
+ /**
+ * This fragment shows general preferences only. It is used when the
+ * activity is showing a two-pane mainmenu UI.
+ */
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public static class GeneralPreferenceFragment extends PreferenceFragment {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.pref_general);
+ setHasOptionsMenu(true);
+
+ // Bind the summaries of EditText/List/Dialog/Ringtone preferences
+ // to their values. When their values change, their summaries are
+ // updated to reflect the new value, per the Android Design
+ // guidelines.
+ bindPreferenceSummaryToValue(findPreference("pref_api_base"));
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ int id = item.getItemId();
+ if (id == android.R.id.home) {
+ startActivity(new Intent(getActivity(), SettingsActivity.class));
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/net/schueller/peertube/activity/TorrentVideoPlayActivity.java b/app/src/main/java/net/schueller/peertube/activity/TorrentVideoPlayActivity.java
new file mode 100644
index 0000000..3cb6451
--- /dev/null
+++ b/app/src/main/java/net/schueller/peertube/activity/TorrentVideoPlayActivity.java
@@ -0,0 +1,217 @@
+package net.schueller.peertube.activity;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+
+import android.os.Environment;
+import android.support.annotation.NonNull;
+import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.github.se_bastiaan.torrentstream.StreamStatus;
+import com.github.se_bastiaan.torrentstream.Torrent;
+import com.github.se_bastiaan.torrentstream.TorrentOptions;
+import com.github.se_bastiaan.torrentstream.TorrentStream;
+import com.github.se_bastiaan.torrentstream.listeners.TorrentListener;
+import com.google.android.exoplayer2.ExoPlayerFactory;
+import com.google.android.exoplayer2.SimpleExoPlayer;
+import com.google.android.exoplayer2.source.ExtractorMediaSource;
+import com.google.android.exoplayer2.source.MediaSource;
+import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
+import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
+import com.google.android.exoplayer2.trackselection.TrackSelection;
+import com.google.android.exoplayer2.trackselection.TrackSelector;
+import com.google.android.exoplayer2.ui.PlayerView;
+import com.google.android.exoplayer2.upstream.BandwidthMeter;
+import com.google.android.exoplayer2.upstream.DataSource;
+import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
+import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
+import com.google.android.exoplayer2.util.Util;
+
+import net.schueller.peertube.R;
+import net.schueller.peertube.helper.APIUrlHelper;
+import net.schueller.peertube.helper.MetaDataHelper;
+import net.schueller.peertube.model.Video;
+
+import net.schueller.peertube.network.GetVideoDataService;
+import net.schueller.peertube.network.RetrofitInstance;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+public class TorrentVideoPlayActivity extends AppCompatActivity {
+
+ private static final String TAG = "TorrentVideoPlayActivity";
+
+ private ProgressBar progressBar;
+ private SimpleExoPlayer player;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_torrent_video_play);
+
+ // get video ID
+ Intent intent = getIntent();
+ String videoID = intent.getStringExtra(VideoListActivity.EXTRA_VIDEOID);
+ Log.v(TAG, "click: " + videoID);
+
+ progressBar = findViewById(R.id.progress);
+ progressBar.setMax(100);
+
+ PlayerView videoView = findViewById(R.id.video_view);
+
+ // 1. Create a default TrackSelector
+ BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
+ TrackSelection.Factory videoTrackSelectionFactory =
+ new AdaptiveTrackSelection.Factory(bandwidthMeter);
+ TrackSelector trackSelector =
+ new DefaultTrackSelector(videoTrackSelectionFactory);
+
+ // 2. Create the player
+ player = ExoPlayerFactory.newSimpleInstance(getApplicationContext(), trackSelector);
+ videoView.setPlayer(player);
+
+
+ TorrentOptions torrentOptions = new TorrentOptions.Builder()
+ .saveLocation(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS))
+ .removeFilesAfterStop(true)
+ .build();
+
+ TorrentStream torrentStream = TorrentStream.init(torrentOptions);
+
+ torrentStream.addListener(new TorrentListener() {
+ @Override
+ public void onStreamReady(Torrent torrent) {
+ Log.d(TAG, "Ready");
+
+ setupVideoView(torrent);
+
+ }
+
+ @Override
+ public void onStreamProgress(Torrent torrent, StreamStatus streamStatus) {
+ if(streamStatus.bufferProgress <= 100 && progressBar.getProgress() < 100 && progressBar.getProgress() != streamStatus.bufferProgress) {
+ //Log.d(TAG, "Progress: " + streamStatus.bufferProgress);
+ progressBar.setProgress(streamStatus.bufferProgress);
+ }
+ }
+
+ @Override
+ public void onStreamStopped() {
+ Log.d(TAG, "Stopped");
+ }
+
+ @Override
+ public void onStreamPrepared(Torrent torrent) {
+ Log.d(TAG, "Prepared");
+ }
+
+ @Override
+ public void onStreamStarted(Torrent torrent) {
+ Log.d(TAG, "Started");
+ }
+
+ @Override
+ public void onStreamError(Torrent torrent, Exception e) {
+ Log.d(TAG, "Error: " + e.getMessage());
+ }
+
+ });
+
+ // get video details from api
+ String apiBaseURL = APIUrlHelper.getUrl(this);
+ GetVideoDataService service = RetrofitInstance.getRetrofitInstance(apiBaseURL + "/api/v1/").create(GetVideoDataService.class);
+
+ Call