diff --git a/README.md b/README.md
index 101c194..dd4fc90 100644
--- a/README.md
+++ b/README.md
@@ -6,8 +6,9 @@
-
-
+
+
+
## Download
@@ -23,16 +24,19 @@ Beta Test on Google Play: https://play.google.com/store/apps/details?id=net.schu
- [X] Change Server
- [X] Search
- [X] App Icon and assets
+- [X] Themes / Dark mode
+- [X] Background playback
+- [X] NSFW Filter option
+
+## TODO
-# TODO
- [ ] Video Playback via WebRTC
-- [ ] Login
+- [ ] Authentication / Login
- [ ] Like/dislike video
- [ ] Comment video
- [ ] Unit Tests
-
- [ ] Lots more missing at this point...
diff --git a/Screenshot_1545425431.png b/Screenshot_1545425431.png
new file mode 100644
index 0000000..2d7c0f9
Binary files /dev/null and b/Screenshot_1545425431.png differ
diff --git a/Screenshot_1545425504.png b/Screenshot_1545425504.png
new file mode 100644
index 0000000..586bebc
Binary files /dev/null and b/Screenshot_1545425504.png differ
diff --git a/Screenshot_1545425516.png b/Screenshot_1545425516.png
new file mode 100644
index 0000000..a1ae020
Binary files /dev/null and b/Screenshot_1545425516.png differ
diff --git a/app/build.gradle b/app/build.gradle
index ac5af8a..4ecb7fb 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -6,26 +6,24 @@ android {
applicationId "net.schueller.peertube"
minSdkVersion 21
targetSdkVersion 28
- versionCode 1010
- versionName "1.0.10"
- testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ versionCode 1015
+ versionName "1.0.15"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
// Layouts and design
- implementation 'com.android.support.constraint:constraint-layout:1.1.3'
- implementation 'com.android.support:appcompat-v7:28.0.0'
- implementation 'com.android.support:cardview-v7:28.0.0'
- implementation 'com.android.support:recyclerview-v7:28.0.0'
- implementation 'com.android.support:design:28.0.0'
+ implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+ implementation 'androidx.appcompat:appcompat:1.0.2'
+ implementation 'androidx.cardview:cardview:1.0.0'
+ implementation 'androidx.recyclerview:recyclerview:1.0.0'
+ implementation 'androidx.legacy:legacy-support-v13:1.0.0'
+ implementation 'com.google.android.material:material:1.0.0'
implementation 'de.hdodenhof:circleimageview:2.2.0'
- implementation 'com.android.support:support-v13:28.0.0'
// font awesome
- implementation 'com.blackboardtheory:android-iconify-fontawesome:3.0.1-SNAPSHOT'
-
- // BottomNavigationViewEx -> https://github.com/ittianyu/BottomNavigationViewEx
- implementation 'com.github.ittianyu:BottomNavigationViewEx:2.0.2'
+ implementation "com.mikepenz:iconics-core:3.1.0"
+ implementation 'com.mikepenz:fontawesome-typeface:5.3.1.1@aar'
// http client / REST
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
@@ -49,12 +47,12 @@ android {
implementation 'com.google.android.exoplayer:exoplayer-ui:2.9.2'
implementation 'com.google.android.exoplayer:exoplayer-hls:2.9.2'
implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.9.2'
-// implementation 'com.devbrackets.android:exomedia:4.1.0'
+ implementation 'com.google.android.exoplayer:extension-mediasession:2.9.2'
// testing
testImplementation 'junit:junit:4.12'
- androidTestImplementation 'com.android.support.test:runner:1.0.2'
- androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
+ androidTestImplementation 'androidx.test:runner:1.1.1'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}
}
buildTypes {
diff --git a/app/src/androidTest/java/net/schueller/peertube/ExampleInstrumentedTest.java b/app/src/androidTest/java/net/schueller/peertube/ExampleInstrumentedTest.java
index 1079623..0c0fe04 100644
--- a/app/src/androidTest/java/net/schueller/peertube/ExampleInstrumentedTest.java
+++ b/app/src/androidTest/java/net/schueller/peertube/ExampleInstrumentedTest.java
@@ -1,8 +1,8 @@
package net.schueller.peertube;
import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d56e56e..5aa5606 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -19,33 +19,41 @@
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
-
-
-
+ android:resource="@xml/searchable">
+
+ android:launchMode="singleTop" />
+
+
+
+
-
-
\ 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
index 8675d18..f4ef791 100644
--- a/app/src/main/java/net/schueller/peertube/activity/AppCompatPreferenceActivity.java
+++ b/app/src/main/java/net/schueller/peertube/activity/AppCompatPreferenceActivity.java
@@ -3,11 +3,10 @@ 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 androidx.annotation.LayoutRes;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatDelegate;
+
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
diff --git a/app/src/main/java/net/schueller/peertube/activity/LoginActivity.java b/app/src/main/java/net/schueller/peertube/activity/LoginActivity.java
index 40d44df..bbc5e96 100644
--- a/app/src/main/java/net/schueller/peertube/activity/LoginActivity.java
+++ b/app/src/main/java/net/schueller/peertube/activity/LoginActivity.java
@@ -21,6 +21,9 @@ import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
+import static net.schueller.peertube.helper.Constants.DEFAULT_THEME;
+import static net.schueller.peertube.helper.Constants.THEME_PREF_KEY;
+
public class LoginActivity extends AppCompatActivity {
private String TAG = "LoginActivity";
@@ -32,6 +35,15 @@ public class LoginActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ // Set theme
+ SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
+ setTheme(getResources().getIdentifier(
+ sharedPref.getString(THEME_PREF_KEY, DEFAULT_THEME),
+ "style",
+ getPackageName())
+ );
+
setContentView(R.layout.activity_login);
// bind button click
diff --git a/app/src/main/java/net/schueller/peertube/activity/SelectServerActivity.java b/app/src/main/java/net/schueller/peertube/activity/SelectServerActivity.java
index 9c06c8d..5f15750 100644
--- a/app/src/main/java/net/schueller/peertube/activity/SelectServerActivity.java
+++ b/app/src/main/java/net/schueller/peertube/activity/SelectServerActivity.java
@@ -1,7 +1,7 @@
package net.schueller.peertube.activity;
-import android.support.annotation.NonNull;
-import android.support.v7.app.AppCompatActivity;
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import net.schueller.peertube.R;
diff --git a/app/src/main/java/net/schueller/peertube/activity/SettingsActivity.java b/app/src/main/java/net/schueller/peertube/activity/SettingsActivity.java
index f4070fd..0cb0211 100644
--- a/app/src/main/java/net/schueller/peertube/activity/SettingsActivity.java
+++ b/app/src/main/java/net/schueller/peertube/activity/SettingsActivity.java
@@ -3,11 +3,12 @@ package net.schueller.peertube.activity;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.preference.Preference;
-import android.support.v7.app.ActionBar;
+import androidx.appcompat.app.ActionBar;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import android.util.Patterns;
@@ -17,9 +18,13 @@ import android.widget.Toast;
import net.schueller.peertube.R;
import java.util.List;
+import static net.schueller.peertube.helper.Constants.DEFAULT_THEME;
+import static net.schueller.peertube.helper.Constants.THEME_PREF_KEY;
public class SettingsActivity extends AppCompatPreferenceActivity {
+ private static String previousThemeColorValue = "";
+
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
@@ -30,6 +35,21 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
return super.onOptionsItemSelected(item);
}
+ private static String getSelectedColor(Context context, String colorId){
+
+ String res = "Color not found";
+ String [ ] themeArray = context.getResources().getStringArray(R.array.themeValues);
+ String [ ] colorArray = context.getResources().getStringArray(R.array.themeArray);
+
+ for (int i = 0 ; i < themeArray.length ; i++){
+ if (themeArray[i].equals(colorId)){
+ res = colorArray[i];
+ break;
+ }
+ }
+ return res;
+ }
+
/**
* A preference value change listener that updates the preference's summary
* to reflect its new value.
@@ -42,6 +62,19 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
Toast.makeText(preference.getContext(), R.string.invalid_url, Toast.LENGTH_LONG).show();
return false;
}
+ // Check if Theme color has change & Provide selected color
+ else if (preference.getKey().equals("pref_theme")) {
+
+ stringValue = getSelectedColor(preference.getContext(), stringValue);
+
+ if (!previousThemeColorValue.equals("") && !previousThemeColorValue.equals(stringValue)) {
+ Toast.makeText(preference.getContext(), R.string.pref_description_app_theme, Toast.LENGTH_LONG).show();
+ }
+
+ previousThemeColorValue = stringValue;
+ preference.setSummary(stringValue);
+ return true;
+ }
preference.setSummary(stringValue);
@@ -80,10 +113,19 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
+
+ // Set theme
+ SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
+ setTheme(getResources().getIdentifier(
+ sharedPref.getString(THEME_PREF_KEY, DEFAULT_THEME),
+ "style",
+ getPackageName())
+ );
+
super.onCreate(savedInstanceState);
+
setupActionBar();
getFragmentManager().beginTransaction().replace(android.R.id.content, new GeneralPreferenceFragment()).commit();
-
}
/**
@@ -140,6 +182,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
// updated to reflect the new value, per the Android Design
// guidelines.
bindPreferenceSummaryToValue(findPreference("pref_api_base"));
+ bindPreferenceSummaryToValue(findPreference("pref_theme"));
}
@Override
diff --git a/app/src/main/java/net/schueller/peertube/activity/VideoListActivity.java b/app/src/main/java/net/schueller/peertube/activity/VideoListActivity.java
index 697a4b2..67514c3 100644
--- a/app/src/main/java/net/schueller/peertube/activity/VideoListActivity.java
+++ b/app/src/main/java/net/schueller/peertube/activity/VideoListActivity.java
@@ -8,17 +8,20 @@ import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.preference.PreferenceManager;
import android.provider.SearchRecentSuggestions;
-import android.support.annotation.NonNull;
-import android.support.design.bottomnavigation.LabelVisibilityMode;
-import android.support.v4.app.ActivityCompat;
-import android.support.v4.widget.SwipeRefreshLayout;
-import android.support.v7.app.AppCompatActivity;
+import androidx.annotation.NonNull;
+
+import com.google.android.material.bottomnavigation.BottomNavigationView;
+import com.google.android.material.bottomnavigation.LabelVisibilityMode;
+import androidx.core.app.ActivityCompat;
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.SearchView;
-import android.support.v7.widget.Toolbar;
+import androidx.appcompat.app.AppCompatDelegate;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.appcompat.widget.SearchView;
+import androidx.appcompat.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
@@ -26,17 +29,8 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
-
-//import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
-//import com.google.android.gms.common.GooglePlayServicesRepairableException;
-//import com.google.android.gms.common.GooglePlayServicesUtil;
-//import com.google.android.gms.security.ProviderInstaller;
-import com.ittianyu.bottomnavigationviewex.BottomNavigationViewEx;
-import com.joanzapata.iconify.IconDrawable;
-import com.joanzapata.iconify.Iconify;
-import com.joanzapata.iconify.fonts.FontAwesomeIcons;
-import com.joanzapata.iconify.fonts.FontAwesomeModule;
-
+import com.mikepenz.fontawesome_typeface_library.FontAwesome;
+import com.mikepenz.iconics.IconicsDrawable;
import net.schueller.peertube.R;
import net.schueller.peertube.adapter.VideoAdapter;
import net.schueller.peertube.helper.APIUrlHelper;
@@ -44,6 +38,7 @@ import net.schueller.peertube.model.VideoList;
import net.schueller.peertube.network.GetVideoDataService;
import net.schueller.peertube.network.RetrofitInstance;
import net.schueller.peertube.provider.SearchSuggestionsProvider;
+import net.schueller.peertube.service.VideoPlayerService;
import java.util.ArrayList;
@@ -52,6 +47,9 @@ import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
+import static net.schueller.peertube.helper.Constants.DEFAULT_THEME;
+import static net.schueller.peertube.helper.Constants.THEME_PREF_KEY;
+
public class VideoListActivity extends AppCompatActivity {
private String TAG = "VideoListActivity";
@@ -72,85 +70,35 @@ public class VideoListActivity extends AppCompatActivity {
private boolean isLoading = false;
- private BottomNavigationViewEx.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
- = item -> {
- switch (item.getItemId()) {
- case R.id.navigation_home:
- //Log.v(TAG, "navigation_home");
-
- if (!isLoading) {
- sort = "-createdAt";
- currentStart = 0;
- loadVideos(currentStart, count, sort, filter);
- }
-
- return true;
- case R.id.navigation_trending:
- //Log.v(TAG, "navigation_trending");
-
- if (!isLoading) {
- sort = "-trending";
- currentStart = 0;
- loadVideos(currentStart, count, sort, filter);
- }
-
- return true;
- case R.id.navigation_subscriptions:
- //Log.v(TAG, "navigation_subscriptions");
- Toast.makeText(VideoListActivity.this, "Subscriptions Not Implemented", Toast.LENGTH_SHORT).show();
-
- return false;
-
- case R.id.navigation_account:
- //Log.v(TAG, "navigation_account");
-// Toast.makeText(VideoListActivity.this, "Account Not Implemented", Toast.LENGTH_SHORT).show();
-
- Intent intent = new Intent(this, UserActivity.class);
- this.startActivity(intent);
-
- return false;
- }
- return false;
- };
@Override
protected void onCreate(Bundle savedInstanceState) {
+
super.onCreate(savedInstanceState);
+
+ // Set Night Mode
+ SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
+ AppCompatDelegate.setDefaultNightMode(sharedPref.getBoolean("pref_dark_mode", false) ?
+ AppCompatDelegate.MODE_NIGHT_YES : AppCompatDelegate.MODE_NIGHT_NO);
+
+ // Set theme
+ setTheme(getResources().getIdentifier(
+ sharedPref.getString(THEME_PREF_KEY, DEFAULT_THEME),
+ "style",
+ getPackageName())
+ );
+
setContentView(R.layout.activity_video_list);
filter = "";
- // Init icons
- Iconify.with(new FontAwesomeModule());
+ createBottomBarNavigation();
// Attaching the layout to the toolbar object
Toolbar toolbar = findViewById(R.id.tool_bar);
// Setting toolbar as the ActionBar with setSupportActionBar() call
setSupportActionBar(toolbar);
- // fix android trying to use SSLv3 for handshake
-// updateAndroidSecurityProvider(this);
-
- // Bottom Navigation
- BottomNavigationViewEx navigation = findViewById(R.id.navigation);
-
- navigation.enableAnimation(false);
- navigation.setLabelVisibilityMode(LabelVisibilityMode.LABEL_VISIBILITY_LABELED); // enableShiftingMode
- navigation.setItemHorizontalTranslationEnabled(false); // enableItemShiftingMode
-
- Menu navMenu = navigation.getMenu();
- navMenu.findItem(R.id.navigation_home).setIcon(
- new IconDrawable(this, FontAwesomeIcons.fa_home));
- navMenu.findItem(R.id.navigation_trending).setIcon(
- new IconDrawable(this, FontAwesomeIcons.fa_fire));
- navMenu.findItem(R.id.navigation_subscriptions).setIcon(
- new IconDrawable(this, FontAwesomeIcons.fa_folder));
- navMenu.findItem(R.id.navigation_account).setIcon(
- new IconDrawable(this, FontAwesomeIcons.fa_user_circle));
-
- navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
-
-
// load Video List
createList();
@@ -163,16 +111,12 @@ public class VideoListActivity extends AppCompatActivity {
// Set an icon in the ActionBar
menu.findItem(R.id.action_settings).setIcon(
- new IconDrawable(this, FontAwesomeIcons.fa_cog)
- .colorRes(R.color.cardview_light_background)
- .actionBarSize());
-
+ new IconicsDrawable(this, FontAwesome.Icon.faw_cog).actionBar());
MenuItem searchMenuItem = menu.findItem(R.id.action_search);
+
searchMenuItem.setIcon(
- new IconDrawable(this, FontAwesomeIcons.fa_search)
- .colorRes(R.color.cardview_light_background)
- .actionBarSize());
+ new IconicsDrawable(this, FontAwesome.Icon.faw_search).actionBar());
// Get the SearchView and set the searchable configuration
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
@@ -208,6 +152,11 @@ public class VideoListActivity extends AppCompatActivity {
return true;
}
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ stopService(new Intent(this, VideoPlayerService.class));
+ }
@Override
public boolean onOptionsItemSelected(MenuItem item) {
@@ -338,22 +287,7 @@ public class VideoListActivity extends AppCompatActivity {
});
}
- /**
- * Force android to not use SSLv3
- *
- * // * @param callingActivity Activity
- */
-// private void updateAndroidSecurityProvider(Activity callingActivity) {
-// try {
-// ProviderInstaller.installIfNeeded(this);
-// } catch (GooglePlayServicesRepairableException e) {
-// // Thrown when Google Play Services is not installed, up-to-date, or enabled
-// // Show dialog to allow users to install, update, or otherwise enable Google Play services.
-// GooglePlayServicesUtil.getErrorDialog(e.getConnectionStatusCode(), callingActivity, 0);
-// } catch (GooglePlayServicesNotAvailableException e) {
-// Log.e("SecurityException", "Google Play Services not available.");
-// }
-// }
+
@Override
protected void onResume() {
super.onResume();
@@ -397,4 +331,66 @@ public class VideoListActivity extends AppCompatActivity {
}
+ private void createBottomBarNavigation() {
+
+ // Get Bottom Navigation
+ BottomNavigationView navigation = findViewById(R.id.navigation);
+
+ // Always show text label
+ navigation.setLabelVisibilityMode(LabelVisibilityMode.LABEL_VISIBILITY_LABELED);
+
+ // Add Icon font
+ Menu navMenu = navigation.getMenu();
+ navMenu.findItem(R.id.navigation_home).setIcon(
+ new IconicsDrawable(this, FontAwesome.Icon.faw_home));
+ navMenu.findItem(R.id.navigation_trending).setIcon(
+ new IconicsDrawable(this, FontAwesome.Icon.faw_fire));
+ navMenu.findItem(R.id.navigation_subscriptions).setIcon(
+ new IconicsDrawable(this, FontAwesome.Icon.faw_folder));
+ navMenu.findItem(R.id.navigation_account).setIcon(
+ new IconicsDrawable(this, FontAwesome.Icon.faw_user_circle));
+
+ // Click Listener
+ navigation.setOnNavigationItemSelectedListener(menuItem -> {
+ switch (menuItem.getItemId()) {
+ case R.id.navigation_home:
+ //Log.v(TAG, "navigation_home");
+
+ if (!isLoading) {
+ sort = "-createdAt";
+ currentStart = 0;
+ loadVideos(currentStart, count, sort, filter);
+ }
+
+ return true;
+ case R.id.navigation_trending:
+ //Log.v(TAG, "navigation_trending");
+
+ if (!isLoading) {
+ sort = "-trending";
+ currentStart = 0;
+ loadVideos(currentStart, count, sort, filter);
+ }
+
+ return true;
+ case R.id.navigation_subscriptions:
+ //Log.v(TAG, "navigation_subscriptions");
+ Toast.makeText(VideoListActivity.this, "Subscriptions Not Implemented", Toast.LENGTH_SHORT).show();
+
+ return false;
+
+ case R.id.navigation_account:
+ //Log.v(TAG, "navigation_account");
+ Toast.makeText(VideoListActivity.this, "Account Not Implemented", Toast.LENGTH_SHORT).show();
+
+// Intent intent = new Intent(this, LoginActivity.class);
+// this.startActivity(intent);
+
+ return false;
+ }
+ return false;
+ });
+
+ }
+
}
diff --git a/app/src/main/java/net/schueller/peertube/activity/VideoPlayActivity.java b/app/src/main/java/net/schueller/peertube/activity/VideoPlayActivity.java
index b1cef54..fa781a2 100644
--- a/app/src/main/java/net/schueller/peertube/activity/VideoPlayActivity.java
+++ b/app/src/main/java/net/schueller/peertube/activity/VideoPlayActivity.java
@@ -12,14 +12,15 @@ import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.preference.PreferenceManager;
-import android.support.annotation.NonNull;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.PopupMenu;
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.PopupMenu;
import android.util.Log;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
+import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
@@ -39,6 +40,7 @@ import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.VideoRendererEventListener;
import com.squareup.picasso.Picasso;
import net.schueller.peertube.R;
+import net.schueller.peertube.fragment.VideoOptionsFragment;
import net.schueller.peertube.helper.APIUrlHelper;
import net.schueller.peertube.helper.MetaDataHelper;
import net.schueller.peertube.intents.Intents;
@@ -51,6 +53,9 @@ import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
+import static net.schueller.peertube.helper.Constants.DEFAULT_THEME;
+import static net.schueller.peertube.helper.Constants.THEME_PREF_KEY;
+
public class VideoPlayActivity extends AppCompatActivity implements VideoRendererEventListener {
private static final String TAG = "VideoPlayActivity";
@@ -89,6 +94,15 @@ public class VideoPlayActivity extends AppCompatActivity implements VideoRendere
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ // Set theme
+ SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
+ setTheme(getResources().getIdentifier(
+ sharedPref.getString(THEME_PREF_KEY, DEFAULT_THEME),
+ "style",
+ getPackageName())
+ );
+
setContentView(R.layout.activity_video_play);
progressBar = findViewById(R.id.progress);
@@ -229,6 +243,8 @@ public class VideoPlayActivity extends AppCompatActivity implements VideoRendere
TextView videoMeta = findViewById(R.id.videoMeta);
ImageView avatarView = findViewById(R.id.avatar);
ImageButton moreButton = findViewById(R.id.moreButton);
+ ImageButton videoOptions = findViewById(R.id.exo_more);
+
Video video = response.body();
@@ -275,8 +291,18 @@ public class VideoPlayActivity extends AppCompatActivity implements VideoRendere
popup.show();
});
+ // video player options
+ videoOptions.setOnClickListener(v -> {
+
+ VideoOptionsFragment videoOptionsFragment =
+ VideoOptionsFragment.newInstance(mService);
+ videoOptionsFragment.show(getSupportFragmentManager(),
+ "video_options_fragment");
+ });
+
mService.setCurrentStreamUrl(video.getFiles().get(0).getFileUrl());
+
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
if (sharedPref.getBoolean("pref_torrent_player", false)) {
diff --git a/app/src/main/java/net/schueller/peertube/adapter/VideoAdapter.java b/app/src/main/java/net/schueller/peertube/adapter/VideoAdapter.java
index d1b7796..b898133 100644
--- a/app/src/main/java/net/schueller/peertube/adapter/VideoAdapter.java
+++ b/app/src/main/java/net/schueller/peertube/adapter/VideoAdapter.java
@@ -2,9 +2,9 @@ package net.schueller.peertube.adapter;
import android.content.Context;
import android.content.Intent;
-import android.support.annotation.NonNull;
-import android.support.v7.widget.PopupMenu;
-import android.support.v7.widget.RecyclerView;
+import androidx.annotation.NonNull;
+import androidx.appcompat.widget.PopupMenu;
+import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
diff --git a/app/src/main/java/net/schueller/peertube/fragment/VideoOptionsFragment.java b/app/src/main/java/net/schueller/peertube/fragment/VideoOptionsFragment.java
new file mode 100644
index 0000000..1ed17e0
--- /dev/null
+++ b/app/src/main/java/net/schueller/peertube/fragment/VideoOptionsFragment.java
@@ -0,0 +1,78 @@
+package net.schueller.peertube.fragment;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
+import com.mikepenz.iconics.Iconics;
+import net.schueller.peertube.R;
+import net.schueller.peertube.service.VideoPlayerService;
+
+import androidx.annotation.Nullable;
+
+public class VideoOptionsFragment extends BottomSheetDialogFragment {
+
+ private static VideoPlayerService videoPlayerService;
+
+ private TextView speed05Icon;
+ private TextView speed10Icon;
+ private TextView speed15Icon;
+ private TextView speed20Icon;
+
+ public static VideoOptionsFragment newInstance(VideoPlayerService mService) {
+ videoPlayerService = mService;
+ return new VideoOptionsFragment();
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater,
+ @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+
+ View view = inflater.inflate(R.layout.bottom_sheet_video_options_fragment, container,
+ false);
+
+ // Icons
+ speed05Icon = view.findViewById(R.id.video_speed05_icon);
+ speed10Icon = view.findViewById(R.id.video_speed10_icon);
+ speed15Icon = view.findViewById(R.id.video_speed15_icon);
+ speed20Icon = view.findViewById(R.id.video_speed20_icon);
+
+ // Buttons
+ TextView speed05 = view.findViewById(R.id.video_speed05);
+ TextView speed10 = view.findViewById(R.id.video_speed10);
+ TextView speed15 = view.findViewById(R.id.video_speed15);
+ TextView speed20 = view.findViewById(R.id.video_speed20);
+
+ // Default
+ setVideoSpeed(1.0f, speed10Icon);
+
+ // Attach the listener
+ speed05.setOnClickListener(v -> setVideoSpeed(0.5f, speed05Icon));
+ speed10.setOnClickListener(v -> setVideoSpeed(1.0f, speed10Icon));
+ speed15.setOnClickListener(v -> setVideoSpeed(1.5f, speed15Icon));
+ speed20.setOnClickListener(v -> setVideoSpeed(2.0f, speed20Icon));
+
+ return view;
+
+ }
+
+
+ private void setVideoSpeed(Float speed, TextView icon) {
+
+ speed05Icon.setText("");
+ speed10Icon.setText("");
+ speed15Icon.setText("");
+ speed20Icon.setText("");
+
+ videoPlayerService.setPlayBackSpeed(speed);
+
+ icon.setText(R.string.video_speed_active_icon);
+ new Iconics.IconicsBuilder().ctx(getContext()).on(icon).build();
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/net/schueller/peertube/helper/Constants.java b/app/src/main/java/net/schueller/peertube/helper/Constants.java
new file mode 100644
index 0000000..02d03bf
--- /dev/null
+++ b/app/src/main/java/net/schueller/peertube/helper/Constants.java
@@ -0,0 +1,6 @@
+package net.schueller.peertube.helper;
+
+public class Constants {
+ public static final String THEME_PREF_KEY = "pref_theme";
+ public static final String DEFAULT_THEME = "AppTheme.ORANGE";
+}
diff --git a/app/src/main/java/net/schueller/peertube/model/Video.java b/app/src/main/java/net/schueller/peertube/model/Video.java
index a640c73..377f645 100644
--- a/app/src/main/java/net/schueller/peertube/model/Video.java
+++ b/app/src/main/java/net/schueller/peertube/model/Video.java
@@ -1,8 +1,21 @@
package net.schueller.peertube.model;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.media.MediaDescriptionCompat;
+
+import com.squareup.picasso.Picasso;
+
+import net.schueller.peertube.R;
+import net.schueller.peertube.helper.APIUrlHelper;
+
import java.util.ArrayList;
import java.util.Date;
+import androidx.annotation.DrawableRes;
+
public class Video {
private Integer id;
@@ -247,4 +260,31 @@ public class Video {
public void setFiles(ArrayList files) {
this.files = files;
}
+
+
+
+ public static MediaDescriptionCompat getMediaDescription(Context context, Video video) {
+
+// String apiBaseURL = APIUrlHelper.getUrlWithVersion(context);
+
+// Bundle extras = new Bundle();
+// Bitmap bitmap = getBitmap(context, Uri.parse(apiBaseURL + video.thumbnailPath));
+// extras.putParcelable(MediaDescriptionCompat.DESCRIPTION_KEY_MEDIA_URI, bitmap);
+
+ return new MediaDescriptionCompat.Builder()
+ .setMediaId(video.getUuid())
+// .setIconBitmap(bitmap)
+// .setExtras(extras)
+ .setTitle(video.getName())
+ .setDescription(video.getDescription())
+ .build();
+ }
+
+// TODO: add support for the thumbnail
+// public static Bitmap getBitmap(Context context, Uri fullThumbnailUrl) {
+//
+// return Picasso.with(context).load(fullThumbnailUrl)
+// .placeholder(R.drawable.ic_peertube)
+// .error(R.drawable.ic_peertube).get();
+// }
}
diff --git a/app/src/main/java/net/schueller/peertube/service/VideoPlayerService.java b/app/src/main/java/net/schueller/peertube/service/VideoPlayerService.java
index 2f42dd1..8eb4f14 100644
--- a/app/src/main/java/net/schueller/peertube/service/VideoPlayerService.java
+++ b/app/src/main/java/net/schueller/peertube/service/VideoPlayerService.java
@@ -11,13 +11,22 @@ import android.graphics.Bitmap;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder;
-import android.support.annotation.Nullable;
+import androidx.annotation.Nullable;
+
+import android.support.v4.media.MediaDescriptionCompat;
+import android.support.v4.media.session.MediaSessionCompat;
import android.util.Log;
+import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayerFactory;
+import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
+import com.google.android.exoplayer2.audio.AudioAttributes;
+import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
+import com.google.android.exoplayer2.ext.mediasession.TimelineQueueNavigator;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.ui.PlayerNotificationManager;
@@ -36,6 +45,9 @@ import static net.schueller.peertube.activity.VideoListActivity.EXTRA_VIDEOID;
public class VideoPlayerService extends Service {
+ private static final String TAG = "VideoPlayerService";
+ private static final String MEDIA_SESSION_TAG = "peertube_player";
+
private final IBinder mBinder = new LocalBinder();
private static final String PLAYBACK_CHANNEL_ID = "playback_channel";
@@ -63,12 +75,12 @@ public class VideoPlayerService extends Service {
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
if (playbackState == ACTION_PAUSE) { // this means that pause is available, hence the audio is playing
- Log.v("VideoPlayerService", "ACTION_PLAY: " + playbackState);
+ Log.v(TAG, "ACTION_PLAY: " + playbackState);
registerReceiver(myNoisyAudioStreamReceiver, becomeNoisyIntentFilter);
}
if (playbackState == ACTION_PLAY) { // this means that play is available, hence the audio is paused or stopped
- Log.v("VideoPlayerService", "ACTION_PAUSE: " + playbackState);
+ Log.v(TAG, "ACTION_PAUSE: " + playbackState);
unregisterReceiver(myNoisyAudioStreamReceiver);
}
}
@@ -88,7 +100,7 @@ public class VideoPlayerService extends Service {
@Override
public void onDestroy() {
- Log.v("VideoPlayerService", "onDestroy...");
+ Log.v(TAG, "onDestroy...");
playerNotificationManager.setPlayer(null);
player.release();
@@ -105,7 +117,7 @@ public class VideoPlayerService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
- Log.v("VideoPlayerService", "onStartCommand...");
+ Log.v(TAG, "onStartCommand...");
playVideo();
return START_STICKY;
}
@@ -113,21 +125,27 @@ public class VideoPlayerService extends Service {
public void setCurrentVideo(Video video)
{
- Log.v("VideoPlayerService", "setCurrentVideo...");
+ Log.v(TAG, "setCurrentVideo...");
currentVideo = video;
}
public void setCurrentStreamUrl(String streamUrl)
{
- Log.v("VideoPlayerService", "setCurrentStreamUrl...");
+ Log.v(TAG, "setCurrentStreamUrl...");
currentStreamUrl = streamUrl;
}
- public void playVideo()
- {
+ //Playback speed control
+ public void setPlayBackSpeed(float speed) {
+
+ Log.v(TAG, "setPlayBackSpeed...");
+ player.setPlaybackParameters(new PlaybackParameters(speed));
+ }
+
+ public void playVideo() {
Context context = this;
- Log.v("VideoPlayerService", "playVideo...");
+ Log.v(TAG, "playVideo...");
// Produces DataSource instances through which media data is loaded.
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(getApplicationContext(),
@@ -143,6 +161,9 @@ public class VideoPlayerService extends Service {
// Auto play
player.setPlayWhenReady(true);
+ //reset playback speed
+ this.setPlayBackSpeed(1.0f);
+
playerNotificationManager = PlayerNotificationManager.createWithNotificationChannel(
context, PLAYBACK_CHANNEL_ID, R.string.playback_channel_name,
PLAYBACK_NOTIFICATION_ID,
@@ -193,7 +214,7 @@ public class VideoPlayerService extends Service {
@Override
public void onNotificationCancelled(int notificationId) {
- Log.v("VideoPlayerService", "onNotificationCancelled...");
+ Log.v(TAG, "onNotificationCancelled...");
// TODO: only kill the notification if we no longer have a bound activity
stopForeground(true);
@@ -203,6 +224,26 @@ public class VideoPlayerService extends Service {
playerNotificationManager.setPlayer(player);
+ // external Media control, Android Wear / Google Home etc.
+ MediaSessionCompat mediaSession = new MediaSessionCompat(context, MEDIA_SESSION_TAG);
+ mediaSession.setActive(true);
+ playerNotificationManager.setMediaSessionToken(mediaSession.getSessionToken());
+ MediaSessionConnector mediaSessionConnector = new MediaSessionConnector(mediaSession);
+ mediaSessionConnector.setQueueNavigator(new TimelineQueueNavigator(mediaSession) {
+ @Override
+ public MediaDescriptionCompat getMediaDescription(Player player, int windowIndex) {
+ return Video.getMediaDescription(context, currentVideo);
+ }
+ });
+ mediaSessionConnector.setPlayer(player, null);
+
+ // Audio Focus
+ AudioAttributes audioAttributes = new AudioAttributes.Builder()
+ .setUsage(C.USAGE_MEDIA)
+ .setContentType(C.CONTENT_TYPE_MOVIE)
+ .build();
+ player.setAudioAttributes(audioAttributes,true);
+
}
// pause playback on audio output change
@@ -215,4 +256,5 @@ public class VideoPlayerService extends Service {
}
}
+
}
diff --git a/app/src/main/res/color/bottom_bar.xml b/app/src/main/res/color/bottom_bar.xml
deleted file mode 100644
index e0f548d..0000000
--- a/app/src/main/res/color/bottom_bar.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml
index 1c90889..82d760d 100644
--- a/app/src/main/res/layout/activity_login.xml
+++ b/app/src/main/res/layout/activity_login.xml
@@ -30,7 +30,7 @@
android:layout_height="wrap_content"
android:orientation="vertical">
-
@@ -43,9 +43,9 @@
android:maxLines="1"
android:singleLine="true" />
-
+
-
@@ -61,7 +61,7 @@
android:maxLines="1"
android:singleLine="true" />
-
+