diff --git a/CHANGELOG.md b/CHANGELOG.md
index 638ec4d..8de2769 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,20 @@
+### Version 1.0.39 Tag: v1.0.39 (2020-06-27)
+ * exoplayer update (@lishoujun)
+ * Floating window support (@dhk2)
+ * Various translations
+
+### Version 1.0.38 Tag: v1.0.38 (2020-06-21)
+ * Multi server login address book
+ * Clear search history (@dhk2)
+ * Android SDK to 29
+ * Various translations
+
+### Version 1.0.37 Tag: v1.0.37 (2020-06-19)
+ * Making Selecting a search suggestion fill search field (@dhk2)
+ * Adding configuration setting and supporting code to choose language (@dhk2)
+ * Adding configuration setting and code for configurable back button behavior (@dhk2)
+ * Various translations
+
### Version 1.0.36 Tag: v1.0.36 (2020-06-14)
* fix 'cannot make a new request because the previous response (@lishoujun)
* Various translations
@@ -17,6 +34,11 @@
* Gradle update
* Translations
+### Version 1.0.31 Tag: v1.0.31 (2019-09-25)
+* Renamed overview to discover (PeerTube v1.4.0)
+* Translations
+* Gradle dependencies updates
+
### Version 1.0.30 Tag: v1.0.30 (2019-08-07)
* Gradle update
* Translations
diff --git a/app/build.gradle b/app/build.gradle
index 3844e9c..6016cfa 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,19 +1,27 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 28
+ compileSdkVersion 29
defaultConfig {
applicationId "net.schueller.peertube"
minSdkVersion 21
- targetSdkVersion 28
- versionCode 1036
- versionName "1.0.36"
+ targetSdkVersion 29
+ versionCode 1039
+ versionName "1.0.39"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
ext {
libVersions = [
- exoplayer: '2.9.3'
+ exoplayer: '2.11.6'
]
}
+ javaCompileOptions {
+ annotationProcessorOptions {
+ arguments = [
+ "room.schemaLocation" : "$projectDir/schemas".toString(),
+ "room.incremental" : "true",
+ "room.expandProjection": "true"]
+ }
+ }
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
@@ -70,7 +78,6 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
- buildToolsVersion '28.0.3'
applicationVariants.all { variant ->
variant.resValue "string", "versionName", variant.versionName
@@ -78,6 +85,23 @@ android {
}
dependencies {
- implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta6'
+ def room_version = "2.2.5"
+ def archLifecycleVersion = '2.1.0'
+
+ implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta7'
implementation 'androidx.appcompat:appcompat:1.1.0'
+ implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+ implementation 'com.google.android.material:material:1.1.0'
+
+ // database lib
+ implementation "androidx.room:room-runtime:$room_version"
+ implementation 'androidx.legacy:legacy-support-v4:1.0.0'
+ implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
+ annotationProcessor "androidx.room:room-compiler:$room_version"
+ androidTestImplementation "androidx.room:room-testing:$room_version"
+
+ // Lifecycle components
+ implementation "androidx.lifecycle:lifecycle-extensions:$archLifecycleVersion"
+ annotationProcessor "androidx.lifecycle:lifecycle-common-java8:$archLifecycleVersion"
+
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 5250d9f..32125e5 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,11 +2,7 @@
-
-
-
-
-
+
+
+ android:resource="@xml/searchable" />
-
-
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/net/schueller/peertube/activity/AccountActivity.java b/app/src/main/java/net/schueller/peertube/activity/AccountActivity.java
index 4c4b5cf..d80b58c 100644
--- a/app/src/main/java/net/schueller/peertube/activity/AccountActivity.java
+++ b/app/src/main/java/net/schueller/peertube/activity/AccountActivity.java
@@ -48,6 +48,7 @@ import net.schueller.peertube.network.RetrofitInstance;
import java.util.ArrayList;
+import java.util.Objects;
import java.util.Set;
import androidx.annotation.NonNull;
@@ -145,11 +146,10 @@ public class AccountActivity extends CommonActivity {
Toolbar toolbar = findViewById(R.id.tool_bar_account);
// Setting toolbar as the ActionBar with setSupportActionBar() call
setSupportActionBar(toolbar);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_baseline_close_24);
getSupportActionBar().setTitle(displayNameAndHost);
- getSupportActionBar().setHomeAsUpIndicator(
- new IconicsDrawable(this, FontAwesome.Icon.faw_chevron_left).actionBar()
- );
loadAccountVideos(displayNameAndHost);
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 b2d9461..36d5fe0 100644
--- a/app/src/main/java/net/schueller/peertube/activity/AppCompatPreferenceActivity.java
+++ b/app/src/main/java/net/schueller/peertube/activity/AppCompatPreferenceActivity.java
@@ -18,6 +18,7 @@
package net.schueller.peertube.activity;
+import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.os.Bundle;
import android.preference.PreferenceActivity;
@@ -25,10 +26,14 @@ import androidx.annotation.LayoutRes;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatDelegate;
+import android.preference.PreferenceManager;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
+import static net.schueller.peertube.helper.Constants.DEFAULT_THEME;
+import static net.schueller.peertube.helper.Constants.THEME_PREF_KEY;
+
/**
* A {@link android.preference.PreferenceActivity} which implements and proxies the necessary calls
* to be used with AppCompat.
@@ -42,6 +47,20 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity {
getDelegate().installViewFactory();
getDelegate().onCreate(savedInstanceState);
super.onCreate(savedInstanceState);
+
+ // TODO: cleanup this duplication
+
+ // 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())
+ );
}
@Override
diff --git a/app/src/main/java/net/schueller/peertube/activity/CommonActivity.java b/app/src/main/java/net/schueller/peertube/activity/CommonActivity.java
index 60c3589..a733fca 100644
--- a/app/src/main/java/net/schueller/peertube/activity/CommonActivity.java
+++ b/app/src/main/java/net/schueller/peertube/activity/CommonActivity.java
@@ -19,12 +19,15 @@
package net.schueller.peertube.activity;
import android.content.SharedPreferences;
+import android.content.res.Configuration;
import android.os.Bundle;
import android.preference.PreferenceManager;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
+import java.util.Locale;
+
import static net.schueller.peertube.helper.Constants.DEFAULT_THEME;
import static net.schueller.peertube.helper.Constants.THEME_PREF_KEY;
@@ -45,6 +48,21 @@ public class CommonActivity extends AppCompatActivity {
"style",
getPackageName())
);
+
+ // Set language
+ String countryCode=sharedPref.getString("pref_language_app","en");
+ Locale locale=new Locale(countryCode);;
+ //Neither Chinese language choice was working, found this fix on stack overflow
+ if(countryCode.equals("zh-rCN"))
+ locale = Locale.SIMPLIFIED_CHINESE;
+ if(countryCode.equals("zh-rTW"))
+ locale = Locale.TRADITIONAL_CHINESE;
+
+ Locale.setDefault(locale);
+ Configuration config = getBaseContext().getResources().getConfiguration();
+ config.locale = locale;
+ getBaseContext().getResources().updateConfiguration(config,
+ getBaseContext().getResources().getDisplayMetrics());
}
}
diff --git a/app/src/main/java/net/schueller/peertube/activity/MeActivity.java b/app/src/main/java/net/schueller/peertube/activity/MeActivity.java
index 4153d74..572bb68 100644
--- a/app/src/main/java/net/schueller/peertube/activity/MeActivity.java
+++ b/app/src/main/java/net/schueller/peertube/activity/MeActivity.java
@@ -19,18 +19,20 @@
package net.schueller.peertube.activity;
import android.content.Intent;
+import android.net.Uri;
+import android.nfc.Tag;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
-import android.view.MenuItem;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.TextView;
-import com.mikepenz.fontawesome_typeface_library.FontAwesome;
-import com.mikepenz.iconics.IconicsDrawable;
-
import net.schueller.peertube.R;
import net.schueller.peertube.helper.APIUrlHelper;
+import net.schueller.peertube.model.Avatar;
import net.schueller.peertube.model.Me;
import net.schueller.peertube.network.GetUserService;
import net.schueller.peertube.network.RetrofitInstance;
@@ -38,10 +40,17 @@ import net.schueller.peertube.network.Session;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar;
+
+import com.squareup.picasso.Picasso;
+
+import java.util.Objects;
+
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
+import static net.schueller.peertube.application.AppApplication.getContext;
+
public class MeActivity extends CommonActivity {
@@ -52,34 +61,10 @@ public class MeActivity extends CommonActivity {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_top_account, menu);
- // Set an icon in the ActionBar
- menu.findItem(R.id.action_logout).setIcon(
- new IconicsDrawable(this, FontAwesome.Icon.faw_sign_out_alt).actionBar());
-
return true;
}
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
-
- switch (item.getItemId()) {
- // action with ID action_refresh was selected
-
- case R.id.action_logout:
- Session.getInstance().invalidate();
- Intent intent = new Intent(this, LoginActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- this.startActivity(intent);
- finish();
- return true;
- default:
- break;
- }
-
- return super.onOptionsItemSelected(item);
- }
-
@Override
public boolean onSupportNavigateUp() {
finish(); // close this activity as oppose to navigating up
@@ -97,66 +82,99 @@ public class MeActivity extends CommonActivity {
Toolbar toolbar = findViewById(R.id.tool_bar_me);
// Setting toolbar as the ActionBar with setSupportActionBar() call
setSupportActionBar(toolbar);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- getSupportActionBar().setHomeAsUpIndicator(
- new IconicsDrawable(this, FontAwesome.Icon.faw_chevron_left).actionBar()
- );
+ Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_baseline_close_24);
+
+ LinearLayout account = findViewById(R.id.a_me_account_line);
+ LinearLayout settings = findViewById(R.id.a_me_settings);
+ LinearLayout help = findViewById(R.id.a_me_helpnfeedback);
+
+ TextView logout = findViewById(R.id.a_me_logout);
- init();
- }
+ settings.setOnClickListener(view -> {
+ Intent settingsActivity = new Intent(getContext(), SettingsActivity.class);
+ //overridePendingTransition(R.anim.slide_in_bottom, 0);
+ startActivity(settingsActivity);
+ });
+
+ help.setOnClickListener(view -> {
+ String url = "https://github.com/sschueller/peertube-android/issues";
+ Intent i = new Intent(Intent.ACTION_VIEW);
+ i.setData(Uri.parse(url));
+ startActivity(i);
+ });
+
+ logout.setOnClickListener(view -> {
+ Session.getInstance().invalidate();
+ account.setVisibility(View.GONE);
+
+ });
- private void init() {
- // try to get user data
getUserData();
}
- private boolean getUserData() {
-
- // TODO
+ private void getUserData() {
String apiBaseURL = APIUrlHelper.getUrlWithVersion(this);
+ String baseURL = APIUrlHelper.getUrl(this);
GetUserService service = RetrofitInstance.getRetrofitInstance(apiBaseURL).create(GetUserService.class);
Call call = service.getMe();
call.enqueue(new Callback() {
+
+ LinearLayout account = findViewById(R.id.a_me_account_line);
+
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) {
+
if (response.isSuccessful()) {
Me me = response.body();
- TextView username = findViewById(R.id.account_username);
- TextView email = findViewById(R.id.account_email);
+ Log.d(TAG, response.body().toString());
+
+ TextView username = findViewById(R.id.a_me_username);
+ TextView email = findViewById(R.id.a_me_email);
+ ImageView avatarView = findViewById(R.id.a_me_avatar);
+
username.setText(me.getUsername());
email.setText(me.getEmail());
- Log.v(TAG, me.getEmail());
+ Avatar avatar = me.getAccount().getAvatar();
+ if (avatar != null) {
+ String avatarPath = avatar.getPath();
+ Picasso.get()
+ .load(baseURL + avatarPath)
+ .into(avatarView);
+ }
+ account.setVisibility(View.VISIBLE);
+
+ } else {
+ account.setVisibility(View.GONE);
}
-
}
@Override
- public void onFailure(Call call, Throwable t) {
-
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+ account.setVisibility(View.GONE);
}
});
- return true;
}
@Override
protected void onResume() {
super.onResume();
- init();
+ getUserData();
}
}
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 b2da65f..26d56f2 100644
--- a/app/src/main/java/net/schueller/peertube/activity/SelectServerActivity.java
+++ b/app/src/main/java/net/schueller/peertube/activity/SelectServerActivity.java
@@ -1,3 +1,20 @@
+/*
+ * Copyright 2018 Stefan Schüller
+ *
+ * License: GPL-3.0+
+ * 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 3 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, see .
+ */
package net.schueller.peertube.activity;
import androidx.annotation.NonNull;
@@ -9,6 +26,7 @@ import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
+import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
@@ -54,28 +72,6 @@ public class SelectServerActivity extends AppCompatActivity {
loadList();
- // set url
- TextView selectedUrl = findViewById(R.id.serverSelectedUrl);
- selectedUrl.setText(APIUrlHelper.getUrl(SelectServerActivity.this));
-
- Button setServerButton = findViewById(R.id.server_selection_set);
- setServerButton.setOnClickListener(v -> {
-
- SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
- SharedPreferences.Editor editor = sharedPref.edit();
-
- String serverUrl = APIUrlHelper.cleanServerUrl(selectedUrl.getText().toString());
-
- if (!Patterns.WEB_URL.matcher(serverUrl).matches()) {
- Toast.makeText(this, R.string.invalid_url, Toast.LENGTH_LONG).show();
- } else {
- editor.putString("pref_api_base", serverUrl);
- editor.apply();
- this.finish();
- }
-
- });
-
}
diff --git a/app/src/main/java/net/schueller/peertube/activity/ServerAddressBookActivity.java b/app/src/main/java/net/schueller/peertube/activity/ServerAddressBookActivity.java
new file mode 100644
index 0000000..8d2f934
--- /dev/null
+++ b/app/src/main/java/net/schueller/peertube/activity/ServerAddressBookActivity.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2018 Stefan Schüller
+ *
+ * License: GPL-3.0+
+ * 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 3 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, see .
+ */
+package net.schueller.peertube.activity;
+
+import android.app.AlertDialog;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.EditText;
+
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.recyclerview.widget.ItemTouchHelper;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import net.schueller.peertube.R;
+import net.schueller.peertube.adapter.ServerListAdapter;
+import net.schueller.peertube.database.Server;
+import net.schueller.peertube.database.ServerViewModel;
+import net.schueller.peertube.fragment.AddServerFragment;
+
+
+public class ServerAddressBookActivity extends CommonActivity implements AddServerFragment.OnFragmentInteractionListener {
+
+ private String TAG = "ServerAddressBookActivity";
+ public static final String EXTRA_REPLY = "net.schueller.peertube.room.REPLY";
+
+ private ServerViewModel mServerViewModel;
+ private AddServerFragment addServerFragment;
+ private FloatingActionButton floatingActionButton;
+ private FragmentManager fragmentManager;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_server_address_book);
+
+
+ mServerViewModel = new ViewModelProvider(this).get(ServerViewModel.class);
+
+ showServers();
+
+ floatingActionButton = findViewById(R.id.add_server);
+ floatingActionButton.setOnClickListener(view -> {
+
+ Log.d(TAG, "Click");
+
+ fragmentManager = getSupportFragmentManager();
+ FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
+
+ addServerFragment = new AddServerFragment();
+ fragmentTransaction.replace(R.id.server_book, addServerFragment);
+ fragmentTransaction.commit();
+
+ floatingActionButton.hide();
+
+ });
+
+ }
+
+ @Override
+ public void onFragmentInteraction(Uri uri) {
+
+ }
+
+ @Override
+ public void onPointerCaptureChanged(boolean hasCapture) {
+
+ }
+
+
+ public void showServers()
+ {
+ RecyclerView recyclerView = findViewById(R.id.server_list_recyclerview);
+ final ServerListAdapter adapter = new ServerListAdapter(this);
+ recyclerView.setAdapter(adapter);
+ recyclerView.setLayoutManager(new LinearLayoutManager(this));
+
+ // Delete items on swipe
+ ItemTouchHelper helper = new ItemTouchHelper(
+ new ItemTouchHelper.SimpleCallback(0,
+ ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
+ @Override
+ public boolean onMove(@NonNull RecyclerView recyclerView,
+ @NonNull RecyclerView.ViewHolder viewHolder,
+ @NonNull RecyclerView.ViewHolder target) {
+ return false;
+ }
+
+ @Override
+ public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder,
+ int direction) {
+
+
+ new AlertDialog.Builder(ServerAddressBookActivity.this)
+ .setTitle(getString(R.string.server_book_del_alert_title))
+ .setMessage(getString(R.string.server_book_del_alert_msg))
+ .setPositiveButton(android.R.string.yes, (dialog, which) -> {
+ int position = viewHolder.getAdapterPosition();
+ Server server = adapter.getServerAtPosition(position);
+// Toast.makeText(ServerAddressBookActivity.this, "Deleting " +
+// server.getServerName(), Toast.LENGTH_LONG).show();
+ // Delete the server
+ mServerViewModel.delete(server);
+ })
+ .setNegativeButton(android.R.string.no, (dialog, which) -> {
+ adapter.notifyItemChanged(viewHolder.getAdapterPosition());
+ })
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .show();
+
+ }
+ });
+ helper.attachToRecyclerView(recyclerView);
+
+
+ // Update the cached copy of the words in the adapter.
+ mServerViewModel.getAllServers().observe(this, adapter::setServers);
+
+ }
+
+ public void addServer(View view)
+ {
+ Log.d(TAG, "addServer");
+
+ EditText serverLabel = view.findViewById(R.id.serverLabel);
+ EditText serverUrl = view.findViewById(R.id.serverUrl);
+ EditText serverUsername = view.findViewById(R.id.serverUsername);
+ EditText serverPassword = view.findViewById(R.id.serverPassword);
+
+ Server server = new Server(serverLabel.getText().toString());
+
+ server.setServerHost(serverUrl.getText().toString());
+ server.setUsername(serverUsername.getText().toString());
+ server.setPassword(serverPassword.getText().toString());
+
+ mServerViewModel.insert(server);
+
+ FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
+ fragmentTransaction.remove(addServerFragment);
+ fragmentTransaction.commit();
+
+ floatingActionButton.show();
+
+ }
+
+ public void testServer()
+ {
+
+ }
+
+}
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 2179de4..c8be62b 100644
--- a/app/src/main/java/net/schueller/peertube/activity/SettingsActivity.java
+++ b/app/src/main/java/net/schueller/peertube/activity/SettingsActivity.java
@@ -135,13 +135,6 @@ 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);
@@ -202,7 +195,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
// 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"));
+ //bindPreferenceSummaryToValue(findPreference("pref_api_base"));
bindPreferenceSummaryToValue(findPreference("pref_theme"));
}
@@ -219,12 +212,12 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
Preference preference) {
- String key = preference.getKey();
- if ("pref_api_base".equals(key)) {
- Intent intentServer = new Intent(preference.getContext(), SelectServerActivity.class);
- startActivity(intentServer);
- return true;
- }
+// String key = preference.getKey();
+// if ("pref_api_base".equals(key)) {
+// Intent intentServer = new Intent(preference.getContext(), SelectServerActivity.class);
+// startActivity(intentServer);
+// return true;
+// }
return false;
}
@@ -232,7 +225,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
public void onResume() {
setPreferenceScreen(null);
addPreferencesFromResource(R.xml.pref_general);
- bindPreferenceSummaryToValue(findPreference("pref_api_base"));
+ //bindPreferenceSummaryToValue(findPreference("pref_api_base"));
super.onResume();
}
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 8a68ce6..c4ca315 100644
--- a/app/src/main/java/net/schueller/peertube/activity/VideoListActivity.java
+++ b/app/src/main/java/net/schueller/peertube/activity/VideoListActivity.java
@@ -19,11 +19,14 @@
package net.schueller.peertube.activity;
import android.Manifest;
+import android.app.AlertDialog;
import android.app.SearchManager;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
+import android.database.Cursor;
import android.preference.PreferenceManager;
import android.provider.SearchRecentSuggestions;
@@ -47,6 +50,7 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
+import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
@@ -78,6 +82,7 @@ public class VideoListActivity extends CommonActivity {
public static final String EXTRA_VIDEOID = "VIDEOID";
public static final String EXTRA_ACCOUNTDISPLAYNAME = "ACCOUNTDISPLAYNAMEANDHOST";
+ public static final Integer SWITCH_INSTANCE = 2;
private VideoAdapter videoAdapter;
private SwipeRefreshLayout swipeRefreshLayout;
@@ -122,14 +127,11 @@ public class VideoListActivity extends CommonActivity {
inflater.inflate(R.menu.menu_top_videolist, menu);
// Set an icon in the ActionBar
- menu.findItem(R.id.action_settings).setIcon(
- new IconicsDrawable(this, FontAwesome.Icon.faw_cog).actionBar());
-
menu.findItem(R.id.action_account).setIcon(
new IconicsDrawable(this, FontAwesome.Icon.faw_user_circle).actionBar());
-// menu.findItem(R.id.action_server_selection).setIcon(
-// new IconicsDrawable(this, FontAwesome.Icon.faw_server).actionBar());
+ menu.findItem(R.id.action_server_address_book).setIcon(
+ new IconicsDrawable(this, FontAwesome.Icon.faw_server).actionBar());
MenuItem searchMenuItem = menu.findItem(R.id.action_search);
@@ -145,6 +147,26 @@ public class VideoListActivity extends CommonActivity {
searchView.setIconifiedByDefault(false); // Do not iconify the widget; expand it by default
searchView.setQueryRefinementEnabled(true);
+ searchMenuItem.getActionView().setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ new AlertDialog.Builder(VideoListActivity.this)
+ .setTitle(getString(R.string.clear_search_history))
+ .setMessage(getString(R.string.clear_search_history_prompt))
+ .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ SearchRecentSuggestions suggestions = new SearchRecentSuggestions(getApplicationContext(),
+ SearchSuggestionsProvider.AUTHORITY,
+ SearchSuggestionsProvider.MODE);
+ suggestions.clearHistory();
+ }
+ })
+ .setNegativeButton(android.R.string.no, null)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .show();
+ return true;
+ }
+ });
searchMenuItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
@Override
public boolean onMenuItemActionExpand(MenuItem menuItem) {
@@ -166,7 +188,27 @@ public class VideoListActivity extends CommonActivity {
Log.d(TAG, "onDismiss: ");
loadVideos(0, count, sort, filter);
});
+ searchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() {
+ @Override
+ public boolean onSuggestionClick(int position) {
+ String suggestion = getSuggestion(position);
+ searchView.setQuery(suggestion, true);
+ return true;
+ }
+ private String getSuggestion(int position) {
+ Cursor cursor = (Cursor) searchView.getSuggestionsAdapter().getItem(
+ position);
+ return cursor.getString(cursor
+ .getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1));
+ }
+
+ @Override
+ public boolean onSuggestionSelect(int position) {
+ /* Required to implement */
+ return true;
+ }
+ });
return true;
}
@@ -176,6 +218,16 @@ public class VideoListActivity extends CommonActivity {
stopService(new Intent(this, VideoPlayerService.class));
}
+
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == SWITCH_INSTANCE) {
+ if(resultCode == RESULT_OK) {
+ loadVideos(currentStart, count, sort, filter);
+ }
+ }
+ }
+
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
@@ -188,25 +240,28 @@ public class VideoListActivity extends CommonActivity {
//Toast.makeText(this, "Search Selected", Toast.LENGTH_SHORT).show();
return false;
- case R.id.action_settings:
-// Toast.makeText(this, "Login Selected", Toast.LENGTH_SHORT).show();
- Intent intentSettings = new Intent(this, SettingsActivity.class);
- this.startActivity(intentSettings);
-
- return true;
case R.id.action_account:
- if (!Session.getInstance().isLoggedIn()) {
- Intent intentLogin = new Intent(this, LoginActivity.class);
- this.startActivity(intentLogin);
- } else {
+// if (!Session.getInstance().isLoggedIn()) {
+
+ //Intent intentLogin = new Intent(this, ServerAddressBookActivity.class);
+
Intent intentMe = new Intent(this, MeActivity.class);
this.startActivity(intentMe);
- }
+
+ //overridePendingTransition(R.anim.slide_in_bottom, 0);
+
+
+ // this.startActivity(intentLogin);
+
+// } else {
+// Intent intentMe = new Intent(this, MeActivity.class);
+// this.startActivity(intentMe);
+// }
+ return false;
+ case R.id.action_server_address_book:
+ Intent addressBookActivityIntent = new Intent(this, ServerAddressBookActivity.class);
+ this.startActivityForResult(addressBookActivityIntent, SWITCH_INSTANCE);
return false;
-// case R.id.action_server_selection:
-// Intent intentServer = new Intent(this, SelectServerActivity.class);
-// this.startActivity(intentServer);
-// return false;
default:
break;
}
@@ -335,6 +390,7 @@ public class VideoListActivity extends CommonActivity {
@Override
protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
setIntent(intent);
handleIntent(intent);
}
@@ -440,8 +496,10 @@ public class VideoListActivity extends CommonActivity {
//Log.v(TAG, "navigation_subscriptions");
if (!Session.getInstance().isLoggedIn()) {
- Intent intent = new Intent(this, LoginActivity.class);
- this.startActivity(intent);
+// Intent intent = new Intent(this, LoginActivity.class);
+// this.startActivity(intent);
+ Intent addressBookActivityIntent = new Intent(this, ServerAddressBookActivity.class);
+ this.startActivityForResult(addressBookActivityIntent, SWITCH_INSTANCE);
return false;
} else {
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 2b69783..28e80ad 100644
--- a/app/src/main/java/net/schueller/peertube/activity/VideoPlayActivity.java
+++ b/app/src/main/java/net/schueller/peertube/activity/VideoPlayActivity.java
@@ -19,16 +19,24 @@
package net.schueller.peertube.activity;
+import android.annotation.SuppressLint;
+import android.app.AppOpsManager;
+import android.app.PictureInPictureParams;
+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.PreferenceManager;
+import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
+import android.text.TextUtils;
import android.util.Log;
+import android.util.Rational;
import android.util.TypedValue;
import android.view.WindowManager;
@@ -39,6 +47,7 @@ import android.widget.RelativeLayout;
import net.schueller.peertube.R;
import net.schueller.peertube.fragment.VideoMetaDataFragment;
import net.schueller.peertube.fragment.VideoPlayerFragment;
+import net.schueller.peertube.service.VideoPlayerService;
import java.util.Objects;
@@ -71,13 +80,23 @@ public class VideoPlayActivity extends AppCompatActivity {
// get video ID
Intent intent = getIntent();
String videoUuid = intent.getStringExtra(VideoListActivity.EXTRA_VIDEOID);
- Log.v(TAG, "click: " + videoUuid);
-
VideoPlayerFragment videoPlayerFragment = (VideoPlayerFragment)
getSupportFragmentManager().findFragmentById(R.id.video_player_fragment);
assert videoPlayerFragment != null;
- videoPlayerFragment.start(videoUuid);
+ String playingVideo = videoPlayerFragment.getVideoUuid();
+ Log.v(TAG, "oncreate click: " + videoUuid +" is trying to replace: "+playingVideo);
+
+ if (TextUtils.isEmpty(playingVideo)){
+ Log.v(TAG,"oncreate no video currently playing");
+ videoPlayerFragment.start(videoUuid);
+ } else if(!playingVideo.equals(videoUuid)){
+ Log.v(TAG,"oncreate different video playing currently");
+ videoPlayerFragment.stopVideo();
+ videoPlayerFragment.start(videoUuid);
+ } else {
+ Log.v(TAG,"oncreate same video playing currently");
+ }
// if we are in landscape set the video to fullscreen
int orientation = this.getResources().getConfiguration().orientation;
@@ -86,6 +105,36 @@ public class VideoPlayActivity extends AppCompatActivity {
}
}
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ setIntent(intent);
+ VideoPlayerFragment videoPlayerFragment = (VideoPlayerFragment)
+ getSupportFragmentManager().findFragmentById(R.id.video_player_fragment);
+ assert videoPlayerFragment != null;
+ String videoUuid = intent.getStringExtra(VideoListActivity.EXTRA_VIDEOID);
+ Log.v(TAG, "new intent click: " + videoUuid +" is trying to replace: "+videoPlayerFragment.getVideoUuid());
+ assert videoPlayerFragment != null;
+ String playingVideo = videoPlayerFragment.getVideoUuid();
+
+ if (TextUtils.isEmpty(playingVideo)){
+ Log.v(TAG,"new intent no video currently playing");
+ videoPlayerFragment.start(videoUuid);
+ } else if(!playingVideo.equals(videoUuid)){
+ Log.v(TAG,"new intent different video playing currently");
+ videoPlayerFragment.stopVideo();
+ videoPlayerFragment.start(videoUuid);
+ } else {
+ Log.v(TAG,"new intent same video playing currently");
+ }
+
+ // if we are in landscape set the video to fullscreen
+ int orientation = this.getResources().getConfiguration().orientation;
+ if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ setOrientation(true);
+ }
+
+ }
@Override
public void onConfigurationChanged(Configuration newConfig) {
@@ -199,4 +248,115 @@ public class VideoPlayActivity extends AppCompatActivity {
Log.v(TAG, "onStart()...");
}
+ @SuppressLint("NewApi")
+ @Override
+ public void onUserLeaveHint () {
+ SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
+ FragmentManager fragmentManager = getSupportFragmentManager();
+ VideoPlayerFragment videoPlayerFragment = (VideoPlayerFragment) fragmentManager.findFragmentById(R.id.video_player_fragment);
+ VideoMetaDataFragment videoMetaFragment = (VideoMetaDataFragment) fragmentManager.findFragmentById(R.id.video_meta_data_fragment);
+ String backgroundBehavior = sharedPref.getString("pref_background_behavior","backgroundStop");
+
+ switch(backgroundBehavior){
+ case "backgroundStop":
+ Log.v(TAG,"stop the video");
+ videoPlayerFragment.pauseVideo();
+ stopService(new Intent(this, VideoPlayerService.class));
+ super.onBackPressed();
+ break;
+ case "backgroundAudio":
+ Log.v(TAG,"play the Audio");
+ super.onBackPressed();
+ break;
+ case "backgroundFloat":
+ Log.v(TAG,"play in floating video");
+ //canEnterPIPMode makes sure API level is high enough
+ if (canEnterPipMode(this)) {
+ Log.v(TAG, "enabling pip");
+ enterPipMode();
+ } else {
+ Log.v(TAG, "unable to use pip");
+ }
+ break;
+ }
+ Log.v(TAG, "onUserLeaveHint()...");
+ }
+
+ // @RequiresApi(api = Build.VERSION_CODES.O)
+ @SuppressLint("NewApi")
+ public void onBackPressed() {
+
+ SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
+ VideoPlayerFragment videoPlayerFragment = (VideoPlayerFragment)
+ getSupportFragmentManager().findFragmentById(R.id.video_player_fragment);
+
+ //copying Youtube behavior to have back button exit full screen.
+ if (videoPlayerFragment.getIsFullscreen()){
+ Log.v(TAG,"exiting full screen");
+ videoPlayerFragment.fullScreenToggle();
+ return;
+ }
+
+ if (sharedPref.getBoolean("pref_back_pause", true)) {
+ assert videoPlayerFragment != null;
+ videoPlayerFragment.pauseVideo();
+ }
+
+ String backgroundBehavior = sharedPref.getString("pref_background_behavior","backgroundStop");
+ switch (backgroundBehavior){
+ case "backgroundStop":
+ Log.v(TAG,"stop the video");
+ videoPlayerFragment.pauseVideo();
+ stopService(new Intent(this, VideoPlayerService.class));
+ super.onBackPressed();
+ break;
+ case "backgroundAudio":
+ Log.v(TAG,"play the Audio");
+ super.onBackPressed();
+ break;
+ case "backgroundFloat":
+ Log.v(TAG,"play in floating video");
+ //canEnterPIPMode makes sure API level is high enough
+ if (canEnterPipMode(this)) {
+ Log.v(TAG, "enabling pip");
+ enterPipMode();
+ //fixes problem where back press doesn't bring up video list after returning from PIP mode
+ Intent intentSettings = new Intent(this, VideoListActivity.class);
+ this.startActivity(intentSettings);
+ } else {
+ Log.v(TAG,"Unable to enter PIP mode");
+ super.onBackPressed();
+ }
+ break;
+ }
+ Log.v(TAG, "onBackPressed()...");
+ }
+ public boolean canEnterPipMode(Context context) {
+ Log.v(TAG,"api version "+Build.VERSION.SDK_INT);
+ if (Build.VERSION.SDK_INT<28){
+ return false;
+ }
+ AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ return (AppOpsManager.MODE_ALLOWED== appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_PICTURE_IN_PICTURE, android.os.Process.myUid(), context.getPackageName()));
+ }
+ @RequiresApi(api = Build.VERSION_CODES.O)
+ public void enterPipMode() {
+ Rational rational = new Rational(239, 100);
+ Log.v(TAG,rational.toString());
+ PictureInPictureParams mParams =
+ new PictureInPictureParams.Builder()
+ .setAspectRatio(rational)
+// .setSourceRectHint(new Rect(0,500,400,600))
+ .build();
+
+ enterPictureInPictureMode(mParams);
+ }
+ @Override
+ public void onPictureInPictureModeChanged (boolean isInPictureInPictureMode, Configuration newConfig) {
+ if (isInPictureInPictureMode) {
+ Log.v(TAG,"switched to pip ");
+ } else {
+ Log.v(TAG,"switched to normal");
+ }
+ }
}
diff --git a/app/src/main/java/net/schueller/peertube/adapter/ServerAdapter.java b/app/src/main/java/net/schueller/peertube/adapter/ServerAdapter.java
index 0e3de08..7bde807 100644
--- a/app/src/main/java/net/schueller/peertube/adapter/ServerAdapter.java
+++ b/app/src/main/java/net/schueller/peertube/adapter/ServerAdapter.java
@@ -18,6 +18,7 @@
package net.schueller.peertube.adapter;
import android.content.Context;
+import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
@@ -37,6 +38,8 @@ import java.util.ArrayList;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
+import static android.app.Activity.RESULT_OK;
+
public class ServerAdapter extends RecyclerView.Adapter {
@@ -75,18 +78,25 @@ public class ServerAdapter extends RecyclerView.Adapter {
- SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(activity);
- SharedPreferences.Editor editor = sharedPref.edit();
+// SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(activity);
+// SharedPreferences.Editor editor = sharedPref.edit();
String serverUrl = APIUrlHelper.cleanServerUrl(serverList.get(position).getHost());
- editor.putString("pref_api_base", serverUrl);
- editor.apply();
-
- activity.finish();
+// editor.putString("pref_api_base", serverUrl);
+// editor.apply();
+//
+//
Toast.makeText(activity, activity.getString(R.string.server_selection_set_server, serverUrl), Toast.LENGTH_LONG).show();
+ Intent intent = new Intent();
+ intent.putExtra("serverUrl", serverUrl);
+ intent.putExtra("serverName", serverList.get(position).getName());
+ activity.setResult(RESULT_OK, intent);
+
+ activity.finish();
+
});
//
diff --git a/app/src/main/java/net/schueller/peertube/adapter/ServerListAdapter.java b/app/src/main/java/net/schueller/peertube/adapter/ServerListAdapter.java
new file mode 100644
index 0000000..f7225df
--- /dev/null
+++ b/app/src/main/java/net/schueller/peertube/adapter/ServerListAdapter.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2018 Stefan Schüller
+ *
+ * License: GPL-3.0+
+ * 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 3 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, see .
+ */
+package net.schueller.peertube.adapter;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import android.provider.SearchRecentSuggestions;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import net.schueller.peertube.R;
+
+import net.schueller.peertube.activity.SelectServerActivity;
+import net.schueller.peertube.activity.ServerAddressBookActivity;
+import net.schueller.peertube.activity.VideoListActivity;
+import net.schueller.peertube.database.Server;
+import net.schueller.peertube.helper.APIUrlHelper;
+import net.schueller.peertube.provider.SearchSuggestionsProvider;
+import net.schueller.peertube.service.LoginService;
+
+
+import java.util.List;
+
+import static android.app.Activity.RESULT_OK;
+
+public class ServerListAdapter extends RecyclerView.Adapter {
+
+
+ private final LayoutInflater mInflater;
+ private List mServers; // Cached copy of Servers
+
+ public ServerListAdapter(Context context) {
+ this.mInflater = LayoutInflater.from(context);
+ }
+
+ @NonNull
+ @Override
+ public ServerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ View itemView = mInflater.inflate(R.layout.row_serverbook, parent, false);
+ return new ServerViewHolder(itemView);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ServerViewHolder holder, int position) {
+
+ if (mServers != null) {
+ Server current = mServers.get(position);
+ holder.serverLabel.setText(current.getServerName());
+ holder.serverUrl.setText(current.getServerHost());
+
+ if (TextUtils.isEmpty(current.getUsername())) {
+ holder.hasLogin.setVisibility(View.GONE);
+ } else {
+ holder.hasLogin.setVisibility(View.VISIBLE);
+ }
+
+ } else {
+ // Covers the case of data not being ready yet.
+ holder.serverLabel.setText(R.string.server_book_no_servers_found);
+ }
+
+ holder.itemView.setOnClickListener(v -> {
+ SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(mInflater.getContext());
+ SharedPreferences.Editor editor = sharedPref.edit();
+
+ String serverUrl = APIUrlHelper.cleanServerUrl(getServerAtPosition(position).getServerHost());
+
+ editor.putString("pref_api_base", serverUrl);
+ editor.apply();
+
+ // attempt authentication if we have a username
+ if (!TextUtils.isEmpty(getServerAtPosition(position).getUsername())) {
+ LoginService.Authenticate(
+ getServerAtPosition(position).getUsername(),
+ getServerAtPosition(position).getPassword()
+ );
+ }
+
+ // tell server list activity to reload list
+ Intent intent = new Intent();
+ ((Activity) mInflater.getContext()).setResult(RESULT_OK, intent);
+
+ // close this activity
+ ((Activity) mInflater.getContext()).finish();
+
+ Toast.makeText(mInflater.getContext(), mInflater.getContext().getString(R.string.server_selection_set_server, serverUrl), Toast.LENGTH_LONG).show();
+
+ });
+
+
+//
+// holder.itemView.setOnLongClickListener(v -> {
+// Log.v("ServerListAdapter", "setOnLongClickListener " + position);
+// return true;
+// });
+
+
+ }
+
+ public void setServers(List Servers) {
+ mServers = Servers;
+ this.notifyDataSetChanged();
+ }
+
+ // getItemCount() is called many times, and when it is first called,
+ // mServers has not been updated (means initially, it's null, and we can't return null).
+ @Override
+ public int getItemCount() {
+ if (mServers != null)
+ return mServers.size();
+ else return 0;
+ }
+
+ static class ServerViewHolder extends RecyclerView.ViewHolder {
+ TextView serverLabel, serverUrl, serverUsername;
+ ImageView hasLogin;
+
+ private ServerViewHolder(View itemView) {
+ super(itemView);
+ serverLabel = itemView.findViewById(R.id.serverLabelRow);
+ serverUrl = itemView.findViewById(R.id.serverUrlRow);
+ hasLogin = itemView.findViewById(R.id.sb_row_has_login_icon);
+ }
+ }
+
+ public Server getServerAtPosition (int position) {
+ return mServers.get(position);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/net/schueller/peertube/application/AppApplication.java b/app/src/main/java/net/schueller/peertube/application/AppApplication.java
index 05869b4..98f0996 100644
--- a/app/src/main/java/net/schueller/peertube/application/AppApplication.java
+++ b/app/src/main/java/net/schueller/peertube/application/AppApplication.java
@@ -1,3 +1,20 @@
+/*
+ * Copyright 2018 Stefan Schüller
+ *
+ * License: GPL-3.0+
+ * 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 3 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, see .
+ */
package net.schueller.peertube.application;
import android.app.Application;
diff --git a/app/src/main/java/net/schueller/peertube/database/AppDatabase.java b/app/src/main/java/net/schueller/peertube/database/AppDatabase.java
new file mode 100644
index 0000000..bc1643e
--- /dev/null
+++ b/app/src/main/java/net/schueller/peertube/database/AppDatabase.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2018 Stefan Schüller
+ *
+ * License: GPL-3.0+
+ * 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 3 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, see .
+ */
+package net.schueller.peertube.database;
+
+import androidx.room.Database;
+import androidx.room.RoomDatabase;
+
+@Database(entities = {Server.class}, version = 1)
+public abstract class AppDatabase extends RoomDatabase {
+ public abstract ServerDao serverDao();
+}
\ No newline at end of file
diff --git a/app/src/main/java/net/schueller/peertube/database/Server.java b/app/src/main/java/net/schueller/peertube/database/Server.java
new file mode 100644
index 0000000..5c0f7e1
--- /dev/null
+++ b/app/src/main/java/net/schueller/peertube/database/Server.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2018 Stefan Schüller
+ *
+ * License: GPL-3.0+
+ * 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 3 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, see .
+ */
+package net.schueller.peertube.database;
+
+import androidx.annotation.NonNull;
+import androidx.room.ColumnInfo;
+import androidx.room.Entity;
+import androidx.room.PrimaryKey;
+
+@Entity(tableName = "server_table")
+public class Server {
+
+ @PrimaryKey(autoGenerate = true)
+ private int id;
+
+ @NonNull
+ @ColumnInfo(name = "server_name")
+ private String serverName;
+
+ @ColumnInfo(name = "server_host")
+ private String serverHost;
+
+ @ColumnInfo(name = "username")
+ private String username;
+
+ @ColumnInfo(name = "password")
+ private String password;
+
+ public Server(@NonNull String serverName) {
+ this.serverName = serverName;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getServerName() {
+ return serverName;
+ }
+
+ public void setServerName(String serverName) {
+ this.serverName = serverName;
+ }
+
+ public String getServerHost() {
+ return serverHost;
+ }
+
+ public void setServerHost(String serverHost) {
+ this.serverHost = serverHost;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+}
diff --git a/app/src/main/java/net/schueller/peertube/database/ServerDao.java b/app/src/main/java/net/schueller/peertube/database/ServerDao.java
new file mode 100644
index 0000000..e01ef5f
--- /dev/null
+++ b/app/src/main/java/net/schueller/peertube/database/ServerDao.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2018 Stefan Schüller
+ *
+ * License: GPL-3.0+
+ * 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 3 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, see .
+ */
+package net.schueller.peertube.database;
+
+import androidx.lifecycle.LiveData;
+import androidx.room.Dao;
+import androidx.room.Delete;
+import androidx.room.Insert;
+import androidx.room.OnConflictStrategy;
+import androidx.room.Query;
+
+import java.util.List;
+
+@Dao
+public interface ServerDao {
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ void insert(Server server);
+
+ @Query("DELETE FROM server_table")
+ void deleteAll();
+
+ @Delete
+ void delete(Server server);
+
+ @Query("SELECT * from server_table ORDER BY server_name DESC")
+ LiveData> getAllServers();
+}
\ No newline at end of file
diff --git a/app/src/main/java/net/schueller/peertube/database/ServerRepository.java b/app/src/main/java/net/schueller/peertube/database/ServerRepository.java
new file mode 100644
index 0000000..001cb7c
--- /dev/null
+++ b/app/src/main/java/net/schueller/peertube/database/ServerRepository.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2018 Stefan Schüller
+ *
+ * License: GPL-3.0+
+ * 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 3 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, see .
+ */
+package net.schueller.peertube.database;
+
+import android.app.Application;
+import android.os.AsyncTask;
+
+
+import androidx.lifecycle.LiveData;
+
+import java.util.List;
+
+class ServerRepository {
+
+ private ServerDao mServerDao;
+ private LiveData> mAllServers;
+
+ ServerRepository(Application application) {
+ ServerRoomDatabase db = ServerRoomDatabase.getDatabase(application);
+ mServerDao = db.serverDao();
+ mAllServers = mServerDao.getAllServers();
+
+ }
+
+ LiveData> getAllServers() {
+ return mAllServers;
+ }
+
+ void insert (Server server) {
+ new insertAsyncTask(mServerDao).execute(server);
+ }
+
+ public void delete(Server server) {
+ new deleteServerAsyncTask(mServerDao).execute(server);
+ }
+
+ private static class insertAsyncTask extends AsyncTask {
+
+ private ServerDao mAsyncTaskDao;
+
+ insertAsyncTask(ServerDao dao) {
+ mAsyncTaskDao = dao;
+ }
+
+ @Override
+ protected Void doInBackground(final Server... params) {
+ mAsyncTaskDao.insert(params[0]);
+ return null;
+ }
+ }
+
+ private static class deleteServerAsyncTask extends AsyncTask {
+ private ServerDao mAsyncTaskDao;
+
+ deleteServerAsyncTask(ServerDao dao) {
+ mAsyncTaskDao = dao;
+ }
+
+ @Override
+ protected Void doInBackground(final Server... params) {
+ mAsyncTaskDao.delete(params[0]);
+ return null;
+ }
+ }
+}
diff --git a/app/src/main/java/net/schueller/peertube/database/ServerRoomDatabase.java b/app/src/main/java/net/schueller/peertube/database/ServerRoomDatabase.java
new file mode 100644
index 0000000..deb79a2
--- /dev/null
+++ b/app/src/main/java/net/schueller/peertube/database/ServerRoomDatabase.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2018 Stefan Schüller
+ *
+ * License: GPL-3.0+
+ * 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 3 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, see .
+ */
+package net.schueller.peertube.database;
+
+import android.content.Context;
+
+import androidx.room.Database;
+import androidx.room.Room;
+import androidx.room.RoomDatabase;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+@Database(entities = {Server.class}, version = 1, exportSchema = false)
+public abstract class ServerRoomDatabase extends RoomDatabase {
+
+ public abstract ServerDao serverDao();
+
+ private static volatile ServerRoomDatabase INSTANCE;
+
+ private static final int NUMBER_OF_THREADS = 4;
+
+ static final ExecutorService databaseWriteExecutor =
+ Executors.newFixedThreadPool(NUMBER_OF_THREADS);
+
+ public static ServerRoomDatabase getDatabase(final Context context) {
+ if (INSTANCE == null) {
+ synchronized (ServerRoomDatabase.class) {
+ if (INSTANCE == null) {
+ if (INSTANCE == null) {
+ INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
+ ServerRoomDatabase.class, "server_database")
+ .build();
+ }
+ }
+ }
+ }
+ return INSTANCE;
+ }
+
+}
+
diff --git a/app/src/main/java/net/schueller/peertube/database/ServerViewModel.java b/app/src/main/java/net/schueller/peertube/database/ServerViewModel.java
new file mode 100644
index 0000000..bdd9b1c
--- /dev/null
+++ b/app/src/main/java/net/schueller/peertube/database/ServerViewModel.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2018 Stefan Schüller
+ *
+ * License: GPL-3.0+
+ * 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 3 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, see .
+ */
+package net.schueller.peertube.database;
+
+import android.app.Application;
+
+import androidx.lifecycle.AndroidViewModel;
+import androidx.lifecycle.LiveData;
+
+import java.util.List;
+
+public class ServerViewModel extends AndroidViewModel {
+
+ private ServerRepository mRepository;
+
+ private LiveData> mAllServers;
+
+ public ServerViewModel (Application application) {
+ super(application);
+ mRepository = new ServerRepository(application);
+ mAllServers = mRepository.getAllServers();
+ }
+
+ public LiveData> getAllServers() { return mAllServers; }
+
+ public void insert(Server server) { mRepository.insert(server); }
+
+ public void delete(Server server) {mRepository.delete(server);}
+
+}
diff --git a/app/src/main/java/net/schueller/peertube/fragment/AddServerFragment.java b/app/src/main/java/net/schueller/peertube/fragment/AddServerFragment.java
new file mode 100644
index 0000000..6e15a2b
--- /dev/null
+++ b/app/src/main/java/net/schueller/peertube/fragment/AddServerFragment.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2018 Stefan Schüller
+ *
+ * License: GPL-3.0+
+ * 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 3 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, see .
+ */
+package net.schueller.peertube.fragment;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Patterns;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Toast;
+
+import net.schueller.peertube.R;
+import net.schueller.peertube.activity.SelectServerActivity;
+import net.schueller.peertube.activity.ServerAddressBookActivity;
+import net.schueller.peertube.helper.APIUrlHelper;
+
+import static android.app.Activity.RESULT_OK;
+
+
+public class AddServerFragment extends Fragment {
+
+ public static final String TAG = "AddServerFragment";
+ public static final Integer PICK_SERVER = 1;
+
+ private OnFragmentInteractionListener mListener;
+
+ private View mView;
+
+ public AddServerFragment() {
+ // Required empty public constructor
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ Log.d(TAG, "onCreateView");
+ // Inflate the layout for this fragment
+
+ mView = inflater.inflate(R.layout.fragment_add_server, container, false);
+
+ // bind button click
+ Button addServerButton = mView.findViewById(R.id.addServerButton);
+ addServerButton.setOnClickListener(view -> {
+
+ Activity act = getActivity();
+
+ Boolean formValid = true;
+
+ // close keyboard
+ try {
+ InputMethodManager inputManager = (InputMethodManager)
+ act.getSystemService(Context.INPUT_METHOD_SERVICE);
+
+ inputManager.hideSoftInputFromWindow(act.getCurrentFocus().getWindowToken(),
+ InputMethodManager.HIDE_NOT_ALWAYS);
+ } catch (Exception e) {
+
+ }
+
+ EditText selectedLabel = mView.findViewById(R.id.serverLabel);
+ if ( TextUtils.isEmpty(selectedLabel.getText())){
+ selectedLabel.setError( act.getString(R.string.server_book_label_is_required ));
+ Toast.makeText(act, R.string.invalid_url, Toast.LENGTH_LONG).show();
+ formValid = false;
+ }
+
+ // validate url
+ EditText selectedUrl = mView.findViewById(R.id.serverUrl);
+ String serverUrl = APIUrlHelper.cleanServerUrl(selectedUrl.getText().toString());
+ selectedUrl.setText(serverUrl);
+
+ if (!Patterns.WEB_URL.matcher(serverUrl).matches()) {
+ selectedUrl.setError( act.getString(R.string.server_book_valid_url_is_required ) );
+ Toast.makeText(act, R.string.invalid_url, Toast.LENGTH_LONG).show();
+ formValid = false;
+ }
+
+ if (formValid) {
+ if (act instanceof ServerAddressBookActivity) {
+ ((ServerAddressBookActivity) act).addServer(mView);
+
+ }
+ }
+
+ });
+
+// Button testServerButton = mView.findViewById(R.id.testServerButton);
+// testServerButton.setOnClickListener(view -> {
+// Activity act = getActivity();
+// if (act instanceof ServerAddressBookActivity) {
+// ((ServerAddressBookActivity) act).testServer();
+// }
+// });
+
+ Button pickServerUrl = mView.findViewById(R.id.pickServerUrl);
+ pickServerUrl.setOnClickListener(view -> {
+ Intent intentServer = new Intent(getActivity(), SelectServerActivity.class);
+ this.startActivityForResult(intentServer, PICK_SERVER);
+ });
+
+ return mView;
+ }
+
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == PICK_SERVER) {
+ if(resultCode == RESULT_OK) {
+
+ String serverUrlTest = data.getStringExtra("serverUrl");
+ //Log.d(TAG, "serverUrl " + serverUrlTest);
+ EditText serverUrl = mView.findViewById(R.id.serverUrl);
+ serverUrl.setText(serverUrlTest);
+
+ EditText serverLabel = mView.findViewById(R.id.serverLabel);
+ if ("".equals(serverLabel.getText().toString())) {
+ serverLabel.setText(data.getStringExtra("serverName"));
+ }
+
+ }
+ }
+ }
+
+ @Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+ if (context instanceof OnFragmentInteractionListener) {
+ mListener = (OnFragmentInteractionListener) context;
+ } else {
+ throw new RuntimeException(context.toString()
+ + " must implement OnFragmentInteractionListener");
+ }
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ mListener = null;
+ }
+
+ public interface OnFragmentInteractionListener {
+ // TODO: Update argument type and name
+ void onFragmentInteraction(Uri uri);
+ }
+}
diff --git a/app/src/main/java/net/schueller/peertube/fragment/VideoPlayerFragment.java b/app/src/main/java/net/schueller/peertube/fragment/VideoPlayerFragment.java
index 62e8d24..a37af1c 100644
--- a/app/src/main/java/net/schueller/peertube/fragment/VideoPlayerFragment.java
+++ b/app/src/main/java/net/schueller/peertube/fragment/VideoPlayerFragment.java
@@ -18,6 +18,7 @@
package net.schueller.peertube.fragment;
import android.app.Activity;
+import android.app.AppOpsManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -25,12 +26,15 @@ import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.util.Log;
+import android.view.GestureDetector;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
@@ -65,6 +69,7 @@ import net.schueller.peertube.service.VideoPlayerService;
import java.util.Objects;
import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
import androidx.fragment.app.Fragment;
import retrofit2.Call;
import retrofit2.Callback;
@@ -83,7 +88,7 @@ public class VideoPlayerFragment extends Fragment implements VideoRendererEventL
private LinearLayout torrentStatus;
private static final String TAG = "VideoPlayerFragment";
-
+ private GestureDetector mDetector;
private ServiceConnection mConnection = new ServiceConnection() {
@@ -135,6 +140,9 @@ public class VideoPlayerFragment extends Fragment implements VideoRendererEventL
simpleExoPlayerView.setControllerShowTimeoutMs(1000);
simpleExoPlayerView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FIT);
+ mDetector = new GestureDetector(context, new MyGestureListener());
+ simpleExoPlayerView.setOnTouchListener(touchListener);
+
torrentStatus = activity.findViewById(R.id.exo_torrent_status);
// Full screen Icon
@@ -146,13 +154,7 @@ public class VideoPlayerFragment extends Fragment implements VideoRendererEventL
fullscreenButton.setOnClickListener(view -> {
Log.d(TAG, "Fullscreen");
- if (!isFullscreen) {
- isFullscreen = true;
- activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
- } else {
- isFullscreen = false;
- activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
- }
+ fullScreenToggle();
});
if (!mBound) {
@@ -182,7 +184,7 @@ public class VideoPlayerFragment extends Fragment implements VideoRendererEventL
mService.setCurrentVideo(video);
if (video == null) {
- Toast.makeText(context, "Something went wrong...Please try later!", Toast.LENGTH_SHORT).show();
+ Toast.makeText(context, "Unable to retrieve video information, try again later.", Toast.LENGTH_SHORT).show();
return;
}
@@ -193,7 +195,7 @@ public class VideoPlayerFragment extends Fragment implements VideoRendererEventL
@Override
public void onFailure(@NonNull Call