diff --git a/app/build.gradle b/app/build.gradle
index 8e0b66e4..d5e61613 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -67,9 +67,14 @@ android {
res.srcDirs = [
'src/main/res/layouts/mastodon',
'src/main/res/layouts/peertube',
- ' src/main/res/layouts',
+ 'src/main/res/layouts',
+ 'src/main/res/drawables/mastodon',
+ 'src/main/res/drawables/peertube',
+ 'src/main/res/drawables',
+ 'src/main/res/menus/mastodon',
+ 'src/main/res/menus/peertube',
+ 'src/main/res/menus',
'src/main/res'
-
]
}
}
@@ -154,6 +159,49 @@ dependencies {
// debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1'
implementation 'com.r0adkll:slidableactivity:2.1.0'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
+
+
+ implementation 'androidx.vectordrawable:vectordrawable:1.1.0'
+ implementation "androidx.fragment:fragment:1.5.5"
+ implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
+ implementation 'androidx.browser:browser:1.4.0'
+ implementation 'androidx.documentfile:documentfile:1.0.1'
+ implementation 'com.github.amoskorir:avatarimagegenerator:1.5.0'
+ implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.1.0'
+ implementation 'com.google.android.exoplayer:extension-mediasession:2.18.1'
+ implementation "com.github.mabbas007:TagsEditText:1.0.5"
+ implementation "net.gotev:uploadservice:4.5.1"
+ implementation "net.gotev:uploadservice-okhttp:4.5.1"
+ implementation 'androidx.media:media:1.6.0'
+ implementation 'com.github.ybq:Android-SpinKit:1.4.0'
+ implementation 'com.github.mancj:MaterialSearchBar:0.8.5'
+
+ implementation 'com.github.vkay94:DoubleTapPlayerView:1.0.0'
+
+
+ //************ CAST **************///
+
+ //---> Google libs (google_full)
+ playstoreImplementation "com.google.android.gms:play-services-cast-tv:19.0.1"
+ playstoreImplementation "com.google.android.gms:play-services-cast:21.0.1"
+ playstoreImplementation "androidx.mediarouter:mediarouter:1.3.0"
+ playstoreImplementation 'com.google.android.gms:play-services-cast-framework:21.0.1'
+
+ playstoreImplementation "com.google.android.gms:play-services-cast-tv:19.0.1"
+ playstoreImplementation "com.google.android.gms:play-services-cast:21.0.1"
+ playstoreImplementation "androidx.mediarouter:mediarouter:1.3.0"
+ playstoreImplementation 'com.google.android.gms:play-services-cast-framework:21.0.1'
+
+ //----> Other flavors
+ fdroidImplementation 'su.litvak.chromecast:api-v2:0.11.3'
+ fdroidImplementation 'com.fasterxml.jackson.core:jackson-core:2.12.0'
+ fdroidImplementation 'org.slf4j:slf4j-simple:1.7.30'
+ fdroidImplementation 'com.github.evozi:Cyanea:1.0.7'
+
+ fdroidImplementation 'su.litvak.chromecast:api-v2:0.11.3'
+ fdroidImplementation 'com.fasterxml.jackson.core:jackson-core:2.12.0'
+ fdroidImplementation 'org.slf4j:slf4j-simple:1.7.30'
+
}
def getCurrentFlavor() {
Gradle gradle = getGradle()
diff --git a/app/src/fdroid/res/layout/min_controller.xml b/app/src/fdroid/res/layout/min_controller.xml
new file mode 100644
index 00000000..1c226909
--- /dev/null
+++ b/app/src/fdroid/res/layout/min_controller.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d1bd6870..02c699d2 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -369,5 +369,122 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/AccountActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/AccountActivity.java
new file mode 100644
index 00000000..1dfec337
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/peertube/activities/AccountActivity.java
@@ -0,0 +1,299 @@
+package app.fedilab.android.peertube.activities;
+/* Copyright 2020 Thomas Schneider
+ *
+ * This file is a part of TubeLab
+ *
+ * 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.
+ *
+ * TubeLab 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 TubeLab; if not,
+ * see . */
+
+import static app.fedilab.android.peertube.activities.MainActivity.badgeCount;
+
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.database.sqlite.SQLiteDatabase;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.text.Html;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.UnderlineSpan;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentStatePagerAdapter;
+import androidx.viewpager.widget.PagerAdapter;
+import androidx.viewpager.widget.ViewPager;
+
+import com.google.android.material.tabs.TabLayout;
+
+import org.jetbrains.annotations.NotNull;
+
+import app.fedilab.android.R;
+import app.fedilab.android.databinding.ActivityAccountBinding;
+import app.fedilab.android.mastodon.activities.BaseActivity;
+import app.fedilab.android.peertube.client.RetrofitPeertubeAPI;
+import app.fedilab.android.peertube.client.data.AccountData.Account;
+import app.fedilab.android.peertube.fragment.DisplayAccountsFragment;
+import app.fedilab.android.peertube.fragment.DisplayChannelsFragment;
+import app.fedilab.android.peertube.fragment.DisplayNotificationsFragment;
+import app.fedilab.android.peertube.helper.Helper;
+import app.fedilab.android.peertube.helper.SwitchAccountHelper;
+import app.fedilab.android.peertube.sqlite.AccountDAO;
+import app.fedilab.android.peertube.sqlite.Sqlite;
+
+
+public class AccountActivity extends BaseActivity {
+
+
+ private ActivityAccountBinding binding;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ binding = ActivityAccountBinding.inflate(getLayoutInflater());
+ View view = binding.getRoot();
+ setContentView(view);
+ if (getSupportActionBar() != null)
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+
+ SpannableString content_create = new SpannableString(getString(R.string.join_peertube));
+ content_create.setSpan(new UnderlineSpan(), 0, content_create.length(), 0);
+ content_create.setSpan(new ForegroundColorSpan(Helper.fetchAccentColor(AccountActivity.this)), 0, content_create.length(),
+ Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+
+
+ SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
+ SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
+ String token = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null);
+
+ Account account = new AccountDAO(AccountActivity.this, db).getAccountByToken(token);
+ if (account == null) {
+ Helper.logoutCurrentUser(AccountActivity.this, null);
+ return;
+ }
+
+
+ setTitle(String.format("@%s", account.getUsername()));
+
+ Helper.loadAvatar(AccountActivity.this, account, binding.profilePicture);
+ binding.username.setText(String.format("@%s", account.getUsername()));
+ binding.displayname.setText(account.getDisplayName());
+
+ binding.instance.setText(account.getHost());
+
+ binding.logoutButton.setOnClickListener(v -> {
+ AlertDialog.Builder dialogBuilderLogoutAccount = new AlertDialog.Builder(AccountActivity.this);
+ dialogBuilderLogoutAccount.setMessage(getString(R.string.logout_account_confirmation, account.getUsername(), account.getHost()));
+ dialogBuilderLogoutAccount.setPositiveButton(R.string.action_logout, (dialog, id) -> {
+ Helper.logoutCurrentUser(AccountActivity.this, account);
+ dialog.dismiss();
+ });
+ dialogBuilderLogoutAccount.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss());
+ AlertDialog alertDialogLogoutAccount = dialogBuilderLogoutAccount.create();
+ alertDialogLogoutAccount.show();
+ });
+
+ binding.settings.setOnClickListener(v -> {
+ Intent intent = new Intent(AccountActivity.this, SettingsActivity.class);
+ startActivity(intent);
+ });
+
+
+ TabLayout.Tab notificationTab = binding.accountTabLayout.newTab();
+ if (Helper.isLoggedIn(AccountActivity.this)) {
+ if (badgeCount > 0) {
+ binding.accountTabLayout.addTab(notificationTab.setText(getString(R.string.title_notifications) + " (" + badgeCount + ")"));
+ } else {
+ binding.accountTabLayout.addTab(notificationTab.setText(getString(R.string.title_notifications)));
+ }
+ binding.accountTabLayout.addTab(binding.accountTabLayout.newTab().setText(getString(R.string.title_muted)));
+ binding.accountTabLayout.addTab(binding.accountTabLayout.newTab().setText(getString(R.string.title_channel)));
+
+ binding.accountViewpager.setOffscreenPageLimit(3);
+
+
+ binding.accountViewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ TabLayout.Tab tab = binding.accountTabLayout.getTabAt(position);
+ if (tab != null)
+ tab.select();
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+
+ }
+ });
+
+
+ binding.accountTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
+ @Override
+ public void onTabSelected(TabLayout.Tab tab) {
+ binding.accountViewpager.setCurrentItem(tab.getPosition());
+ }
+
+ @Override
+ public void onTabUnselected(TabLayout.Tab tab) {
+
+ }
+
+ @Override
+ public void onTabReselected(TabLayout.Tab tab) {
+ Fragment fragment = null;
+ if (binding.accountViewpager.getAdapter() != null)
+ fragment = (Fragment) binding.accountViewpager.getAdapter().instantiateItem(binding.accountViewpager, tab.getPosition());
+ switch (tab.getPosition()) {
+ case 0:
+ if (badgeCount > 0) {
+ android.app.AlertDialog.Builder builder;
+ builder = new android.app.AlertDialog.Builder(AccountActivity.this);
+ builder.setMessage(R.string.mark_all_notifications_as_read_confirm);
+ builder.setIcon(android.R.drawable.ic_dialog_alert)
+ .setPositiveButton(R.string.mark_all_as_read, (dialog, which) -> {
+ new Thread(() -> {
+ new RetrofitPeertubeAPI(AccountActivity.this).markAllAsRead();
+ Handler mainHandler = new Handler(Looper.getMainLooper());
+ badgeCount = 0;
+ Runnable myRunnable = () -> binding.accountTabLayout.getTabAt(0).setText(getString(R.string.title_notifications));
+ mainHandler.post(myRunnable);
+ }).start();
+
+ dialog.dismiss();
+ })
+ .setNegativeButton(R.string.no, (dialog, which) -> dialog.dismiss())
+ .show();
+ } else {
+ if (fragment != null) {
+ DisplayNotificationsFragment displayNotificationsFragment = ((DisplayNotificationsFragment) fragment);
+ displayNotificationsFragment.scrollToTop();
+ }
+ }
+ break;
+ case 1:
+ if (fragment != null) {
+ DisplayAccountsFragment displayAccountsFragment = ((DisplayAccountsFragment) fragment);
+ displayAccountsFragment.scrollToTop();
+ }
+ break;
+ case 2:
+ if (fragment != null) {
+ DisplayChannelsFragment displayChannelsFragment = ((DisplayChannelsFragment) fragment);
+ displayChannelsFragment.scrollToTop();
+ }
+ break;
+ }
+ }
+ });
+
+ PagerAdapter mPagerAdapter = new AccountsPagerAdapter(getSupportFragmentManager());
+ binding.accountViewpager.setAdapter(mPagerAdapter);
+ } else {
+ binding.accountTabLayout.setVisibility(View.GONE);
+ binding.accountViewpager.setVisibility(View.GONE);
+ binding.remoteAccount.setVisibility(View.VISIBLE);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
+ binding.remoteAccount.setText(Html.fromHtml(getString(R.string.remote_account_from, account.getSoftware()), Html.FROM_HTML_MODE_LEGACY));
+ else
+ binding.remoteAccount.setText(Html.fromHtml(getString(R.string.remote_account_from, account.getSoftware())));
+ }
+ }
+
+ public void updateCounter() {
+ if (badgeCount > 0) {
+ binding.accountTabLayout.getTabAt(0).setText(getString(R.string.title_notifications) + " (" + badgeCount + ")");
+ } else {
+ binding.accountTabLayout.getTabAt(0).setText(getString(R.string.title_notifications));
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(@NotNull Menu menu) {
+ getMenuInflater().inflate(R.menu.main_profile_peertube, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ finish();
+ overridePendingTransition(R.anim.slide_out_up, R.anim.slide_in_up_down);
+ return true;
+ } else if (item.getItemId() == R.id.action_add_account) {
+ SwitchAccountHelper.switchDialog(AccountActivity.this, true);
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+ @Override
+ public void onBackPressed() {
+ super.onBackPressed();
+ overridePendingTransition(R.anim.slide_out_up, R.anim.slide_in_up_down);
+ }
+
+ /**
+ * Pager adapter for three tabs (notifications, muted, blocked)
+ */
+ private static class AccountsPagerAdapter extends FragmentStatePagerAdapter {
+
+ AccountsPagerAdapter(FragmentManager fm) {
+ super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
+ }
+
+ @NotNull
+ @Override
+ public Fragment getItem(int position) {
+ Bundle bundle = new Bundle();
+ switch (position) {
+ case 1:
+ DisplayAccountsFragment displayAccountsFragment = new DisplayAccountsFragment();
+ bundle.putSerializable("accountFetch", RetrofitPeertubeAPI.DataType.MUTED);
+ displayAccountsFragment.setArguments(bundle);
+ return displayAccountsFragment;
+ case 2:
+ return new DisplayChannelsFragment();
+ default:
+ return new DisplayNotificationsFragment();
+ }
+ }
+
+ @Override
+ public int getCount() {
+ return 3;
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/AllLocalPlaylistsActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/AllLocalPlaylistsActivity.java
new file mode 100644
index 00000000..3b58f7a5
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/peertube/activities/AllLocalPlaylistsActivity.java
@@ -0,0 +1,123 @@
+package app.fedilab.android.peertube.activities;
+/* Copyright 2020 Thomas Schneider
+ *
+ * This file is a part of TubeLab
+ *
+ * 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.
+ *
+ * TubeLab 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 TubeLab; if not,
+ * see . */
+
+
+import android.os.Bundle;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import androidx.lifecycle.ViewModelProvider;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import app.fedilab.android.peertube.R;
+import app.fedilab.android.peertube.client.data.PlaylistData.Playlist;
+import app.fedilab.android.peertube.client.data.VideoPlaylistData;
+import app.fedilab.android.peertube.drawer.PlaylistAdapter;
+import app.fedilab.android.peertube.helper.HelperInstance;
+import app.fedilab.android.peertube.helper.Theme;
+import app.fedilab.android.peertube.viewmodel.PlaylistsVM;
+
+
+public class AllLocalPlaylistsActivity extends BaseActivity implements PlaylistAdapter.AllPlaylistRemoved {
+
+
+ PlaylistAdapter playlistAdapter;
+ private RelativeLayout mainLoader;
+ private RelativeLayout textviewNoAction;
+ private List playlists;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Theme.setTheme(this, HelperInstance.getLiveInstance(this), false);
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_all_playlist);
+
+ if (getSupportActionBar() != null)
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ setTitle(R.string.playlists);
+
+
+ textviewNoAction = findViewById(R.id.no_action);
+ mainLoader = findViewById(R.id.loader);
+ RelativeLayout nextElementLoader = findViewById(R.id.loading_next_items);
+ mainLoader.setVisibility(View.VISIBLE);
+ nextElementLoader.setVisibility(View.GONE);
+
+ PlaylistsVM viewModel = new ViewModelProvider(AllLocalPlaylistsActivity.this).get(PlaylistsVM.class);
+ viewModel.localePlaylist().observe(AllLocalPlaylistsActivity.this, this::manageVIewPlaylists);
+
+ FloatingActionButton add_new = findViewById(R.id.add_new);
+ add_new.setVisibility(View.GONE);
+
+ TextView no_action_text = findViewById(R.id.no_action_text);
+ no_action_text.setText(R.string.no_playlist);
+ playlists = new ArrayList<>();
+ RecyclerView lv_playlist = findViewById(R.id.lv_playlist);
+ playlistAdapter = new PlaylistAdapter(playlists, true);
+ playlistAdapter.allPlaylistRemoved = this;
+ lv_playlist.setAdapter(playlistAdapter);
+ LinearLayoutManager mLayoutManager = new LinearLayoutManager(AllLocalPlaylistsActivity.this);
+ lv_playlist.setLayoutManager(mLayoutManager);
+
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+
+ public void manageVIewPlaylists(List videoPlaylistExports) {
+ mainLoader.setVisibility(View.GONE);
+ if (videoPlaylistExports == null) {
+ textviewNoAction.setVisibility(View.VISIBLE);
+ return;
+ }
+ if (videoPlaylistExports.size() > 0) {
+ for (VideoPlaylistData.VideoPlaylistExport videoPlaylistExport : videoPlaylistExports) {
+ playlists.add(videoPlaylistExport.getPlaylist());
+ }
+ playlistAdapter.notifyDataSetChanged();
+ textviewNoAction.setVisibility(View.GONE);
+ } else {
+ textviewNoAction.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ public void onAllPlaylistRemoved() {
+ textviewNoAction.setVisibility(View.VISIBLE);
+ }
+
+}
diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/AllPlaylistsActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/AllPlaylistsActivity.java
new file mode 100644
index 00000000..6cf47348
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/peertube/activities/AllPlaylistsActivity.java
@@ -0,0 +1,431 @@
+package app.fedilab.android.peertube.activities;
+/* Copyright 2020 Thomas Schneider
+ *
+ * This file is a part of TubeLab
+ *
+ * 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.
+ *
+ * TubeLab 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 TubeLab; if not,
+ * see . */
+
+import static app.fedilab.android.peertube.activities.PeertubeUploadActivity.MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE;
+import static app.fedilab.android.peertube.helper.Helper.peertubeInformation;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.text.InputFilter;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.recyclerview.widget.LinearLayoutManager;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.resource.bitmap.CenterCrop;
+import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
+import com.bumptech.glide.request.RequestOptions;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import app.fedilab.android.peertube.R;
+import app.fedilab.android.peertube.client.APIResponse;
+import app.fedilab.android.peertube.client.RetrofitPeertubeAPI;
+import app.fedilab.android.peertube.client.data.ChannelData;
+import app.fedilab.android.peertube.client.data.PlaylistData.Playlist;
+import app.fedilab.android.peertube.client.entities.Item;
+import app.fedilab.android.peertube.client.entities.PlaylistParams;
+import app.fedilab.android.peertube.databinding.ActivityAllPlaylistBinding;
+import app.fedilab.android.peertube.databinding.AddPlaylistBinding;
+import app.fedilab.android.peertube.drawer.PlaylistAdapter;
+import app.fedilab.android.peertube.helper.Helper;
+import app.fedilab.android.peertube.helper.HelperInstance;
+import app.fedilab.android.peertube.helper.Theme;
+import app.fedilab.android.peertube.viewmodel.ChannelsVM;
+import app.fedilab.android.peertube.viewmodel.PlaylistsVM;
+import es.dmoral.toasty.Toasty;
+
+
+public class AllPlaylistsActivity extends BaseActivity implements PlaylistAdapter.AllPlaylistRemoved {
+
+
+ private static final int PICK_AVATAR = 467;
+ PlaylistAdapter playlistAdapter;
+ private HashMap privacyToSend;
+ private String idChannel;
+ private List playlists;
+ private Playlist playlistToEdit;
+ private List myChannels;
+ private ChannelData.Channel selectedChannel;
+ private AddPlaylistBinding bindingDialog;
+ private Uri inputData;
+ private ActivityAllPlaylistBinding binding;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Theme.setTheme(this, HelperInstance.getLiveInstance(this), false);
+ binding = ActivityAllPlaylistBinding.inflate(getLayoutInflater());
+ View viewRoot = binding.getRoot();
+ setContentView(viewRoot);
+
+ if (getSupportActionBar() != null)
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ setTitle(R.string.playlists);
+
+
+ binding.loader.setVisibility(View.VISIBLE);
+ binding.loadingNextItems.setVisibility(View.GONE);
+ idChannel = null;
+
+ PlaylistsVM viewModel = new ViewModelProvider(AllPlaylistsActivity.this).get(PlaylistsVM.class);
+ viewModel.manage(PlaylistsVM.action.GET_PLAYLISTS, null, null).observe(AllPlaylistsActivity.this, apiResponse -> manageVIewPlaylists(PlaylistsVM.action.GET_PLAYLISTS, apiResponse));
+
+
+ LinkedHashMap privaciesInit = new LinkedHashMap<>(peertubeInformation.getPrivacies());
+ if (privaciesInit.size() > 0) {
+ Map.Entry entryInt = privaciesInit.entrySet().iterator().next();
+ privacyToSend = new HashMap<>();
+ privacyToSend.put(entryInt.getKey(), entryInt.getValue());
+ }
+
+
+ playlists = new ArrayList<>();
+ playlistAdapter = new PlaylistAdapter(playlists, false);
+ playlistAdapter.allPlaylistRemoved = this;
+ binding.lvPlaylist.setAdapter(playlistAdapter);
+ LinearLayoutManager mLayoutManager = new LinearLayoutManager(AllPlaylistsActivity.this);
+ binding.lvPlaylist.setLayoutManager(mLayoutManager);
+ binding.addNew.setOnClickListener(view -> manageAlert(null));
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+
+ public void manageVIewPlaylists(PlaylistsVM.action actionType, APIResponse apiResponse) {
+ binding.loader.setVisibility(View.GONE);
+ if (apiResponse.getError() != null) {
+ Toasty.error(AllPlaylistsActivity.this, apiResponse.getError().getError(), Toast.LENGTH_LONG).show();
+ return;
+ }
+ if (actionType == PlaylistsVM.action.GET_PLAYLISTS) {
+ if (apiResponse.getPlaylists() != null && apiResponse.getPlaylists().size() > 0) {
+ playlists.addAll(apiResponse.getPlaylists());
+ playlistAdapter.notifyDataSetChanged();
+ binding.noAction.setVisibility(View.GONE);
+ } else {
+ binding.noAction.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+
+ public void manageAlert(Playlist playlistParam) {
+
+ playlistToEdit = playlistParam;
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(AllPlaylistsActivity.this);
+ bindingDialog = AddPlaylistBinding.inflate(LayoutInflater.from(AllPlaylistsActivity.this), null, false);
+ dialogBuilder.setView(bindingDialog.getRoot());
+
+ dialogBuilder.setView(bindingDialog.getRoot());
+
+
+ ChannelsVM viewModelC = new ViewModelProvider(AllPlaylistsActivity.this).get(ChannelsVM.class);
+ viewModelC.get(RetrofitPeertubeAPI.DataType.MY_CHANNELS, null).observe(AllPlaylistsActivity.this, this::manageVIewChannels);
+
+ bindingDialog.displayName.setFilters(new InputFilter[]{new InputFilter.LengthFilter(120)});
+ bindingDialog.description.setFilters(new InputFilter[]{new InputFilter.LengthFilter(1000)});
+
+ if (playlistToEdit != null) {
+ bindingDialog.displayName.setText(playlistToEdit.getDisplayName());
+ bindingDialog.description.setText(playlistToEdit.getDescription());
+ }
+ dialogBuilder.setPositiveButton(R.string.validate, null);
+ dialogBuilder.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss());
+
+ AlertDialog alertDialog = dialogBuilder.create();
+
+ bindingDialog.selectFile.setOnClickListener(v -> {
+ if (ContextCompat.checkSelfPermission(AllPlaylistsActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) !=
+ PackageManager.PERMISSION_GRANTED) {
+ ActivityCompat.requestPermissions(AllPlaylistsActivity.this,
+ new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
+ MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
+ return;
+ }
+
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setType("*/*");
+ String[] mimetypes = {"image/*"};
+ intent.putExtra(Intent.EXTRA_MIME_TYPES, mimetypes);
+ startActivityForResult(intent, PICK_AVATAR);
+ });
+ Helper.loadGiF(AllPlaylistsActivity.this, playlistParam != null ? playlistParam.getThumbnailPath() : null, bindingDialog.profilePicture);
+ alertDialog.setOnShowListener(dialogInterface -> {
+
+ Button button = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
+ button.setOnClickListener(view -> {
+ if (bindingDialog.displayName.getText() != null && bindingDialog.displayName.getText().toString().trim().length() > 0) {
+ PlaylistParams playlistElement = new PlaylistParams();
+ playlistElement.setDisplayName(bindingDialog.displayName.getText().toString().trim());
+ if (bindingDialog.description.getText() != null && bindingDialog.description.getText().toString().trim().length() > 0) {
+ playlistElement.setDescription(bindingDialog.description.getText().toString().trim());
+ }
+ playlistElement.setVideoChannelId(idChannel);
+ String label;
+ Map.Entry privacyM = privacyToSend.entrySet().iterator().next();
+ Item privacyItem = new Item();
+ privacyItem.setId(privacyM.getKey());
+ privacyItem.setLabel(privacyM.getValue());
+ label = privacyM.getValue();
+ if ((label.trim().compareTo("Public") == 0 && (playlistElement.getVideoChannelId() == null || playlistElement.getVideoChannelId().trim().compareTo("null") == 0))) {
+ Toasty.error(AllPlaylistsActivity.this, getString(R.string.error_channel_mandatory), Toast.LENGTH_LONG).show();
+ } else {
+ if (privacyToSend != null) {
+ playlistElement.setPrivacy(privacyItem.getId());
+ }
+ new Thread(() -> {
+ String playlistId;
+ if (playlistToEdit == null) {
+ APIResponse apiResponse = new RetrofitPeertubeAPI(AllPlaylistsActivity.this).createOrUpdatePlaylist(PlaylistsVM.action.CREATE_PLAYLIST, null, playlistElement, inputData);
+ playlistId = apiResponse.getActionReturn();
+ } else {
+ playlistId = playlistToEdit.getId();
+ new RetrofitPeertubeAPI(AllPlaylistsActivity.this).createOrUpdatePlaylist(PlaylistsVM.action.UPDATE_PLAYLIST, playlistId, playlistElement, inputData);
+ }
+ Handler mainHandler = new Handler(Looper.getMainLooper());
+ Runnable myRunnable = () -> {
+ Playlist playlist;
+ if (playlistToEdit == null) {
+ playlist = new Playlist();
+ } else {
+ playlist = playlistToEdit;
+ }
+ playlist.setId(playlistId);
+ playlist.setUuid(playlistId);
+ playlist.setDescription(playlistElement.getDescription());
+ playlist.setDisplayName(playlistElement.getDisplayName());
+ playlist.setVideoChannel(selectedChannel);
+ playlist.setPrivacy(privacyItem);
+ if (playlistToEdit == null) {
+ playlists.add(playlist);
+ }
+ playlistAdapter.notifyDataSetChanged();
+ };
+ mainHandler.post(myRunnable);
+ }).start();
+ alertDialog.dismiss();
+ }
+ } else {
+ Toasty.error(AllPlaylistsActivity.this, getString(R.string.error_display_name), Toast.LENGTH_LONG).show();
+ }
+ });
+ });
+
+ if (playlistToEdit == null) {
+ alertDialog.setTitle(getString(R.string.action_playlist_create));
+ } else {
+ alertDialog.setTitle(getString(R.string.action_playlist_edit));
+ }
+ alertDialog.setOnDismissListener(dialogInterface -> {
+ //Hide keyboard
+ InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ assert imm != null;
+ imm.hideSoftInputFromWindow(bindingDialog.displayName.getWindowToken(), 0);
+ });
+ if (alertDialog.getWindow() != null)
+ alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
+ alertDialog.show();
+ }
+
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == PICK_AVATAR && resultCode == Activity.RESULT_OK) {
+ if (data == null || data.getData() == null) {
+ Toasty.error(AllPlaylistsActivity.this, getString(R.string.toot_select_image_error), Toast.LENGTH_LONG).show();
+ return;
+ }
+ inputData = data.getData();
+ Glide.with(AllPlaylistsActivity.this)
+ .load(inputData)
+ .thumbnail(0.1f)
+ .apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10)))
+ .into(bindingDialog.profilePicture);
+
+ }
+ }
+
+ public void manageVIewChannels(APIResponse apiResponse) {
+ if (apiResponse.getError() != null || apiResponse.getChannels() == null || apiResponse.getChannels().size() == 0) {
+ if (apiResponse.getError() != null && apiResponse.getError().getError() != null)
+ Toasty.error(AllPlaylistsActivity.this, apiResponse.getError().getError(), Toast.LENGTH_LONG).show();
+ else
+ Toasty.error(AllPlaylistsActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
+ return;
+ }
+
+ //Populate channels
+ myChannels = apiResponse.getChannels();
+ String[] channelName = new String[myChannels.size() + 1];
+ String[] channelId = new String[myChannels.size() + 1];
+ int i = 1;
+ channelName[0] = "";
+ channelId[0] = "null";
+
+ for (ChannelData.Channel channel : myChannels) {
+ channelName[i] = channel.getName();
+ channelId[i] = channel.getId();
+ i++;
+ }
+
+ ArrayAdapter adapterChannel = new ArrayAdapter<>(AllPlaylistsActivity.this,
+ android.R.layout.simple_spinner_dropdown_item, channelName);
+ bindingDialog.setUploadChannel.setAdapter(adapterChannel);
+
+
+ LinkedHashMap translations = null;
+ if (peertubeInformation.getTranslations() != null)
+ translations = new LinkedHashMap<>(peertubeInformation.getTranslations());
+
+ LinkedHashMap privaciesInit = new LinkedHashMap<>(peertubeInformation.getPlaylistPrivacies());
+ Map.Entry entryInt = privaciesInit.entrySet().iterator().next();
+ privacyToSend = new HashMap<>();
+ privacyToSend.put(entryInt.getKey(), entryInt.getValue());
+ LinkedHashMap privacies = new LinkedHashMap<>(peertubeInformation.getPlaylistPrivacies());
+ //Populate privacies
+ String[] privaciesA = new String[privacies.size()];
+ Iterator> it = privacies.entrySet().iterator();
+ i = 0;
+ while (it.hasNext()) {
+ Map.Entry pair = it.next();
+ if (translations == null || translations.size() == 0 || !translations.containsKey(pair.getValue()))
+ privaciesA[i] = pair.getValue();
+ else
+ privaciesA[i] = translations.get(pair.getValue());
+ it.remove();
+ i++;
+ }
+
+ ArrayAdapter adapterPrivacies = new ArrayAdapter<>(AllPlaylistsActivity.this,
+ android.R.layout.simple_spinner_dropdown_item, privaciesA);
+ bindingDialog.setUploadPrivacy.setAdapter(adapterPrivacies);
+
+ if (playlistToEdit != null) {
+ Item privacy = playlistToEdit.getPrivacy();
+ if (privacy.getId() > 0) {
+ bindingDialog.setUploadPrivacy.setSelection(privacy.getId() - 1);
+ }
+ } else {
+ bindingDialog.setUploadPrivacy.setSelection(2);
+ }
+
+ //Manage privacies
+ bindingDialog.setUploadPrivacy.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ LinkedHashMap privaciesCheck = new LinkedHashMap<>(peertubeInformation.getPrivacies());
+ Iterator> it = privaciesCheck.entrySet().iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ Map.Entry pair = it.next();
+ if (i == position) {
+ privacyToSend = new HashMap<>();
+ privacyToSend.put(pair.getKey(), pair.getValue());
+ break;
+ }
+ it.remove();
+ i++;
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+
+ }
+ });
+
+ if (playlistToEdit != null) {
+ Item privacy = playlistToEdit.getPrivacy();
+
+ if (privacy.getId() > 0) {
+ bindingDialog.setUploadPrivacy.setSelection(privacy.getId() - 1);
+ }
+ }
+
+ //Manage languages
+ bindingDialog.setUploadChannel.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ idChannel = channelId[position];
+ if (position > 0) {
+ selectedChannel = myChannels.get(position - 1);
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+
+ }
+ });
+
+ if (playlistToEdit != null) {
+ int position = 0;
+ int k = 1;
+ for (ChannelData.Channel ac : myChannels) {
+ if (playlistToEdit.getVideoChannel() != null && playlistToEdit.getVideoChannel().getId() != null && ac.getId().compareTo(playlistToEdit.getVideoChannel().getId()) == 0) {
+ position = k;
+ break;
+ }
+ k++;
+ }
+ bindingDialog.setUploadChannel.setSelection(position);
+ }
+ }
+
+ @Override
+ public void onAllPlaylistRemoved() {
+ binding.noAction.setVisibility(View.VISIBLE);
+ }
+}
diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/InstancePickerActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/InstancePickerActivity.java
new file mode 100644
index 00000000..26348032
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/peertube/activities/InstancePickerActivity.java
@@ -0,0 +1,275 @@
+package app.fedilab.android.peertube.activities;
+/* Copyright 2020 Thomas Schneider
+ *
+ * This file is a part of TubeLab
+ *
+ * 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.
+ *
+ * TubeLab 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 TubeLab; if not,
+ * see . */
+
+
+import static app.fedilab.android.peertube.helper.Helper.peertubeInformation;
+
+import android.os.Bundle;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import app.fedilab.android.peertube.R;
+import app.fedilab.android.peertube.client.APIResponse;
+import app.fedilab.android.peertube.client.data.InstanceData;
+import app.fedilab.android.peertube.client.entities.InstanceParams;
+import app.fedilab.android.peertube.databinding.ActivityInstancePickerBinding;
+import app.fedilab.android.peertube.drawer.InstanceAdapter;
+import app.fedilab.android.peertube.helper.HelperInstance;
+import app.fedilab.android.peertube.helper.RoundedBackgroundSpan;
+import app.fedilab.android.peertube.helper.Theme;
+import app.fedilab.android.peertube.viewmodel.InstancesVM;
+import es.dmoral.toasty.Toasty;
+
+
+public class InstancePickerActivity extends BaseActivity {
+
+
+ boolean[] checkedItemsCategory;
+ int[] itemsKeyCategory;
+ String[] itemsLabelCategory;
+ boolean[] checkedItemsLanguage;
+ String[] itemsKeyLanguage;
+ String[] itemsLabelLanguage;
+ InstanceParams instanceParams;
+ private InstancesVM viewModel;
+ private ActivityInstancePickerBinding binding;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Theme.setTheme(this, HelperInstance.getLiveInstance(this), false);
+ super.onCreate(savedInstanceState);
+ if (getSupportActionBar() != null)
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ binding = ActivityInstancePickerBinding.inflate(getLayoutInflater());
+ setContentView(binding.getRoot());
+
+ binding.loader.setVisibility(View.VISIBLE);
+
+
+ String[] channelSensitive = new String[]{"do_not_list", "blur", "display", "no_opinion"};
+ String[] channelSensitivesLabel = new String[]{getString(R.string.do_not_list), getString(R.string.blur), getString(R.string.display), getString(R.string.no_opinion)};
+ ArrayAdapter adapterChannel = new ArrayAdapter<>(InstancePickerActivity.this,
+ android.R.layout.simple_spinner_dropdown_item, channelSensitivesLabel);
+ binding.sensitive.setAdapter(adapterChannel);
+
+
+ viewModel = new ViewModelProvider(InstancePickerActivity.this).get(InstancesVM.class);
+
+ binding.sensitive.setSelection(1, false);
+ binding.sensitive.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ instanceParams.setNsfwPolicy(channelSensitive[position]);
+ binding.loader.setVisibility(View.VISIBLE);
+ viewModel.getInstances(instanceParams).observe(InstancePickerActivity.this, apiResponse -> manageVIewInstance(apiResponse));
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+
+ }
+ });
+
+ if (peertubeInformation != null && peertubeInformation.getLanguages() != null) {
+ LinkedHashMap languages = new LinkedHashMap<>(peertubeInformation.getLanguages());
+ checkedItemsLanguage = new boolean[languages.size()];
+ itemsLabelLanguage = new String[languages.size()];
+ itemsKeyLanguage = new String[languages.size()];
+
+ binding.pickupLanguages.setOnClickListener(v -> {
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(InstancePickerActivity.this);
+
+ int i = 0;
+ if (languages.size() > 0) {
+ Iterator> it = languages.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry pair = it.next();
+ itemsLabelLanguage[i] = pair.getValue();
+ checkedItemsLanguage[i] = false;
+ itemsKeyLanguage[i] = pair.getKey();
+ it.remove();
+ i++;
+ }
+ }
+
+ dialogBuilder.setMultiChoiceItems(itemsLabelLanguage, checkedItemsLanguage, (dialog, which, isChecked) -> {
+ // The user checked or unchecked a box
+ checkedItemsLanguage[which] = isChecked;
+ });
+
+ dialogBuilder.setOnDismissListener(dialogInterface -> {
+
+ SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
+ String between = "";
+ stringBuilder.append(between);
+ List langs = new ArrayList<>();
+ int j = 0;
+ for (boolean itemcheked : checkedItemsLanguage) {
+ if (itemcheked) {
+ langs.add(itemsKeyLanguage[j]);
+ String lang = itemsLabelLanguage[j];
+ if (lang != null && lang.trim().toLowerCase().compareTo("null") != 0) {
+ if (between.length() == 0) between = " ";
+ String tag = " " + lang + " ";
+ stringBuilder.append(tag);
+ stringBuilder.setSpan(new RoundedBackgroundSpan(InstancePickerActivity.this), stringBuilder.length() - tag.length(), stringBuilder.length() - tag.length() + tag.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ stringBuilder.append(" ");
+ }
+ }
+ j++;
+ }
+ instanceParams.setLanguagesOr(langs);
+ binding.languagesView.setText(stringBuilder, TextView.BufferType.SPANNABLE);
+ if (binding.languagesView.getText().toString().trim().length() > 0) {
+ binding.languagesView.setVisibility(View.VISIBLE);
+ } else {
+ binding.languagesView.setVisibility(View.GONE);
+ }
+ binding.loader.setVisibility(View.VISIBLE);
+ viewModel.getInstances(instanceParams).observe(InstancePickerActivity.this, this::manageVIewInstance);
+ });
+ dialogBuilder.setPositiveButton(R.string.validate, (dialog, id) -> dialog.dismiss());
+
+ AlertDialog alertDialog = dialogBuilder.create();
+ alertDialog.setTitle(getString(R.string.pickup_languages));
+ alertDialog.show();
+ });
+ }
+
+ if (peertubeInformation != null && peertubeInformation.getCategories() != null) {
+ LinkedHashMap categories = new LinkedHashMap<>(peertubeInformation.getCategories());
+ checkedItemsCategory = new boolean[categories.size()];
+ itemsLabelCategory = new String[categories.size()];
+ itemsKeyCategory = new int[categories.size()];
+
+
+ binding.pickupCategories.setOnClickListener(v -> {
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(InstancePickerActivity.this);
+ int i = 0;
+ if (categories.size() > 0) {
+ Iterator> it = categories.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry pair = it.next();
+ itemsLabelCategory[i] = pair.getValue();
+ itemsKeyCategory[i] = pair.getKey();
+ checkedItemsCategory[i] = false;
+ it.remove();
+ i++;
+ }
+ }
+
+ dialogBuilder.setMultiChoiceItems(itemsLabelCategory, checkedItemsCategory, (dialog, which, isChecked) -> {
+ // The user checked or unchecked a box
+ checkedItemsCategory[which] = isChecked;
+ });
+
+ dialogBuilder.setOnDismissListener(dialogInterface -> {
+ int j = 0;
+ SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
+ String between = "";
+ stringBuilder.append(between);
+ List cats = new ArrayList<>();
+ for (boolean itemcheked : checkedItemsCategory) {
+ if (itemcheked) {
+ cats.add(itemsKeyCategory[j]);
+ String cat = itemsLabelCategory[j];
+ if (cat != null && cat.trim().toLowerCase().compareTo("null") != 0) {
+ if (between.length() == 0) between = " ";
+ String tag = " " + cat + " ";
+ stringBuilder.append(tag);
+ stringBuilder.setSpan(new RoundedBackgroundSpan(InstancePickerActivity.this), stringBuilder.length() - tag.length(), stringBuilder.length() - tag.length() + tag.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ stringBuilder.append(" ");
+ }
+ }
+ j++;
+ }
+ instanceParams.setCategoriesOr(cats);
+ binding.categoriesView.setText(stringBuilder, TextView.BufferType.SPANNABLE);
+ if (binding.categoriesView.getText().toString().trim().length() > 0) {
+ binding.categoriesView.setVisibility(View.VISIBLE);
+ } else {
+ binding.categoriesView.setVisibility(View.GONE);
+ }
+ binding.loader.setVisibility(View.VISIBLE);
+ viewModel.getInstances(instanceParams).observe(InstancePickerActivity.this, this::manageVIewInstance);
+ });
+ dialogBuilder.setPositiveButton(R.string.validate, (dialog, id) -> dialog.dismiss());
+
+ AlertDialog alertDialog = dialogBuilder.create();
+ alertDialog.setTitle(getString(R.string.pickup_categories));
+ alertDialog.show();
+ });
+ }
+
+
+ binding.loader.setVisibility(View.VISIBLE);
+
+ setTitle(R.string.instances_picker);
+
+ instanceParams = new InstanceParams();
+ instanceParams.setNsfwPolicy(channelSensitive[1]);
+ viewModel.getInstances(instanceParams).observe(InstancePickerActivity.this, this::manageVIewInstance);
+ }
+
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+
+ public void manageVIewInstance(APIResponse apiResponse) {
+ binding.loader.setVisibility(View.GONE);
+ if (apiResponse.getError() != null) {
+ Toasty.error(InstancePickerActivity.this, apiResponse.getError().getError(), Toast.LENGTH_LONG).show();
+ return;
+ }
+ List instances = apiResponse.getInstances();
+ RecyclerView lv_instances = findViewById(R.id.lv_instances);
+ if ((instances == null || instances.size() == 0)) {
+ binding.noAction.setVisibility(View.VISIBLE);
+ lv_instances.setVisibility(View.GONE);
+ } else {
+ binding.noAction.setVisibility(View.GONE);
+ lv_instances.setVisibility(View.VISIBLE);
+ InstanceAdapter instanceAdapter = new InstanceAdapter(instances);
+ lv_instances.setAdapter(instanceAdapter);
+ lv_instances.setLayoutManager(new LinearLayoutManager(InstancePickerActivity.this));
+ }
+ }
+}
diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/LocalPlaylistsActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/LocalPlaylistsActivity.java
new file mode 100644
index 00000000..864c1703
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/peertube/activities/LocalPlaylistsActivity.java
@@ -0,0 +1,82 @@
+package app.fedilab.android.peertube.activities;
+/* Copyright 2020 Thomas Schneider
+ *
+ * This file is a part of TubeLab
+ *
+ * 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.
+ *
+ * TubeLab 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 TubeLab; if not,
+ * see . */
+
+import android.os.Bundle;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.FragmentTransaction;
+
+import app.fedilab.android.peertube.R;
+import app.fedilab.android.peertube.client.data.PlaylistData;
+import app.fedilab.android.peertube.fragment.DisplayVideosFragment;
+import app.fedilab.android.peertube.helper.Helper;
+import app.fedilab.android.peertube.helper.HelperInstance;
+import app.fedilab.android.peertube.helper.Theme;
+import app.fedilab.android.peertube.viewmodel.TimelineVM;
+import es.dmoral.toasty.Toasty;
+
+
+public class LocalPlaylistsActivity extends AppCompatActivity {
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Theme.setTheme(this, HelperInstance.getLiveInstance(this), false);
+ if (getSupportActionBar() != null)
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+
+ setContentView(R.layout.activity_playlists);
+
+
+ PlaylistData.Playlist playlist;
+ Bundle b = getIntent().getExtras();
+ if (b != null) {
+ playlist = b.getParcelable("playlist");
+ if (playlist == null) {
+ return;
+ }
+ } else {
+ Toasty.error(LocalPlaylistsActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
+ return;
+ }
+ setTitle(playlist.getDisplayName());
+ if (savedInstanceState == null) {
+ DisplayVideosFragment displayVideosFragment = new DisplayVideosFragment();
+ Bundle bundle = new Bundle();
+ bundle.putSerializable(Helper.TIMELINE_TYPE, TimelineVM.TimelineType.VIDEOS_IN_LOCAL_PLAYLIST);
+ bundle.putSerializable("playlistId", playlist.getUuid());
+ displayVideosFragment.setArguments(bundle);
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+ ft.add(R.id.nav_host_fragment, displayVideosFragment).commit();
+ }
+
+ }
+
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+}
diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/LoginActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/LoginActivity.java
new file mode 100644
index 00000000..982cc976
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/peertube/activities/LoginActivity.java
@@ -0,0 +1,418 @@
+package app.fedilab.android.peertube.activities;
+/* Copyright 2020 Thomas Schneider
+ *
+ * This file is a part of TubeLab
+ *
+ * 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.
+ *
+ * TubeLab 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 TubeLab; if not,
+ * see . */
+
+import static app.fedilab.android.peertube.client.RetrofitPeertubeAPI.updateCredential;
+
+import android.annotation.SuppressLint;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.UnderlineSpan;
+import android.util.Patterns;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.List;
+
+import app.fedilab.android.peertube.BuildConfig;
+import app.fedilab.android.peertube.R;
+import app.fedilab.android.peertube.client.RetrofitPeertubeAPI;
+import app.fedilab.android.peertube.client.data.InstanceData;
+import app.fedilab.android.peertube.client.data.PluginData;
+import app.fedilab.android.peertube.client.entities.AcadInstances;
+import app.fedilab.android.peertube.client.entities.Oauth;
+import app.fedilab.android.peertube.client.entities.OauthParams;
+import app.fedilab.android.peertube.client.entities.Token;
+import app.fedilab.android.peertube.client.entities.WellKnownNodeinfo;
+import app.fedilab.android.peertube.client.mastodon.RetrofitMastodonAPI;
+import app.fedilab.android.peertube.databinding.ActivityLoginBinding;
+import app.fedilab.android.peertube.helper.Helper;
+import app.fedilab.android.peertube.helper.HelperAcadInstance;
+import app.fedilab.android.peertube.helper.HelperInstance;
+import app.fedilab.android.peertube.helper.Theme;
+import es.dmoral.toasty.Toasty;
+
+
+public class LoginActivity extends BaseActivity {
+
+
+ private static String client_id;
+ private static String client_secret;
+ private ActivityLoginBinding binding;
+ private String acadInstance;
+
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Theme.setTheme(this, HelperInstance.getLiveInstance(this), false);
+ super.onCreate(savedInstanceState);
+ binding = ActivityLoginBinding.inflate(getLayoutInflater());
+ View view = binding.getRoot();
+ setContentView(view);
+
+ if (getSupportActionBar() != null)
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+
+ SpannableString content_create;
+ content_create = new SpannableString(getString(R.string.join_peertube));
+
+ content_create.setSpan(new UnderlineSpan(), 0, content_create.length(), 0);
+ content_create.setSpan(new ForegroundColorSpan(Helper.fetchAccentColor(LoginActivity.this)), 0, content_create.length(),
+ Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ binding.createAnAccountPeertube.setText(content_create, TextView.BufferType.SPANNABLE);
+
+ binding.createAnAccountPeertube.setOnClickListener(v -> {
+ Intent mainActivity = new Intent(LoginActivity.this, PeertubeRegisterActivity.class);
+ Bundle b = new Bundle();
+ mainActivity.putExtras(b);
+ startActivity(mainActivity);
+ });
+
+
+ if (BuildConfig.full_instances && BuildConfig.instance_switcher) {
+ binding.loginInstanceContainer.setVisibility(View.VISIBLE);
+ }
+
+
+ if (Helper.isTablet(LoginActivity.this)) {
+
+ ViewGroup.LayoutParams layoutParamsI = binding.loginInstanceContainer.getLayoutParams();
+ layoutParamsI.width = (int) Helper.convertDpToPixel(300, LoginActivity.this);
+ binding.loginInstanceContainer.setLayoutParams(layoutParamsI);
+
+ ViewGroup.LayoutParams layoutParamsU = binding.loginUidContainer.getLayoutParams();
+ layoutParamsU.width = (int) Helper.convertDpToPixel(300, LoginActivity.this);
+ binding.loginUidContainer.setLayoutParams(layoutParamsU);
+
+ ViewGroup.LayoutParams layoutParamsP = binding.loginPasswdContainer.getLayoutParams();
+ layoutParamsP.width = (int) Helper.convertDpToPixel(300, LoginActivity.this);
+ binding.loginPasswdContainer.setLayoutParams(layoutParamsP);
+ }
+
+ if (!BuildConfig.full_instances) {
+
+ binding.loginUidContainer.setVisibility(View.GONE);
+ binding.loginPasswdContainer.setVisibility(View.GONE);
+ binding.loginInstanceContainer.setVisibility(View.GONE);
+ binding.createAnAccountPeertube.setVisibility(View.GONE);
+ binding.instancePickerTitle.setVisibility(View.VISIBLE);
+ binding.instancePicker.setVisibility(View.VISIBLE);
+
+
+ List acadInstances = AcadInstances.getInstances();
+ String[] academiesKey = new String[acadInstances.size()];
+ String[] academiesValue = new String[acadInstances.size()];
+ String acad = HelperInstance.getLiveInstance(LoginActivity.this);
+ int position = 0;
+ int i = 0;
+ for (AcadInstances ac : acadInstances) {
+ academiesKey[i] = ac.getName();
+ academiesValue[i] = ac.getUrl();
+ if (ac.getUrl().compareTo(acad) == 0) {
+ position = i;
+ }
+ i++;
+ }
+ ArrayAdapter adapterChannel = new ArrayAdapter<>(LoginActivity.this,
+ android.R.layout.simple_spinner_dropdown_item, academiesKey);
+ binding.instancePicker.setAdapter(adapterChannel);
+ binding.instancePicker.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ acadInstance = academiesValue[position];
+ binding.loginUidContainer.setVisibility(View.GONE);
+ binding.loginPasswdContainer.setVisibility(View.GONE);
+ binding.loginInstanceContainer.setVisibility(View.GONE);
+ binding.createAnAccountPeertube.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+
+ }
+ });
+ binding.instancePicker.setSelection(position, true);
+ }
+ if (BuildConfig.allow_remote_connections) {
+ binding.loginInstance.setOnFocusChangeListener((v, hasFocus) -> {
+ if (!hasFocus) {
+ if (binding.loginInstance.getText() != null) {
+ new Thread(() -> {
+ String testInstance = binding.loginInstance.getText().toString().trim();
+ if (testInstance.length() == 0) {
+ return;
+ }
+ WellKnownNodeinfo.NodeInfo instanceNodeInfo = null;
+ if (BuildConfig.allow_remote_connections) {
+ instanceNodeInfo = new RetrofitPeertubeAPI(LoginActivity.this, testInstance, null).getNodeInfo();
+ }
+ if (instanceNodeInfo != null &&
+ (instanceNodeInfo.getSoftware().getName().toUpperCase().trim().compareTo("MASTODON") == 0 ||
+ instanceNodeInfo.getSoftware().getName().toUpperCase().trim().compareTo("PLEROMA") == 0)
+ ) {
+ connectToFediverse(testInstance, instanceNodeInfo);
+ }
+ }).start();
+ }
+ }
+ });
+ }
+
+ binding.loginButton.setOnClickListener(v -> {
+ if (!BuildConfig.full_instances && AcadInstances.isOpenId(acadInstance)) {
+ new Thread(() -> {
+ try {
+ InstanceData.InstanceConfig instanceConfig = new RetrofitPeertubeAPI(LoginActivity.this).getConfigInstance();
+ PluginData.Plugin plugin = instanceConfig.getPlugin();
+ List pluginInfos = plugin.getRegistered();
+ String openIdVersion = "0.0.7";
+ for (PluginData.PluginInfo pluginInfo : pluginInfos) {
+ if (pluginInfo.getName().toLowerCase().contains("openid")) {
+ openIdVersion = pluginInfo.getVersion();
+ }
+ }
+ Oauth oauth = new RetrofitPeertubeAPI(LoginActivity.this, acadInstance, null).oauthClient(null, null, null, null);
+ if (oauth == null) {
+ runOnUiThread(() -> {
+ binding.loginButton.setEnabled(true);
+ Toasty.error(LoginActivity.this, getString(R.string.client_error), Toast.LENGTH_LONG).show();
+ });
+ return;
+ }
+ client_id = oauth.getClient_id();
+ client_secret = oauth.getClient_secret();
+ SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
+ SharedPreferences.Editor editor = sharedpreferences.edit();
+ editor.putString(Helper.CLIENT_ID, client_id);
+ editor.putString(Helper.CLIENT_SECRET, client_secret);
+ editor.apply();
+ Intent intent = new Intent(LoginActivity.this, WebviewConnectActivity.class);
+ Bundle b = new Bundle();
+ b.putString("url", "https://" + acadInstance + "/plugins/auth-openid-connect/" + openIdVersion + "/auth/openid-connect");
+ intent.putExtras(b);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ } catch (Exception e) {
+ e.printStackTrace();
+ runOnUiThread(() -> {
+ binding.loginButton.setEnabled(true);
+ Toasty.error(LoginActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
+ });
+ }
+
+ }).start();
+ } else {
+ if (binding.loginUid.getText() != null && binding.loginUid.getText().toString().contains("@") && !Patterns.EMAIL_ADDRESS.matcher(binding.loginUid.getText().toString().trim()).matches()) {
+ Toasty.error(LoginActivity.this, getString(R.string.email_error)).show();
+ return;
+ }
+ binding.loginButton.setEnabled(false);
+ String instance;
+ if (!BuildConfig.full_instances) {
+ String[] emailArray = binding.loginUid.getText().toString().split("@");
+ if (emailArray.length > 1 && !Arrays.asList(HelperAcadInstance.valideEmails).contains(emailArray[1])) {
+ Toasty.error(LoginActivity.this, getString(R.string.email_error_domain, emailArray[1])).show();
+ binding.loginButton.setEnabled(true);
+ return;
+ }
+
+ instance = HelperInstance.getLiveInstance(LoginActivity.this);
+ } else {
+ if (binding.loginInstance.getText() == null || binding.loginInstance.getText().toString().trim().length() == 0) {
+ Toasty.error(LoginActivity.this, getString(R.string.not_valide_instance)).show();
+ binding.loginButton.setEnabled(true);
+ return;
+ }
+ instance = binding.loginInstance.getText().toString().trim().toLowerCase();
+ }
+
+ if (instance.startsWith("http")) {
+ try {
+ URL url = new URL(instance);
+ instance = url.getHost();
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ }
+ } else if (instance.endsWith("/")) {
+ try {
+ URL url = new URL("https://" + instance);
+ instance = url.getHost();
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ }
+ }
+ if (!Patterns.WEB_URL.matcher("https://" + instance).matches()) {
+ Toasty.error(LoginActivity.this, getString(R.string.not_valide_instance)).show();
+ binding.loginButton.setEnabled(true);
+ return;
+ }
+ String finalInstance = instance;
+ new Thread(() -> {
+ WellKnownNodeinfo.NodeInfo instanceNodeInfo = null;
+ if (BuildConfig.allow_remote_connections) {
+ instanceNodeInfo = new RetrofitPeertubeAPI(LoginActivity.this, finalInstance, null).getNodeInfo();
+ }
+ connectToFediverse(finalInstance, instanceNodeInfo);
+ }).start();
+ }
+ });
+ }
+
+ /**
+ * Oauth process for Peertube
+ *
+ * @param finalInstance String
+ */
+ private void connectToFediverse(String finalInstance, WellKnownNodeinfo.NodeInfo instanceNodeInfo) {
+ Oauth oauth = null;
+ String software;
+ if (instanceNodeInfo != null) {
+ software = instanceNodeInfo.getSoftware().getName().toUpperCase().trim();
+ switch (software) {
+ case "MASTODON":
+ case "PLEROMA":
+ oauth = new RetrofitMastodonAPI(LoginActivity.this, finalInstance, null).oauthClient(Helper.CLIENT_NAME_VALUE, Helper.REDIRECT_CONTENT_WEB, Helper.OAUTH_SCOPES_MASTODON, Helper.WEBSITE_VALUE);
+ break;
+
+ case "FRIENDICA":
+
+ break;
+
+ default:
+ oauth = new RetrofitPeertubeAPI(LoginActivity.this, finalInstance, null).oauthClient(Helper.CLIENT_NAME_VALUE, Helper.WEBSITE_VALUE, Helper.OAUTH_SCOPES_PEERTUBE, Helper.WEBSITE_VALUE);
+ }
+ } else {
+ oauth = new RetrofitPeertubeAPI(LoginActivity.this, finalInstance, null).oauthClient(Helper.CLIENT_NAME_VALUE, Helper.WEBSITE_VALUE, Helper.OAUTH_SCOPES_PEERTUBE, Helper.WEBSITE_VALUE);
+ software = "PEERTUBE";
+ }
+ if (oauth == null) {
+ runOnUiThread(() -> {
+ binding.loginButton.setEnabled(true);
+ Toasty.error(LoginActivity.this, getString(R.string.client_error), Toast.LENGTH_LONG).show();
+ });
+ return;
+ }
+ client_id = oauth.getClient_id();
+ client_secret = oauth.getClient_secret();
+
+ SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
+ SharedPreferences.Editor editor = sharedpreferences.edit();
+ editor.putString(Helper.CLIENT_ID, client_id);
+ editor.putString(Helper.CLIENT_SECRET, client_secret);
+ editor.apply();
+ OauthParams oauthParams = new OauthParams();
+ oauthParams.setClient_id(client_id);
+ oauthParams.setClient_secret(client_secret);
+ oauthParams.setGrant_type("password");
+ final boolean isMastodonAPI = software.compareTo("MASTODON") == 0 || software.compareTo("PLEROMA") == 0;
+ if (software.compareTo("PEERTUBE") == 0) {
+ oauthParams.setScope("user");
+ } else if (isMastodonAPI) {
+ oauthParams.setScope("read write follow");
+ }
+ if (binding.loginUid.getText() != null) {
+ oauthParams.setUsername(binding.loginUid.getText().toString().trim());
+ }
+ if (binding.loginPasswd.getText() != null) {
+ oauthParams.setPassword(binding.loginPasswd.getText().toString());
+ }
+ try {
+ Token token = null;
+ if (software.compareTo("PEERTUBE") == 0) {
+ token = new RetrofitPeertubeAPI(LoginActivity.this, finalInstance, null).manageToken(oauthParams);
+ } else if (isMastodonAPI) {
+ Intent i = new Intent(LoginActivity.this, MastodonWebviewConnectActivity.class);
+ i.putExtra("software", software);
+ i.putExtra("instance", finalInstance);
+ i.putExtra("client_id", client_id);
+ i.putExtra("client_secret", client_secret);
+ startActivity(i);
+ return;
+ }
+ proceedLogin(token, finalInstance, software.compareTo("PEERTUBE") == 0 ? null : software);
+ } catch (final Exception | Error e) {
+ oauthParams.setUsername(binding.loginUid.getText().toString().toLowerCase().trim());
+ try {
+ if (software.compareTo("PEERTUBE") == 0) {
+ Token token = new RetrofitPeertubeAPI(LoginActivity.this, finalInstance, null).manageToken(oauthParams);
+ proceedLogin(token, finalInstance, software.compareTo("PEERTUBE") == 0 ? null : software);
+ }
+ } catch (Error error) {
+ Error.displayError(LoginActivity.this, error);
+ error.printStackTrace();
+ runOnUiThread(() -> binding.loginButton.setEnabled(true));
+ }
+ }
+ }
+
+
+ @SuppressLint("ApplySharedPref")
+ private void proceedLogin(Token token, String host, String software) {
+ runOnUiThread(() -> {
+ if (token != null) {
+ boolean remote_account = software != null && software.toUpperCase().trim().compareTo("PEERTUBE") != 0;
+ SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
+ SharedPreferences.Editor editor = sharedpreferences.edit();
+ editor.putString(Helper.PREF_KEY_OAUTH_TOKEN, token.getAccess_token());
+ editor.putString(Helper.PREF_SOFTWARE, remote_account ? software : null);
+ editor.putString(Helper.PREF_REMOTE_INSTANCE, remote_account ? host : null);
+ if (!remote_account) {
+ editor.putString(Helper.PREF_INSTANCE, host);
+ }
+ editor.commit();
+ //Update the account with the token;
+ updateCredential(LoginActivity.this, token.getAccess_token(), client_id, client_secret, token.getRefresh_token(), host, software);
+ } else {
+ binding.loginButton.setEnabled(true);
+ }
+ });
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/MainActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/MainActivity.java
new file mode 100644
index 00000000..81a71151
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/peertube/activities/MainActivity.java
@@ -0,0 +1,815 @@
+package app.fedilab.android.peertube.activities;
+/* Copyright 2020 Thomas Schneider
+ *
+ * This file is a part of TubeLab
+ *
+ * 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.
+ *
+ * TubeLab 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 TubeLab; if not,
+ * see . */
+
+import static app.fedilab.android.peertube.helper.Helper.peertubeInformation;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.widget.SearchView;
+import androidx.appcompat.widget.Toolbar;
+import androidx.appcompat.widget.TooltipCompat;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentStatePagerAdapter;
+import androidx.viewpager.widget.PagerAdapter;
+import androidx.viewpager.widget.ViewPager;
+
+import com.google.android.material.bottomnavigation.BottomNavigationView;
+import com.kobakei.ratethisapp.RateThisApp;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.net.URL;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import app.fedilab.android.R;
+import app.fedilab.android.databinding.ActivityMainPeertubeBinding;
+import app.fedilab.android.peertube.client.RetrofitPeertubeAPI;
+import app.fedilab.android.peertube.client.data.AccountData.Account;
+import app.fedilab.android.peertube.client.data.InstanceData;
+import app.fedilab.android.peertube.client.entities.AcadInstances;
+import app.fedilab.android.peertube.client.entities.OauthParams;
+import app.fedilab.android.peertube.client.entities.PeertubeInformation;
+import app.fedilab.android.peertube.client.entities.Token;
+import app.fedilab.android.peertube.client.entities.UserMe;
+import app.fedilab.android.peertube.client.entities.UserSettings;
+import app.fedilab.android.peertube.client.entities.WellKnownNodeinfo;
+import app.fedilab.android.peertube.fragment.DisplayOverviewFragment;
+import app.fedilab.android.peertube.fragment.DisplayVideosFragment;
+import app.fedilab.android.peertube.helper.Helper;
+import app.fedilab.android.peertube.helper.HelperAcadInstance;
+import app.fedilab.android.peertube.helper.HelperInstance;
+import app.fedilab.android.peertube.helper.PlaylistExportHelper;
+import app.fedilab.android.peertube.helper.SwitchAccountHelper;
+import app.fedilab.android.peertube.services.RetrieveInfoService;
+import app.fedilab.android.peertube.sqlite.AccountDAO;
+import app.fedilab.android.peertube.sqlite.Sqlite;
+import app.fedilab.android.peertube.sqlite.StoredInstanceDAO;
+import app.fedilab.android.peertube.viewmodel.TimelineVM;
+import es.dmoral.toasty.Toasty;
+
+
+public class MainActivity extends app.fedilab.android.activities.MainActivity {
+
+
+ public static int PICK_INSTANCE = 5641;
+ public static int PICK_INSTANCE_SURF = 5642;
+ public static UserMe userMe;
+ public static InstanceData.InstanceConfig instanceConfig;
+ public static TypeOfConnection typeOfConnection;
+ public static int badgeCount;
+ private DisplayVideosFragment recentFragment, locaFragment, trendingFragment, subscriptionFragment, mostLikedFragment;
+ private DisplayOverviewFragment overviewFragment;
+ private ActivityMainPeertubeBinding binding;
+ private final BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
+ = item -> {
+ int itemId = item.getItemId();
+ String type = null;
+ if (itemId == R.id.navigation_discover) {
+ setTitleCustom(R.string.title_discover);
+ binding.viewpager.setCurrentItem(3);
+ type = HelperAcadInstance.DISCOVER;
+ } else if (itemId == R.id.navigation_subscription) {
+ binding.viewpager.setCurrentItem(4);
+ setTitleCustom(R.string.subscriptions);
+ type = HelperAcadInstance.SUBSCRIPTIONS;
+ } else if (itemId == R.id.navigation_trending) {
+ setTitleCustom(R.string.title_trending);
+ binding.viewpager.setCurrentItem(2);
+ type = HelperAcadInstance.TRENDING;
+ } else if (itemId == R.id.navigation_recently_added) {
+ setTitleCustom(R.string.title_recently_added);
+ binding.viewpager.setCurrentItem(1);
+ type = HelperAcadInstance.RECENTLY_ADDED;
+ } else if (itemId == R.id.navigation_local) {
+ setTitleCustom(R.string.title_local);
+ binding.viewpager.setCurrentItem(0);
+ type = HelperAcadInstance.LOCAL;
+ }
+ return true;
+ };
+
+
+ @SuppressLint("ApplySharedPref")
+ public static void showRadioButtonDialogFullInstances(Activity activity, boolean storeInDb) {
+ final SharedPreferences sharedpreferences = activity.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
+ AlertDialog.Builder alt_bld = new AlertDialog.Builder(activity);
+ alt_bld.setTitle(R.string.instance_choice);
+ String instance = HelperInstance.getLiveInstance(activity);
+ final EditText input = new EditText(activity);
+ LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.MATCH_PARENT);
+ input.setLayoutParams(lp);
+ alt_bld.setView(input);
+ input.setText(instance);
+ alt_bld.setPositiveButton(R.string.validate,
+ (dialog, which) -> new Thread(() -> {
+ try {
+ String newInstance = input.getText().toString().trim();
+ if (!newInstance.startsWith("http")) {
+ newInstance = "http://" + newInstance;
+ }
+ URL url = new URL(newInstance);
+ newInstance = url.getHost();
+
+ WellKnownNodeinfo.NodeInfo instanceNodeInfo = new RetrofitPeertubeAPI(activity, newInstance, null).getNodeInfo();
+ if (instanceNodeInfo.getSoftware() != null && instanceNodeInfo.getSoftware().getName().trim().toLowerCase().compareTo("peertube") == 0) {
+ SharedPreferences.Editor editor = sharedpreferences.edit();
+ editor.putString(Helper.PREF_INSTANCE, newInstance);
+ editor.commit();
+ if (storeInDb) {
+ newInstance = newInstance.trim().toLowerCase();
+ InstanceData.AboutInstance aboutInstance = new RetrofitPeertubeAPI(activity, newInstance, null).getAboutInstance();
+ SQLiteDatabase db = Sqlite.getInstance(activity.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
+ new StoredInstanceDAO(activity, db).insertInstance(aboutInstance, newInstance);
+ activity.runOnUiThread(() -> {
+ dialog.dismiss();
+ Helper.logoutNoRemoval(activity);
+ });
+ } else {
+ activity.runOnUiThread(() -> {
+ dialog.dismiss();
+ Intent intent = new Intent(activity, MainActivity.class);
+ activity.startActivity(intent);
+ });
+ }
+ } else {
+ activity.runOnUiThread(() -> Toasty.error(activity, activity.getString(R.string.not_valide_instance), Toast.LENGTH_LONG).show());
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }).start());
+ alt_bld.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
+ alt_bld.setNeutralButton(R.string.help, (dialog, which) -> {
+ Intent intent = new Intent(activity, InstancePickerActivity.class);
+ if (storeInDb) {
+ activity.startActivityForResult(intent, PICK_INSTANCE_SURF);
+ } else {
+ activity.startActivityForResult(intent, PICK_INSTANCE);
+ }
+ });
+ AlertDialog alert = alt_bld.create();
+ alert.show();
+ }
+
+ private void setTitleCustom(int titleRId) {
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ TextView mTitle = toolbar.findViewById(R.id.toolbar_title);
+ if (mTitle != null) {
+ mTitle.setText(getString(titleRId));
+ }
+ }
+
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ binding = null;
+ }
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+
+ super.onCreate(savedInstanceState);
+
+ binding = super.binding;
+
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ typeOfConnection = TypeOfConnection.UNKNOWN;
+ badgeCount = 0;
+
+ binding.navView.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
+
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setDisplayShowTitleEnabled(false);
+ }
+ checkIfConnectedUsers();
+
+ recentFragment = new DisplayVideosFragment();
+ Bundle bundle = new Bundle();
+ bundle.putSerializable(Helper.TIMELINE_TYPE, TimelineVM.TimelineType.RECENT);
+ recentFragment.setArguments(bundle);
+
+ locaFragment = new DisplayVideosFragment();
+ bundle = new Bundle();
+ bundle.putSerializable(Helper.TIMELINE_TYPE, TimelineVM.TimelineType.LOCAL);
+ locaFragment.setArguments(bundle);
+
+ trendingFragment = new DisplayVideosFragment();
+ bundle = new Bundle();
+ bundle.putSerializable(Helper.TIMELINE_TYPE, TimelineVM.TimelineType.TRENDING);
+ trendingFragment.setArguments(bundle);
+
+ subscriptionFragment = new DisplayVideosFragment();
+ bundle = new Bundle();
+ bundle.putSerializable(Helper.TIMELINE_TYPE, TimelineVM.TimelineType.SUBSCRIBTIONS);
+ subscriptionFragment.setArguments(bundle);
+
+ mostLikedFragment = new DisplayVideosFragment();
+ bundle = new Bundle();
+ bundle.putSerializable(Helper.TIMELINE_TYPE, TimelineVM.TimelineType.MOST_LIKED);
+ mostLikedFragment.setArguments(bundle);
+
+ overviewFragment = new DisplayOverviewFragment();
+ if (!Helper.isLoggedIn(MainActivity.this)) {
+ PagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
+ binding.viewpager.setAdapter(mPagerAdapter);
+ } else {
+ new Thread(() -> {
+ badgeCount = new RetrofitPeertubeAPI(MainActivity.this).unreadNotifications();
+ invalidateOptionsMenu();
+ }).start();
+ }
+ if (Helper.isLoggedIn(MainActivity.this)) {
+ binding.viewpager.setOffscreenPageLimit(5);
+ } else {
+ binding.viewpager.setOffscreenPageLimit(4);
+ }
+
+
+ binding.viewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ MenuItem item = binding.navView.getMenu().getItem(position);
+ binding.navView.setSelectedItemId(item.getItemId());
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+
+ }
+ });
+
+
+ toolbar.setOnClickListener(v -> {
+ if (binding.viewpager.getAdapter() == null) {
+ return;
+ }
+ if (binding.viewpager.getAdapter().instantiateItem(binding.viewpager, binding.viewpager.getCurrentItem()) instanceof DisplayVideosFragment) {
+ ((DisplayVideosFragment) binding.viewpager.getAdapter().instantiateItem(binding.viewpager, binding.viewpager.getCurrentItem())).scrollToTop();
+ } else if (binding.viewpager.getAdapter().instantiateItem(binding.viewpager, binding.viewpager.getCurrentItem()) instanceof DisplayOverviewFragment) {
+ ((DisplayOverviewFragment) binding.viewpager.getAdapter().instantiateItem(binding.viewpager, binding.viewpager.getCurrentItem())).scrollToTop();
+ }
+ });
+
+ setTitleCustom(R.string.title_discover);
+
+ if (Helper.isLoggedIn(MainActivity.this)) {
+ binding.navView.inflateMenu(R.menu.bottom_nav_menu_connected_peertube);
+ refreshToken();
+
+ } else {
+ binding.navView.inflateMenu(R.menu.bottom_nav_menu);
+ }
+ peertubeInformation = new PeertubeInformation();
+ peertubeInformation.setCategories(new LinkedHashMap<>());
+ peertubeInformation.setLanguages(new LinkedHashMap<>());
+ peertubeInformation.setLicences(new LinkedHashMap<>());
+ peertubeInformation.setPrivacies(new LinkedHashMap<>());
+ peertubeInformation.setPlaylistPrivacies(new LinkedHashMap<>());
+ peertubeInformation.setTranslations(new LinkedHashMap<>());
+ startInForeground();
+
+ if (BuildConfig.google_restriction && BuildConfig.full_instances) {
+ RateThisApp.onCreate(this);
+ RateThisApp.showRateDialogIfNeeded(this);
+ }
+ if (!BuildConfig.full_instances) {
+ PlaylistExportHelper.manageIntentUrl(MainActivity.this, getIntent());
+ }
+
+
+ final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
+
+
+ int search_cast = sharedpreferences.getInt(getString(R.string.set_cast_choice), BuildConfig.cast_enabled);
+ if (search_cast == 1) {
+ super.discoverCast();
+ }
+ //Instance
+ if (HelperInstance.getLiveInstance(MainActivity.this) == null) {
+ Intent intent = new Intent(MainActivity.this, InstancePickerActivity.class);
+ startActivityForResult(intent, PICK_INSTANCE);
+ }
+ }
+
+ public DisplayVideosFragment getSubscriptionFragment() {
+ return subscriptionFragment;
+ }
+
+ private void startInForeground() {
+ Intent notificationIntent = new Intent(this, RetrieveInfoService.class);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ startForegroundService(notificationIntent);
+ } else {
+ startService(notificationIntent);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ invalidateOptionsMenu();
+ }
+
+ private void refreshToken() {
+ new Thread(() -> {
+ final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
+ String tokenStr = Helper.getToken(MainActivity.this);
+ String instance = HelperInstance.getLiveInstance(MainActivity.this);
+ SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
+ String instanceShar = sharedpreferences.getString(Helper.PREF_INSTANCE, null);
+ String userIdShar = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
+ Account account = new AccountDAO(MainActivity.this, db).getAccountByToken(tokenStr);
+ if (account == null) {
+ account = new AccountDAO(MainActivity.this, db).getAccountByIdInstance(userIdShar, instanceShar);
+ }
+ if (account != null) {
+ Account finalAccount = account;
+ OauthParams oauthParams = new OauthParams();
+ oauthParams.setGrant_type("refresh_token");
+ oauthParams.setClient_id(account.getClient_id());
+ oauthParams.setClient_secret(account.getClient_secret());
+ oauthParams.setRefresh_token(account.getRefresh_token());
+ oauthParams.setAccess_token(account.getToken());
+ try {
+ Token token = new RetrofitPeertubeAPI(MainActivity.this).manageToken(oauthParams);
+ if (token == null) {
+ return;
+ }
+ runOnUiThread(() -> {
+ //To avoid a token issue with subscriptions, adding fragment is done when the token is refreshed.
+ new Handler().post(() -> {
+ if (Helper.isLoggedIn(MainActivity.this)) {
+ PagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
+ binding.viewpager.setAdapter(mPagerAdapter);
+ }
+ });
+ });
+
+ userMe = new RetrofitPeertubeAPI(MainActivity.this, instance, token.getAccess_token()).verifyCredentials();
+ if (userMe != null && userMe.getAccount() != null) {
+ new AccountDAO(MainActivity.this, db).updateAccount(userMe.getAccount());
+ SharedPreferences.Editor editor = sharedpreferences.edit();
+ editor.putString(Helper.PREF_KEY_ID, account.getId());
+ editor.putString(Helper.PREF_KEY_NAME, account.getUsername());
+ editor.putBoolean(getString(R.string.set_autoplay_choice), userMe.isAutoPlayVideo());
+ editor.putBoolean(getString(R.string.set_store_in_history), userMe.isVideosHistoryEnabled());
+ editor.putBoolean(getString(R.string.set_autoplay_next_video_choice), userMe.isAutoPlayNextVideo());
+ editor.putString(getString(R.string.set_video_sensitive_choice), userMe.getNsfwPolicy());
+ //Sync languages from server
+ List videoLanguageServer = userMe.getVideoLanguages();
+ if (videoLanguageServer != null) {
+ Set videoLanguageServerSet = new TreeSet<>(videoLanguageServer);
+ videoLanguageServerSet.addAll(videoLanguageServer);
+ Set videoLanguageLocal = sharedpreferences.getStringSet(getString(R.string.set_video_language_choice), null);
+ if (videoLanguageServerSet.size() > 0 && videoLanguageLocal != null) {
+ videoLanguageServer.addAll(videoLanguageLocal);
+ }
+ editor.putStringSet(getString(R.string.set_video_language_choice), videoLanguageServerSet);
+ editor.apply();
+ }
+ }
+ instanceConfig = new RetrofitPeertubeAPI(MainActivity.this).getConfigInstance();
+ } catch (Error error) {
+ runOnUiThread(() -> {
+ AlertDialog.Builder alt_bld = new AlertDialog.Builder(this);
+ alt_bld.setTitle(R.string.refresh_token_failed);
+ alt_bld.setMessage(R.string.refresh_token_failed_message);
+ alt_bld.setNegativeButton(R.string.action_logout, (dialog, id) -> {
+ dialog.dismiss();
+ Helper.logoutCurrentUser(MainActivity.this, finalAccount);
+ });
+ alt_bld.setPositiveButton(R.string._retry, (dialog, id) -> {
+ dialog.dismiss();
+ refreshToken();
+ });
+ AlertDialog alert = alt_bld.create();
+ alert.show();
+
+ });
+ error.printStackTrace();
+ }
+ }
+ }).start();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(@NotNull Menu menu) {
+ getMenuInflater().inflate(R.menu.main_menu, menu);
+
+ MenuItem myActionMenuItem = menu.findItem(R.id.action_search);
+ SearchView searchView = (SearchView) myActionMenuItem.getActionView();
+ searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ Pattern link = Pattern.compile("(https?://[\\da-z.-]+\\.[a-z.]{2,10})/videos/watch/(\\w{8}-\\w{4}-\\w{4}-\\w{4}-\\w{12})(\\?start=(\\d+[hH])?(\\d+[mM])?(\\d+[sS])?)?$");
+ Matcher matcherLink = link.matcher(query.trim());
+ if (matcherLink.find()) {
+ Intent intent = new Intent(MainActivity.this, PeertubeActivity.class);
+ intent.setData(Uri.parse(query.trim()));
+ startActivity(intent);
+ myActionMenuItem.collapseActionView();
+ return false;
+ }
+ Intent intent = new Intent(MainActivity.this, SearchActivity.class);
+ Bundle b = new Bundle();
+ String search = query.trim();
+ b.putString("search", search);
+ intent.putExtras(b);
+ startActivity(intent);
+ if (!searchView.isIconified()) {
+ searchView.setIconified(true);
+ }
+ myActionMenuItem.collapseActionView();
+ return false;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String s) {
+ return false;
+ }
+ });
+
+ MenuItem uploadItem = menu.findItem(R.id.action_upload);
+ MenuItem myVideosItem = menu.findItem(R.id.action_myvideos);
+ MenuItem playslistItem = menu.findItem(R.id.action_playlist);
+ MenuItem historyItem = menu.findItem(R.id.action_history);
+ MenuItem mostLikedItem = menu.findItem(R.id.action_most_liked);
+ MenuItem settingsItem = menu.findItem(R.id.action_settings);
+ MenuItem sepiaSearchItem = menu.findItem(R.id.action_sepia_search);
+ MenuItem incognitoItem = menu.findItem(R.id.action_incognito);
+ MenuItem accountItem = menu.findItem(R.id.action_account);
+ MenuItem donateItem = menu.findItem(R.id.action_donate);
+ MenuItem changeInstanceItem = menu.findItem(R.id.action_change_instance);
+
+ FrameLayout rootView = (FrameLayout) accountItem.getActionView();
+
+ FrameLayout redCircle = rootView.findViewById(R.id.view_alert_red_circle);
+ TextView countTextView = rootView.findViewById(R.id.view_alert_count_textview);
+ //change counter for notifications
+ if (badgeCount > 0) {
+ countTextView.setText(String.valueOf(badgeCount));
+ redCircle.setVisibility(View.VISIBLE);
+ } else {
+ redCircle.setVisibility(View.GONE);
+ }
+ TooltipCompat.setTooltipText(accountItem.getActionView(), getText(R.string.account));
+ if (BuildConfig.FLAVOR.compareTo("google_full") == 0) {
+ donateItem.setVisible(true);
+ }
+
+ if (!BuildConfig.instance_switcher) {
+ changeInstanceItem.setVisible(false);
+ }
+ switch (typeOfConnection) {
+ case UNKNOWN:
+ accountItem.setVisible(false);
+ uploadItem.setVisible(false);
+ myVideosItem.setVisible(false);
+ playslistItem.setVisible(false);
+ historyItem.setVisible(false);
+ settingsItem.setVisible(false);
+ mostLikedItem.setVisible(false);
+ incognitoItem.setVisible(false);
+ break;
+ case REMOTE_ACCOUNT:
+ case NORMAL:
+ accountItem.setVisible(true);
+ if (Helper.isLoggedIn(MainActivity.this)) {
+ if (!BuildConfig.full_instances) {
+ changeInstanceItem.setVisible(false);
+ }
+ uploadItem.setVisible(true);
+ myVideosItem.setVisible(true);
+ playslistItem.setVisible(true);
+ historyItem.setVisible(true);
+ settingsItem.setVisible(false);
+ mostLikedItem.setVisible(true);
+ incognitoItem.setVisible(true);
+ final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
+ boolean checked = sharedpreferences.getBoolean(getString(R.string.set_store_in_history), true);
+ incognitoItem.setChecked(checked);
+ } else {
+ uploadItem.setVisible(false);
+ myVideosItem.setVisible(false);
+ playslistItem.setVisible(!BuildConfig.full_instances);
+ historyItem.setVisible(false);
+ settingsItem.setVisible(true);
+ mostLikedItem.setVisible(true);
+ incognitoItem.setVisible(false);
+ }
+ break;
+ case SURFING:
+ accountItem.setVisible(true);
+ uploadItem.setVisible(false);
+ myVideosItem.setVisible(false);
+ playslistItem.setVisible(false);
+ historyItem.setVisible(false);
+ settingsItem.setVisible(false);
+ mostLikedItem.setVisible(false);
+ incognitoItem.setVisible(false);
+ break;
+ }
+
+
+ if (!BuildConfig.sepia_search) {
+ sepiaSearchItem.setVisible(false);
+ }
+ return true;
+ }
+
+
+ private void checkIfConnectedUsers() {
+ new Thread(() -> {
+ try {
+ typeOfConnection = TypeOfConnection.NORMAL;
+ if (!Helper.canMakeAction(MainActivity.this)) {
+ SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
+ List accounts = new AccountDAO(MainActivity.this, db).getAllAccount();
+ if (accounts != null && accounts.size() > 0) {
+ //The user is not authenticated and there accounts in db. That means the user is surfing some other instances
+ typeOfConnection = TypeOfConnection.SURFING;
+ }
+ }
+ runOnUiThread(this::invalidateOptionsMenu);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }).start();
+ }
+
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ final MenuItem accountItem = menu.findItem(R.id.action_account);
+ FrameLayout rootView = (FrameLayout) accountItem.getActionView();
+ rootView.setOnClickListener(v -> onOptionsItemSelected(accountItem));
+ return super.onPrepareOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ String type = null;
+ String action = "TIMELINE";
+ if (item.getItemId() == R.id.action_change_instance) {
+ if (BuildConfig.full_instances) {
+ Intent intent = new Intent(MainActivity.this, ManageInstancesActivity.class);
+ startActivity(intent);
+ overridePendingTransition(R.anim.slide_in_up, R.anim.slide_out_up);
+ } else {
+ showRadioButtonDialog();
+ }
+ action = "CHANGE_INSTANCE";
+ type = "";
+ } else if (item.getItemId() == R.id.action_settings) {
+ Intent intent = new Intent(MainActivity.this, SettingsActivity.class);
+ startActivity(intent);
+ } else if (item.getItemId() == R.id.action_account) {
+ Intent intent;
+ if (typeOfConnection == TypeOfConnection.SURFING) {
+ SwitchAccountHelper.switchDialog(MainActivity.this, false);
+ } else {
+ if (Helper.canMakeAction(MainActivity.this)) {
+ intent = new Intent(MainActivity.this, AccountActivity.class);
+ startActivity(intent);
+ overridePendingTransition(R.anim.slide_in_up, R.anim.slide_out_up);
+ } else {
+ intent = new Intent(MainActivity.this, LoginActivity.class);
+ startActivity(intent);
+ }
+
+ }
+ } else if (item.getItemId() == R.id.action_upload) {
+ Intent intent = new Intent(MainActivity.this, PeertubeUploadActivity.class);
+ startActivity(intent);
+ } else if (item.getItemId() == R.id.action_myvideos) {
+ Intent intent = new Intent(MainActivity.this, VideosTimelineActivity.class);
+ Bundle bundle = new Bundle();
+ bundle.putSerializable("type", TimelineVM.TimelineType.MY_VIDEOS);
+ intent.putExtras(bundle);
+ startActivity(intent);
+ type = HelperAcadInstance.MYVIDEOS;
+ } else if (item.getItemId() == R.id.action_history) {
+ Intent intent = new Intent(MainActivity.this, VideosTimelineActivity.class);
+ Bundle bundle = new Bundle();
+ bundle.putSerializable("type", TimelineVM.TimelineType.HISTORY);
+ intent.putExtras(bundle);
+ startActivity(intent);
+ type = HelperAcadInstance.HISTORY;
+ } else if (item.getItemId() == R.id.action_most_liked) {
+ Intent intent = new Intent(MainActivity.this, VideosTimelineActivity.class);
+ Bundle bundle = new Bundle();
+ bundle.putSerializable("type", TimelineVM.TimelineType.MOST_LIKED);
+ intent.putExtras(bundle);
+ startActivity(intent);
+ type = HelperAcadInstance.MOSTLIKED;
+ } else if (item.getItemId() == R.id.action_playlist) {
+ Intent intent;
+ if (Helper.isLoggedIn(MainActivity.this)) {
+ intent = new Intent(MainActivity.this, AllPlaylistsActivity.class);
+ } else {
+ intent = new Intent(MainActivity.this, AllLocalPlaylistsActivity.class);
+ }
+ startActivity(intent);
+ } else if (item.getItemId() == R.id.action_sepia_search) {
+ Intent intent = new Intent(MainActivity.this, SepiaSearchActivity.class);
+ startActivity(intent);
+ } else if (item.getItemId() == R.id.action_about) {
+ Intent intent = new Intent(MainActivity.this, AboutActivity.class);
+ startActivity(intent);
+ } else if (item.getItemId() == R.id.action_donate) {
+ Intent intent = new Intent(MainActivity.this, DonationActivity.class);
+ startActivity(intent);
+ } else if (item.getItemId() == R.id.action_incognito) {
+ item.setChecked(!item.isChecked());
+ final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = sharedpreferences.edit();
+ editor.putBoolean(getString(R.string.set_store_in_history), item.isChecked());
+ editor.apply();
+ new Thread(() -> {
+ UserSettings userSettings = new UserSettings();
+ userSettings.setVideosHistoryEnabled(item.isChecked());
+ try {
+ RetrofitPeertubeAPI api = new RetrofitPeertubeAPI(MainActivity.this);
+ api.updateUser(userSettings);
+ } catch (Exception | Error e) {
+ e.printStackTrace();
+ }
+ }).start();
+ return false;
+ }
+ if (type != null) {
+ Matomo.sendScreen(MainActivity.this, action, type);
+ }
+ return true;
+ }
+
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ if (intent == null)
+ return;
+ Bundle extras = intent.getExtras();
+ if (extras != null && extras.containsKey(Helper.INTENT_ACTION)) {
+ if (extras.getInt(Helper.INTENT_ACTION) == Helper.ADD_USER_INTENT) {
+ recreate();
+ }
+ } else if (!BuildConfig.full_instances) {
+ PlaylistExportHelper.manageIntentUrl(MainActivity.this, intent);
+ }
+ }
+
+
+ @SuppressLint("ApplySharedPref")
+ private void showRadioButtonDialog() {
+
+ AlertDialog.Builder alt_bld = new AlertDialog.Builder(this);
+ alt_bld.setTitle(R.string.instance_choice);
+ final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
+ String acad = HelperInstance.getLiveInstance(MainActivity.this);
+ int i = 0;
+ List acadInstances = AcadInstances.getInstances();
+ String[] academiesKey = new String[acadInstances.size()];
+ String[] academiesValue = new String[acadInstances.size()];
+ int position = 0;
+ for (AcadInstances ac : acadInstances) {
+ academiesKey[i] = ac.getName();
+ academiesValue[i] = ac.getUrl();
+ if (ac.getUrl().compareTo(acad) == 0) {
+ position = i;
+ }
+ i++;
+ }
+
+ alt_bld.setSingleChoiceItems(academiesKey, position, (dialog, item) -> {
+ String newInstance = academiesValue[item];
+ SharedPreferences.Editor editor = sharedpreferences.edit();
+ editor.putString(Helper.PREF_INSTANCE, newInstance);
+ editor.commit();
+ dialog.dismiss();
+ recreate();
+ });
+ alt_bld.setPositiveButton(R.string.close, (dialog, id) -> dialog.dismiss());
+ AlertDialog alert = alt_bld.create();
+ alert.show();
+ }
+
+ @SuppressLint("ApplySharedPref")
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == PICK_INSTANCE && resultCode == Activity.RESULT_OK) {
+ if (data != null && data.getData() != null) {
+ final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = sharedpreferences.edit();
+ editor.putString(Helper.PREF_INSTANCE, String.valueOf(data.getData()));
+ editor.commit();
+ recreate();
+ }
+ }
+ }
+
+ public enum TypeOfConnection {
+ UNKNOWN,
+ NORMAL,
+ SURFING,
+ REMOTE_ACCOUNT,
+ }
+
+ private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
+
+ ScreenSlidePagerAdapter(FragmentManager fm) {
+ super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
+ }
+
+ @NotNull
+ @Override
+ public Fragment getItem(final int position) {
+ if (Helper.isLoggedIn(MainActivity.this)) {
+ switch (position) {
+ case 0:
+ return locaFragment;
+ case 1:
+ return recentFragment;
+ case 2:
+ return trendingFragment;
+ case 3:
+ return overviewFragment;
+ case 4:
+ return subscriptionFragment;
+ }
+ } else {
+ switch (position) {
+ case 0:
+ return locaFragment;
+ case 1:
+ return recentFragment;
+ case 2:
+ return trendingFragment;
+ case 3:
+ return overviewFragment;
+ }
+ }
+ return overviewFragment;
+ }
+
+ @Override
+ public int getCount() {
+ if (Helper.isLoggedIn(MainActivity.this)) {
+ return 5;
+ } else {
+ return 4;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/ManageInstancesActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/ManageInstancesActivity.java
new file mode 100644
index 00000000..c23c86b9
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/peertube/activities/ManageInstancesActivity.java
@@ -0,0 +1,134 @@
+package app.fedilab.android.peertube.activities;
+/* Copyright 2020 Thomas Schneider
+ *
+ * This file is a part of TubeLab
+ *
+ * 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.
+ *
+ * TubeLab 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 TubeLab; if not,
+ * see . */
+
+import static app.fedilab.android.peertube.activities.MainActivity.PICK_INSTANCE_SURF;
+import static app.fedilab.android.peertube.activities.MainActivity.showRadioButtonDialogFullInstances;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.Intent;
+import android.database.sqlite.SQLiteDatabase;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.MenuItem;
+import android.view.View;
+
+import androidx.lifecycle.ViewModelProvider;
+import androidx.recyclerview.widget.LinearLayoutManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import app.fedilab.android.peertube.R;
+import app.fedilab.android.peertube.client.RetrofitPeertubeAPI;
+import app.fedilab.android.peertube.client.data.InstanceData;
+import app.fedilab.android.peertube.databinding.ActivityManageInstancesBinding;
+import app.fedilab.android.peertube.drawer.AboutInstanceAdapter;
+import app.fedilab.android.peertube.helper.Helper;
+import app.fedilab.android.peertube.helper.HelperInstance;
+import app.fedilab.android.peertube.helper.Theme;
+import app.fedilab.android.peertube.sqlite.Sqlite;
+import app.fedilab.android.peertube.sqlite.StoredInstanceDAO;
+import app.fedilab.android.peertube.viewmodel.InfoInstanceVM;
+
+
+public class ManageInstancesActivity extends BaseActivity implements AboutInstanceAdapter.AllInstancesRemoved {
+
+ private ActivityManageInstancesBinding binding;
+ private List aboutInstances;
+ private AboutInstanceAdapter aboutInstanceAdapter;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Theme.setTheme(this, HelperInstance.getLiveInstance(this), false);
+ super.onCreate(savedInstanceState);
+ binding = ActivityManageInstancesBinding.inflate(getLayoutInflater());
+ View view = binding.getRoot();
+ setContentView(view);
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setDisplayShowHomeEnabled(true);
+ }
+ binding.loader.setVisibility(View.VISIBLE);
+ binding.noAction.setVisibility(View.GONE);
+ binding.lvInstances.setVisibility(View.GONE);
+ binding.actionButton.setOnClickListener(v -> showRadioButtonDialogFullInstances(ManageInstancesActivity.this, true));
+ aboutInstances = new ArrayList<>();
+ aboutInstanceAdapter = new AboutInstanceAdapter(this.aboutInstances);
+ aboutInstanceAdapter.allInstancesRemoved = this;
+ binding.lvInstances.setAdapter(aboutInstanceAdapter);
+ LinearLayoutManager layoutManager
+ = new LinearLayoutManager(ManageInstancesActivity.this);
+ binding.lvInstances.setLayoutManager(layoutManager);
+ InfoInstanceVM viewModelInfoInstance = new ViewModelProvider(ManageInstancesActivity.this).get(InfoInstanceVM.class);
+ viewModelInfoInstance.getInstances().observe(ManageInstancesActivity.this, this::manageVIewInfoInstance);
+ }
+
+ private void manageVIewInfoInstance(List aboutInstances) {
+ binding.loader.setVisibility(View.GONE);
+ if (aboutInstances == null || aboutInstances.size() == 0) {
+ binding.noAction.setVisibility(View.VISIBLE);
+ binding.lvInstances.setVisibility(View.GONE);
+ return;
+ }
+ binding.noAction.setVisibility(View.GONE);
+ binding.lvInstances.setVisibility(View.VISIBLE);
+ this.aboutInstances.addAll(aboutInstances);
+ aboutInstanceAdapter.notifyItemRangeInserted(0, aboutInstances.size());
+
+ }
+
+ @Override
+ public void onBackPressed() {
+ super.onBackPressed();
+ overridePendingTransition(R.anim.slide_out_up, R.anim.slide_in_up_down);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ finish();
+ overridePendingTransition(R.anim.slide_out_up, R.anim.slide_in_up_down);
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+
+ @SuppressLint("ApplySharedPref")
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == PICK_INSTANCE_SURF && resultCode == Activity.RESULT_OK) {
+ if (data != null && data.getData() != null) {
+ new Thread(() -> {
+ String newInstance = data.getData().toString().trim().toLowerCase();
+ InstanceData.AboutInstance aboutInstance = new RetrofitPeertubeAPI(ManageInstancesActivity.this, newInstance, null).getAboutInstance();
+ SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
+ new StoredInstanceDAO(ManageInstancesActivity.this, db).insertInstance(aboutInstance, newInstance);
+ runOnUiThread(() -> new Handler().post(() -> Helper.logoutNoRemoval(ManageInstancesActivity.this)));
+ }).start();
+ }
+ }
+ }
+
+ @Override
+ public void onAllInstancesRemoved() {
+ binding.noAction.setVisibility(View.VISIBLE);
+ binding.lvInstances.setVisibility(View.GONE);
+ }
+}
diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/MastodonWebviewConnectActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/MastodonWebviewConnectActivity.java
new file mode 100644
index 00000000..1fcfc99e
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/peertube/activities/MastodonWebviewConnectActivity.java
@@ -0,0 +1,194 @@
+/* Copyright 2020 Thomas Schneider
+ *
+ * This file is a part of TubeLab
+ *
+ * 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.
+ *
+ * TubeLab 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 TubeLab; if not,
+ * see . */
+
+package app.fedilab.android.peertube.activities;
+
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.MenuItem;
+import android.webkit.CookieManager;
+import android.webkit.CookieSyncManager;
+import android.webkit.WebChromeClient;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.ProgressBar;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AlertDialog;
+
+import app.fedilab.android.peertube.R;
+import app.fedilab.android.peertube.client.entities.OauthParams;
+import app.fedilab.android.peertube.client.entities.Token;
+import app.fedilab.android.peertube.client.mastodon.RetrofitMastodonAPI;
+import app.fedilab.android.peertube.helper.Helper;
+import app.fedilab.android.peertube.helper.HelperInstance;
+import app.fedilab.android.peertube.helper.Theme;
+import es.dmoral.toasty.Toasty;
+
+
+public class MastodonWebviewConnectActivity extends BaseActivity {
+
+
+ private WebView webView;
+ private AlertDialog alert;
+ private String clientId, clientSecret;
+ private String instance, software;
+
+ @SuppressWarnings("deprecation")
+ public static void clearCookies(Context context) {
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
+ CookieManager.getInstance().removeAllCookies(null);
+ CookieManager.getInstance().flush();
+ } else {
+ CookieSyncManager cookieSyncMngr = CookieSyncManager.createInstance(context);
+ cookieSyncMngr.startSync();
+ CookieManager cookieManager = CookieManager.getInstance();
+ cookieManager.removeAllCookie();
+ cookieManager.removeSessionCookie();
+ cookieSyncMngr.stopSync();
+ cookieSyncMngr.sync();
+ }
+ }
+
+ private static String redirectUserToAuthorizeAndLogin(String clientId, String instance) {
+ String queryString = Helper.CLIENT_ID + "=" + clientId;
+ queryString += "&" + Helper.REDIRECT_URI + "=" + Uri.encode(Helper.REDIRECT_CONTENT_WEB);
+ queryString += "&response_type=code";
+ queryString += "&scope=read write follow";
+ return "https://" + instance + "/oauth/authorize?" + queryString;
+ }
+
+ @SuppressLint("SetJavaScriptEnabled")
+ public void onCreate(Bundle savedInstanceState) {
+ Theme.setTheme(this, HelperInstance.getLiveInstance(this), false);
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_webview_connect);
+ Bundle b = getIntent().getExtras();
+ if (b != null) {
+ instance = b.getString("instance");
+ clientId = b.getString("client_id");
+ clientSecret = b.getString("client_secret");
+ software = b.getString("software");
+ }
+ if (instance == null)
+ finish();
+ if (getSupportActionBar() != null)
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ setTitle(R.string.login);
+ webView = findViewById(R.id.webviewConnect);
+ clearCookies(MastodonWebviewConnectActivity.this);
+ webView.getSettings().setJavaScriptEnabled(true);
+
+ CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true);
+
+ final ProgressBar pbar = findViewById(R.id.progress_bar);
+ webView.setWebChromeClient(new WebChromeClient() {
+ @Override
+ public void onProgressChanged(WebView view, int progress) {
+ if (progress < 100 && pbar.getVisibility() == ProgressBar.GONE) {
+ pbar.setVisibility(ProgressBar.VISIBLE);
+ }
+ pbar.setProgress(progress);
+ if (progress == 100) {
+ pbar.setVisibility(ProgressBar.GONE);
+ }
+ }
+ });
+
+ if (instance == null) {
+ finish();
+ }
+ webView.setWebViewClient(new WebViewClient() {
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
+ super.shouldOverrideUrlLoading(view, url);
+ if (url.contains(Helper.REDIRECT_CONTENT_WEB)) {
+ String[] val = url.split("code=");
+ if (val.length < 2) {
+ Toasty.error(MastodonWebviewConnectActivity.this, getString(R.string.toast_code_error), Toast.LENGTH_LONG).show();
+ Intent myIntent = new Intent(MastodonWebviewConnectActivity.this, LoginActivity.class);
+ startActivity(myIntent);
+ finish();
+ return false;
+ }
+ String code = val[1];
+ if (code.contains("&")) {
+ code = code.split("&")[0];
+ }
+ OauthParams oauthParams = new OauthParams();
+ oauthParams.setClient_id(clientId);
+ oauthParams.setClient_secret(clientSecret);
+ oauthParams.setGrant_type("authorization_code");
+ oauthParams.setCode(code);
+ oauthParams.setRedirect_uri(Helper.REDIRECT_CONTENT_WEB);
+
+ new Thread(() -> {
+ try {
+ Token token = new RetrofitMastodonAPI(MastodonWebviewConnectActivity.this, instance, null).manageToken(oauthParams);
+ SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = sharedpreferences.edit();
+ editor.putString(Helper.PREF_KEY_OAUTH_TOKEN, token.getAccess_token());
+ editor.apply();
+ new RetrofitMastodonAPI(MastodonWebviewConnectActivity.this, instance, token.getAccess_token()).updateCredential(MastodonWebviewConnectActivity.this, clientId, clientSecret, token.getRefresh_token(), software);
+ } catch (Exception | Error ignored) {
+ }
+ }).start();
+ return true;
+ }
+ return false;
+ }
+
+ });
+ webView.loadUrl(redirectUserToAuthorizeAndLogin(clientId, instance));
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ int itemId = item.getItemId();
+ if (itemId == android.R.id.home) {
+ finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (webView != null && webView.canGoBack()) {
+ webView.goBack();
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (alert != null) {
+ alert.dismiss();
+ alert = null;
+ }
+ if (webView != null) {
+ webView.destroy();
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/MyAccountActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/MyAccountActivity.java
new file mode 100644
index 00000000..fc7c3f55
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/peertube/activities/MyAccountActivity.java
@@ -0,0 +1,362 @@
+package app.fedilab.android.peertube.activities;
+/* Copyright 2020 Thomas Schneider
+ *
+ * This file is a part of TubeLab
+ *
+ * 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.
+ *
+ * TubeLab 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 TubeLab; if not,
+ * see . */
+
+import static app.fedilab.android.peertube.activities.PeertubeUploadActivity.MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE;
+import static app.fedilab.android.peertube.worker.WorkHelper.NOTIFICATION_WORKER;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Toast;
+
+import androidx.appcompat.widget.SwitchCompat;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+import androidx.documentfile.provider.DocumentFile;
+import androidx.work.WorkManager;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.resource.bitmap.CenterCrop;
+import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
+import com.bumptech.glide.request.RequestOptions;
+
+import org.jetbrains.annotations.NotNull;
+
+import app.fedilab.android.peertube.R;
+import app.fedilab.android.peertube.client.RetrofitPeertubeAPI;
+import app.fedilab.android.peertube.client.entities.NotificationSettings;
+import app.fedilab.android.peertube.client.entities.UserMe;
+import app.fedilab.android.peertube.client.entities.UserSettings;
+import app.fedilab.android.peertube.databinding.ActivityMyAccountSettingsBinding;
+import app.fedilab.android.peertube.helper.Helper;
+import app.fedilab.android.peertube.helper.HelperInstance;
+import app.fedilab.android.peertube.helper.Theme;
+import app.fedilab.android.peertube.worker.WorkHelper;
+import es.dmoral.toasty.Toasty;
+
+public class MyAccountActivity extends BaseActivity {
+
+ private static final int PICK_IMAGE = 466;
+ ActivityMyAccountSettingsBinding binding;
+ private Uri inputData;
+ private String fileName;
+ private NotificationSettings notificationSettings;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Theme.setTheme(this, HelperInstance.getLiveInstance(this), false);
+ super.onCreate(savedInstanceState);
+ binding = ActivityMyAccountSettingsBinding.inflate(getLayoutInflater());
+ View view = binding.getRoot();
+ setContentView(view);
+
+ if (getSupportActionBar() != null)
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ if (MainActivity.userMe == null) {
+ finish();
+ return;
+ }
+ setTitle(String.format("@%s", MainActivity.userMe.getUsername()));
+ binding.displayname.setText(MainActivity.userMe.getAccount().getDisplayName());
+ binding.description.setText(MainActivity.userMe.getAccount().getDescription());
+
+ notificationSettings = MainActivity.userMe.getNotificationSettings();
+ initializeValues(notificationSettings.getAbuseStateChange(), binding.notifAbuseAcceptedApp, binding.notifAbuseAcceptedMail);
+ initializeValues(notificationSettings.getAbuseNewMessage(), binding.notifAbuseReceivedApp, binding.notifAbuseReceivedMail);
+ initializeValues(notificationSettings.getCommentMention(), binding.notifVideoMentionApp, binding.notifVideoMentionMail);
+ initializeValues(notificationSettings.getNewFollow(), binding.notifNewFollowersApp, binding.notifNewFollowersMail);
+ initializeValues(notificationSettings.getMyVideoImportFinished(), binding.notifVideoImportedApp, binding.notifVideoImportedMail);
+ initializeValues(notificationSettings.getMyVideoPublished(), binding.notifVideoPublishedApp, binding.notifVideoPublishedMail);
+ initializeValues(notificationSettings.getBlacklistOnMyVideo(), binding.notifBlockedApp, binding.notifBlockedMail);
+ initializeValues(notificationSettings.getNewCommentOnMyVideo(), binding.notifNewCommentApp, binding.notifNewCommentMail);
+ initializeValues(notificationSettings.getNewVideoFromSubscription(), binding.notifNewVideoApp, binding.notifNewVideoMail);
+
+ Helper.loadAvatar(MyAccountActivity.this, MainActivity.userMe.getAccount(), binding.profilePicture);
+ String[] refresh_array = getResources().getStringArray(R.array.refresh_time);
+ ArrayAdapter refreshArray = new ArrayAdapter<>(MyAccountActivity.this,
+ android.R.layout.simple_spinner_dropdown_item, refresh_array);
+ binding.refreshTime.setAdapter(refreshArray);
+
+ SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
+ int interval = sharedpreferences.getInt(Helper.NOTIFICATION_INTERVAL, 60);
+ binding.refreshTime.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ SharedPreferences.Editor editor = sharedpreferences.edit();
+ int time;
+ switch (position) {
+ case 1:
+ time = 15;
+ break;
+ case 2:
+ time = 30;
+ break;
+ case 3:
+ time = 60;
+ break;
+ case 4:
+ time = 120;
+ break;
+ case 5:
+ time = 360;
+ break;
+ case 6:
+ time = 720;
+ break;
+ default:
+ time = 0;
+ }
+ editor.putInt(Helper.NOTIFICATION_INTERVAL, time);
+ editor.apply();
+ WorkManager.getInstance(getApplicationContext()).cancelAllWorkByTag(NOTIFICATION_WORKER);
+ if (time > 0) {
+ WorkHelper.fetchNotifications(getApplication(), time);
+
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+
+ }
+ });
+ int position = 0;
+ switch (interval) {
+ case 0:
+ position = 0;
+ break;
+ case 15:
+ position = 1;
+ break;
+ case 30:
+ position = 2;
+ break;
+ case 60:
+ position = 3;
+ break;
+ case 120:
+ position = 4;
+ break;
+ case 360:
+ position = 5;
+ break;
+ case 720:
+ position = 6;
+ break;
+ }
+ binding.refreshTime.setSelection(position, false);
+
+ binding.selectFile.setOnClickListener(v -> {
+ if (ContextCompat.checkSelfPermission(MyAccountActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) !=
+ PackageManager.PERMISSION_GRANTED) {
+ ActivityCompat.requestPermissions(MyAccountActivity.this,
+ new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
+ MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
+ return;
+ }
+
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setType("*/*");
+ String[] mimetypes = {"image/*"};
+ intent.putExtra(Intent.EXTRA_MIME_TYPES, mimetypes);
+ startActivityForResult(intent, PICK_IMAGE);
+ });
+
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(@NotNull Menu menu) {
+ getMenuInflater().inflate(R.menu.main_my_account, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ finish();
+ return true;
+ } else if (item.getItemId() == R.id.action_validate) {
+ item.setEnabled(false);
+ new Thread(() -> {
+ UserSettings userSettings = new UserSettings();
+ userSettings.setNotificationSettings(notificationSettings);
+ if (binding.displayname.getText() != null) {
+ userSettings.setDisplayName(binding.displayname.getText().toString().trim());
+ }
+ if (binding.description.getText() != null) {
+ userSettings.setDescription(binding.description.getText().toString().trim());
+ }
+ if (inputData != null) {
+ userSettings.setAvatarfile(inputData);
+ userSettings.setFileName(fileName);
+ }
+ try {
+ RetrofitPeertubeAPI api = new RetrofitPeertubeAPI(MyAccountActivity.this);
+ UserMe.AvatarResponse avatarResponse = api.updateUser(userSettings);
+ MainActivity.userMe.getAccount().setDisplayName(binding.displayname.getText().toString().trim());
+ MainActivity.userMe.getAccount().setDescription(binding.description.getText().toString().trim());
+ if (avatarResponse != null && avatarResponse.getAvatar() != null) {
+ MainActivity.userMe.getAccount().setAvatar(avatarResponse.getAvatar());
+ }
+
+ Handler mainHandler = new Handler(Looper.getMainLooper());
+ Runnable myRunnable = () -> {
+ Toasty.info(MyAccountActivity.this, getString(R.string.account_updated), Toasty.LENGTH_LONG).show();
+ item.setEnabled(true);
+ };
+ mainHandler.post(myRunnable);
+ } catch (Exception | Error e) {
+ Handler mainHandler = new Handler(Looper.getMainLooper());
+ Runnable myRunnable = () -> {
+ Toasty.error(MyAccountActivity.this, getString(R.string.toast_error), Toasty.LENGTH_LONG).show();
+ item.setEnabled(true);
+ };
+ mainHandler.post(myRunnable);
+ }
+ }).start();
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == PICK_IMAGE && resultCode == Activity.RESULT_OK) {
+ if (data == null || data.getData() == null) {
+ Toasty.error(MyAccountActivity.this, getString(R.string.toot_select_image_error), Toast.LENGTH_LONG).show();
+ return;
+ }
+ inputData = data.getData();
+ DocumentFile documentFile = DocumentFile.fromSingleUri(this, inputData);
+ if (documentFile != null) {
+ fileName = documentFile.getName();
+ }
+ Glide.with(MyAccountActivity.this)
+ .load(inputData)
+ .thumbnail(0.1f)
+ .apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10)))
+ .into(binding.profilePicture);
+
+ }
+ }
+
+ private void initializeValues(int value, SwitchCompat app, SwitchCompat email) {
+ switch (value) {
+ case 1:
+ app.setChecked(true);
+ email.setChecked(false);
+ break;
+ case 2:
+ app.setChecked(false);
+ email.setChecked(true);
+ break;
+ case 3:
+ app.setChecked(true);
+ email.setChecked(true);
+ break;
+ default:
+ app.setChecked(false);
+ email.setChecked(false);
+ }
+ app.setOnCheckedChangeListener((compoundButton, checked) -> {
+ int id = app.getId();
+ if (id == R.id.notif_new_video_app) {
+ notificationSettings.setNewVideoFromSubscription(getNewAppCheckedValue(checked, email));
+ } else if (id == R.id.notif_new_comment_app) {
+ notificationSettings.setNewCommentOnMyVideo(getNewAppCheckedValue(checked, email));
+ } else if (id == R.id.notif_blocked_app) {
+ notificationSettings.setBlacklistOnMyVideo(getNewAppCheckedValue(checked, email));
+ } else if (id == R.id.notif_video_published_app) {
+ notificationSettings.setMyVideoPublished(getNewAppCheckedValue(checked, email));
+ } else if (id == R.id.notif_video_imported_app) {
+ notificationSettings.setMyVideoImportFinished(getNewAppCheckedValue(checked, email));
+ } else if (id == R.id.notif_new_followers_app) {
+ notificationSettings.setNewFollow(getNewAppCheckedValue(checked, email));
+ } else if (id == R.id.notif_video_mention_app) {
+ notificationSettings.setCommentMention(getNewAppCheckedValue(checked, email));
+ } else if (id == R.id.notif_abuse_received_app) {
+ notificationSettings.setAbuseNewMessage(getNewAppCheckedValue(checked, email));
+ } else if (id == R.id.notif_abuse_accepted_app) {
+ notificationSettings.setAbuseStateChange(getNewAppCheckedValue(checked, email));
+ }
+ });
+ email.setOnCheckedChangeListener((compoundButtonMail, checkedMail) -> {
+ int id = email.getId();
+ if (id == R.id.notif_new_video_mail) {
+ notificationSettings.setNewVideoFromSubscription(getNewMailCheckedValue(checkedMail, app));
+ } else if (id == R.id.notif_new_comment_mail) {
+ notificationSettings.setNewCommentOnMyVideo(getNewMailCheckedValue(checkedMail, app));
+ } else if (id == R.id.notif_blocked_mail) {
+ notificationSettings.setBlacklistOnMyVideo(getNewMailCheckedValue(checkedMail, app));
+ } else if (id == R.id.notif_video_published_mail) {
+ notificationSettings.setMyVideoPublished(getNewMailCheckedValue(checkedMail, app));
+ } else if (id == R.id.notif_video_imported_mail) {
+ notificationSettings.setMyVideoImportFinished(getNewMailCheckedValue(checkedMail, app));
+ } else if (id == R.id.notif_new_followers_mail) {
+ notificationSettings.setNewFollow(getNewMailCheckedValue(checkedMail, app));
+ } else if (id == R.id.notif_video_mention_mail) {
+ notificationSettings.setCommentMention(getNewMailCheckedValue(checkedMail, app));
+ } else if (id == R.id.notif_abuse_received_mail) {
+ notificationSettings.setAbuseNewMessage(getNewMailCheckedValue(checkedMail, app));
+ } else if (id == R.id.notif_abuse_accepted_mail) {
+ notificationSettings.setAbuseStateChange(getNewMailCheckedValue(checkedMail, app));
+ }
+ });
+ }
+
+ private int getNewAppCheckedValue(boolean checked, SwitchCompat email) {
+ int newValue;
+ if (checked && email.isChecked()) {
+ newValue = 3;
+ } else if (!checked && email.isChecked()) {
+ newValue = 2;
+ } else if (checked && !email.isChecked()) {
+ newValue = 1;
+ } else {
+ newValue = 0;
+ }
+ return newValue;
+ }
+
+ private int getNewMailCheckedValue(boolean checked, SwitchCompat app) {
+ int newValue;
+ if (checked && app.isChecked()) {
+ newValue = 3;
+ } else if (!checked && app.isChecked()) {
+ newValue = 1;
+ } else if (checked && !app.isChecked()) {
+ newValue = 2;
+ } else {
+ newValue = 0;
+ }
+ return newValue;
+ }
+}
diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeActivity.java
new file mode 100644
index 00000000..11daf502
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeActivity.java
@@ -0,0 +1,2319 @@
+package app.fedilab.android.peertube.activities;
+/* Copyright 2020 Thomas Schneider
+ *
+ * This file is a part of TubeLab
+ *
+ * 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.
+ *
+ * TubeLab 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 TubeLab; if not,
+ * see . */
+
+import static com.google.android.exoplayer2.Player.MEDIA_ITEM_TRANSITION_REASON_AUTO;
+import static app.fedilab.android.peertube.client.RetrofitPeertubeAPI.ActionType.ADD_COMMENT;
+import static app.fedilab.android.peertube.client.RetrofitPeertubeAPI.ActionType.RATEVIDEO;
+import static app.fedilab.android.peertube.client.RetrofitPeertubeAPI.ActionType.REPLY;
+import static app.fedilab.android.peertube.client.RetrofitPeertubeAPI.ActionType.REPORT_ACCOUNT;
+import static app.fedilab.android.peertube.client.RetrofitPeertubeAPI.ActionType.REPORT_VIDEO;
+import static app.fedilab.android.peertube.helper.Helper.canMakeAction;
+import static app.fedilab.android.peertube.helper.Helper.getAttColor;
+import static app.fedilab.android.peertube.helper.Helper.isLoggedIn;
+import static app.fedilab.android.peertube.helper.Helper.peertubeInformation;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.PictureInPictureParams;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.database.sqlite.SQLiteDatabase;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.v4.media.session.MediaSessionCompat;
+import android.text.Html;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.TextPaint;
+import android.text.method.LinkMovementMethod;
+import android.text.style.ClickableSpan;
+import android.util.DisplayMetrics;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.animation.Animation;
+import android.view.animation.TranslateAnimation;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.widget.PopupMenu;
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+import androidx.core.graphics.drawable.DrawableCompat;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.bumptech.glide.Glide;
+import com.github.vkay94.dtpv.youtube.YouTubeOverlay;
+import com.google.android.exoplayer2.C;
+import com.google.android.exoplayer2.ExoPlaybackException;
+import com.google.android.exoplayer2.Format;
+import com.google.android.exoplayer2.MediaItem;
+import com.google.android.exoplayer2.PlaybackParameters;
+import com.google.android.exoplayer2.Player;
+import com.google.android.exoplayer2.SimpleExoPlayer;
+import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
+import com.google.android.exoplayer2.source.MergingMediaSource;
+import com.google.android.exoplayer2.source.ProgressiveMediaSource;
+import com.google.android.exoplayer2.source.SingleSampleMediaSource;
+import com.google.android.exoplayer2.source.hls.HlsMediaSource;
+import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
+import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
+import com.google.android.exoplayer2.trackselection.TrackSelector;
+import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
+import com.google.android.exoplayer2.ui.DefaultTimeBar;
+import com.google.android.exoplayer2.ui.PlayerControlView;
+import com.google.android.exoplayer2.upstream.DataSource;
+import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
+import com.google.android.exoplayer2.util.MimeTypes;
+import com.google.android.exoplayer2.util.Util;
+import com.google.android.exoplayer2.video.VideoListener;
+import com.google.android.material.snackbar.Snackbar;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import app.fedilab.android.peertube.BasePeertubeActivity;
+import app.fedilab.android.peertube.BuildConfig;
+import app.fedilab.android.peertube.Matomo;
+import app.fedilab.android.peertube.R;
+import app.fedilab.android.peertube.client.APIResponse;
+import app.fedilab.android.peertube.client.MenuItemVideo;
+import app.fedilab.android.peertube.client.RetrofitPeertubeAPI;
+import app.fedilab.android.peertube.client.data.AccountData.Account;
+import app.fedilab.android.peertube.client.data.CaptionData.Caption;
+import app.fedilab.android.peertube.client.data.CommentData;
+import app.fedilab.android.peertube.client.data.CommentData.Comment;
+import app.fedilab.android.peertube.client.data.InstanceData;
+import app.fedilab.android.peertube.client.data.PlaylistData;
+import app.fedilab.android.peertube.client.data.PluginData;
+import app.fedilab.android.peertube.client.data.VideoData;
+import app.fedilab.android.peertube.client.entities.File;
+import app.fedilab.android.peertube.client.entities.MenuItemView;
+import app.fedilab.android.peertube.client.entities.PlaylistExist;
+import app.fedilab.android.peertube.client.entities.Report;
+import app.fedilab.android.peertube.client.entities.UserSettings;
+import app.fedilab.android.peertube.client.mastodon.RetrofitMastodonAPI;
+import app.fedilab.android.peertube.databinding.ActivityPeertubeBinding;
+import app.fedilab.android.peertube.drawer.CommentListAdapter;
+import app.fedilab.android.peertube.drawer.MenuAdapter;
+import app.fedilab.android.peertube.drawer.MenuItemAdapter;
+import app.fedilab.android.peertube.helper.CacheDataSourceFactory;
+import app.fedilab.android.peertube.helper.Helper;
+import app.fedilab.android.peertube.helper.HelperInstance;
+import app.fedilab.android.peertube.helper.Theme;
+import app.fedilab.android.peertube.sqlite.AccountDAO;
+import app.fedilab.android.peertube.sqlite.Sqlite;
+import app.fedilab.android.peertube.viewmodel.CaptionsVM;
+import app.fedilab.android.peertube.viewmodel.CommentVM;
+import app.fedilab.android.peertube.viewmodel.PlaylistsVM;
+import app.fedilab.android.peertube.viewmodel.PostActionsVM;
+import app.fedilab.android.peertube.viewmodel.SearchVM;
+import app.fedilab.android.peertube.viewmodel.TimelineVM;
+import app.fedilab.android.peertube.viewmodel.mastodon.MastodonPostActionsVM;
+import app.fedilab.android.peertube.webview.CustomWebview;
+import app.fedilab.android.peertube.webview.MastalabWebChromeClient;
+import app.fedilab.android.peertube.webview.MastalabWebViewClient;
+import es.dmoral.toasty.Toasty;
+
+
+public class PeertubeActivity extends BasePeertubeActivity implements CommentListAdapter.AllCommentRemoved, Player.EventListener, VideoListener, MenuAdapter.ItemClicked, MenuItemAdapter.ItemAction {
+
+ public static String video_id;
+ public static List playedVideos = new ArrayList<>();
+ Uri captionURI;
+ String captionLang;
+ private String peertubeInstance, videoUuid;
+ private ImageView fullScreenIcon;
+ private boolean fullScreenMode;
+ private int mode;
+ private Map> playlists;
+ private boolean playInMinimized, autoPlay, autoFullscreen;
+ private boolean onStopCalled;
+ private List captions;
+ private String max_id;
+ private boolean flag_loading;
+ private boolean isMyVideo;
+ private List comments;
+ private CommentListAdapter commentListAdapter;
+ private CommentListAdapter commentReplyListAdapter;
+ private boolean sepiaSearch;
+ private ActivityPeertubeBinding binding;
+ private List commentsThread;
+ private BroadcastReceiver mPowerKeyReceiver = null;
+ private boolean isPlayInMinimized;
+ private VideoData.Video nextVideo;
+ private String show_more_content;
+ private videoOrientation videoOrientationType;
+ private int initialOrientation;
+ private String currentResolution;
+ private String currentCaption;
+ private boolean isRemote;
+ private boolean willPlayFromIntent;
+ private app.fedilab.android.peertube.client.mastodon.Status status;
+
+ public static void hideKeyboard(Activity activity) {
+ if (activity != null && activity.getWindow() != null) {
+ activity.getWindow().getDecorView();
+ InputMethodManager imm = activity.getSystemService(INPUT_METHOD_SERVICE);
+ assert imm != null;
+ imm.hideSoftInputFromWindow(activity.getWindow().getDecorView().getWindowToken(), 0);
+ }
+ }
+
+ @Override
+ public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
+ if (width < height) {
+ videoOrientationType = videoOrientation.PORTRAIT;
+ } else {
+ videoOrientationType = videoOrientation.LANDSCAPE;
+ }
+ }
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Theme.setTheme(this, HelperInstance.getLiveInstance(this), false);
+ super.onCreate(savedInstanceState);
+ binding = super.binding;
+ videoOrientationType = videoOrientation.LANDSCAPE;
+ max_id = "0";
+ SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
+ SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
+ String token = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null);
+ if (Helper.canMakeAction(PeertubeActivity.this) && !sepiaSearch) {
+ Account account = new AccountDAO(PeertubeActivity.this, db).getAccountByToken(token);
+ Helper.loadAvatar(PeertubeActivity.this, account, binding.myPp);
+ }
+ isRemote = false;
+
+
+ fullScreenMode = false;
+ initialOrientation = getResources().getConfiguration().orientation;
+ if (Helper.isTablet(PeertubeActivity.this)) {
+
+ LinearLayout.LayoutParams param = new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ 0,
+ 2.0f
+ );
+ binding.videoContainer.setLayoutParams(param);
+ } else {
+ if (initialOrientation == Configuration.ORIENTATION_LANDSCAPE) {
+ LinearLayout.LayoutParams param = new LinearLayout.LayoutParams(
+ ConstraintLayout.LayoutParams.MATCH_PARENT,
+ 0,
+ 4.0f
+ );
+ binding.videoContainer.setLayoutParams(param);
+ }
+ }
+ isPlayInMinimized = false;
+ if (getSupportActionBar() != null)
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ mode = sharedpreferences.getInt(Helper.SET_VIDEO_MODE, Helper.VIDEO_MODE_NORMAL);
+
+ Intent intent = getIntent();
+
+ Bundle b = intent.getExtras();
+ if (b != null) {
+ peertubeInstance = b.getString("peertube_instance", HelperInstance.getLiveInstance(PeertubeActivity.this));
+ videoUuid = b.getString("video_uuid", null);
+ isMyVideo = b.getBoolean("isMyVideo", false);
+ sepiaSearch = b.getBoolean("sepia_search", false);
+ peertube = b.getParcelable("video");
+ }
+
+
+ willPlayFromIntent = manageIntentUrl(intent);
+
+ if (BuildConfig.allow_remote_connections && Helper.isLoggedInType(PeertubeActivity.this) == MainActivity.TypeOfConnection.REMOTE_ACCOUNT) {
+ binding.peertubeLikeCount.setVisibility(View.GONE);
+ binding.peertubeDislikeCount.setVisibility(View.GONE);
+ binding.peertubePlaylist.setVisibility(View.GONE);
+ binding.peertubeReblog.setVisibility(View.VISIBLE);
+ binding.peertubeFavorite.setVisibility(View.VISIBLE);
+ binding.peertubeBookmark.setVisibility(View.VISIBLE);
+ } else {
+ binding.peertubePlaylist.setVisibility(View.VISIBLE);
+ }
+
+ binding.peertubeDescriptionMore.setOnClickListener(v -> {
+ if (show_more_content != null && peertube != null) {
+ if (binding.peertubeDescriptionMore.getText().toString().compareTo(getString(R.string.show_more)) == 0) {
+ binding.peertubeDescriptionMore.setText(getString(R.string.show_less));
+ binding.peertubeDescription.setText(show_more_content);
+ } else {
+ binding.peertubeDescriptionMore.setText(getString(R.string.show_more));
+ binding.peertubeDescription.setText(peertube.getDescription());
+ }
+ }
+ });
+ if (!Helper.canMakeAction(PeertubeActivity.this) || sepiaSearch) {
+ binding.writeCommentContainer.setVisibility(View.GONE);
+ }
+ playInMinimized = sharedpreferences.getBoolean(getString(R.string.set_video_minimize_choice), true);
+ autoPlay = sharedpreferences.getBoolean(getString(R.string.set_autoplay_choice), true);
+ autoFullscreen = sharedpreferences.getBoolean(getString(R.string.set_fullscreen_choice), false);
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O
+ || !getPackageManager().hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)) {
+ playInMinimized = false;
+ }
+
+ if (peertube != null && peertube.isNsfw()) {
+ binding.videoSensitive.setVisibility(View.VISIBLE);
+ } else {
+ binding.videoSensitive.setVisibility(View.INVISIBLE);
+ }
+ if (mode == Helper.VIDEO_MODE_WEBVIEW) {
+ binding.webviewVideo.setVisibility(View.VISIBLE);
+ binding.mediaVideo.setVisibility(View.GONE);
+ binding.doubleTapPlayerView.setVisibility(View.GONE);
+ CustomWebview webview_video = Helper.initializeWebview(PeertubeActivity.this, R.id.webview_video, null);
+
+ MastalabWebChromeClient mastalabWebChromeClient = new MastalabWebChromeClient(PeertubeActivity.this, webview_video, binding.mainMediaFrame, binding.videoLayout);
+ mastalabWebChromeClient.setOnToggledFullscreen(fullscreen -> {
+ if (fullscreen) {
+ binding.videoLayout.setVisibility(View.VISIBLE);
+ } else {
+ binding.videoLayout.setVisibility(View.GONE);
+ }
+ toogleFullscreen(fullscreen);
+ });
+ binding.webviewVideo.getSettings().setAllowFileAccess(true);
+ binding.webviewVideo.setWebChromeClient(mastalabWebChromeClient);
+ binding.webviewVideo.getSettings().setDomStorageEnabled(true);
+ binding.webviewVideo.getSettings().setAppCacheEnabled(true);
+ binding.webviewVideo.getSettings().setMediaPlaybackRequiresUserGesture(false);
+ binding.webviewVideo.setWebViewClient(new MastalabWebViewClient(PeertubeActivity.this));
+ binding.webviewVideo.loadUrl("https://" + peertubeInstance + "/videos/embed/" + videoUuid);
+ } else {
+ binding.webviewVideo.setVisibility(View.GONE);
+ binding.loader.setVisibility(View.VISIBLE);
+ }
+
+ if (mode != Helper.VIDEO_MODE_WEBVIEW) {
+ binding.doubleTapPlayerView.setControllerShowTimeoutMs(1000);
+ binding.doubleTapPlayerView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FIT);
+ initControllerButtons();
+
+ binding.doubleTapPlayerView
+ .setDoubleTapDelay(500);
+ binding.doubleTapPlayerView.setDoubleTapEnabled(true);
+ binding.doubleTapPlayerView.setControllerShowTimeoutMs(0);
+ binding.mediaVideo.performListener(new YouTubeOverlay.PerformListener() {
+ @Override
+ public void onAnimationStart() {
+ binding.mediaVideo.setVisibility(View.VISIBLE);
+ binding.doubleTapPlayerView.setUseController(false);
+ }
+
+ @Override
+ public void onAnimationEnd() {
+ binding.mediaVideo.setVisibility(View.GONE);
+ binding.doubleTapPlayerView.setUseController(true);
+ }
+ }).playerView(binding.doubleTapPlayerView).seekSeconds(10);
+ binding.doubleTapPlayerView.setPlayer(player);
+ binding.doubleTapPlayerView.controller(binding.mediaVideo);
+ if (player != null)
+ binding.mediaVideo.player(player);
+ }
+ flag_loading = true;
+ comments = new ArrayList<>();
+
+ binding.closeReply.setOnClickListener(v -> closeCommentThread());
+ binding.closePost.setOnClickListener(v -> closePostComment());
+
+ commentListAdapter = new CommentListAdapter(comments, isMyVideo || Helper.isVideoOwner(PeertubeActivity.this, peertube), false, peertubeInstance, sepiaSearch);
+ commentListAdapter.allCommentRemoved = PeertubeActivity.this;
+ LinearLayoutManager mLayoutManager = new LinearLayoutManager(PeertubeActivity.this);
+ binding.peertubeComments.setLayoutManager(mLayoutManager);
+ binding.peertubeComments.setNestedScrollingEnabled(false);
+ binding.peertubeComments.setAdapter(commentListAdapter);
+ binding.peertubeComments.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
+ if (dy > 0) {
+ int visibleItemCount = mLayoutManager.getChildCount();
+ int totalItemCount = mLayoutManager.getItemCount();
+ int firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();
+ if (firstVisibleItem + visibleItemCount == totalItemCount) {
+ if (!flag_loading) {
+ CommentVM commentViewModel = new ViewModelProvider(PeertubeActivity.this).get(CommentVM.class);
+ commentViewModel.getThread(sepiaSearch ? peertubeInstance : null, videoUuid, max_id).observe(PeertubeActivity.this, apiresponse -> manageVIewComment(apiresponse));
+ }
+ }
+ }
+ }
+ });
+ if (!willPlayFromIntent && peertube != null && sepiaSearch && peertube.getEmbedUrl() != null && Helper.isLoggedIn(PeertubeActivity.this)) {
+ SearchVM viewModelSearch = new ViewModelProvider(PeertubeActivity.this).get(SearchVM.class);
+ viewModelSearch.getVideos("0", peertube.getUuid()).observe(PeertubeActivity.this, this::manageVIewVideos);
+ } else {
+ playVideo();
+ }
+
+ registBroadcastReceiver();
+ if (autoFullscreen && autoPlay) {
+ openFullscreenDialog();
+ }
+ binding.postCommentButton.setOnClickListener(v -> {
+ if (canMakeAction(PeertubeActivity.this) && !sepiaSearch) {
+ openPostComment(null, 0);
+ } else {
+ if (sepiaSearch) {
+ Toasty.info(PeertubeActivity.this, getString(R.string.federation_issue), Toasty.LENGTH_SHORT).show();
+ } else {
+ Toasty.error(PeertubeActivity.this, getString(R.string.not_logged_in), Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+
+
+ }
+
+ private void manageVIewVideos(APIResponse apiResponse) {
+ if (apiResponse == null || apiResponse.getPeertubes() == null || apiResponse.getPeertubes().size() == 0) {
+ playVideo();
+ return;
+ }
+ peertube = apiResponse.getPeertubes().get(0);
+ Matomo.sendScreen(PeertubeActivity.this, "VIDEO_REGARDEE", peertube.getUuid());
+ if (peertube.isNsfw()) {
+ binding.videoSensitive.setVisibility(View.VISIBLE);
+ } else {
+ binding.videoSensitive.setVisibility(View.INVISIBLE);
+ }
+ if (player != null && peertube.getUserHistory() != null) {
+ player.seekTo(peertube.getUserHistory().getCurrentTime() * 1000);
+ }
+ sepiaSearch = false;
+ playVideo();
+ }
+
+ public void manageVIewComment(APIResponse apiResponse) {
+ flag_loading = false;
+ if (apiResponse == null || (apiResponse.getError() != null)) {
+ if (apiResponse == null)
+ Toasty.error(PeertubeActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
+ else
+ Toasty.error(PeertubeActivity.this, apiResponse.getError().getError(), Toast.LENGTH_LONG).show();
+ return;
+ }
+ int oldSize = comments.size();
+ int newComments = 0;
+ for (Comment comment : apiResponse.getComments()) {
+ if (comment.getText() != null && comment.getText().trim().length() > 0) {
+ comments.add(comment);
+ newComments++;
+ }
+ }
+ if (comments.size() > 0) {
+ binding.peertubeComments.setVisibility(View.VISIBLE);
+ commentListAdapter.notifyItemRangeInserted(oldSize, newComments);
+ }
+ }
+
+ public void manageVIewCommentReply(Comment comment, APIResponse apiResponse) {
+ if (apiResponse == null || apiResponse.getError() != null || apiResponse.getCommentThreadData() == null) {
+ if (apiResponse == null || apiResponse.getError() == null)
+ Toasty.error(PeertubeActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
+ else
+ Toasty.error(PeertubeActivity.this, apiResponse.getError().getError(), Toast.LENGTH_LONG).show();
+ return;
+ }
+ List commentThreadDataList = apiResponse.getCommentThreadData().getChildren();
+ commentsThread = generateCommentReply(commentThreadDataList, new ArrayList<>());
+ comment.setInReplyToCommentId(null);
+ comment.setTotalReplies(0);
+ commentsThread.add(0, comment);
+ commentReplyListAdapter = new CommentListAdapter(commentsThread, Helper.isVideoOwner(PeertubeActivity.this, peertube), true, peertubeInstance, sepiaSearch);
+ LinearLayoutManager mLayoutManager = new LinearLayoutManager(PeertubeActivity.this);
+ binding.peertubeReply.setLayoutManager(mLayoutManager);
+ binding.peertubeReply.setNestedScrollingEnabled(false);
+ binding.peertubeReply.setAdapter(commentReplyListAdapter);
+ binding.peertubeReply.setVisibility(View.VISIBLE);
+ if (commentsThread.size() > 0) {
+ commentReplyListAdapter.notifyItemRangeInserted(0, commentsThread.size());
+ }
+ }
+
+ private List generateCommentReply(List commentThreadDataList, List comments) {
+ for (CommentData.CommentThreadData commentThreadData : commentThreadDataList) {
+ if (commentThreadData.getComment().getText() != null && commentThreadData.getComment().getText().trim().length() > 0) {
+ commentThreadData.getComment().setReply(true);
+ comments.add(commentThreadData.getComment());
+ }
+ if (commentThreadData.getChildren() != null && commentThreadData.getChildren().size() > 0) {
+ generateCommentReply(commentThreadData.getChildren(), comments);
+ }
+ }
+ return comments;
+ }
+
+ @Override
+ public void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ Bundle b = intent.getExtras();
+ if (b != null) {
+ isRemote = false;
+ peertubeInstance = b.getString("peertube_instance", HelperInstance.getLiveInstance(PeertubeActivity.this));
+ videoUuid = b.getString("video_uuid", null);
+ setRequestedOrientationCustom(initialOrientation);
+ if (comments != null && comments.size() > 0) {
+ int number = comments.size();
+ comments.clear();
+ commentListAdapter.notifyItemRangeRemoved(0, number);
+ }
+ playVideo();
+ }
+ willPlayFromIntent = manageIntentUrl(intent);
+ }
+
+ private boolean manageIntentUrl(Intent intent) {
+ if (intent.getData() != null) { //Comes from a link
+ String url = intent.getData().toString();
+ Pattern link = Pattern.compile("(https?://[\\da-z.-]+\\.[a-z.]{2,10})/videos/watch/(\\w{8}-\\w{4}-\\w{4}-\\w{4}-\\w{12})(\\?start=(\\d+[hH])?(\\d+[mM])?(\\d+[sS])?)?$");
+ Matcher matcherLink = link.matcher(url);
+ if (matcherLink.find()) {
+ String instance = matcherLink.group(1);
+ String uuid = matcherLink.group(2);
+ String hour = matcherLink.group(4);
+ String min = matcherLink.group(5);
+ String sec = matcherLink.group(6);
+ int hourInt, minInt, secInt;
+ int totalSeconds = 0;
+ if (hour != null) {
+ hourInt = Integer.parseInt(hour.replace("h", ""));
+ totalSeconds += 3600 * hourInt;
+ }
+ if (min != null) {
+ minInt = Integer.parseInt(min.replace("m", ""));
+ totalSeconds += 60 * minInt;
+ }
+ if (sec != null) {
+ secInt = Integer.parseInt(sec.replace("s", ""));
+ totalSeconds += secInt;
+ }
+ captionURI = null;
+ captionLang = null;
+ if (instance != null && uuid != null) {
+ peertubeInstance = instance.replace("https://", "").replace("http://", "");
+ sepiaSearch = true; // Sepia search flag is used because, at this time we don't know if the video is federated.
+ videoUuid = uuid;
+ peertube = new VideoData.Video();
+ peertube.setUuid(uuid);
+ peertube.setEmbedUrl(url);
+ if (totalSeconds > 0) {
+ VideoData.UserHistory userHistory = new VideoData.UserHistory();
+ userHistory.setCurrentTime(totalSeconds);
+ peertube.setUserHistory(userHistory);
+ }
+ TimelineVM viewModelTimeline = new ViewModelProvider(PeertubeActivity.this).get(TimelineVM.class);
+ viewModelTimeline.getVideo(peertubeInstance, peertube.getUuid(), false).observe(PeertubeActivity.this, this::manageVIewVideo);
+ if (player != null) {
+ player.release();
+ }
+ if (comments != null && comments.size() > 0) {
+ int number = comments.size();
+ comments.clear();
+ commentListAdapter.notifyItemRangeRemoved(0, number);
+ }
+ fetchComments();
+ isRemote = true;
+ return true;
+ } else {
+ Helper.forwardToAnotherApp(PeertubeActivity.this, intent);
+ finish();
+ }
+ } else {
+ Helper.forwardToAnotherApp(PeertubeActivity.this, intent);
+ finish();
+ }
+ }
+ return false;
+ }
+
+ private void playVideo() {
+ if (player != null) {
+ player.release();
+ player = new SimpleExoPlayer.Builder(PeertubeActivity.this).build();
+ binding.mediaVideo.player(player);
+ binding.doubleTapPlayerView.setPlayer(player);
+ binding.loader.setVisibility(View.GONE);
+ player.setPlayWhenReady(autoPlay);
+ if (autoPlay) {
+ binding.doubleTapPlayerView.hideController();
+ }
+ captions = null;
+ }
+ currentResolution = null;
+ show_more_content = null;
+ currentCaption = "null";
+ binding.peertubeDescriptionMore.setVisibility(View.GONE);
+
+ if (autoFullscreen && autoPlay) {
+ openFullscreenDialog();
+ }
+
+ TimelineVM feedsViewModel = new ViewModelProvider(PeertubeActivity.this).get(TimelineVM.class);
+ if (!isRemote) {
+ feedsViewModel.getVideo(sepiaSearch ? peertubeInstance : null, videoUuid, isMyVideo).observe(PeertubeActivity.this, this::manageVIewVideo);
+ }
+ CaptionsVM captionsViewModel = new ViewModelProvider(PeertubeActivity.this).get(CaptionsVM.class);
+ captionsViewModel.getCaptions(sepiaSearch ? peertubeInstance : null, videoUuid).observe(PeertubeActivity.this, this::manageCaptions);
+
+ //Post view count
+ new Thread(() -> {
+ String videoId = peertube != null ? peertube.getUuid() : videoUuid;
+ new RetrofitPeertubeAPI(PeertubeActivity.this).postView(videoId);
+ }).start();
+ //manage plugin
+ new Thread(() -> {
+ String videoInstance = peertubeInstance != null ? peertubeInstance : peertube.getAccount().getHost();
+ InstanceData.InstanceConfig instanceConfig = new RetrofitPeertubeAPI(PeertubeActivity.this, videoInstance, null).getConfigInstance();
+ if (instanceConfig != null && instanceConfig.getPlugin() != null && instanceConfig.getPlugin().getRegistered() != null) {
+ for (PluginData.PluginInfo pluginInfo : instanceConfig.getPlugin().getRegistered()) {
+ if (pluginInfo.getName().compareTo("player-watermark") == 0) {
+ PluginData.WaterMark getWaterMark = new RetrofitPeertubeAPI(PeertubeActivity.this, videoInstance, null).getWaterMark();
+ if (getWaterMark != null && getWaterMark.getDescription() != null && getWaterMark.getDescription().getWatermarkImageUrl() != null) {
+ Handler mainHandler = new Handler(Looper.getMainLooper());
+ Runnable myRunnable = () -> {
+ Glide.with(binding.watermark.getContext())
+ .asDrawable()
+ .load(getWaterMark.getDescription().getWatermarkImageUrl())
+ .into(binding.watermark);
+ binding.watermark.setVisibility(View.VISIBLE);
+ };
+ mainHandler.post(myRunnable);
+ }
+ }
+ }
+ }
+ }).start();
+ new Thread(() -> {
+ try {
+ RetrofitPeertubeAPI api;
+ if (peertubeInstance != null) {
+ api = new RetrofitPeertubeAPI(PeertubeActivity.this, peertubeInstance, null);
+ } else {
+ api = new RetrofitPeertubeAPI(PeertubeActivity.this);
+ }
+ VideoData.Description description = api.getVideoDescription(videoUuid);
+ Handler mainHandler = new Handler(Looper.getMainLooper());
+ Runnable myRunnable = () -> {
+ if (!isFinishing()) {
+ if (description == null) {
+ binding.peertubeDescriptionMore.setVisibility(View.GONE);
+ show_more_content = null;
+ } else {
+ if (!PeertubeActivity.this.isFinishing()) {
+ if (peertube != null && ((peertube.getDescription() == null && description.getDescription() != null && description.getDescription().trim().length() > 0) || (peertube.getDescription() != null && description.getDescription() != null
+ && description.getDescription().compareTo(peertube.getDescription()) > 0))) {
+ binding.peertubeDescriptionMore.setVisibility(View.VISIBLE);
+ show_more_content = description.getDescription();
+ } else {
+ binding.peertubeDescriptionMore.setVisibility(View.GONE);
+ show_more_content = null;
+ }
+ }
+ }
+ }
+ };
+ mainHandler.post(myRunnable);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }).start();
+ }
+
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ if (playInMinimized && player != null) {
+ finishAndRemoveTask();
+ }
+ finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void reportAlert(RetrofitPeertubeAPI.ActionType type, AlertDialog alertDialog) {
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(PeertubeActivity.this);
+ LayoutInflater inflater1 = getLayoutInflater();
+ View dialogView = inflater1.inflate(R.layout.popup_report, new LinearLayout(PeertubeActivity.this), false);
+ dialogBuilder.setView(dialogView);
+ dialogBuilder.setNeutralButton(R.string.cancel, (dialog, id) -> dialog.dismiss());
+ EditText report_content = dialogView.findViewById(R.id.report_content);
+ dialogBuilder.setPositiveButton(R.string.report, (dialog, id) -> {
+ if (report_content.getText().toString().trim().length() == 0) {
+ Toasty.info(PeertubeActivity.this, getString(R.string.report_comment_size), Toasty.LENGTH_LONG).show();
+ } else {
+ PostActionsVM viewModel = new ViewModelProvider(PeertubeActivity.this).get(PostActionsVM.class);
+ if (type == RetrofitPeertubeAPI.ActionType.REPORT_VIDEO) {
+ Report report = new Report();
+ Report.VideoReport videoReport = new Report.VideoReport();
+ videoReport.setId(peertube.getId());
+ report.setVideo(videoReport);
+ report.setReason(report_content.getText().toString());
+ viewModel.report(report).observe(PeertubeActivity.this, apiResponse -> manageVIewPostActions(RetrofitPeertubeAPI.ActionType.REPORT_VIDEO, 0, apiResponse));
+ alertDialog.dismiss();
+ dialog.dismiss();
+ } else if (type == RetrofitPeertubeAPI.ActionType.REPORT_ACCOUNT) {
+ Report report = new Report();
+ Report.AccountReport accountReport = new Report.AccountReport();
+ accountReport.setId(peertube.getAccount().getId());
+ report.setAccount(accountReport);
+ report.setReason(report_content.getText().toString());
+ viewModel.report(report).observe(PeertubeActivity.this, apiResponse -> manageVIewPostActions(RetrofitPeertubeAPI.ActionType.REPORT_ACCOUNT, 0, apiResponse));
+ alertDialog.dismiss();
+ dialog.dismiss();
+ }
+ }
+ });
+ AlertDialog alertDialog2 = dialogBuilder.create();
+ alertDialog2.show();
+ }
+
+
+ public void manageCaptions(APIResponse apiResponse) {
+ if (apiResponse == null || (apiResponse.getError() != null) || apiResponse.getCaptions() == null || apiResponse.getCaptions().size() == 0) {
+ return;
+ }
+ captions = apiResponse.getCaptions();
+ }
+
+ public void manageNextVideos(APIResponse apiResponse) {
+ if (apiResponse == null || apiResponse.getError() != null || apiResponse.getPeertubes() == null || apiResponse.getPeertubes().size() == 0) {
+ return;
+ }
+ List suggestedVideos = apiResponse.getPeertubes();
+ for (VideoData.Video video : suggestedVideos) {
+ if (!playedVideos.contains(video.getId())) {
+ TimelineVM feedsViewModel = new ViewModelProvider(PeertubeActivity.this).get(TimelineVM.class);
+ feedsViewModel.getVideo(null, suggestedVideos.get(0).getUuid(), false).observe(PeertubeActivity.this, this::nextVideoDetails);
+ return;
+ }
+ }
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ public void nextVideoDetails(APIResponse apiResponse) {
+ if (apiResponse == null || (apiResponse.getError() != null) || apiResponse.getPeertubes() == null || apiResponse.getPeertubes().size() == 0) {
+ return;
+ }
+ int i = 0;
+ while (i < (apiResponse.getPeertubes().size() - 1) && playedVideos.contains(apiResponse.getPeertubes().get(i).getId())) {
+ i++;
+ }
+ nextVideo = apiResponse.getPeertubes().get(i);
+ if (!playedVideos.contains(nextVideo.getId()) && player != null && nextVideo.getFileUrl(null, PeertubeActivity.this) != null) {
+ MediaItem mediaItem = new MediaItem.Builder().setUri(Uri.parse(nextVideo.getFileUrl(null, PeertubeActivity.this))).build();
+ player.addMediaItem(mediaItem);
+ }
+ }
+
+
+ public void manageVIewVideo(APIResponse apiResponse) {
+ if (!isRemote && apiResponse != null && apiResponse.getPeertubes() != null && apiResponse.getPeertubes().get(0).getErrorCode() == 1 && apiResponse.getPeertubes().get(0).getOriginUrl() != null) {
+ String url = apiResponse.getPeertubes().get(0).getOriginUrl();
+ Pattern link = Pattern.compile("(https?://[\\da-z.-]+\\.[a-z.]{2,10})/videos/watch/(\\w{8}-\\w{4}-\\w{4}-\\w{4}-\\w{12})(\\?start=(\\d+[hH])?(\\d+[mM])?(\\d+[sS])?)?$");
+ Matcher matcherLink = link.matcher(url);
+ if (matcherLink.find()) {
+ String instance = matcherLink.group(1);
+ String uuid = matcherLink.group(2);
+ String hour = matcherLink.group(4);
+ String min = matcherLink.group(5);
+ String sec = matcherLink.group(6);
+ int hourInt, minInt, secInt;
+ int totalSeconds = 0;
+ if (hour != null) {
+ hourInt = Integer.parseInt(hour.replace("h", ""));
+ totalSeconds += 3600 * hourInt;
+ }
+ if (min != null) {
+ minInt = Integer.parseInt(min.replace("m", ""));
+ totalSeconds += 60 * minInt;
+ }
+ if (sec != null) {
+ secInt = Integer.parseInt(sec.replace("strue", ""));
+ totalSeconds += secInt;
+ }
+ captionURI = null;
+ captionLang = null;
+ if (instance != null && uuid != null) {
+ peertubeInstance = instance.replace("https://", "").replace("http://", "");
+ sepiaSearch = true; // Sepia search flag is used because, at this time we don't know if the video is federated.
+ videoUuid = uuid;
+ peertube = new VideoData.Video();
+ peertube.setUuid(uuid);
+ peertube.setEmbedUrl(url);
+ isRemote = true;
+ if (totalSeconds > 0) {
+ VideoData.UserHistory userHistory = new VideoData.UserHistory();
+ userHistory.setCurrentTime(totalSeconds);
+ peertube.setUserHistory(userHistory);
+ }
+ TimelineVM viewModelTimeline = new ViewModelProvider(PeertubeActivity.this).get(TimelineVM.class);
+ viewModelTimeline.getVideo(peertubeInstance, peertube.getUuid(), false).observe(PeertubeActivity.this, this::manageVIewVideo);
+ }
+ }
+ return;
+ }
+
+ if (apiResponse != null && apiResponse.getPeertubes() != null && apiResponse.getPeertubes().size() > 0 && apiResponse.getPeertubes().get(0).getErrorMessage() != null) {
+ Toasty.error(PeertubeActivity.this, apiResponse.getPeertubes().get(0).getErrorMessage(), Toast.LENGTH_LONG).show();
+ binding.loader.setVisibility(View.GONE);
+ return;
+ }
+ if (apiResponse == null || (apiResponse.getError() != null) || apiResponse.getPeertubes() == null || apiResponse.getPeertubes().size() == 0) {
+ Toasty.error(PeertubeActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
+ binding.loader.setVisibility(View.GONE);
+ return;
+ }
+ if (apiResponse.getPeertubes() == null || apiResponse.getPeertubes().get(0) == null || (!apiResponse.getPeertubes().get(0).isLive() && apiResponse.getPeertubes().get(0).getFileUrl(null, PeertubeActivity.this) == null)) {
+ Toasty.error(PeertubeActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
+ binding.loader.setVisibility(View.GONE);
+ return;
+ }
+ long position = -1;
+
+ long previousPositionHistory = 0;
+ if (peertube != null && peertube.getUserHistory() != null) {
+ previousPositionHistory = peertube.getUserHistory().getCurrentTime();
+ }
+ peertube = apiResponse.getPeertubes().get(0);
+ VideoData.UserHistory userHistory = new VideoData.UserHistory();
+ userHistory.setCurrentTime(previousPositionHistory);
+ peertube.setUserHistory(userHistory);
+
+
+ PlayerControlView controlView = binding.doubleTapPlayerView.findViewById(R.id.exo_controller);
+ DefaultTimeBar exo_progress = controlView.findViewById(R.id.exo_progress);
+ TextView exo_duration = controlView.findViewById(R.id.exo_duration);
+ TextView exo_position = controlView.findViewById(R.id.exo_position);
+ TextView exo_live_badge = controlView.findViewById(R.id.exo_live_badge);
+ if (peertube.isLive()) {
+ exo_progress.setVisibility(View.INVISIBLE);
+ exo_duration.setVisibility(View.GONE);
+ exo_live_badge.setVisibility(View.VISIBLE);
+ exo_live_badge.setText(R.string.live);
+ exo_live_badge.setBackgroundResource(R.drawable.rounded_live);
+ exo_position.setVisibility(View.GONE);
+ } else {
+ exo_progress.setVisibility(View.VISIBLE);
+ exo_live_badge.setVisibility(View.GONE);
+ exo_position.setVisibility(View.VISIBLE);
+ exo_duration.setBackground(null);
+ }
+
+ if (peertube.getUserHistory() != null) {
+ position = peertube.getUserHistory().getCurrentTime() * 1000;
+ }
+ if (peertube.getTags() != null && peertube.getTags().size() > 0) {
+ SearchVM searchViewModel = new ViewModelProvider(PeertubeActivity.this).get(SearchVM.class);
+ searchViewModel.searchNextVideos(peertube.getTags()).observe(PeertubeActivity.this, this::manageNextVideos);
+ }
+ if (sepiaSearch) {
+ peertubeInstance = peertube.getAccount().getHost();
+ }
+ List videoIds = new ArrayList<>();
+ videoIds.add(peertube.getId());
+ PlaylistsVM viewModel = new ViewModelProvider(this).get(PlaylistsVM.class);
+ viewModel.videoExists(videoIds).observe(this, this::manageVIewPlaylist);
+
+ if (!Helper.canMakeAction(PeertubeActivity.this) || sepiaSearch) {
+ binding.writeCommentContainer.setVisibility(View.GONE);
+ }
+
+
+ if (peertube.isNsfw()) {
+ binding.videoSensitive.setVisibility(View.VISIBLE);
+ } else {
+ binding.videoSensitive.setVisibility(View.INVISIBLE);
+ }
+
+ binding.peertubePlaylist.setOnClickListener(v -> {
+ PlaylistsVM viewModelOwnerPlaylist = new ViewModelProvider(PeertubeActivity.this).get(PlaylistsVM.class);
+ viewModelOwnerPlaylist.manage(PlaylistsVM.action.GET_PLAYLISTS, null, null).observe(PeertubeActivity.this, this::manageVIewPlaylists);
+ });
+
+ binding.videoInformation.setOnClickListener(v -> {
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(PeertubeActivity.this);
+ LayoutInflater inflater = getLayoutInflater();
+ View dialogView = inflater.inflate(R.layout.popup_video_info, new LinearLayout(PeertubeActivity.this), false);
+ TextView info_privacy = dialogView.findViewById(R.id.info_privacy);
+ TextView info_published_at = dialogView.findViewById(R.id.info_published_at);
+ TextView info_category = dialogView.findViewById(R.id.info_category);
+ TextView info_license = dialogView.findViewById(R.id.info_license);
+ TextView info_language = dialogView.findViewById(R.id.info_language);
+ TextView info_duration = dialogView.findViewById(R.id.info_duration);
+ TextView info_tags = dialogView.findViewById(R.id.info_tags);
+
+ LinkedHashMap privaciesInit = new LinkedHashMap<>(peertubeInformation.getPrivacies());
+ info_privacy.setText(privaciesInit.get(peertube.getPrivacy().getId()));
+ LinkedHashMap licenseInit = new LinkedHashMap<>(peertubeInformation.getLicences());
+ info_license.setText(licenseInit.get(peertube.getLicence().getId()));
+ LinkedHashMap languageStr = new LinkedHashMap<>(peertubeInformation.getLanguages());
+ info_language.setText(languageStr.get(peertube.getLanguage().getId()));
+ LinkedHashMap categoryInit = new LinkedHashMap<>(peertubeInformation.getCategories());
+ info_category.setText(categoryInit.get(peertube.getCategory().getId()));
+
+ if (peertube.isLive()) {
+ info_duration.setText(R.string.live);
+ info_duration.setBackgroundResource(R.drawable.rounded_live);
+ info_duration.setBackgroundResource(R.drawable.rounded_live);
+ } else {
+ info_duration.setText(Helper.secondsToString(peertube.getDuration()));
+ }
+
+ String format = DateFormat.getDateInstance(DateFormat.LONG).format(peertube.getPublishedAt());
+ info_published_at.setText(format);
+ List tags = peertube.getTags();
+ StringBuilder sb = new StringBuilder();
+ for (String tag : tags) {
+ sb.append("#").append(tag).append(" ");
+ }
+
+ SpannableString spannableString = new SpannableString(sb.toString());
+ for (String tag : tags) {
+ String target = "#" + tag;
+ if (spannableString.toString().contains(target)) {
+ for (int startPosition = -1; (startPosition = spannableString.toString().indexOf(target, startPosition + 1)) != -1; startPosition++) {
+ final int endPosition = startPosition + target.length();
+ if (endPosition <= spannableString.toString().length() && endPosition >= startPosition) {
+ spannableString.setSpan(new ClickableSpan() {
+ @Override
+ public void onClick(@NonNull View textView) {
+ Intent intent = new Intent(PeertubeActivity.this, SearchActivity.class);
+ Bundle b = new Bundle();
+ String search = tag.trim();
+ b.putString("search", search);
+ intent.putExtras(b);
+ startActivity(intent);
+ }
+
+ @Override
+ public void updateDrawState(@NonNull TextPaint ds) {
+ super.updateDrawState(ds);
+ ds.setUnderlineText(false);
+ ds.setColor(getResources().getColor(R.color.colorAccent));
+ }
+ },
+ startPosition, endPosition,
+ Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ }
+ }
+ }
+ }
+ info_tags.setText(spannableString, TextView.BufferType.SPANNABLE);
+ info_tags.setMovementMethod(LinkMovementMethod.getInstance());
+ dialogBuilder.setView(dialogView);
+ dialogBuilder.setNeutralButton(R.string.close, (dialog, id) -> dialog.dismiss());
+ AlertDialog alertDialog = dialogBuilder.create();
+ alertDialog.show();
+ });
+
+ fetchComments();
+ SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
+ setTitle(peertube.getName());
+
+ binding.peertubeDescription.setText(peertube.getDescription());
+
+
+ binding.peertubeTitle.setText(peertube.getName());
+ binding.peertubeDislikeCount.setText(Helper.withSuffix(peertube.getDislikes()));
+ binding.peertubeLikeCount.setText(Helper.withSuffix(peertube.getLikes()));
+ binding.peertubeViewCount.setText(Helper.withSuffix(peertube.getViews()));
+ loadAvatar(PeertubeActivity.this, peertube.getChannel(), binding.ppChannel);
+ binding.ppChannel.setOnClickListener(v -> {
+ Intent intent = new Intent(PeertubeActivity.this, ShowChannelActivity.class);
+ Bundle b = new Bundle();
+ b.putParcelable("channel", peertube.getChannel());
+ b.putBoolean("sepia_search", sepiaSearch);
+ if (sepiaSearch) {
+ b.putString("peertube_instance", peertube.getAccount().getHost());
+ }
+ intent.putExtras(b);
+ startActivity(intent);
+ });
+
+ video_id = peertube.getId();
+
+ changeColor();
+ initResolution();
+
+ binding.peertubeReblog.setOnClickListener(v -> {
+ if (status != null) {
+ MastodonPostActionsVM mastodonPostActionsVM = new ViewModelProvider(PeertubeActivity.this).get(MastodonPostActionsVM.class);
+ RetrofitMastodonAPI.actionType type = status.isReblogged() ? RetrofitMastodonAPI.actionType.UNBOOST : RetrofitMastodonAPI.actionType.BOOST;
+ mastodonPostActionsVM.post(type, status).observe(PeertubeActivity.this, this::manageVIewPostActionsMastodon);
+ } else {
+ Toasty.error(PeertubeActivity.this, getString(R.string.federation_issue), Toasty.LENGTH_LONG).show();
+ }
+ });
+
+ binding.peertubeFavorite.setOnClickListener(v -> {
+ if (status != null) {
+ MastodonPostActionsVM mastodonPostActionsVM = new ViewModelProvider(PeertubeActivity.this).get(MastodonPostActionsVM.class);
+ RetrofitMastodonAPI.actionType type = status.isFavourited() ? RetrofitMastodonAPI.actionType.UNFAVOURITE : RetrofitMastodonAPI.actionType.FAVOURITE;
+ mastodonPostActionsVM.post(type, status).observe(PeertubeActivity.this, this::manageVIewPostActionsMastodon);
+ } else {
+ Toasty.error(PeertubeActivity.this, getString(R.string.federation_issue), Toasty.LENGTH_LONG).show();
+ }
+ });
+
+ binding.peertubeBookmark.setOnClickListener(v -> {
+ if (status != null) {
+ MastodonPostActionsVM mastodonPostActionsVM = new ViewModelProvider(PeertubeActivity.this).get(MastodonPostActionsVM.class);
+ RetrofitMastodonAPI.actionType type = status.isBookmarked() ? RetrofitMastodonAPI.actionType.UNBOOKMARK : RetrofitMastodonAPI.actionType.BOOKMARK;
+ mastodonPostActionsVM.post(type, status).observe(PeertubeActivity.this, this::manageVIewPostActionsMastodon);
+ } else {
+ Toasty.error(PeertubeActivity.this, getString(R.string.federation_issue), Toasty.LENGTH_LONG).show();
+ }
+ });
+
+ binding.peertubeLikeCount.setOnClickListener(v -> {
+ if (isLoggedIn(PeertubeActivity.this) && !sepiaSearch) {
+ String newState = peertube.getMyRating().equals("like") ? "none" : "like";
+ PostActionsVM viewModelLike = new ViewModelProvider(PeertubeActivity.this).get(PostActionsVM.class);
+ viewModelLike.post(RATEVIDEO, peertube.getId(), newState).observe(PeertubeActivity.this, apiResponse1 -> manageVIewPostActions(RATEVIDEO, 0, apiResponse1));
+ peertube.setMyRating(newState);
+ int count = Integer.parseInt(binding.peertubeLikeCount.getText().toString());
+ if (newState.compareTo("none") == 0) {
+ count--;
+ if (count - 1 < 0) {
+ count = 0;
+ }
+ } else {
+ count++;
+ }
+ binding.peertubeLikeCount.setText(String.valueOf(count));
+ changeColor();
+ } else {
+ if (sepiaSearch) {
+ Toasty.info(PeertubeActivity.this, getString(R.string.federation_issue), Toasty.LENGTH_SHORT).show();
+ } else {
+ Toasty.error(PeertubeActivity.this, getString(R.string.not_logged_in), Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+ binding.peertubeDislikeCount.setOnClickListener(v -> {
+ if (isLoggedIn(PeertubeActivity.this) && !sepiaSearch) {
+ String newState = peertube.getMyRating().equals("dislike") ? "none" : "dislike";
+ PostActionsVM viewModelLike = new ViewModelProvider(PeertubeActivity.this).get(PostActionsVM.class);
+ viewModelLike.post(RATEVIDEO, peertube.getId(), newState).observe(PeertubeActivity.this, apiResponse1 -> manageVIewPostActions(RATEVIDEO, 0, apiResponse1));
+ peertube.setMyRating(newState);
+ int count = Integer.parseInt(binding.peertubeDislikeCount.getText().toString());
+ if (newState.compareTo("none") == 0) {
+ count--;
+ if (count - 1 < 0) {
+ count = 0;
+ }
+ } else {
+ count++;
+ }
+ binding.peertubeDislikeCount.setText(String.valueOf(count));
+ changeColor();
+ } else {
+ if (sepiaSearch) {
+ Toasty.info(PeertubeActivity.this, getString(R.string.federation_issue), Toasty.LENGTH_SHORT).show();
+ } else {
+ Toasty.error(PeertubeActivity.this, getString(R.string.not_logged_in), Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+
+ if (BuildConfig.allow_remote_connections && Helper.isLoggedInType(PeertubeActivity.this) == MainActivity.TypeOfConnection.REMOTE_ACCOUNT) {
+ String url = "https://" + peertube.getChannel().getHost() + "/videos/watch/" + peertube.getUuid();
+ MastodonPostActionsVM postActionsVM = new ViewModelProvider(PeertubeActivity.this).get(MastodonPostActionsVM.class);
+ postActionsVM.searchRemoteStatus(url).observe(PeertubeActivity.this, this::retrieveRemoteStatus);
+ }
+
+ if (mode != Helper.VIDEO_MODE_WEBVIEW) {
+
+ player = new SimpleExoPlayer.Builder(PeertubeActivity.this).build();
+ player.addVideoListener(PeertubeActivity.this);
+ player.addListener(this);
+ binding.mediaVideo.player(player);
+ binding.doubleTapPlayerView.setPlayer(player);
+ binding.loader.setVisibility(View.GONE);
+ startStream(
+ apiResponse.getPeertubes().get(0),
+ null,
+ autoPlay, position, null, null, true);
+ player.prepare();
+ player.setPlayWhenReady(autoPlay);
+ if (autoPlay) {
+ binding.doubleTapPlayerView.hideController();
+ }
+ }
+
+
+ binding.moreActions.setOnClickListener(view -> {
+ PopupMenu popup = new PopupMenu(PeertubeActivity.this, binding.moreActions);
+ popup.getMenuInflater()
+ .inflate(R.menu.main_video, popup.getMenu());
+
+ if (!isMyVideo) {
+ popup.getMenu().findItem(R.id.action_edit).setVisible(false);
+ }
+
+ MenuItem itemDownload = popup.getMenu().findItem(R.id.action_download);
+ itemDownload.setEnabled(peertube != null && peertube.isDownloadEnabled());
+ popup.setOnMenuItemClickListener(item -> {
+ int itemId = item.getItemId();
+ if (itemId == R.id.action_download) {
+ if (Build.VERSION.SDK_INT >= 23) {
+ if (ContextCompat.checkSelfPermission(PeertubeActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(PeertubeActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
+ ActivityCompat.requestPermissions(PeertubeActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, Helper.EXTERNAL_STORAGE_REQUEST_CODE);
+ } else {
+ Helper.manageDownloads(PeertubeActivity.this, peertube.getFileDownloadUrl(null));
+ }
+ } else {
+ Helper.manageDownloads(PeertubeActivity.this, peertube.getFileDownloadUrl(null));
+ }
+ } else if (itemId == R.id.action_share) {
+ Intent sendIntent = new Intent(Intent.ACTION_SEND);
+ sendIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.shared_via));
+ boolean share_details = sharedpreferences.getBoolean(Helper.SET_SHARE_DETAILS, true);
+ String extra_text;
+ if (share_details) {
+ extra_text = "@" + peertube.getAccount().getAcct();
+ extra_text += "\r\n\r\n" + peertube.getName();
+ extra_text += "\n\n\uD83D\uDD17 https://" + peertube.getChannel().getHost() + "/videos/watch/" + peertube.getUuid() + "\r\n-\n";
+ final String contentToot;
+ if (peertube.getDescription() != null) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
+ contentToot = Html.fromHtml(peertube.getDescription(), Html.FROM_HTML_MODE_LEGACY).toString();
+ else
+ contentToot = Html.fromHtml(peertube.getDescription()).toString();
+ } else {
+ contentToot = "";
+ }
+ extra_text += contentToot;
+ } else {
+ extra_text = "https://" + peertube.getChannel().getHost() + "/videos/watch/" + peertube.getUuid();
+ }
+ sendIntent.putExtra(Intent.EXTRA_TEXT, extra_text);
+ sendIntent.setType("text/plain");
+ try {
+ startActivity(Intent.createChooser(sendIntent, getString(R.string.share_with)));
+ } catch (Exception e) {
+ Toasty.error(PeertubeActivity.this, getString(R.string.toast_error), Toasty.LENGTH_LONG).show();
+ }
+ } else if (itemId == R.id.action_edit) {
+ Intent intent = new Intent(PeertubeActivity.this, PeertubeEditUploadActivity.class);
+ Bundle b = new Bundle();
+ b.putString("video_id", peertube.getUuid());
+ intent.putExtras(b);
+ startActivity(intent);
+ } else if (itemId == R.id.action_report) {
+ AlertDialog alertDialog;
+ AlertDialog.Builder dialogBuilder;
+ dialogBuilder = new AlertDialog.Builder(PeertubeActivity.this);
+ LayoutInflater inflater1 = getLayoutInflater();
+ View dialogView = inflater1.inflate(R.layout.popup_report_choice, new LinearLayout(PeertubeActivity.this), false);
+ dialogBuilder.setView(dialogView);
+
+ dialogBuilder.setNeutralButton(R.string.cancel, (dialog, id) -> dialog.dismiss());
+ alertDialog = dialogBuilder.create();
+ alertDialog.show();
+ dialogView.findViewById(R.id.report_video).setOnClickListener(v -> reportAlert(REPORT_VIDEO, alertDialog));
+ dialogView.findViewById(R.id.report_account).setOnClickListener(v -> reportAlert(REPORT_ACCOUNT, alertDialog));
+ }
+ return true;
+ });
+ popup.show();
+ });
+ }
+
+
+ /**
+ * Manage video to play with different factors
+ *
+ * @param video VideoData.Video
+ * @param resolution String the current resolution asked
+ * @param autoPlay boolean
+ * @param position int current position
+ * @param subtitles Uri uri for subtitles
+ * @param lang String ("en","fr", etc.)
+ */
+ private void stream(VideoData.Video video, String resolution, boolean autoPlay, long position, Uri subtitles, String lang) {
+ videoURL = video.getFileUrl(resolution, PeertubeActivity.this);
+ if (subtitles != null) {
+ subtitlesStr = subtitles.toString();
+ }
+ SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
+ int video_cache = sharedpreferences.getInt(Helper.SET_VIDEO_CACHE, Helper.DEFAULT_VIDEO_CACHE_MB);
+ ProgressiveMediaSource videoSource = null;
+ HlsMediaSource hlsMediaSource = null;
+ SingleSampleMediaSource subtitleSource = null;
+ DataSource.Factory dataSourceFactory;
+ if (video_cache == 0 || video.isLive()) {
+ dataSourceFactory = new DefaultDataSourceFactory(PeertubeActivity.this,
+ Util.getUserAgent(PeertubeActivity.this, null), null);
+
+ if (subtitles != null) {
+ MediaItem.Subtitle mediaSubtitle = new MediaItem.Subtitle(subtitles, MimeTypes.TEXT_VTT, lang);
+ subtitleSource = new SingleSampleMediaSource.Factory(dataSourceFactory).createMediaSource(mediaSubtitle, C.TIME_UNSET);
+ }
+ MediaItem mediaItem = new MediaItem.Builder().setUri(Uri.parse(videoURL)).build();
+ if (videoURL != null && !videoURL.endsWith("m3u8")) {
+ videoSource = new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mediaItem);
+ } else {
+ hlsMediaSource = new HlsMediaSource.Factory(dataSourceFactory)
+ .createMediaSource(mediaItem);
+ }
+ } else {
+ CacheDataSourceFactory cacheDataSourceFactory = new CacheDataSourceFactory(PeertubeActivity.this);
+ MediaItem mediaItem = new MediaItem.Builder().setUri(videoURL).build();
+ if (subtitles != null) {
+ MediaItem.Subtitle mediaSubtitle = new MediaItem.Subtitle(subtitles, MimeTypes.TEXT_VTT, lang, Format.NO_VALUE);
+ subtitleSource = new SingleSampleMediaSource.Factory(cacheDataSourceFactory).createMediaSource(mediaSubtitle, C.TIME_UNSET);
+ }
+ if (videoURL != null && !videoURL.endsWith("m3u8")) {
+ videoSource = new ProgressiveMediaSource.Factory(cacheDataSourceFactory).createMediaSource(mediaItem);
+ } else {
+ hlsMediaSource = new HlsMediaSource.Factory(cacheDataSourceFactory)
+ .createMediaSource(mediaItem);
+ }
+ }
+ if (lang != null && subtitleSource != null && videoSource != null) {
+ MergingMediaSource mergedSource =
+ new MergingMediaSource(videoSource, subtitleSource);
+ player.setMediaSource(mergedSource);
+ } else if (lang != null && subtitleSource != null) {
+ MergingMediaSource mergedSource =
+ new MergingMediaSource(hlsMediaSource, subtitleSource);
+ player.setMediaSource(mergedSource);
+ } else if (videoSource != null) {
+ player.setMediaSource(videoSource);
+ } else {
+ player.setMediaSource(hlsMediaSource);
+ }
+ player.prepare();
+ if (position > 0) {
+ player.seekTo(0, position);
+ }
+ player.setPlayWhenReady(autoPlay);
+ if (autoPlay) {
+ binding.doubleTapPlayerView.hideController();
+ }
+ // loadCast(video, videoURL, subtitles!=null?subtitles.toString():null);
+ }
+
+ private void fetchComments() {
+ if (peertube.isCommentsEnabled()) {
+ if (Helper.canMakeAction(PeertubeActivity.this)) {
+ binding.postCommentButton.setVisibility(View.VISIBLE);
+ } else {
+ binding.postCommentButton.setVisibility(View.GONE);
+ }
+ CommentVM commentViewModel = new ViewModelProvider(PeertubeActivity.this).get(CommentVM.class);
+ commentViewModel.getThread(sepiaSearch ? peertubeInstance : null, videoUuid, max_id).observe(PeertubeActivity.this, this::manageVIewComment);
+ if (Helper.canMakeAction(PeertubeActivity.this) && !sepiaSearch) {
+ binding.writeCommentContainer.setVisibility(View.VISIBLE);
+ }
+ binding.peertubeComments.setVisibility(View.VISIBLE);
+ binding.noAction.setVisibility(View.GONE);
+ } else {
+ binding.postCommentButton.setVisibility(View.GONE);
+ binding.peertubeComments.setVisibility(View.GONE);
+ binding.writeCommentContainer.setVisibility(View.GONE);
+ binding.noActionText.setText(getString(R.string.comment_no_allowed_peertube));
+ binding.noAction.setVisibility(View.VISIBLE);
+ binding.writeCommentContainer.setVisibility(View.GONE);
+ }
+ }
+
+ private void startStream(VideoData.Video video, String resolution, boolean autoPlay, long position, Uri subtitles, String lang, boolean promptNSFW) {
+
+ String videoURL = peertube.getFileUrl(resolution, PeertubeActivity.this);
+ if (peertube != null && peertube.isLive() && videoURL == null) {
+ View parentLayout = findViewById(android.R.id.content);
+ Snackbar snackbar = Snackbar.make(parentLayout, R.string.live_not_started, Snackbar.LENGTH_INDEFINITE);
+ snackbar.setAction(R.string.close, view -> finish());
+ snackbar.show();
+ return;
+ }
+
+ SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
+ String nsfwAction = sharedpreferences.getString(getString(R.string.set_video_sensitive_choice), Helper.BLUR);
+ if (promptNSFW && peertube != null && peertube.isNsfw() && (nsfwAction.compareTo(Helper.BLUR) == 0 || nsfwAction.compareTo(Helper.DO_NOT_LIST) == 0)) {
+ AlertDialog alertDialog;
+ AlertDialog.Builder dialogBuilder;
+ dialogBuilder = new AlertDialog.Builder(PeertubeActivity.this);
+ dialogBuilder.setTitle(R.string.nsfw_title_warning);
+ dialogBuilder.setCancelable(false);
+ dialogBuilder.setMessage(R.string.nsfw_message_warning);
+ dialogBuilder.setNegativeButton(R.string.no, (dialog, id) -> {
+ dialog.dismiss();
+ finish();
+ });
+ dialogBuilder.setPositiveButton(R.string.play, (dialog, id) -> {
+ stream(video, resolution, autoPlay, position, subtitles, lang);
+ dialog.dismiss();
+ });
+ alertDialog = dialogBuilder.create();
+ alertDialog.show();
+ } else {
+ stream(video, resolution, autoPlay, position, subtitles, lang);
+ }
+
+
+ }
+
+ @Override
+ public void onConfigurationChanged(@NotNull Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ if (binding.minController.castMiniController.getVisibility() == View.VISIBLE) {
+ return;
+ }
+ if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ if (mode != Helper.VIDEO_MODE_WEBVIEW) {
+ openFullscreenDialog();
+ }
+ if (initialOrientation == Configuration.ORIENTATION_LANDSCAPE) {
+ LinearLayout.LayoutParams param = new LinearLayout.LayoutParams(
+ ConstraintLayout.LayoutParams.MATCH_PARENT,
+ 0,
+ 4.0f
+ );
+ binding.videoContainer.setLayoutParams(param);
+ }
+ } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
+ if (mode != Helper.VIDEO_MODE_WEBVIEW) {
+ closeFullscreenDialog();
+ }
+ if (initialOrientation == Configuration.ORIENTATION_LANDSCAPE) {
+ LinearLayout.LayoutParams param = new LinearLayout.LayoutParams(
+ ConstraintLayout.LayoutParams.MATCH_PARENT,
+ 0,
+ 1.0f
+ );
+ binding.videoContainer.setLayoutParams(param);
+ }
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ binding = null;
+ if (player != null) {
+ player.release();
+ }
+ unregisterReceiver();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ onStopCalled = true;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ onStopCalled = false;
+ if (player != null && !player.isPlaying() && binding.minController.castMiniController.getVisibility() != View.VISIBLE) {
+ player.setPlayWhenReady(autoPlay);
+ if (autoPlay) {
+ binding.doubleTapPlayerView.hideController();
+ }
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (player != null) {
+ updateHistory(player.getCurrentPosition() / 1000);
+ }
+ if (player != null && (!isPlayInMinimized || !playInMinimized)) {
+ player.setPlayWhenReady(false);
+ } else if (playInMinimized && binding.minController.castMiniController.getVisibility() != View.VISIBLE) {
+ enterVideoMode();
+ }
+ }
+
+ private void registBroadcastReceiver() {
+ final IntentFilter theFilter = new IntentFilter();
+ theFilter.addAction(Intent.ACTION_SCREEN_ON);
+ theFilter.addAction(Intent.ACTION_SCREEN_OFF);
+ final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
+ mPowerKeyReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String strAction = intent.getAction();
+ if (strAction.equals(Intent.ACTION_SCREEN_OFF)) {
+ if (player != null) {
+ if (!sharedpreferences.getBoolean(getString(R.string.set_play_screen_lock_choice), false)) {
+ player.setPlayWhenReady(false);
+ } else {
+ player.setWakeMode(C.WAKE_MODE_NETWORK);
+ }
+ }
+ }
+ }
+ };
+ getApplicationContext().registerReceiver(mPowerKeyReceiver, theFilter);
+ }
+
+ private void unregisterReceiver() {
+ if (mPowerKeyReceiver != null) {
+ getApplicationContext().unregisterReceiver(mPowerKeyReceiver);
+ mPowerKeyReceiver = null;
+ }
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ @Override
+ public void onUserLeaveHint() {
+ enterVideoMode();
+ }
+
+ private void enterVideoMode() {
+ if (playInMinimized && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && player != null) {
+ isPlayInMinimized = true;
+ setRequestedOrientationCustom(initialOrientation);
+ MediaSessionCompat mediaSession = new MediaSessionCompat(this, getPackageName());
+ MediaSessionConnector mediaSessionConnector = new MediaSessionConnector(mediaSession);
+ mediaSessionConnector.setPlayer(player);
+ PlayerControlView controlView = binding.doubleTapPlayerView.findViewById(R.id.exo_controller);
+ controlView.hide();
+ binding.doubleTapPlayerView.setControllerAutoShow(false);
+ mediaSession.setActive(true);
+ PictureInPictureParams params = new PictureInPictureParams.Builder().build();
+ enterPictureInPictureMode(params);
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+
+ if (binding.videoParamsSubmenu.getVisibility() == View.VISIBLE) {
+ closeSubMenuMenuOptions();
+ return;
+ }
+ if (binding.videoParams.getVisibility() == View.VISIBLE) {
+ closeMainMenuOptions();
+ return;
+ }
+ if (binding.postComment.getVisibility() == View.VISIBLE) {
+ closePostComment();
+ return;
+ }
+ if (binding.replyThread.getVisibility() == View.VISIBLE) {
+ closeCommentThread();
+ return;
+ }
+
+ if (fullScreenMode && player != null && player.isPlaying()) {
+ player.setPlayWhenReady(false);
+ return;
+ }
+
+ if (playInMinimized && player != null) {
+ enterVideoMode();
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ @Override
+ public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) {
+ if (!isInPictureInPictureMode) {
+ if (onStopCalled) {
+ isPlayInMinimized = false;
+ finishAndRemoveTask();
+ }
+ }
+ }
+
+
+ private void toogleFullscreen(boolean fullscreen) {
+
+ if (fullscreen) {
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN |
+ WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ Objects.requireNonNull(getSupportActionBar()).hide();
+ binding.bottomVideo.setVisibility(View.GONE);
+ Objects.requireNonNull(getSupportActionBar()).hide();
+ if (videoOrientationType == videoOrientation.LANDSCAPE) {
+ if (getResources().getConfiguration().orientation != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
+ setRequestedOrientationCustom(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ }
+ }
+ } else {
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
+ Objects.requireNonNull(getSupportActionBar()).show();
+ binding.bottomVideo.setVisibility(View.VISIBLE);
+ Objects.requireNonNull(getSupportActionBar()).show();
+ }
+ }
+
+ private void openFullscreenDialog() {
+ fullScreenIcon.setImageDrawable(ContextCompat.getDrawable(PeertubeActivity.this, R.drawable.ic_baseline_fullscreen_exit_24));
+ fullScreenMode = true;
+ toogleFullscreen(true);
+ }
+
+ private void closeFullscreenDialog() {
+ fullScreenMode = false;
+ fullScreenIcon.setImageDrawable(ContextCompat.getDrawable(PeertubeActivity.this, R.drawable.ic_baseline_fullscreen_24));
+ toogleFullscreen(false);
+ }
+
+ public void openCommentThread(Comment comment) {
+
+ CommentVM commentViewModel = new ViewModelProvider(PeertubeActivity.this).get(CommentVM.class);
+ binding.peertubeReply.setVisibility(View.GONE);
+ commentViewModel.getRepliesComment(sepiaSearch ? peertubeInstance : null, videoUuid, comment.getId()).observe(PeertubeActivity.this, apiResponse -> manageVIewCommentReply(comment, apiResponse));
+
+ binding.replyThread.setVisibility(View.VISIBLE);
+ TranslateAnimation animate = new TranslateAnimation(
+ binding.peertubeInformationContainer.getWidth(),
+ 0,
+ 0,
+ 0);
+ animate.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ binding.peertubeInformationContainer.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+ });
+ animate.setDuration(500);
+ binding.replyThread.startAnimation(animate);
+ }
+
+
+ public void openMainMenuOptions() {
+ binding.videoParams.setVisibility(View.VISIBLE);
+ DisplayMetrics displayMetrics = new DisplayMetrics();
+ getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
+ int height = displayMetrics.heightPixels;
+ binding.doubleTapPlayerView.hideController();
+ List menuItemVideos = new ArrayList<>();
+ if (peertube.getAllFile(PeertubeActivity.this) != null && peertube.getAllFile(PeertubeActivity.this).size() > 0) {
+ MenuItemVideo resolutionItem = new MenuItemVideo();
+ resolutionItem.setIcon(R.drawable.ic_baseline_high_quality_24);
+ resolutionItem.setTitle(getString(R.string.pickup_resolution));
+ resolutionItem.setAction(MenuItemVideo.actionType.RESOLUTION);
+ menuItemVideos.add(resolutionItem);
+
+ }
+ MenuItemVideo speedItem = new MenuItemVideo();
+ speedItem.setIcon(R.drawable.ic_baseline_speed_24);
+ speedItem.setTitle(getString(R.string.playback_speed));
+ speedItem.setAction(MenuItemVideo.actionType.SPEED);
+ menuItemVideos.add(speedItem);
+
+ if (captions != null) {
+ MenuItemVideo captionItem = new MenuItemVideo();
+ captionItem.setIcon(R.drawable.ic_baseline_subtitles_24);
+ captionItem.setTitle(getString(R.string.captions));
+ captionItem.setAction(MenuItemVideo.actionType.CAPTION);
+ menuItemVideos.add(captionItem);
+ }
+
+ MenuItemVideo autoNextItem = new MenuItemVideo();
+ autoNextItem.setIcon(R.drawable.ic_baseline_play_arrow_24);
+ autoNextItem.setTitle(getString(R.string.set_autoplay_next_video_settings));
+ autoNextItem.setAction(MenuItemVideo.actionType.AUTONEXT);
+ menuItemVideos.add(autoNextItem);
+
+ MenuAdapter menuAdapter = new MenuAdapter(menuItemVideos);
+ binding.mainOptionsVideo.setAdapter(menuAdapter);
+ menuAdapter.itemClicked = this;
+ binding.mainOptionsVideo.setLayoutManager(new LinearLayoutManager(PeertubeActivity.this));
+
+ TranslateAnimation animate = new TranslateAnimation(
+ 0,
+ 0,
+ height,
+ 0);
+ animate.setDuration(500);
+ binding.videoParams.startAnimation(animate);
+ }
+
+ @Override
+ public void onItemClicked(MenuItemVideo.actionType action) {
+ binding.videoParamsSubmenu.setVisibility(View.VISIBLE);
+ List items = new ArrayList<>();
+ switch (action) {
+ case RESOLUTION:
+ binding.subMenuTitle.setText(R.string.pickup_resolution);
+ int position = 0;
+ for (File file : peertube.getFiles()) {
+ if (file.getResolutions() != null) {
+ if (file.getResolutions().getLabel().compareTo("0p") != 0) {
+ MenuItemView item = new MenuItemView();
+ item.setId(position);
+ item.setLabel(file.getResolutions().getLabel());
+ if (file.getResolutions().getLabel().compareTo(currentResolution) == 0) {
+ item.setSelected(true);
+ }
+ items.add(item);
+ position++;
+ }
+ }
+ }
+ break;
+ case SPEED:
+ binding.subMenuTitle.setText(R.string.playback_speed);
+ items = new ArrayList<>();
+ items.add(new MenuItemView(25, "0.25x", player.getPlaybackParameters().speed == 0.25));
+ items.add(new MenuItemView(50, "0.5x", player.getPlaybackParameters().speed == 0.5));
+ items.add(new MenuItemView(75, "0.75x", player.getPlaybackParameters().speed == 0.75));
+ items.add(new MenuItemView(100, getString(R.string.normal), player.getPlaybackParameters().speed == 1));
+ items.add(new MenuItemView(125, "1.25x", player.getPlaybackParameters().speed == 1.25));
+ items.add(new MenuItemView(150, "1.5x", player.getPlaybackParameters().speed == 1.5));
+ items.add(new MenuItemView(175, "1.75x", player.getPlaybackParameters().speed == 1.75));
+ items.add(new MenuItemView(200, "2x", player.getPlaybackParameters().speed == 2.0));
+ break;
+ case CAPTION:
+ binding.subMenuTitle.setText(R.string.pickup_captions);
+ items = new ArrayList<>();
+ items.add(new MenuItemView(-1, "null", getString(R.string.none), currentCaption.compareTo("null") == 0));
+ int i = 0;
+ for (Caption caption : captions) {
+ items.add(new MenuItemView(i, caption.getLanguage().getId(), caption.getLanguage().getLabel(), currentCaption.compareTo(caption.getLanguage().getId()) == 0));
+ }
+ break;
+ case AUTONEXT:
+ binding.subMenuTitle.setText(R.string.set_autoplay_next_video_settings);
+ SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
+ boolean autoplayNextVideo = sharedpreferences.getBoolean(getString(R.string.set_autoplay_next_video_choice), true);
+ items.add(new MenuItemView(0, getString(R.string.no), !autoplayNextVideo));
+ items.add(new MenuItemView(1, getString(R.string.yes), autoplayNextVideo));
+ break;
+ }
+ MenuItemAdapter menuItemAdapter = new MenuItemAdapter(action, items);
+ binding.subMenuRecycler.setAdapter(menuItemAdapter);
+ menuItemAdapter.itemAction = this;
+ binding.subMenuRecycler.setLayoutManager(new LinearLayoutManager(PeertubeActivity.this));
+
+ DisplayMetrics displayMetrics = new DisplayMetrics();
+ getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
+ int height = displayMetrics.heightPixels;
+ TranslateAnimation animate = new TranslateAnimation(
+ 0,
+ 0,
+ height,
+ 0);
+ animate.setDuration(500);
+ binding.videoParamsSubmenu.startAnimation(animate);
+ }
+
+ @Override
+ public void which(MenuItemVideo.actionType action, MenuItemView item) {
+ closeMainMenuOptions();
+ switch (action) {
+ case RESOLUTION:
+ String res = item.getLabel();
+ binding.loader.setVisibility(View.VISIBLE);
+ long position = player.getCurrentPosition();
+ PlayerControlView controlView = binding.doubleTapPlayerView.findViewById(R.id.exo_controller);
+ TextView resolution = controlView.findViewById(R.id.resolution);
+ currentResolution = res;
+ resolution.setText(String.format("%s", res));
+ if (mode == Helper.VIDEO_MODE_NORMAL) {
+ if (player != null)
+ player.release();
+ player = new SimpleExoPlayer.Builder(PeertubeActivity.this).build();
+ binding.mediaVideo.player(player);
+ binding.doubleTapPlayerView.setPlayer(player);
+ binding.loader.setVisibility(View.GONE);
+
+ startStream(
+ peertube,
+ res,
+ true, position, captionURI, captionLang, false);
+ }
+ break;
+ case SPEED:
+ int speed = item.getId();
+ float ratio = (float) speed / 100;
+ PlaybackParameters param = new PlaybackParameters(ratio);
+ if (player != null) {
+ player.setPlaybackParameters(param);
+ }
+ break;
+ case CAPTION:
+ Caption captionToUse = null;
+ for (Caption caption : captions) {
+ if (caption.getLanguage().getId().compareTo(item.getStrId()) == 0) {
+ captionToUse = caption;
+ break;
+ }
+ }
+ if (captionToUse != null) {
+ if (!sepiaSearch) {
+ captionURI = Uri.parse("https://" + HelperInstance.getLiveInstance(PeertubeActivity.this) + captionToUse.getCaptionPath());
+ } else {
+ captionURI = Uri.parse("https://" + peertubeInstance + captionToUse.getCaptionPath());
+ }
+ } else {
+ captionURI = null;
+ }
+ currentCaption = item.getStrId();
+ long newPosition = player.getCurrentPosition();
+
+ if (player != null)
+ player.release();
+
+ TrackSelector trackSelector = new DefaultTrackSelector(PeertubeActivity.this, new AdaptiveTrackSelection.Factory());
+ player = new SimpleExoPlayer.Builder(PeertubeActivity.this).setTrackSelector(trackSelector).build();
+ binding.mediaVideo.player(player);
+ binding.doubleTapPlayerView.setPlayer(player);
+ captionLang = item.getStrId();
+ startStream(
+ peertube,
+ null,
+ true,
+ newPosition,
+ captionURI,
+ captionLang,
+ false
+ );
+ break;
+ case AUTONEXT:
+ SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = sharedpreferences.edit();
+ editor.putBoolean(getString(R.string.set_autoplay_next_video_choice), item.getId() == 1);
+ editor.apply();
+ if (Helper.isLoggedIn(PeertubeActivity.this)) {
+ new Thread(() -> {
+ UserSettings userSettings = new UserSettings();
+ userSettings.setAutoPlayNextVideo(item.getId() == 1);
+ try {
+ RetrofitPeertubeAPI api = new RetrofitPeertubeAPI(PeertubeActivity.this);
+ api.updateUser(userSettings);
+ } catch (Exception | Error e) {
+ e.printStackTrace();
+ }
+ }).start();
+ }
+ break;
+ }
+ closeSubMenuMenuOptions();
+ }
+
+ public void closeMainMenuOptions() {
+ DisplayMetrics displayMetrics = new DisplayMetrics();
+ getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
+ int height = displayMetrics.heightPixels;
+ TranslateAnimation animate = new TranslateAnimation(
+ 0,
+ 0,
+ 0,
+ height);
+ animate.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ binding.videoParams.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+ });
+ animate.setDuration(500);
+ binding.videoParams.startAnimation(animate);
+ }
+
+
+ public void closeSubMenuMenuOptions() {
+ DisplayMetrics displayMetrics = new DisplayMetrics();
+ getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
+ int height = displayMetrics.heightPixels;
+ TranslateAnimation animate = new TranslateAnimation(
+ 0,
+ 0,
+ 0,
+ height);
+ animate.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ binding.videoParamsSubmenu.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+ });
+ animate.setDuration(500);
+ binding.videoParamsSubmenu.startAnimation(animate);
+ }
+
+
+ private void sendComment(Comment comment, int position) {
+ if (canMakeAction(PeertubeActivity.this) && !sepiaSearch) {
+ if (comment == null) {
+ String commentStr = binding.addCommentWrite.getText() != null ? binding.addCommentWrite.getText().toString() : "";
+ if (commentStr.trim().length() > 0) {
+ if (Helper.isLoggedIn(PeertubeActivity.this)) {
+ PostActionsVM viewModelComment = new ViewModelProvider(PeertubeActivity.this).get(PostActionsVM.class);
+ viewModelComment.comment(ADD_COMMENT, peertube.getId(), null, commentStr).observe(PeertubeActivity.this, apiResponse1 -> manageVIewPostActions(ADD_COMMENT, 0, apiResponse1));
+ } else {//Remote account is posting a message
+ String url = "https://" + peertube.getChannel().getHost() + "/videos/watch/" + peertube.getUuid();
+ MastodonPostActionsVM viewModelCommentMastodon = new ViewModelProvider(PeertubeActivity.this).get(MastodonPostActionsVM.class);
+ viewModelCommentMastodon.comment(url, commentStr).observe(PeertubeActivity.this, status -> manageVIewPostActionsMastodon(ADD_COMMENT, position, status));
+ }
+ binding.addCommentWrite.setText("");
+ }
+ } else {
+ String commentView = binding.addCommentWrite.getText() != null ? binding.addCommentWrite.getText().toString() : "";
+ if (commentView.trim().length() > 0) {
+ if (Helper.isLoggedIn(PeertubeActivity.this)) {
+ PostActionsVM viewModelComment = new ViewModelProvider(PeertubeActivity.this).get(PostActionsVM.class);
+ viewModelComment.comment(REPLY, peertube.getId(), comment.getId(), commentView).observe(PeertubeActivity.this, apiResponse1 -> manageVIewPostActions(REPLY, position, apiResponse1));
+ } else {//Remote account is posting a message
+ MastodonPostActionsVM viewModelCommentMastodon = new ViewModelProvider(PeertubeActivity.this).get(MastodonPostActionsVM.class);
+ viewModelCommentMastodon.comment(comment.getUrl(), commentView).observe(PeertubeActivity.this, status -> manageVIewPostActionsMastodon(REPLY, position, status));
+ }
+ binding.addCommentWrite.setText("");
+ }
+ }
+ closePostComment();
+ } else {
+ if (sepiaSearch) {
+ Toasty.info(PeertubeActivity.this, getString(R.string.federation_issue), Toasty.LENGTH_SHORT).show();
+ } else {
+ Toasty.error(PeertubeActivity.this, getString(R.string.not_logged_in), Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+
+ private void closeCommentThread() {
+ binding.peertubeInformationContainer.setVisibility(View.VISIBLE);
+ hideKeyboard(this);
+ TranslateAnimation animate = new TranslateAnimation(
+ 0,
+ binding.replyThread.getWidth(),
+ 0,
+ 0);
+ animate.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ binding.replyThread.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+ });
+ animate.setDuration(500);
+ binding.replyThread.startAnimation(animate);
+ }
+
+
+ public void openPostComment(Comment comment, int position) {
+ if (comment != null) {
+ binding.replyContent.setVisibility(View.VISIBLE);
+ Account account = comment.getAccount();
+ Helper.loadAvatar(PeertubeActivity.this, account, binding.commentAccountProfile);
+ binding.commentAccountDisplayname.setText(account.getDisplayName());
+ binding.commentAccountUsername.setText(account.getAcct());
+ Spanned commentSpan;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
+ commentSpan = Html.fromHtml(comment.getText(), Html.FROM_HTML_MODE_COMPACT);
+ else
+ commentSpan = Html.fromHtml(comment.getText());
+ binding.commentContent.setText(commentSpan);
+ binding.commentDate.setText(Helper.dateDiff(PeertubeActivity.this, comment.getCreatedAt()));
+ } else {
+ binding.replyContent.setVisibility(View.GONE);
+ }
+ binding.postComment.setVisibility(View.VISIBLE);
+ DisplayMetrics displayMetrics = new DisplayMetrics();
+ getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
+ int height = displayMetrics.heightPixels;
+ TranslateAnimation animateComment = new TranslateAnimation(
+ 0,
+ 0,
+ height,
+ 0);
+ animateComment.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ binding.peertubeInformationContainer.setVisibility(View.GONE);
+ InputMethodManager inputMethodManager =
+ (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ inputMethodManager.toggleSoftInputFromWindow(
+ binding.addCommentWrite.getApplicationWindowToken(),
+ InputMethodManager.SHOW_FORCED, 0);
+ binding.addCommentWrite.requestFocus();
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+ });
+ animateComment.setDuration(500);
+ binding.postComment.startAnimation(animateComment);
+ if (comment != null) {
+ binding.addCommentWrite.setText(String.format("@%s ", comment.getAccount().getAcct()));
+ } else {
+ binding.addCommentWrite.setText(String.format("@%s ", peertube.getAccount().getAcct()));
+ }
+ binding.addCommentWrite.setSelection(binding.addCommentWrite.getText() != null ? binding.addCommentWrite.getText().length() : 0);
+
+ binding.send.setOnClickListener(null);
+ binding.send.setOnClickListener(v -> sendComment(comment, position));
+ }
+
+ private void closePostComment() {
+ DisplayMetrics displayMetrics = new DisplayMetrics();
+ getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
+ int height = displayMetrics.heightPixels;
+ binding.peertubeInformationContainer.setVisibility(View.VISIBLE);
+ hideKeyboard(this);
+ TranslateAnimation animateComment = new TranslateAnimation(
+ 0,
+ 0,
+ 0,
+ height);
+ animateComment.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ binding.postComment.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+ });
+ animateComment.setDuration(500);
+ binding.postComment.startAnimation(animateComment);
+ }
+
+ public void manageVIewPostActionsMastodon(app.fedilab.android.peertube.client.mastodon.Status status) {
+ if (status != null) {
+ this.status = status;
+ changeColorMastodon();
+ binding.peertubeFavorite.setText(String.valueOf(status.getFavouriteCount()));
+ binding.peertubeReblog.setText(String.valueOf(status.getReblogsCount()));
+ } else {
+ Toasty.error(PeertubeActivity.this, getString(R.string.toast_error), Toasty.LENGTH_LONG).show();
+ }
+ }
+
+ public void manageVIewPostActionsMastodon(RetrofitPeertubeAPI.ActionType statusAction, int position, app.fedilab.android.peertube.client.mastodon.Status status) {
+ if (peertube.isCommentsEnabled() && statusAction == ADD_COMMENT) {
+ if (status != null) {
+ Comment comment = app.fedilab.android.peertube.client.mastodon.Status.convertStatusToComment(status);
+ comments.add(0, comment);
+ commentListAdapter.notifyItemInserted(0);
+ }
+ } else if (peertube.isCommentsEnabled() && statusAction == REPLY) {
+ if (status != null) {
+ Comment comment = app.fedilab.android.peertube.client.mastodon.Status.convertStatusToComment(status);
+ if (commentsThread == null) {
+ commentsThread = new ArrayList<>();
+ }
+ commentsThread.add(position + 1, comment);
+ commentReplyListAdapter.notifyItemInserted(position + 1);
+ }
+ }
+ }
+
+ public void retrieveRemoteStatus(app.fedilab.android.peertube.client.mastodon.Status status) {
+ this.status = status;
+ if (status != null) {
+ changeColorMastodon();
+ binding.peertubeFavorite.setText(String.valueOf(status.getFavouriteCount()));
+ binding.peertubeReblog.setText(String.valueOf(status.getReblogsCount()));
+ }
+ }
+
+ @SuppressWarnings({"unused", "RedundantSuppression"})
+ public void manageVIewPostActions(RetrofitPeertubeAPI.ActionType statusAction, int position, APIResponse apiResponse) {
+
+ if (peertube.isCommentsEnabled() && statusAction == ADD_COMMENT) {
+ if (apiResponse.getComments() != null && apiResponse.getComments().size() > 0) {
+ comments.add(0, apiResponse.getComments().get(0));
+ commentListAdapter.notifyItemInserted(0);
+ }
+ } else if (peertube.isCommentsEnabled() && statusAction == REPLY) {
+ if (apiResponse.getComments() != null && apiResponse.getComments().size() > 0) {
+ commentsThread.add(position + 1, apiResponse.getComments().get(0));
+ commentReplyListAdapter.notifyItemInserted(position + 1);
+ }
+ } else if (statusAction == RetrofitPeertubeAPI.ActionType.REPORT_ACCOUNT) {
+ Toasty.success(PeertubeActivity.this, getString(R.string.successful_report), Toasty.LENGTH_LONG).show();
+ } else if (statusAction == RetrofitPeertubeAPI.ActionType.REPORT_VIDEO) {
+ Toasty.success(PeertubeActivity.this, getString(R.string.successful_video_report), Toasty.LENGTH_LONG).show();
+ }
+ }
+
+
+ private void initControllerButtons() {
+
+ PlayerControlView controlView = binding.doubleTapPlayerView.findViewById(R.id.exo_controller);
+ fullScreenIcon = controlView.findViewById(R.id.exo_fullscreen_icon);
+ View fullScreenButton = controlView.findViewById(R.id.exo_fullscreen_button);
+ fullScreenButton.setOnClickListener(v -> {
+ if (!fullScreenMode) {
+ openFullscreenDialog();
+ } else {
+ closeFullscreenDialog();
+ setRequestedOrientationCustom(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ }
+ });
+
+ ImageButton playButton = controlView.findViewById(R.id.exo_play);
+ playButton.setOnClickListener(v -> {
+ if (autoFullscreen && !fullScreenMode) {
+ openFullscreenDialog();
+ }
+ player.setPlayWhenReady(true);
+ });
+ View exo_next = controlView.findViewById(R.id.exo_next);
+ exo_next.setOnClickListener(v -> playNextVideo());
+
+ View exoSettings = controlView.findViewById(R.id.exo_settings);
+ exoSettings.setOnClickListener(v -> {
+ if (binding.videoParams.getVisibility() == View.VISIBLE) {
+ closeMainMenuOptions();
+ } else {
+ openMainMenuOptions();
+ }
+ });
+
+ }
+
+ private void setRequestedOrientationCustom(int orientationCustom) {
+ setRequestedOrientation(orientationCustom);
+ Handler handler = new Handler();
+ handler.postDelayed(() -> setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR), 2000);
+ }
+
+ private void initResolution() {
+ PlayerControlView controlView = binding.doubleTapPlayerView.findViewById(R.id.exo_controller);
+ TextView resolution = controlView.findViewById(R.id.resolution);
+ if (Helper.defaultFile(PeertubeActivity.this, peertube.getFiles()) != null) {
+ currentResolution = Helper.defaultFile(PeertubeActivity.this, peertube.getFiles()).getResolutions().getLabel();
+ if (peertube.getFiles() != null && peertube.getFiles().size() > 0) {
+ resolution.setText(String.format("%s", currentResolution));
+ } else {
+ resolution.setVisibility(View.GONE);
+ }
+ } else {
+ resolution.setVisibility(View.GONE);
+ }
+ }
+
+
+ private void changeColorMastodon() {
+ Drawable reblog = ContextCompat.getDrawable(PeertubeActivity.this, R.drawable.ic_baseline_repeat_24);
+ Drawable favorite = ContextCompat.getDrawable(PeertubeActivity.this, R.drawable.ic_baseline_star_24);
+ Drawable bookmark = ContextCompat.getDrawable(PeertubeActivity.this, R.drawable.ic_baseline_bookmark_24);
+
+ int color = getAttColor(this, android.R.attr.colorControlNormal);
+
+ if (reblog != null) {
+ reblog.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
+ DrawableCompat.setTint(reblog, color);
+ }
+ if (favorite != null) {
+ favorite.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
+ DrawableCompat.setTint(favorite, color);
+ }
+
+ if (bookmark != null) {
+ bookmark.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
+ DrawableCompat.setTint(bookmark, color);
+ }
+
+ if (reblog != null && status.isReblogged()) {
+ reblog.setColorFilter(getResources().getColor(R.color.positive_thumbs), PorterDuff.Mode.SRC_ATOP);
+ DrawableCompat.setTint(reblog, getResources().getColor(R.color.positive_thumbs));
+ }
+ if (favorite != null && status.isFavourited()) {
+ favorite.setColorFilter(getResources().getColor(R.color.favorite), PorterDuff.Mode.SRC_ATOP);
+ DrawableCompat.setTint(favorite, getResources().getColor(R.color.favorite));
+ }
+
+ if (bookmark != null && status.isBookmarked()) {
+ bookmark.setColorFilter(getResources().getColor(R.color.bookmark), PorterDuff.Mode.SRC_ATOP);
+ DrawableCompat.setTint(bookmark, getResources().getColor(R.color.bookmark));
+ }
+
+ binding.peertubeReblog.setCompoundDrawablesWithIntrinsicBounds(null, reblog, null, null);
+ binding.peertubeFavorite.setCompoundDrawablesWithIntrinsicBounds(null, favorite, null, null);
+ binding.peertubeBookmark.setCompoundDrawablesWithIntrinsicBounds(null, bookmark, null, null);
+ }
+
+ private void changeColor() {
+
+ Drawable thumbUp = ContextCompat.getDrawable(PeertubeActivity.this, R.drawable.ic_baseline_thumb_up_alt_24);
+ Drawable thumbDown = ContextCompat.getDrawable(PeertubeActivity.this, R.drawable.ic_baseline_thumb_down_alt_24);
+ int color = getAttColor(this, android.R.attr.colorControlNormal);
+
+ if (thumbUp != null) {
+ thumbUp.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
+ DrawableCompat.setTint(thumbUp, color);
+ }
+ if (thumbDown != null) {
+ thumbDown.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
+ DrawableCompat.setTint(thumbDown, color);
+ }
+ if (peertube.getMyRating() != null && peertube.getMyRating().compareTo("like") == 0) {
+ if (thumbUp != null) {
+ thumbUp.setColorFilter(getResources().getColor(R.color.positive_thumbs), PorterDuff.Mode.SRC_ATOP);
+ DrawableCompat.setTint(thumbUp, getResources().getColor(R.color.positive_thumbs));
+ }
+ } else if (peertube.getMyRating() != null && peertube.getMyRating().compareTo("dislike") == 0) {
+ if (thumbDown != null) {
+ thumbDown.setColorFilter(getResources().getColor(R.color.negative_thumbs), PorterDuff.Mode.SRC_ATOP);
+ DrawableCompat.setTint(thumbDown, getResources().getColor(R.color.negative_thumbs));
+ }
+ }
+ binding.peertubeLikeCount.setCompoundDrawablesWithIntrinsicBounds(null, thumbUp, null, null);
+ binding.peertubeDislikeCount.setCompoundDrawablesWithIntrinsicBounds(null, thumbDown, null, null);
+ }
+
+ public void manageVIewPlaylists(APIResponse apiResponse) {
+ if (apiResponse == null || apiResponse.getError() != null || playlists == null || peertube == null) {
+ return;
+ }
+ if (apiResponse.getPlaylists() != null && apiResponse.getPlaylists().size() > 0) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(PeertubeActivity.this);
+ builder.setTitle(R.string.modify_playlists);
+
+ List ownerPlaylists = apiResponse.getPlaylists();
+ if (ownerPlaylists == null) {
+ return;
+ }
+ String[] label = new String[ownerPlaylists.size()];
+ boolean[] checked = new boolean[ownerPlaylists.size()];
+ int i = 0;
+ List playlistsForVideo = playlists.get(peertube.getId());
+
+
+ for (PlaylistData.Playlist playlist : ownerPlaylists) {
+ checked[i] = false;
+ if (playlistsForVideo != null) {
+ for (PlaylistExist playlistExist : playlistsForVideo) {
+ if (playlistExist != null && playlistExist.getPlaylistId().compareTo(playlist.getId()) == 0) {
+ checked[i] = true;
+ break;
+ }
+ }
+ }
+ label[i] = playlist.getDisplayName();
+ i++;
+ }
+
+ builder.setMultiChoiceItems(label, checked, (dialog, which, isChecked) -> {
+ PlaylistsVM playlistsViewModel = new ViewModelProvider(PeertubeActivity.this).get(PlaylistsVM.class);
+ if (isChecked) { //Add to playlist
+ playlistsViewModel.manage(PlaylistsVM.action.ADD_VIDEOS, ownerPlaylists.get(which), peertube.getUuid()).observe(PeertubeActivity.this, apiResponse3 -> addElement(ownerPlaylists.get(which).getId(), peertube.getId(), apiResponse3));
+ } else { //Remove from playlist
+ String elementInPlaylistId = null;
+ for (PlaylistExist playlistExist : peertube.getPlaylistExists()) {
+ if (playlistExist.getPlaylistId().compareTo(ownerPlaylists.get(which).getId()) == 0) {
+ elementInPlaylistId = playlistExist.getPlaylistElementId();
+ }
+ }
+ playlistsViewModel.manage(PlaylistsVM.action.DELETE_VIDEOS, ownerPlaylists.get(which), elementInPlaylistId);
+ playlists.remove(peertube.getId());
+ }
+ });
+ builder.setPositiveButton(R.string.close, (dialog, which) -> dialog.dismiss());
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ }
+ }
+
+ public void manageVIewPlaylist(APIResponse apiResponse) {
+ if (apiResponse.getError() != null || apiResponse.getVideoExistPlaylist() == null) {
+ return;
+ }
+ if (playlists == null) {
+ playlists = new HashMap<>();
+ }
+ playlists.putAll(apiResponse.getVideoExistPlaylist());
+ peertube.setPlaylistExists(playlists.get(peertube.getId()));
+
+ }
+
+ public void addElement(String playlistId, String videoId, APIResponse apiResponse) {
+ if (apiResponse != null && apiResponse.getActionReturn() != null) {
+
+ PlaylistExist playlistExist = new PlaylistExist();
+ playlistExist.setPlaylistId(playlistId);
+ playlistExist.setPlaylistElementId(apiResponse.getActionReturn());
+ List playlistExistList = playlists.get(videoId);
+ if (playlistExistList == null) {
+ playlistExistList = new ArrayList<>();
+ }
+ playlistExistList.add(playlistExist);
+ playlists.put(videoId, playlistExistList);
+ }
+ }
+
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+
+ if (MotionEvent.ACTION_UP == event.getAction()) {
+ Rect viewRectParams = new Rect();
+ binding.videoParams.getGlobalVisibleRect(viewRectParams);
+ if (binding.videoParams.getVisibility() == View.VISIBLE && !viewRectParams.contains((int) event.getRawX(), (int) event.getRawY())) {
+ closeMainMenuOptions();
+ if (binding.videoParamsSubmenu.getVisibility() == View.VISIBLE) {
+ closeSubMenuMenuOptions();
+ }
+ }
+ Rect viewRectParamsSub = new Rect();
+ binding.videoParamsSubmenu.getGlobalVisibleRect(viewRectParamsSub);
+ if (binding.videoParamsSubmenu.getVisibility() == View.VISIBLE && !viewRectParamsSub.contains((int) event.getRawX(), (int) event.getRawY())) {
+ closeSubMenuMenuOptions();
+ if (binding.videoParams.getVisibility() == View.VISIBLE) {
+ closeMainMenuOptions();
+ }
+ }
+ }
+ return super.dispatchTouchEvent(event);
+ }
+
+ private void updateHistory(long position) {
+ SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
+ boolean storeInHistory = sharedpreferences.getBoolean(getString(R.string.set_store_in_history), true);
+ if (Helper.isLoggedIn(PeertubeActivity.this) && peertube != null && storeInHistory) {
+ new Thread(() -> {
+ try {
+ RetrofitPeertubeAPI api = new RetrofitPeertubeAPI(PeertubeActivity.this);
+ api.updateHistory(peertube.getUuid(), position);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }).start();
+ }
+ }
+
+ @Override
+ public void onAllCommentRemoved() {
+ binding.noActionText.setVisibility(View.VISIBLE);
+ }
+
+ private void playNextVideo() {
+ if (nextVideo != null) {
+ Intent intent = new Intent(PeertubeActivity.this, PeertubeActivity.class);
+ Bundle b = new Bundle();
+ b.putParcelable("video", nextVideo);
+ b.putString("video_id", nextVideo.getId());
+ b.putString("video_uuid", nextVideo.getUuid());
+ playedVideos.add(nextVideo.getId());
+ b.putBoolean("sepia_search", sepiaSearch);
+ intent.putExtras(b);
+ startActivity(intent);
+ }
+ }
+
+ @Override
+ public void onMediaItemTransition(MediaItem mediaItem, int reason) {
+ SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
+ boolean autoplayNextVideo = sharedpreferences.getBoolean(getString(R.string.set_autoplay_next_video_choice), true);
+ if (reason == MEDIA_ITEM_TRANSITION_REASON_AUTO) {
+ player.removeMediaItems(0, player.getMediaItemCount());
+ if (!sepiaSearch && autoplayNextVideo) {
+ playNextVideo();
+ }
+ }
+ }
+
+ @Override
+ public void onPlayerError(ExoPlaybackException error) {
+
+ }
+
+
+ enum videoOrientation {
+ LANDSCAPE,
+ PORTRAIT
+ }
+}
diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeEditUploadActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeEditUploadActivity.java
new file mode 100644
index 00000000..53b5aaa5
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeEditUploadActivity.java
@@ -0,0 +1,645 @@
+package app.fedilab.android.peertube.activities;
+/* Copyright 2020 Thomas Schneider
+ *
+ * This file is a part of TubeLab
+ *
+ * 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.
+ *
+ * TubeLab 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 TubeLab; if not,
+ * see . */
+
+import static app.fedilab.android.peertube.client.RetrofitPeertubeAPI.ActionType.PEERTUBEDELETEVIDEO;
+import static app.fedilab.android.peertube.client.RetrofitPeertubeAPI.DataType.MY_CHANNELS;
+import static app.fedilab.android.peertube.helper.Helper.peertubeInformation;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+import androidx.lifecycle.ViewModelProvider;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.resource.bitmap.CenterCrop;
+import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
+import com.bumptech.glide.request.RequestOptions;
+
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import app.fedilab.android.peertube.R;
+import app.fedilab.android.peertube.client.APIResponse;
+import app.fedilab.android.peertube.client.RetrofitPeertubeAPI;
+import app.fedilab.android.peertube.client.data.ChannelData.Channel;
+import app.fedilab.android.peertube.client.data.VideoData;
+import app.fedilab.android.peertube.client.data.VideoData.Video;
+import app.fedilab.android.peertube.client.entities.Item;
+import app.fedilab.android.peertube.client.entities.ItemStr;
+import app.fedilab.android.peertube.client.entities.VideoParams;
+import app.fedilab.android.peertube.databinding.ActivityPeertubeEditBinding;
+import app.fedilab.android.peertube.helper.Helper;
+import app.fedilab.android.peertube.helper.HelperInstance;
+import app.fedilab.android.peertube.helper.Theme;
+import app.fedilab.android.peertube.viewmodel.ChannelsVM;
+import app.fedilab.android.peertube.viewmodel.MyVideoVM;
+import app.fedilab.android.peertube.viewmodel.PostActionsVM;
+import app.fedilab.android.peertube.viewmodel.TimelineVM;
+import es.dmoral.toasty.Toasty;
+
+
+public class PeertubeEditUploadActivity extends BaseActivity {
+
+
+ private final int PICK_IMAGE = 50378;
+ private final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 724;
+ Item licenseToSend, privacyToSend, categoryToSend;
+ ItemStr languageToSend;
+ private LinkedHashMap channels;
+ private String videoId;
+ private Channel channel;
+ private VideoParams videoParams;
+ private Video video;
+ private String channelToSendId;
+ private ActivityPeertubeEditBinding binding;
+ private Uri inputData;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Theme.setTheme(this, HelperInstance.getLiveInstance(this), false);
+ super.onCreate(savedInstanceState);
+ binding = ActivityPeertubeEditBinding.inflate(getLayoutInflater());
+ View view = binding.getRoot();
+ setContentView(view);
+
+ SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
+
+ Bundle b = getIntent().getExtras();
+
+ if (b != null) {
+ videoId = b.getString("video_id", null);
+ }
+ if (videoId == null) {
+ videoId = sharedpreferences.getString(Helper.VIDEO_ID, null);
+ sharedpreferences.edit().remove(Helper.VIDEO_ID).apply();
+ }
+ if (getSupportActionBar() != null)
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ binding.setUploadDelete.setOnClickListener(v -> {
+ AlertDialog.Builder builderInner;
+ builderInner = new AlertDialog.Builder(PeertubeEditUploadActivity.this);
+ builderInner.setMessage(getString(R.string.delete_video_confirmation));
+ builderInner.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
+ builderInner.setPositiveButton(R.string.yes, (dialog, which) -> {
+ PostActionsVM viewModel = new ViewModelProvider(PeertubeEditUploadActivity.this).get(PostActionsVM.class);
+ viewModel.post(PEERTUBEDELETEVIDEO, videoId, null).observe(PeertubeEditUploadActivity.this, apiResponse -> manageVIewPostActions(PEERTUBEDELETEVIDEO, apiResponse));
+ dialog.dismiss();
+ });
+ builderInner.show();
+ });
+ //Get params from the API
+ LinkedHashMap categories = new LinkedHashMap<>(peertubeInformation.getCategories());
+ LinkedHashMap licences = new LinkedHashMap<>(peertubeInformation.getLicences());
+ LinkedHashMap privacies = new LinkedHashMap<>(peertubeInformation.getPrivacies());
+ LinkedHashMap languages = new LinkedHashMap<>(peertubeInformation.getLanguages());
+ LinkedHashMap translations = null;
+
+ if (peertubeInformation.getTranslations() != null)
+ translations = new LinkedHashMap<>(peertubeInformation.getTranslations());
+ //Populate catgories
+ String[] categoriesA = new String[categories.size()];
+ Iterator> it = categories.entrySet().iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ Map.Entry pair = it.next();
+ if (translations == null || translations.size() == 0 || !translations.containsKey(pair.getValue()))
+ categoriesA[i] = pair.getValue();
+ else
+ categoriesA[i] = translations.get(pair.getValue());
+ it.remove();
+ i++;
+ }
+ ArrayAdapter adapterCatgories = new ArrayAdapter<>(PeertubeEditUploadActivity.this,
+ android.R.layout.simple_spinner_dropdown_item, categoriesA);
+ binding.setUploadCategories.setAdapter(adapterCatgories);
+
+
+ //Populate licenses
+ String[] licensesA = new String[licences.size()];
+ it = licences.entrySet().iterator();
+ i = 0;
+ while (it.hasNext()) {
+ Map.Entry pair = it.next();
+ if (translations == null || translations.size() == 0 || !translations.containsKey(pair.getValue()))
+ licensesA[i] = pair.getValue();
+ else
+ licensesA[i] = translations.get(pair.getValue());
+ it.remove();
+ i++;
+ }
+ ArrayAdapter adapterLicenses = new ArrayAdapter<>(PeertubeEditUploadActivity.this,
+ android.R.layout.simple_spinner_dropdown_item, licensesA);
+ binding.setUploadLicenses.setAdapter(adapterLicenses);
+
+
+ //Populate languages
+ String[] languagesA = new String[languages.size()];
+ Iterator> itl = languages.entrySet().iterator();
+ i = 0;
+ while (itl.hasNext()) {
+ Map.Entry pair = itl.next();
+ if (translations == null || translations.size() == 0 || !translations.containsKey(pair.getValue()))
+ languagesA[i] = pair.getValue();
+ else
+ languagesA[i] = translations.get(pair.getValue());
+ itl.remove();
+ i++;
+ }
+ ArrayAdapter adapterLanguages = new ArrayAdapter<>(PeertubeEditUploadActivity.this,
+ android.R.layout.simple_spinner_dropdown_item, languagesA);
+ binding.setUploadLanguages.setAdapter(adapterLanguages);
+
+
+ //Populate languages
+ String[] privaciesA = new String[privacies.size()];
+ it = privacies.entrySet().iterator();
+ i = 0;
+ while (it.hasNext()) {
+ Map.Entry pair = it.next();
+ if (translations == null || translations.size() == 0 || !translations.containsKey(pair.getValue()))
+ privaciesA[i] = pair.getValue();
+ else
+ privaciesA[i] = translations.get(pair.getValue());
+ it.remove();
+ i++;
+ }
+
+ ArrayAdapter adapterPrivacies = new ArrayAdapter<>(PeertubeEditUploadActivity.this,
+ android.R.layout.simple_spinner_dropdown_item, privaciesA);
+ binding.setUploadPrivacy.setAdapter(adapterPrivacies);
+
+
+ TimelineVM feedsViewModel = new ViewModelProvider(PeertubeEditUploadActivity.this).get(TimelineVM.class);
+ feedsViewModel.getMyVideo(null, videoId).observe(PeertubeEditUploadActivity.this, this::manageVIewVideo);
+
+ channels = new LinkedHashMap<>();
+
+ setTitle(R.string.edit_video);
+ }
+
+ public void manageUpdate(APIResponse apiResponse) {
+ binding.setUploadSubmit.setEnabled(true);
+ if (apiResponse.getError() != null) {
+ if (apiResponse.getError() != null && apiResponse.getError().getError() != null)
+ Toasty.error(PeertubeEditUploadActivity.this, apiResponse.getError().getError(), Toast.LENGTH_LONG).show();
+ else
+ Toasty.error(PeertubeEditUploadActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
+ }
+ Toasty.info(PeertubeEditUploadActivity.this, getString(R.string.toast_peertube_video_updated), Toast.LENGTH_LONG).show();
+ }
+
+ public void manageVIewVideo(APIResponse apiResponse) {
+ if (apiResponse.getError() != null || apiResponse.getPeertubes() == null || apiResponse.getPeertubes().size() == 0) {
+ if (apiResponse.getError() != null && apiResponse.getError().getError() != null)
+ Toasty.error(PeertubeEditUploadActivity.this, apiResponse.getError().getError(), Toast.LENGTH_LONG).show();
+ else
+ Toasty.error(PeertubeEditUploadActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
+ binding.setUploadSubmit.setEnabled(true);
+ return;
+ }
+
+ //Peertube video
+ video = apiResponse.getPeertubes().get(0);
+
+ ChannelsVM viewModelC = new ViewModelProvider(PeertubeEditUploadActivity.this).get(ChannelsVM.class);
+ viewModelC.get(MY_CHANNELS, null).observe(PeertubeEditUploadActivity.this, this::manageVIewChannels);
+
+ languageToSend = video.getLanguage();
+ licenseToSend = video.getLicence();
+ privacyToSend = video.getPrivacy();
+ categoryToSend = video.getCategory();
+
+ if (video.getThumbnailPath() != null) {
+ Helper.loadGiF(PeertubeEditUploadActivity.this, video.getThumbnailPath(), binding.pVideoPreview);
+ }
+
+
+ binding.setPreview.setOnClickListener(v -> {
+ if (ContextCompat.checkSelfPermission(PeertubeEditUploadActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) !=
+ PackageManager.PERMISSION_GRANTED) {
+ ActivityCompat.requestPermissions(PeertubeEditUploadActivity.this,
+ new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
+ MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
+ return;
+ }
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setType("image/jpg");
+ String[] mimetypes = {"image/jpg", "image/jpeg"};
+ intent.putExtra(Intent.EXTRA_MIME_TYPES, mimetypes);
+ startActivityForResult(intent, PICK_IMAGE);
+
+ });
+ if (languageToSend == null) {
+ languageToSend = new ItemStr();
+ LinkedHashMap languages = new LinkedHashMap<>(peertubeInformation.getLanguages());
+ Map.Entry entryString = languages.entrySet().iterator().next();
+ languageToSend.setId(entryString.getKey());
+ languageToSend.setLabel(entryString.getValue());
+ }
+
+ if (licenseToSend == null) {
+ licenseToSend = new Item();
+ LinkedHashMap licences = new LinkedHashMap<>(peertubeInformation.getLicences());
+ Map.Entry entryInt = licences.entrySet().iterator().next();
+ licenseToSend.setId(entryInt.getKey());
+ licenseToSend.setLabel(entryInt.getValue());
+ }
+
+ if (categoryToSend == null) {
+ categoryToSend = new Item();
+ LinkedHashMap categories = new LinkedHashMap<>(peertubeInformation.getCategories());
+ Map.Entry entryInt = categories.entrySet().iterator().next();
+ categoryToSend.setId(entryInt.getKey());
+ categoryToSend.setLabel(entryInt.getValue());
+ }
+ if (privacyToSend == null) {
+ privacyToSend = new Item();
+ LinkedHashMap privacies = new LinkedHashMap<>(peertubeInformation.getPrivacies());
+ Map.Entry entryInt = privacies.entrySet().iterator().next();
+ privacyToSend.setId(entryInt.getKey());
+ privacyToSend.setLabel(entryInt.getValue());
+ }
+
+ String language = languageToSend.getId();
+ int license = licenseToSend.getId();
+ int privacy = privacyToSend.getId();
+ int category = categoryToSend.getId();
+
+ channel = video.getChannel();
+ String title = video.getName();
+ boolean commentEnabled = video.isCommentsEnabled();
+ boolean isNSFW = video.isNsfw();
+
+ binding.setUploadEnableComments.setChecked(commentEnabled);
+ binding.setUploadNsfw.setChecked(isNSFW);
+
+ binding.pVideoTitle.setText(title);
+ binding.pVideoDescription.setText(video.getDescription());
+
+ new Thread(() -> {
+ try {
+ RetrofitPeertubeAPI api;
+ api = new RetrofitPeertubeAPI(PeertubeEditUploadActivity.this);
+ VideoData.Description description = api.getVideoDescription(video.getUuid());
+ Handler mainHandler = new Handler(Looper.getMainLooper());
+ Runnable myRunnable = () -> {
+ if (description != null) {
+ binding.pVideoDescription.setText(description.getDescription());
+ }
+ };
+ mainHandler.post(myRunnable);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }).start();
+
+ LinkedHashMap categories = new LinkedHashMap<>(peertubeInformation.getCategories());
+ LinkedHashMap licences = new LinkedHashMap<>(peertubeInformation.getLicences());
+ LinkedHashMap privacies = new LinkedHashMap<>(peertubeInformation.getPrivacies());
+ LinkedHashMap languages = new LinkedHashMap<>(peertubeInformation.getLanguages());
+
+
+ int languagePosition = 0;
+
+ if (languages.containsKey(language)) {
+ Iterator> itstr = languages.entrySet().iterator();
+ while (itstr.hasNext()) {
+ Map.Entry pair = itstr.next();
+ if (pair.getKey().compareTo(language) == 0)
+ break;
+ itstr.remove();
+ languagePosition++;
+ }
+ }
+ int privacyPosition = 0;
+ if (privacies.containsKey(privacy)) {
+ Iterator> it = privacies.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry pair = it.next();
+ if (pair.getKey() == privacy)
+ break;
+ it.remove();
+ privacyPosition++;
+ }
+ }
+ int licensePosition = 0;
+ if (licences.containsKey(license)) {
+ Iterator> it = licences.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry pair = it.next();
+ if (pair.getKey() == license)
+ break;
+ it.remove();
+ licensePosition++;
+ }
+ }
+ int categoryPosition = 0;
+ if (categories.containsKey(category)) {
+ Iterator> it = categories.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry pair = it.next();
+ if (pair.getKey() == category)
+ break;
+ it.remove();
+ categoryPosition++;
+ }
+ }
+ //Manage privacies
+ binding.setUploadPrivacy.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ updatePrivacyPosition(position);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+
+ }
+ });
+ binding.setUploadLicenses.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ updateLicensePosition(position);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+
+ }
+ });
+ //Manage categories
+ binding.setUploadCategories.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ updateCategoryPosition(position);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+
+ }
+ });
+
+ //Manage languages
+ binding.setUploadLanguages.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ updateLanguagesPosition(position);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+
+ }
+ });
+ //Manage languages
+ binding.setUploadChannel.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+
+ }
+ });
+
+
+ Item finalCategoryToSend = categoryToSend;
+ Item finalLicenseToSend = licenseToSend;
+ ItemStr finalLanguageToSend = languageToSend;
+ Item finalPrivacyToSend = privacyToSend;
+ binding.setUploadSubmit.setOnClickListener(v -> {
+ String title1 = binding.pVideoTitle.getText() != null ? binding.pVideoTitle.getText().toString().trim() : "";
+ String description = binding.pVideoDescription.getText() != null ? binding.pVideoDescription.getText().toString().trim() : "";
+ boolean isNSFW1 = binding.setUploadNsfw.isChecked();
+ boolean commentEnabled1 = binding.setUploadEnableComments.isChecked();
+ videoParams = new VideoParams();
+ videoParams.setName(title1);
+ videoParams.setDescription(description);
+ videoParams.setNsfw(isNSFW1);
+ videoParams.setCommentsEnabled(commentEnabled1);
+ videoParams.setCategory(finalCategoryToSend.getId());
+ videoParams.setLicence(String.valueOf(finalLicenseToSend.getId()));
+ videoParams.setLanguage(finalLanguageToSend.getId());
+ videoParams.setChannelId(channelToSendId);
+ videoParams.setPrivacy(finalPrivacyToSend.getId());
+ List tags = binding.pVideoTags.getTags();
+ if (tags.size() > 5) {
+ Toasty.error(PeertubeEditUploadActivity.this, getString(R.string.max_tag_size), Toast.LENGTH_LONG).show();
+ return;
+ }
+ videoParams.setTags(tags);
+ binding.setUploadSubmit.setEnabled(false);
+ MyVideoVM myVideoVM = new ViewModelProvider(PeertubeEditUploadActivity.this).get(MyVideoVM.class);
+ myVideoVM.updateVideo(videoId, videoParams, inputData, inputData).observe(PeertubeEditUploadActivity.this, this::manageUpdate);
+ });
+
+ binding.setUploadPrivacy.setSelection(privacyPosition, false);
+ updatePrivacyPosition(privacyPosition);
+ binding.setUploadLanguages.setSelection(languagePosition, false);
+ updateLanguagesPosition(languagePosition);
+ binding.setUploadLicenses.setSelection(licensePosition, false);
+ updateLicensePosition(licensePosition);
+ binding.setUploadCategories.setSelection(categoryPosition, false);
+ updateCategoryPosition(categoryPosition);
+
+ List tags = video.getTags();
+ if (tags != null && tags.size() > 0) {
+ binding.pVideoTags.setTags(tags.toArray(new String[0]));
+ }
+ }
+
+ private void updateUploadChannel(int position) {
+ LinkedHashMap channelsCheck = new LinkedHashMap<>(channels);
+ Iterator> it = channelsCheck.entrySet().iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ Map.Entry pair = it.next();
+ if (i == position) {
+ channelToSendId = pair.getValue();
+ break;
+ }
+ it.remove();
+ i++;
+ }
+ }
+
+ private void updateLanguagesPosition(int position) {
+ LinkedHashMap languagesCheck = new LinkedHashMap<>(peertubeInformation.getLanguages());
+ Iterator> it = languagesCheck.entrySet().iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ Map.Entry pair = it.next();
+ if (i == position) {
+ languageToSend.setId(pair.getKey());
+ languageToSend.setLabel(pair.getValue());
+ break;
+ }
+ it.remove();
+ i++;
+ }
+ }
+
+ private void updateCategoryPosition(int position) {
+ LinkedHashMap categoriesCheck = new LinkedHashMap<>(peertubeInformation.getCategories());
+ Iterator> it = categoriesCheck.entrySet().iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ Map.Entry pair = it.next();
+ if (i == position) {
+ categoryToSend.setId(pair.getKey());
+ categoryToSend.setLabel(pair.getValue());
+ break;
+ }
+ it.remove();
+ i++;
+ }
+ }
+
+ private void updateLicensePosition(int position) {
+ LinkedHashMap licensesCheck = new LinkedHashMap<>(peertubeInformation.getLicences());
+ Iterator> it = licensesCheck.entrySet().iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ Map.Entry pair = it.next();
+ if (i == position) {
+ licenseToSend.setId(pair.getKey());
+ licenseToSend.setLabel(pair.getValue());
+ break;
+ }
+ it.remove();
+ i++;
+ }
+ }
+
+ private void updatePrivacyPosition(int position) {
+ LinkedHashMap privaciesCheck = new LinkedHashMap<>(peertubeInformation.getPrivacies());
+ Iterator> it = privaciesCheck.entrySet().iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ Map.Entry pair = it.next();
+ if (i == position) {
+ privacyToSend.setId(pair.getKey());
+ privacyToSend.setLabel(pair.getValue());
+ break;
+ }
+ it.remove();
+ i++;
+ }
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == PICK_IMAGE && resultCode == Activity.RESULT_OK) {
+ if (data == null || data.getData() == null) {
+ Toasty.error(PeertubeEditUploadActivity.this, getString(R.string.toot_select_image_error), Toast.LENGTH_LONG).show();
+ return;
+ }
+ inputData = data.getData();
+ Glide.with(PeertubeEditUploadActivity.this)
+ .load(data.getData())
+ .thumbnail(0.1f)
+ .apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10)))
+ .into(binding.pVideoPreview);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+
+ public void manageVIewChannels(APIResponse apiResponse) {
+ if (apiResponse.getError() != null || apiResponse.getChannels() == null || apiResponse.getChannels().size() == 0) {
+ if (apiResponse.getError() != null && apiResponse.getError().getError() != null)
+ Toasty.error(PeertubeEditUploadActivity.this, apiResponse.getError().getError(), Toast.LENGTH_LONG).show();
+ else
+ Toasty.error(PeertubeEditUploadActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
+ return;
+ }
+ //Populate channels
+ List channelsReply = apiResponse.getChannels();
+ String[] channelName = new String[channelsReply.size()];
+ int i = 0;
+ for (Channel channel : channelsReply) {
+ channels.put(channel.getName(), channel.getId());
+ channelName[i] = channel.getName();
+ i++;
+ }
+ ArrayAdapter adapterChannel = new ArrayAdapter<>(PeertubeEditUploadActivity.this,
+ android.R.layout.simple_spinner_dropdown_item, channelName);
+ binding.setUploadChannel.setAdapter(adapterChannel);
+ int channelPosition = 0;
+ if (channels.containsKey(channel.getName())) {
+ LinkedHashMap channelsIterator = new LinkedHashMap<>(channels);
+ Iterator> it = channelsIterator.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry pair = it.next();
+ if (pair.getKey().equals(channel.getName())) {
+ channelToSendId = pair.getKey();
+ break;
+ }
+ it.remove();
+ channelPosition++;
+ }
+ }
+ binding.setUploadChannel.setSelection(channelPosition, false);
+ updateUploadChannel(channelPosition);
+ binding.setUploadSubmit.setEnabled(true);
+ }
+
+ @Override
+ public void onBackPressed() {
+ super.onBackPressed();
+ finish();
+ }
+
+ @SuppressWarnings({"unused", "RedundantSuppression"})
+ public void manageVIewPostActions(RetrofitPeertubeAPI.ActionType statusAction, APIResponse apiResponse) {
+ Intent intent = new Intent(PeertubeEditUploadActivity.this, MainActivity.class);
+ intent.putExtra(Helper.INTENT_ACTION, Helper.RELOAD_MYVIDEOS);
+ startActivity(intent);
+ finish();
+ }
+}
diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeRegisterActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeRegisterActivity.java
new file mode 100644
index 00000000..fdc8f934
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeRegisterActivity.java
@@ -0,0 +1,283 @@
+package app.fedilab.android.peertube.activities;
+/* Copyright 2020 Thomas Schneider
+ *
+ * This file is a part of TubeLab
+ *
+ * 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.
+ *
+ * TubeLab 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 TubeLab; if not,
+ * see . */
+
+import static app.fedilab.android.peertube.activities.MainActivity.PICK_INSTANCE;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.text.Html;
+import android.text.method.LinkMovementMethod;
+import android.util.Patterns;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AlertDialog;
+
+import java.util.Arrays;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import app.fedilab.android.peertube.BuildConfig;
+import app.fedilab.android.peertube.R;
+import app.fedilab.android.peertube.client.APIResponse;
+import app.fedilab.android.peertube.client.RetrofitPeertubeAPI;
+import app.fedilab.android.peertube.client.entities.AccountCreation;
+import app.fedilab.android.peertube.databinding.ActivityRegisterPeertubeBinding;
+import app.fedilab.android.peertube.helper.HelperAcadInstance;
+import app.fedilab.android.peertube.helper.HelperInstance;
+import app.fedilab.android.peertube.helper.Theme;
+import es.dmoral.toasty.Toasty;
+
+public class PeertubeRegisterActivity extends BaseActivity {
+
+
+ private String instance;
+ private ActivityRegisterPeertubeBinding binding;
+
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Theme.setTheme(this, HelperInstance.getLiveInstance(this), false);
+ super.onCreate(savedInstanceState);
+ binding = ActivityRegisterPeertubeBinding.inflate(getLayoutInflater());
+ View mainView = binding.getRoot();
+ setContentView(mainView);
+
+ if (getSupportActionBar() != null)
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+
+ if (BuildConfig.full_instances && BuildConfig.instance_switcher) {
+ binding.loginInstanceContainer.setVisibility(View.VISIBLE);
+ binding.titleLoginInstance.setVisibility(View.VISIBLE);
+ } else {
+ binding.loginInstanceContainer.setVisibility(View.GONE);
+ binding.titleLoginInstance.setVisibility(View.GONE);
+ }
+
+
+ binding.username.setOnFocusChangeListener((view, focused) -> {
+ if (!focused && binding.username.getText() != null) {
+ Pattern patternUsername = Pattern.compile("^[a-z0-9._]{1,50}$");
+ Matcher matcherMaxId = patternUsername.matcher(binding.username.getText().toString());
+ if (!matcherMaxId.matches()) {
+ binding.username.setError(getString(R.string.username_error));
+ }
+ }
+ });
+
+
+ binding.instanceHelp.setOnClickListener(v -> {
+ Intent intent = new Intent(PeertubeRegisterActivity.this, InstancePickerActivity.class);
+ startActivityForResult(intent, PICK_INSTANCE);
+ });
+
+ binding.email.setOnFocusChangeListener((view, focused) -> {
+ if (!focused && binding.email.getText() != null) {
+ Pattern patternUsername = Patterns.EMAIL_ADDRESS;
+ Matcher matcherMaxId = patternUsername.matcher(binding.email.getText().toString());
+ if (!matcherMaxId.matches()) {
+ binding.email.setError(getString(R.string.email_error));
+ }
+ }
+ });
+
+ binding.password.setOnFocusChangeListener((view, focused) -> {
+ if (!focused && binding.password.getText() != null) {
+ if (binding.password.getText().length() < 6) {
+ binding.password.setError(getString(R.string.password_length_error));
+ }
+ }
+ });
+
+ binding.passwordConfirm.setOnFocusChangeListener((view, focused) -> {
+ if (!focused && binding.passwordConfirm.getText() != null && binding.password.getText() != null) {
+ if (binding.passwordConfirm.getText().toString().compareTo(binding.password.getText().toString()) != 0) {
+ binding.passwordConfirm.setError(getString(R.string.password));
+ }
+ }
+ });
+ setTextAgreement();
+ binding.signup.setOnClickListener(view -> {
+ binding.errorMessage.setVisibility(View.GONE);
+ if (binding.username.getText() == null || binding.email.getText() == null || binding.password.getText() == null || binding.passwordConfirm.getText() == null || binding.username.getText().toString().trim().length() == 0 || binding.email.getText().toString().trim().length() == 0 ||
+ binding.password.getText().toString().trim().length() == 0 || binding.passwordConfirm.getText().toString().trim().length() == 0 || !binding.agreement.isChecked()) {
+ Toasty.error(PeertubeRegisterActivity.this, getString(R.string.all_field_filled)).show();
+ return;
+ }
+
+ if (!binding.password.getText().toString().trim().equals(binding.passwordConfirm.getText().toString().trim())) {
+ Toasty.error(PeertubeRegisterActivity.this, getString(R.string.password_error)).show();
+ return;
+ }
+ if (!Patterns.EMAIL_ADDRESS.matcher(binding.email.getText().toString().trim()).matches()) {
+ Toasty.error(PeertubeRegisterActivity.this, getString(R.string.email_error)).show();
+ return;
+ }
+ String[] emailArray = binding.email.getText().toString().split("@");
+ if (!BuildConfig.full_instances) {
+ if (emailArray.length > 1 && !Arrays.asList(HelperAcadInstance.valideEmails).contains(emailArray[1])) {
+ Toasty.error(PeertubeRegisterActivity.this, getString(R.string.email_error_domain, emailArray[1])).show();
+ return;
+ }
+ }
+
+ if (binding.password.getText().toString().trim().length() < 8) {
+ Toasty.error(PeertubeRegisterActivity.this, getString(R.string.password_too_short)).show();
+ return;
+ }
+ if (binding.username.getText().toString().matches("[a-z0-9_]")) {
+ Toasty.error(PeertubeRegisterActivity.this, getString(R.string.username_error)).show();
+ return;
+ }
+ binding.signup.setEnabled(false);
+
+ if (BuildConfig.full_instances) {
+ if (binding.loginInstance.getText() != null) {
+ instance = binding.loginInstance.getText().toString();
+ } else {
+ instance = "";
+ }
+ binding.loginInstance.setOnFocusChangeListener((view1, focus) -> {
+ if (!focus) {
+ setTextAgreement();
+ }
+ });
+ } else {
+ instance = HelperInstance.getLiveInstance(PeertubeRegisterActivity.this);
+ }
+ if (instance != null) {
+ instance = instance.toLowerCase().trim();
+ }
+
+ AccountCreation accountCreation = new AccountCreation();
+ accountCreation.setEmail(binding.email.getText().toString().trim());
+ accountCreation.setPassword(binding.password.getText().toString().trim());
+ accountCreation.setPasswordConfirm(binding.passwordConfirm.getText().toString().trim());
+ accountCreation.setUsername(binding.username.getText().toString().trim());
+ accountCreation.setInstance(instance);
+
+ new Thread(() -> {
+ try {
+ APIResponse apiResponse = new RetrofitPeertubeAPI(PeertubeRegisterActivity.this, instance, null).createAccount(accountCreation);
+ Handler mainHandler = new Handler(Looper.getMainLooper());
+ Runnable myRunnable = () -> {
+ if (apiResponse.getError() != null) {
+ String errorMessage;
+ if (apiResponse.getError().getError() != null) {
+ try {
+ String[] resp = apiResponse.getError().getError().split(":");
+ if (resp.length == 2)
+ errorMessage = apiResponse.getError().getError().split(":")[1];
+ else if (resp.length == 3)
+ errorMessage = apiResponse.getError().getError().split(":")[2];
+ else
+ errorMessage = getString(R.string.toast_error);
+ } catch (Exception e) {
+ errorMessage = getString(R.string.toast_error);
+ }
+ } else {
+ errorMessage = getString(R.string.toast_error);
+ }
+ binding.errorMessage.setText(errorMessage);
+ binding.errorMessage.setVisibility(View.VISIBLE);
+ binding.signup.setEnabled(true);
+ return;
+ }
+
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(PeertubeRegisterActivity.this);
+ dialogBuilder.setCancelable(false);
+ dialogBuilder.setPositiveButton(R.string.validate, (dialog, which) -> {
+ dialog.dismiss();
+ finish();
+ });
+ AlertDialog alertDialog = dialogBuilder.create();
+ alertDialog.setTitle(getString(R.string.account_created));
+ alertDialog.setMessage(getString(R.string.account_created_message, apiResponse.getStringData()));
+ alertDialog.show();
+ };
+ mainHandler.post(myRunnable);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }).start();
+
+ });
+
+ setTitle(R.string.create_an_account);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @SuppressLint("ApplySharedPref")
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == PICK_INSTANCE && resultCode == Activity.RESULT_OK) {
+ if (data != null && data.getData() != null) {
+ String instance = String.valueOf(data.getData());
+ binding.loginInstance.setText(instance);
+ binding.loginInstance.setSelection(instance.length());
+ setTextAgreement();
+ }
+ }
+ }
+
+ private void setTextAgreement() {
+ TextView agreement_text = findViewById(R.id.agreement_text);
+ String tos = getString(R.string.tos);
+ String serverrules = getString(R.string.server_rules);
+ String content_agreement = null;
+ agreement_text.setMovementMethod(null);
+ agreement_text.setText(null);
+ if (BuildConfig.full_instances) {
+ if (binding.loginInstance.getText() != null) {
+ content_agreement = getString(R.string.agreement_check_peertube,
+ "" + tos + ""
+ );
+ }
+ } else {
+ content_agreement = getString(R.string.agreement_check,
+ "" + serverrules + "",
+ "" + tos + ""
+ );
+ }
+ agreement_text.setMovementMethod(LinkMovementMethod.getInstance());
+ if (content_agreement != null) {
+ agreement_text.setText(Html.fromHtml(content_agreement));
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeUploadActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeUploadActivity.java
new file mode 100644
index 00000000..c658a77c
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeUploadActivity.java
@@ -0,0 +1,418 @@
+package app.fedilab.android.peertube.activities;
+/* Copyright 2020 Thomas Schneider
+ *
+ * This file is a part of TubeLab
+ *
+ * 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.
+ *
+ * TubeLab 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 TubeLab; if not,
+ * see . */
+
+import static app.fedilab.android.peertube.activities.MainActivity.userMe;
+import static app.fedilab.android.peertube.client.RetrofitPeertubeAPI.DataType.MY_CHANNELS;
+import static app.fedilab.android.peertube.helper.Helper.peertubeInformation;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.Color;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Toast;
+
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+import androidx.documentfile.provider.DocumentFile;
+import androidx.lifecycle.ViewModelProvider;
+
+import net.gotev.uploadservice.data.UploadNotificationAction;
+import net.gotev.uploadservice.data.UploadNotificationConfig;
+import net.gotev.uploadservice.data.UploadNotificationStatusConfig;
+import net.gotev.uploadservice.extensions.ContextExtensionsKt;
+import net.gotev.uploadservice.protocols.multipart.MultipartUploadRequest;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import app.fedilab.android.peertube.BaseFedilabTube;
+import app.fedilab.android.peertube.BuildConfig;
+import app.fedilab.android.peertube.R;
+import app.fedilab.android.peertube.client.APIResponse;
+import app.fedilab.android.peertube.client.RetrofitPeertubeAPI;
+import app.fedilab.android.peertube.client.data.ChannelData;
+import app.fedilab.android.peertube.client.entities.UserMe;
+import app.fedilab.android.peertube.databinding.ActivityPeertubeUploadBinding;
+import app.fedilab.android.peertube.helper.Helper;
+import app.fedilab.android.peertube.helper.HelperInstance;
+import app.fedilab.android.peertube.helper.Theme;
+import app.fedilab.android.peertube.viewmodel.ChannelsVM;
+import es.dmoral.toasty.Toasty;
+
+
+public class PeertubeUploadActivity extends BaseActivity {
+
+
+ public static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 724;
+ private final int PICK_IVDEO = 52378;
+ private HashMap channels;
+ private Uri uri;
+ private String filename;
+ private HashMap privacyToSend;
+ private HashMap channelToSend;
+ private ActivityPeertubeUploadBinding binding;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Theme.setTheme(this, HelperInstance.getLiveInstance(this), false);
+ super.onCreate(savedInstanceState);
+ if (getSupportActionBar() != null)
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ binding = ActivityPeertubeUploadBinding.inflate(getLayoutInflater());
+ View view = binding.getRoot();
+ setContentView(view);
+
+
+ new Thread(() -> {
+ UserMe.VideoQuota videoQuotaReply = new RetrofitPeertubeAPI(PeertubeUploadActivity.this).getVideoQuota();
+ runOnUiThread(() -> {
+ if (videoQuotaReply != null) {
+ long videoQuota = videoQuotaReply.getVideoQuotaUsed();
+ long dailyQuota = videoQuotaReply.getVideoQuotaUsedDaily();
+ long instanceVideoQuota = userMe.getVideoQuota();
+ long instanceDailyQuota = userMe.getVideoQuotaDaily();
+
+ if (instanceVideoQuota != -1 && instanceVideoQuota != 0) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ binding.totalQuota.setProgress((int) (videoQuota * 100 / instanceVideoQuota), true);
+ } else {
+ binding.totalQuota.setProgress((int) (videoQuota * 100 / instanceVideoQuota));
+ }
+ } else {
+ int progress = videoQuota > 0 ? 30 : 0;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ binding.totalQuota.setProgress(progress, true);
+ } else {
+ binding.totalQuota.setProgress(progress);
+ }
+ }
+ if (instanceDailyQuota != -1 && instanceDailyQuota != 0) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ binding.dailyQuota.setProgress((int) (dailyQuota * 100 / instanceDailyQuota), true);
+ } else {
+ binding.dailyQuota.setProgress((int) (dailyQuota * 100 / instanceDailyQuota));
+ }
+ } else {
+ int progress = dailyQuota > 0 ? 30 : 0;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ binding.dailyQuota.setProgress(progress, true);
+ } else {
+ binding.dailyQuota.setProgress(progress);
+ }
+ }
+ binding.totalQuotaValue.setText(
+ String.format(Locale.getDefault(), "%s/%s",
+ Helper.returnRoundedSize(PeertubeUploadActivity.this, videoQuota),
+ Helper.returnRoundedSize(PeertubeUploadActivity.this, instanceVideoQuota)));
+ binding.dailyQuotaValue.setText(
+ String.format(Locale.getDefault(), "%s/%s",
+ Helper.returnRoundedSize(PeertubeUploadActivity.this, dailyQuota),
+ Helper.returnRoundedSize(PeertubeUploadActivity.this, instanceDailyQuota)));
+ }
+ });
+ }).start();
+
+
+ ChannelsVM viewModelC = new ViewModelProvider(PeertubeUploadActivity.this).get(ChannelsVM.class);
+ viewModelC.get(MY_CHANNELS, null).observe(PeertubeUploadActivity.this, this::manageVIewChannels);
+ channels = new HashMap<>();
+ setTitle(R.string.upload_video);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == PICK_IVDEO && resultCode == Activity.RESULT_OK) {
+ if (data == null || data.getData() == null) {
+ Toasty.error(PeertubeUploadActivity.this, getString(R.string.toot_select_image_error), Toast.LENGTH_LONG).show();
+ return;
+ }
+ binding.setUploadSubmit.setEnabled(true);
+ uri = data.getData();
+ filename = null;
+ DocumentFile documentFile = DocumentFile.fromSingleUri(this, uri);
+ if (documentFile != null) {
+ filename = documentFile.getName();
+ }
+ if (filename == null) {
+ filename = new Date().toString();
+ }
+ binding.setUploadFileName.setVisibility(View.VISIBLE);
+ binding.setUploadFileName.setText(filename);
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ public void manageVIewChannels(APIResponse apiResponse) {
+ if (apiResponse.getError() != null || apiResponse.getChannels() == null || apiResponse.getChannels().size() == 0) {
+ if (apiResponse.getError() != null && apiResponse.getError().getError() != null)
+ Toasty.error(PeertubeUploadActivity.this, apiResponse.getError().getError(), Toast.LENGTH_LONG).show();
+ else
+ Toasty.error(PeertubeUploadActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
+ return;
+ }
+
+ //Populate channels
+ List channelsForUser = apiResponse.getChannels();
+ String[] channelName = new String[channelsForUser.size()];
+ String[] channelId = new String[channelsForUser.size()];
+ int i = 0;
+ for (ChannelData.Channel channel : channelsForUser) {
+ channels.put(channel.getName(), channel.getId());
+ channelName[i] = channel.getName();
+ channelId[i] = channel.getId();
+ i++;
+ }
+
+ channelToSend = new HashMap<>();
+ channelToSend.put(channelName[0], channelId[0]);
+ ArrayAdapter adapterChannel = new ArrayAdapter<>(PeertubeUploadActivity.this,
+ android.R.layout.simple_spinner_dropdown_item, channelName);
+ binding.setUploadChannel.setAdapter(adapterChannel);
+
+ if (peertubeInformation == null) {
+ return;
+ }
+ LinkedHashMap translations = null;
+ if (peertubeInformation.getTranslations() != null)
+ translations = new LinkedHashMap<>(peertubeInformation.getTranslations());
+
+ LinkedHashMap privaciesInit = new LinkedHashMap<>(peertubeInformation.getPrivacies());
+ Map.Entry entryInt = privaciesInit.entrySet().iterator().next();
+ privacyToSend = new HashMap<>();
+ privacyToSend.put(entryInt.getKey(), entryInt.getValue());
+ LinkedHashMap privacies = new LinkedHashMap<>(peertubeInformation.getPrivacies());
+ //Populate privacies
+ String[] privaciesA = new String[privacies.size()];
+ Iterator> it = privacies.entrySet().iterator();
+ i = 0;
+ while (it.hasNext()) {
+ Map.Entry pair = it.next();
+ if (translations == null || translations.size() == 0 || !translations.containsKey(pair.getValue()))
+ privaciesA[i] = pair.getValue();
+ else
+ privaciesA[i] = translations.get(pair.getValue());
+ it.remove();
+ i++;
+ }
+
+ ArrayAdapter adapterPrivacies = new ArrayAdapter<>(PeertubeUploadActivity.this,
+ android.R.layout.simple_spinner_dropdown_item, privaciesA);
+ binding.setUploadPrivacy.setAdapter(adapterPrivacies);
+
+ //Manage privacies
+ binding.setUploadPrivacy.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ LinkedHashMap privaciesCheck = new LinkedHashMap<>(peertubeInformation.getPrivacies());
+ Iterator> it = privaciesCheck.entrySet().iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ Map.Entry pair = it.next();
+ if (i == position) {
+ privacyToSend = new HashMap<>();
+ privacyToSend.put(pair.getKey(), pair.getValue());
+ break;
+ }
+ it.remove();
+ i++;
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+
+ }
+ });
+ binding.setUploadFile.setEnabled(true);
+
+ binding.setUploadFile.setOnClickListener(v -> {
+ if (ContextCompat.checkSelfPermission(PeertubeUploadActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) !=
+ PackageManager.PERMISSION_GRANTED) {
+ ActivityCompat.requestPermissions(PeertubeUploadActivity.this,
+ new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
+ MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
+ return;
+ }
+ Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setType("*/*");
+ String[] mimetypes = {"video/*"};
+ intent.putExtra(Intent.EXTRA_MIME_TYPES, mimetypes);
+ startActivityForResult(intent, PICK_IVDEO);
+
+ });
+
+ //Manage languages
+ binding.setUploadChannel.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ LinkedHashMap channelsCheck = new LinkedHashMap<>(channels);
+ Iterator> it = channelsCheck.entrySet().iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ Map.Entry pair = it.next();
+ if (i == position) {
+ channelToSend = new HashMap<>();
+ channelToSend.put(pair.getKey(), pair.getValue());
+
+ break;
+ }
+ it.remove();
+ i++;
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+
+ }
+ });
+ binding.setUploadSubmit.setOnClickListener(v -> {
+ if (uri != null) {
+ Map.Entry channelM = channelToSend.entrySet().iterator().next();
+ String idChannel = channelM.getValue();
+ Map.Entry privacyM = privacyToSend.entrySet().iterator().next();
+ Integer idPrivacy = privacyM.getKey();
+ if (binding.videoTitle.getText() != null && binding.videoTitle.getText().toString().trim().length() > 0) {
+ filename = binding.videoTitle.getText().toString().trim();
+ }
+ try {
+ String token = Helper.getToken(PeertubeUploadActivity.this);
+ new MultipartUploadRequest(PeertubeUploadActivity.this, "https://" + HelperInstance.getLiveInstance(PeertubeUploadActivity.this) + "/api/v1/videos/upload")
+ .setMethod("POST")
+ .setBearerAuth(token)
+ .addHeader("User-Agent", getString(R.string.app_name) + "/" + BuildConfig.VERSION_NAME)
+ .addParameter("privacy", String.valueOf(idPrivacy))
+ .addParameter("nsfw", "false")
+ .addParameter("name", filename)
+ .addParameter("commentsEnabled", "true")
+ .addParameter("downloadEnabled", "true")
+ .addParameter("waitTranscoding", "true")
+ .addParameter("channelId", idChannel)
+ .addFileToUpload(uri.toString(), "videofile")
+ .setNotificationConfig((context, uploadId) -> getNotificationConfig(uploadId))
+ .setMaxRetries(2)
+ .startUpload();
+ finish();
+ } catch (Exception exc) {
+ exc.printStackTrace();
+ }
+ }
+ });
+ }
+
+ UploadNotificationConfig getNotificationConfig(String uploadId) {
+ PendingIntent clickIntent = PendingIntent.getActivity(
+ PeertubeUploadActivity.this, 1, new Intent(this, PeertubeEditUploadActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
+
+ final boolean autoClear = false;
+ final boolean clearOnAction = true;
+ final boolean ringToneEnabled = true;
+ final ArrayList noActions = new ArrayList<>(1);
+
+ final UploadNotificationAction cancelAction = new UploadNotificationAction(
+ R.drawable.ic_baseline_cancel_24,
+ getString(R.string.cancel),
+ ContextExtensionsKt.getCancelUploadIntent(this, uploadId)
+ );
+
+
+ final ArrayList progressActions = new ArrayList<>(1);
+ progressActions.add(cancelAction);
+
+ UploadNotificationStatusConfig progress = new UploadNotificationStatusConfig(
+ getString(R.string.app_name),
+ getString(R.string.uploading),
+ R.drawable.ic_baseline_cloud_upload_24,
+ Color.BLUE,
+ null,
+ clickIntent,
+ progressActions,
+ clearOnAction,
+ autoClear
+ );
+
+ UploadNotificationStatusConfig success = new UploadNotificationStatusConfig(
+ getString(R.string.app_name),
+ getString(R.string.upload_video_success),
+ R.drawable.ic_baseline_check_24,
+ Color.GREEN,
+ null,
+ clickIntent,
+ noActions,
+ clearOnAction,
+ autoClear
+ );
+
+
+ UploadNotificationStatusConfig error = new UploadNotificationStatusConfig(
+ getString(R.string.app_name),
+ getString(R.string.toast_error),
+ R.drawable.ic_baseline_error_24,
+ Color.RED,
+ null,
+ clickIntent,
+ noActions,
+ clearOnAction,
+ autoClear
+ );
+
+ UploadNotificationStatusConfig cancelled = new UploadNotificationStatusConfig(
+ getString(R.string.app_name),
+ getString(R.string.toast_cancelled),
+ R.drawable.ic_baseline_cancel_24,
+ Color.YELLOW,
+ null,
+ clickIntent,
+ noActions,
+ clearOnAction
+ );
+
+ return new UploadNotificationConfig(BaseFedilabTube.UPLOAD_CHANNEL_ID, ringToneEnabled, progress, success, error, cancelled);
+ }
+}
diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/PlaylistsActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/PlaylistsActivity.java
new file mode 100644
index 00000000..10d2cb8e
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/peertube/activities/PlaylistsActivity.java
@@ -0,0 +1,101 @@
+package app.fedilab.android.peertube.activities;
+/* Copyright 2020 Thomas Schneider
+ *
+ * This file is a part of TubeLab
+ *
+ * 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.
+ *
+ * TubeLab 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 TubeLab; if not,
+ * see . */
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import androidx.fragment.app.FragmentTransaction;
+
+import app.fedilab.android.peertube.R;
+import app.fedilab.android.peertube.client.data.PlaylistData;
+import app.fedilab.android.peertube.fragment.DisplayVideosFragment;
+import app.fedilab.android.peertube.helper.Helper;
+import app.fedilab.android.peertube.helper.HelperInstance;
+import app.fedilab.android.peertube.helper.PlaylistExportHelper;
+import app.fedilab.android.peertube.helper.Theme;
+import app.fedilab.android.peertube.viewmodel.TimelineVM;
+import es.dmoral.toasty.Toasty;
+
+
+public class PlaylistsActivity extends BaseActivity {
+
+ private final int PICK_IMPORT = 5556;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Theme.setTheme(this, HelperInstance.getLiveInstance(this), false);
+ super.onCreate(savedInstanceState);
+ if (getSupportActionBar() != null)
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+
+ setContentView(R.layout.activity_playlists);
+
+
+ PlaylistData.Playlist playlist;
+ Bundle b = getIntent().getExtras();
+ if (b != null) {
+ playlist = b.getParcelable("playlist");
+ if (playlist == null) {
+ return;
+ }
+ } else {
+ Toasty.error(PlaylistsActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
+ return;
+ }
+ setTitle(playlist.getDisplayName());
+ if (savedInstanceState == null) {
+ DisplayVideosFragment displayVideosFragment = new DisplayVideosFragment();
+ Bundle bundle = new Bundle();
+ bundle.putSerializable(Helper.TIMELINE_TYPE, TimelineVM.TimelineType.VIDEOS_IN_PLAYLIST);
+ bundle.putSerializable("playlistId", playlist.getUuid());
+ displayVideosFragment.setArguments(bundle);
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+ ft.add(R.id.nav_host_fragment, displayVideosFragment).commit();
+ }
+
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode,
+ Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == PICK_IMPORT && resultCode == RESULT_OK) {
+ if (data == null || data.getData() == null) {
+ Toasty.error(PlaylistsActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
+ return;
+ }
+ PlaylistExportHelper.manageIntentUrl(PlaylistsActivity.this, data);
+
+ } else if (requestCode == PICK_IMPORT) {
+ Toasty.error(PlaylistsActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
+ }
+ }
+
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+}
diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/SearchActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/SearchActivity.java
new file mode 100644
index 00000000..293ad483
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/peertube/activities/SearchActivity.java
@@ -0,0 +1,169 @@
+package app.fedilab.android.peertube.activities;
+/* Copyright 2020 Thomas Schneider
+ *
+ * This file is a part of TubeLab
+ *
+ * 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.
+ *
+ * TubeLab 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 TubeLab; if not,
+ * see . */
+
+import android.os.Bundle;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Toast;
+
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentStatePagerAdapter;
+import androidx.viewpager.widget.PagerAdapter;
+import androidx.viewpager.widget.ViewPager;
+
+import com.google.android.material.tabs.TabLayout;
+
+import org.jetbrains.annotations.NotNull;
+
+import app.fedilab.android.peertube.R;
+import app.fedilab.android.peertube.databinding.ActivitySearchResultBinding;
+import app.fedilab.android.peertube.fragment.DisplayChannelsFragment;
+import app.fedilab.android.peertube.fragment.DisplayVideosFragment;
+import app.fedilab.android.peertube.helper.HelperInstance;
+import app.fedilab.android.peertube.helper.Theme;
+import es.dmoral.toasty.Toasty;
+
+
+public class SearchActivity extends BaseActivity {
+
+
+ private String search;
+ private ActivitySearchResultBinding binding;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Theme.setTheme(this, HelperInstance.getLiveInstance(this), false);
+ super.onCreate(savedInstanceState);
+ binding = ActivitySearchResultBinding.inflate(getLayoutInflater());
+ View view = binding.getRoot();
+ setContentView(view);
+
+ Bundle b = getIntent().getExtras();
+ if (b != null) {
+ search = b.getString("search");
+ } else {
+ Toasty.error(SearchActivity.this, getString(R.string.toast_error_search), Toast.LENGTH_LONG).show();
+ }
+
+ if (getSupportActionBar() != null)
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ setTitle(search);
+
+ binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.videos)));
+ binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.channels)));
+ binding.searchPager.setOffscreenPageLimit(2);
+
+ PagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
+ binding.searchPager.setAdapter(mPagerAdapter);
+ binding.searchPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ TabLayout.Tab tab = binding.searchTabLayout.getTabAt(position);
+ if (tab != null)
+ tab.select();
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+
+ }
+ });
+
+ binding.searchTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
+ @Override
+ public void onTabSelected(TabLayout.Tab tab) {
+ binding.searchPager.setCurrentItem(tab.getPosition());
+ }
+
+ @Override
+ public void onTabUnselected(TabLayout.Tab tab) {
+
+ }
+
+ @Override
+ public void onTabReselected(TabLayout.Tab tab) {
+ Fragment fragment = null;
+ if (binding.searchPager.getAdapter() != null)
+ fragment = (Fragment) binding.searchPager.getAdapter().instantiateItem(binding.searchPager, tab.getPosition());
+ switch (tab.getPosition()) {
+ case 0:
+ if (fragment != null) {
+ DisplayVideosFragment displayVideosFragment = ((DisplayVideosFragment) fragment);
+ displayVideosFragment.scrollToTop();
+ }
+ break;
+ case 1:
+ if (fragment != null) {
+ DisplayChannelsFragment displayChannelsFragment = ((DisplayChannelsFragment) fragment);
+ displayChannelsFragment.scrollToTop();
+ }
+ break;
+ }
+ }
+ });
+
+ }
+
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+
+ /**
+ * Pager adapter for the 2 fragments
+ */
+ private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
+
+ ScreenSlidePagerAdapter(FragmentManager fm) {
+ super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
+ }
+
+ @NotNull
+ @Override
+ public Fragment getItem(int position) {
+ Bundle bundle = new Bundle();
+ if (position == 0) {
+ DisplayVideosFragment displayVideosFragment = new DisplayVideosFragment();
+ bundle.putString("search_peertube", search);
+ displayVideosFragment.setArguments(bundle);
+ return displayVideosFragment;
+ }
+ DisplayChannelsFragment displayChannelsFragment = new DisplayChannelsFragment();
+ bundle.putString("search_peertube", search);
+ displayChannelsFragment.setArguments(bundle);
+ return displayChannelsFragment;
+ }
+
+
+ @Override
+ public int getCount() {
+ return 2;
+ }
+ }
+
+}
diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/SepiaSearchActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/SepiaSearchActivity.java
new file mode 100644
index 00000000..0a1be8b1
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/peertube/activities/SepiaSearchActivity.java
@@ -0,0 +1,388 @@
+package app.fedilab.android.peertube.activities;
+/* Copyright 2020 Thomas Schneider
+ *
+ * This file is a part of TubeLab
+ *
+ * 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.
+ *
+ * TubeLab 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 TubeLab; if not,
+ * see . */
+
+import static app.fedilab.android.peertube.activities.PeertubeActivity.hideKeyboard;
+import static app.fedilab.android.peertube.helper.Helper.peertubeInformation;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentTransaction;
+
+import com.mancj.materialsearchbar.MaterialSearchBar;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import app.fedilab.android.peertube.R;
+import app.fedilab.android.peertube.client.entities.SepiaSearch;
+import app.fedilab.android.peertube.databinding.ActivitySepiaSearchBinding;
+import app.fedilab.android.peertube.fragment.DisplaySepiaSearchFragment;
+import app.fedilab.android.peertube.helper.Helper;
+import app.fedilab.android.peertube.helper.HelperInstance;
+import app.fedilab.android.peertube.helper.Theme;
+
+
+public class SepiaSearchActivity extends BaseActivity {
+
+
+ private SepiaSearch sepiaSearchVideo, sepiaSearchChannel;
+
+ private ActivitySepiaSearchBinding binding;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Theme.setTheme(this, HelperInstance.getLiveInstance(this), false);
+ super.onCreate(savedInstanceState);
+ binding = ActivitySepiaSearchBinding.inflate(getLayoutInflater());
+ View rootView = binding.getRoot();
+ setContentView(rootView);
+
+
+ sepiaSearchVideo = new SepiaSearch();
+ sepiaSearchChannel = new SepiaSearch();
+ SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
+ sepiaSearchVideo.setCount(String.valueOf(sharedpreferences.getInt(Helper.SET_VIDEOS_PER_PAGE, Helper.VIDEOS_PER_PAGE)));
+ sepiaSearchVideo.setDurationMin(0);
+ sepiaSearchVideo.setDurationMax(9999999);
+ sepiaSearchVideo.setStart("0");
+ sepiaSearchVideo.setSort("-match");
+ if (getSupportActionBar() != null)
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+
+ binding.filter.setOnClickListener(view -> {
+ if (binding.filterElements.getVisibility() == View.VISIBLE) {
+ binding.filterElements.setVisibility(View.GONE);
+ } else {
+ binding.filterElements.setVisibility(View.VISIBLE);
+ }
+ });
+
+
+ binding.sepiaElementNsfw.setOnCheckedChangeListener((group, checkedId) -> sepiaSearchVideo.setNsfw(checkedId != R.id.sepia_element_nsfw_no));
+
+ binding.radioDate.setOnCheckedChangeListener((group, checkedId) -> {
+ if (checkedId == R.id.sepia_element_published_date_today) {
+ Calendar cal = GregorianCalendar.getInstance();
+ cal.set(Calendar.HOUR_OF_DAY, 0);
+ cal.set(Calendar.MINUTE, 0);
+ cal.set(Calendar.SECOND, 0);
+ cal.set(Calendar.MILLISECOND, 0);
+ sepiaSearchVideo.setStartDate(cal.getTime());
+ } else if (checkedId == R.id.sepia_element_published_date_last_7_days) {
+ Calendar cal;
+ cal = GregorianCalendar.getInstance();
+ cal.setTime(new Date());
+ cal.add(Calendar.DAY_OF_YEAR, -7);
+ sepiaSearchVideo.setStartDate(cal.getTime());
+ } else if (checkedId == R.id.sepia_element_published_date_last_30_days) {
+ Calendar cal;
+ cal = GregorianCalendar.getInstance();
+ cal.setTime(new Date());
+ cal.add(Calendar.DAY_OF_YEAR, -30);
+ sepiaSearchVideo.setStartDate(cal.getTime());
+ } else if (checkedId == R.id.sepia_element_published_date_last_365_days) {
+ Calendar cal;
+ cal = GregorianCalendar.getInstance();
+ cal.setTime(new Date());
+ cal.add(Calendar.DAY_OF_YEAR, -365);
+ sepiaSearchVideo.setStartDate(cal.getTime());
+ } else {
+ sepiaSearchVideo.setStartDate(null);
+ }
+ });
+
+
+ binding.duration.setOnCheckedChangeListener((group, checkedId) -> {
+ if (checkedId == R.id.sepia_element_duration_short) {
+ sepiaSearchVideo.setDurationMin(0);
+ sepiaSearchVideo.setDurationMax(240);
+ } else if (checkedId == R.id.sepia_element_duration_medium) {
+ sepiaSearchVideo.setDurationMin(240);
+ sepiaSearchVideo.setDurationMax(600);
+ } else if (checkedId == R.id.sepia_element_duration_long) {
+ sepiaSearchVideo.setDurationMin(600);
+ sepiaSearchVideo.setDurationMax(999999999);
+ } else {
+ sepiaSearchVideo.setDurationMin(0);
+ sepiaSearchVideo.setDurationMax(999999999);
+ }
+ });
+
+
+ ArrayAdapter adapterSortBy = new ArrayAdapter<>(SepiaSearchActivity.this,
+ android.R.layout.simple_spinner_dropdown_item, getResources().getStringArray(R.array.sort_by_array));
+ binding.sortBy.setAdapter(adapterSortBy);
+ binding.sortBy.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ String orderby, channelOrderBy;
+ switch (position) {
+ case 1:
+ orderby = "-publishedAt";
+ channelOrderBy = "-createdAt";
+ break;
+ case 2:
+ orderby = "publishedAt";
+ channelOrderBy = "createdAt";
+ break;
+ default:
+ orderby = "-match";
+ channelOrderBy = null;
+ }
+ sepiaSearchVideo.setSort(orderby);
+ sepiaSearchChannel.setSort(channelOrderBy);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+
+ }
+ });
+
+
+ LinkedHashMap categories = new LinkedHashMap<>(peertubeInformation.getCategories());
+ LinkedHashMap licences = new LinkedHashMap<>(peertubeInformation.getLicences());
+ LinkedHashMap languages = new LinkedHashMap<>(peertubeInformation.getLanguages());
+ LinkedHashMap translations = null;
+
+ if (peertubeInformation.getTranslations() != null) {
+ translations = new LinkedHashMap<>(peertubeInformation.getTranslations());
+ }
+
+ //Populate catgories
+ String[] categoriesA = new String[categories.size() + 1];
+ categoriesA[0] = getString(R.string.display_all_categories);
+ Iterator> it = categories.entrySet().iterator();
+ int i = 1;
+ while (it.hasNext()) {
+ Map.Entry pair = it.next();
+ if (translations == null || translations.size() == 0 || !translations.containsKey(pair.getValue()))
+ categoriesA[i] = pair.getValue();
+ else
+ categoriesA[i] = translations.get(pair.getValue());
+ it.remove();
+ i++;
+ }
+ ArrayAdapter adapterCatgories = new ArrayAdapter<>(SepiaSearchActivity.this,
+ android.R.layout.simple_spinner_dropdown_item, categoriesA);
+ binding.sepiaElementCategory.setAdapter(adapterCatgories);
+
+
+ //Populate licenses
+ String[] licensesA = new String[licences.size() + 1];
+ licensesA[0] = getString(R.string.display_all_licenses);
+ it = licences.entrySet().iterator();
+ i = 1;
+ while (it.hasNext()) {
+ Map.Entry pair = it.next();
+ if (translations == null || translations.size() == 0 || !translations.containsKey(pair.getValue()))
+ licensesA[i] = pair.getValue();
+ else
+ licensesA[i] = translations.get(pair.getValue());
+ it.remove();
+ i++;
+ }
+ ArrayAdapter adapterLicenses = new ArrayAdapter<>(SepiaSearchActivity.this,
+ android.R.layout.simple_spinner_dropdown_item, licensesA);
+ binding.sepiaElementLicense.setAdapter(adapterLicenses);
+
+ //Populate languages
+ String[] languagesA = new String[languages.size() + 1];
+ languagesA[0] = getString(R.string.display_all_languages);
+ Iterator> itl = languages.entrySet().iterator();
+ i = 1;
+ while (itl.hasNext()) {
+ Map.Entry pair = itl.next();
+ if (translations == null || translations.size() == 0 || !translations.containsKey(pair.getValue()))
+ languagesA[i] = pair.getValue();
+ else
+ languagesA[i] = translations.get(pair.getValue());
+ itl.remove();
+ i++;
+ }
+ ArrayAdapter adapterLanguages = new ArrayAdapter<>(SepiaSearchActivity.this,
+ android.R.layout.simple_spinner_dropdown_item, languagesA);
+ binding.sepiaElementLanguage.setAdapter(adapterLanguages);
+
+
+ binding.sepiaElementLicense.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ updateLicensePosition(position);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+
+ }
+ });
+ //Manage categories
+ binding.sepiaElementCategory.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ updateCategoryPosition(position);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+
+ }
+ });
+
+ //Manage languages
+ binding.sepiaElementLanguage.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ updateLanguagesPosition(position);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+
+ }
+ });
+
+
+ binding.searchBar.setOnSearchActionListener(new MaterialSearchBar.OnSearchActionListener() {
+ @Override
+ public void onSearchStateChanged(boolean enabled) {
+
+ }
+
+ @Override
+ public void onSearchConfirmed(CharSequence text) {
+ makeSearch();
+ }
+
+ @Override
+ public void onButtonClicked(int buttonCode) {
+ makeSearch();
+ }
+ });
+ binding.applyFilter.setOnClickListener(v -> makeSearch());
+
+ binding.searchBar.openSearch();
+ }
+
+ private void makeSearch() {
+ hideKeyboard(SepiaSearchActivity.this);
+ sepiaSearchVideo.setStart("0");
+ if (binding.sepiaElementOneOfTags.getTags().size() > 0) {
+ sepiaSearchVideo.setTagsOneOf(binding.sepiaElementOneOfTags.getTags());
+ } else {
+ sepiaSearchVideo.setTagsOneOf(null);
+ }
+ if (binding.sepiaElementAllOfTags.getTags().size() > 0) {
+ sepiaSearchVideo.setTagsAllOf(binding.sepiaElementAllOfTags.getTags());
+ } else {
+ sepiaSearchVideo.setTagsAllOf(null);
+ }
+
+ Fragment fragment = getSupportFragmentManager().findFragmentByTag("SEPIA_SEARCH");
+ if (fragment != null)
+ getSupportFragmentManager().beginTransaction().remove(fragment).commit();
+ binding.filterElements.setVisibility(View.GONE);
+ sepiaSearchVideo.setSearch(binding.searchBar.getText());
+ DisplaySepiaSearchFragment displaySepiaSearchFragment = new DisplaySepiaSearchFragment();
+ Bundle bundle = new Bundle();
+ bundle.putParcelable("sepiaSearchVideo", sepiaSearchVideo);
+ displaySepiaSearchFragment.setArguments(bundle);
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+ ft.add(R.id.container, displaySepiaSearchFragment, "SEPIA_SEARCH").commit();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void updateLanguagesPosition(int position) {
+ LinkedHashMap languagesCheck = new LinkedHashMap<>(peertubeInformation.getLanguages());
+ Iterator> it = languagesCheck.entrySet().iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ Map.Entry pair = it.next();
+ if (i == position && position > 0) {
+ List languages = new ArrayList<>();
+ languages.add(pair.getKey());
+ sepiaSearchVideo.setBoostLanguages(languages);
+ break;
+ } else {
+ sepiaSearchVideo.setBoostLanguages(null);
+ }
+ it.remove();
+ i++;
+ }
+ }
+
+ private void updateCategoryPosition(int position) {
+ LinkedHashMap categoriesCheck = new LinkedHashMap<>(peertubeInformation.getCategories());
+ Iterator> it = categoriesCheck.entrySet().iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ Map.Entry pair = it.next();
+ if (i == position && position > 0) {
+ List categories = new ArrayList<>();
+ categories.add(pair.getKey());
+ sepiaSearchVideo.setCategoryOneOf(categories);
+ break;
+ } else {
+ sepiaSearchVideo.setCategoryOneOf(null);
+ }
+ it.remove();
+ i++;
+ }
+ }
+
+ private void updateLicensePosition(int position) {
+ LinkedHashMap licensesCheck = new LinkedHashMap<>(peertubeInformation.getLicences());
+ Iterator> it = licensesCheck.entrySet().iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ Map.Entry pair = it.next();
+ if (i == position && position > 0) {
+ List licenses = new ArrayList<>();
+ licenses.add(pair.getKey());
+ sepiaSearchVideo.setLicenceOneOf(licenses);
+ break;
+ } else {
+ sepiaSearchVideo.setLicenceOneOf(null);
+ }
+ it.remove();
+ i++;
+ }
+ }
+
+}
diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/SettingsActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/SettingsActivity.java
new file mode 100644
index 00000000..7e0f7845
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/peertube/activities/SettingsActivity.java
@@ -0,0 +1,45 @@
+package app.fedilab.android.peertube.activities;
+
+import android.os.Bundle;
+import android.view.MenuItem;
+
+import app.fedilab.android.peertube.fragment.SettingsFragment;
+import app.fedilab.android.peertube.helper.HelperInstance;
+import app.fedilab.android.peertube.helper.Theme;
+
+/* Copyright 2020 Thomas Schneider
+ *
+ * This file is a part of TubeLab
+ *
+ * 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.
+ *
+ * TubeLab 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 TubeLab; if not,
+ * see . */
+
+public class SettingsActivity extends BaseActivity {
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Theme.setTheme(this, HelperInstance.getLiveInstance(this), false);
+ super.onCreate(savedInstanceState);
+ if (getSupportActionBar() != null)
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportFragmentManager().beginTransaction().replace(android.R.id.content, new SettingsFragment()).commit();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/ShowAccountActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/ShowAccountActivity.java
new file mode 100644
index 00000000..6542e329
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/peertube/activities/ShowAccountActivity.java
@@ -0,0 +1,313 @@
+package app.fedilab.android.peertube.activities;
+/* Copyright 2020 Thomas Schneider
+ *
+ * This file is a part of TubeLab
+ *
+ * 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.
+ *
+ * TubeLab 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 TubeLab; if not,
+ * see . */
+
+import static androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY;
+import static app.fedilab.android.peertube.client.RetrofitPeertubeAPI.ActionType.MUTE;
+import static app.fedilab.android.peertube.client.RetrofitPeertubeAPI.ActionType.REPORT_ACCOUNT;
+
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.text.Html;
+import android.text.SpannableString;
+import android.text.method.LinkMovementMethod;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentStatePagerAdapter;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.viewpager.widget.PagerAdapter;
+import androidx.viewpager.widget.ViewPager;
+
+import com.google.android.material.tabs.TabLayout;
+
+import org.jetbrains.annotations.NotNull;
+
+import app.fedilab.android.peertube.R;
+import app.fedilab.android.peertube.client.APIResponse;
+import app.fedilab.android.peertube.client.RetrofitPeertubeAPI;
+import app.fedilab.android.peertube.client.data.AccountData;
+import app.fedilab.android.peertube.fragment.DisplayChannelsFragment;
+import app.fedilab.android.peertube.fragment.DisplayVideosFragment;
+import app.fedilab.android.peertube.helper.Helper;
+import app.fedilab.android.peertube.helper.HelperInstance;
+import app.fedilab.android.peertube.helper.Theme;
+import app.fedilab.android.peertube.viewmodel.AccountsVM;
+import app.fedilab.android.peertube.viewmodel.PostActionsVM;
+import app.fedilab.android.peertube.viewmodel.TimelineVM;
+import es.dmoral.toasty.Toasty;
+
+
+public class ShowAccountActivity extends BaseActivity {
+
+
+ private ViewPager mPager;
+ private TabLayout tabLayout;
+ private TextView account_note, subscriber_count;
+ private ImageView account_pp;
+ private TextView account_dn;
+ private AccountData.Account account;
+ private String accountAcct;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Theme.setTheme(this, HelperInstance.getLiveInstance(this), false);
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_show_account);
+ setTitle("");
+ Bundle b = getIntent().getExtras();
+ subscriber_count = findViewById(R.id.subscriber_count);
+ account_pp = findViewById(R.id.account_pp);
+ account_dn = findViewById(R.id.account_dn);
+ account_pp.setBackgroundResource(R.drawable.account_pp_border);
+ if (b != null) {
+ account = b.getParcelable("account");
+ accountAcct = b.getString("accountAcct");
+ } else {
+ Toasty.error(ShowAccountActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show();
+ }
+
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+
+ tabLayout = findViewById(R.id.account_tabLayout);
+ account_note = findViewById(R.id.account_note);
+
+ manageAccount();
+ AccountsVM viewModel = new ViewModelProvider(ShowAccountActivity.this).get(AccountsVM.class);
+ viewModel.getAccount(accountAcct == null ? account.getUsername() + "@" + account.getHost() : accountAcct).observe(ShowAccountActivity.this, this::manageViewAccounts);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(@NotNull Menu menu) {
+ getMenuInflater().inflate(R.menu.main_account, menu);
+ if (!Helper.isLoggedIn(ShowAccountActivity.this)) {
+ menu.findItem(R.id.action_mute).setVisible(false);
+ }
+ menu.findItem(R.id.action_display_account).setVisible(false);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ finish();
+ return true;
+ } else if (item.getItemId() == R.id.action_mute) {
+ PostActionsVM viewModel = new ViewModelProvider(ShowAccountActivity.this).get(PostActionsVM.class);
+ viewModel.post(MUTE, accountAcct == null ? account.getUsername() + "@" + account.getHost() : accountAcct, null).observe(ShowAccountActivity.this, apiResponse -> manageVIewPostActions(MUTE, apiResponse));
+ } else if (item.getItemId() == R.id.action_report) {
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(ShowAccountActivity.this);
+ LayoutInflater inflater1 = getLayoutInflater();
+ View dialogView = inflater1.inflate(R.layout.popup_report, new LinearLayout(ShowAccountActivity.this), false);
+ dialogBuilder.setView(dialogView);
+ EditText report_content = dialogView.findViewById(R.id.report_content);
+ dialogBuilder.setNeutralButton(R.string.cancel, (dialog, id) -> dialog.dismiss());
+ dialogBuilder.setPositiveButton(R.string.report, (dialog, id) -> {
+ if (report_content.getText().toString().trim().length() == 0) {
+ Toasty.info(ShowAccountActivity.this, getString(R.string.report_comment_size), Toasty.LENGTH_LONG).show();
+ } else {
+ PostActionsVM viewModel = new ViewModelProvider(ShowAccountActivity.this).get(PostActionsVM.class);
+ viewModel.post(REPORT_ACCOUNT, account.getId(), report_content.getText().toString()).observe(ShowAccountActivity.this, apiResponse -> manageVIewPostActions(REPORT_ACCOUNT, apiResponse));
+ dialog.dismiss();
+ }
+ });
+ AlertDialog alertDialog = dialogBuilder.create();
+ alertDialog.show();
+ } else if (item.getItemId() == R.id.action_share && account != null) {
+ Intent sendIntent = new Intent(Intent.ACTION_SEND);
+ sendIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.shared_via));
+ String extra_text = account.getUrl();
+ sendIntent.putExtra(Intent.EXTRA_TEXT, extra_text);
+ sendIntent.setType("text/plain");
+ try {
+ startActivity(Intent.createChooser(sendIntent, getString(R.string.share_with)));
+ } catch (Exception e) {
+ Toasty.error(ShowAccountActivity.this, getString(R.string.toast_error), Toasty.LENGTH_LONG).show();
+ }
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void manageAccount() {
+
+
+ setTitle(account.getAcct());
+
+ mPager = findViewById(R.id.account_viewpager);
+ tabLayout.addTab(tabLayout.newTab().setText(getString(R.string.channels)));
+ tabLayout.addTab(tabLayout.newTab().setText(getString(R.string.videos)));
+ mPager.setOffscreenPageLimit(2);
+
+ PagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
+ mPager.setAdapter(mPagerAdapter);
+ mPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ TabLayout.Tab tab = tabLayout.getTabAt(position);
+ if (tab != null)
+ tab.select();
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+
+ }
+ });
+
+ tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
+ @Override
+ public void onTabSelected(TabLayout.Tab tab) {
+ mPager.setCurrentItem(tab.getPosition());
+ }
+
+ @Override
+ public void onTabUnselected(TabLayout.Tab tab) {
+
+ }
+
+ @Override
+ public void onTabReselected(TabLayout.Tab tab) {
+ Fragment fragment = null;
+ if (mPager.getAdapter() != null)
+ fragment = (Fragment) mPager.getAdapter().instantiateItem(mPager, tab.getPosition());
+ switch (tab.getPosition()) {
+ case 0:
+ if (fragment != null) {
+ DisplayChannelsFragment displayChannelsFragment = ((DisplayChannelsFragment) fragment);
+ displayChannelsFragment.scrollToTop();
+ }
+ break;
+ case 1:
+ if (fragment != null) {
+ DisplayVideosFragment displayVideosFragment = ((DisplayVideosFragment) fragment);
+ displayVideosFragment.scrollToTop();
+ }
+ break;
+ }
+ }
+ });
+
+ account_dn.setText(account.getDisplayName());
+
+ manageNotes(account);
+ Helper.loadAvatar(ShowAccountActivity.this, account, account_pp);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ }
+
+
+ public void manageVIewPostActions(RetrofitPeertubeAPI.ActionType statusAction, APIResponse apiResponse) {
+
+ if (apiResponse.getError() != null) {
+ Toasty.error(ShowAccountActivity.this, apiResponse.getError().getError(), Toast.LENGTH_LONG).show();
+ return;
+ }
+ if (statusAction == RetrofitPeertubeAPI.ActionType.MUTE) {
+ Toasty.info(ShowAccountActivity.this, getString(R.string.muted_done), Toast.LENGTH_LONG).show();
+ }
+ }
+
+ public void manageViewAccounts(APIResponse apiResponse) {
+ if (apiResponse.getAccounts() != null && apiResponse.getAccounts().size() == 1) {
+ AccountData.Account account = apiResponse.getAccounts().get(0);
+ if (this.account == null) {
+ this.account = account;
+ manageAccount();
+ }
+ subscriber_count.setText(getString(R.string.followers_count, Helper.withSuffix(account.getFollowersCount())));
+ subscriber_count.setVisibility(View.VISIBLE);
+ manageNotes(account);
+ }
+ }
+
+ private void manageNotes(AccountData.Account account) {
+ if (account.getDescription() != null && account.getDescription().compareTo("null") != 0 && account.getDescription().trim().length() > 0) {
+ SpannableString spannableString;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
+ spannableString = new SpannableString(Html.fromHtml(account.getDescription(), FROM_HTML_MODE_LEGACY));
+ else
+ spannableString = new SpannableString(Html.fromHtml(account.getDescription()));
+
+ account_note.setText(spannableString, TextView.BufferType.SPANNABLE);
+ account_note.setMovementMethod(LinkMovementMethod.getInstance());
+ account_note.setVisibility(View.VISIBLE);
+ } else {
+ account_note.setVisibility(View.GONE);
+ }
+ }
+
+
+ /**
+ * Pager adapter for the 2 fragments
+ */
+ private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
+
+ ScreenSlidePagerAdapter(FragmentManager fm) {
+ super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
+ }
+
+ @NotNull
+ @Override
+ public Fragment getItem(int position) {
+ Bundle bundle = new Bundle();
+ if (position == 0) {
+ DisplayChannelsFragment displayChannelsFragment = new DisplayChannelsFragment();
+ bundle.putString("name", account.getAcct());
+ displayChannelsFragment.setArguments(bundle);
+ return displayChannelsFragment;
+ }
+ DisplayVideosFragment displayVideosFragment = new DisplayVideosFragment();
+ bundle.putSerializable(Helper.TIMELINE_TYPE, TimelineVM.TimelineType.ACCOUNT_VIDEOS);
+ bundle.putParcelable("account", account);
+ bundle.putString("peertube_instance", account.getHost());
+ displayVideosFragment.setArguments(bundle);
+ return displayVideosFragment;
+ }
+
+
+ @Override
+ public int getCount() {
+ return 2;
+ }
+ }
+
+}
diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/ShowChannelActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/ShowChannelActivity.java
new file mode 100644
index 00000000..b743eb04
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/peertube/activities/ShowChannelActivity.java
@@ -0,0 +1,469 @@
+package app.fedilab.android.peertube.activities;
+/* Copyright 2020 Thomas Schneider
+ *
+ * This file is a part of TubeLab
+ *
+ * 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.
+ *
+ * TubeLab 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 TubeLab; if not,
+ * see . */
+
+import static androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY;
+import static app.fedilab.android.peertube.activities.MainActivity.TypeOfConnection.SURFING;
+import static app.fedilab.android.peertube.client.RetrofitPeertubeAPI.ActionType.FOLLOW;
+import static app.fedilab.android.peertube.client.RetrofitPeertubeAPI.ActionType.MUTE;
+import static app.fedilab.android.peertube.client.RetrofitPeertubeAPI.ActionType.REPORT_ACCOUNT;
+import static app.fedilab.android.peertube.client.RetrofitPeertubeAPI.ActionType.UNFOLLOW;
+import static app.fedilab.android.peertube.client.RetrofitPeertubeAPI.DataType.CHANNEL;
+import static app.fedilab.android.peertube.helper.Helper.isLoggedIn;
+
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.res.ColorStateList;
+import android.database.sqlite.SQLiteDatabase;
+import android.os.Build;
+import android.os.Bundle;
+import android.text.Html;
+import android.text.SpannableString;
+import android.text.method.LinkMovementMethod;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.core.content.ContextCompat;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentStatePagerAdapter;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.viewpager.widget.PagerAdapter;
+import androidx.viewpager.widget.ViewPager;
+
+import com.google.android.material.tabs.TabLayout;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import app.fedilab.android.peertube.R;
+import app.fedilab.android.peertube.client.APIResponse;
+import app.fedilab.android.peertube.client.RetrofitPeertubeAPI;
+import app.fedilab.android.peertube.client.data.AccountData;
+import app.fedilab.android.peertube.client.data.ChannelData.Channel;
+import app.fedilab.android.peertube.databinding.ActivityShowChannelBinding;
+import app.fedilab.android.peertube.drawer.OwnAccountsAdapter;
+import app.fedilab.android.peertube.fragment.DisplayAccountsFragment;
+import app.fedilab.android.peertube.fragment.DisplayVideosFragment;
+import app.fedilab.android.peertube.helper.Helper;
+import app.fedilab.android.peertube.helper.HelperInstance;
+import app.fedilab.android.peertube.helper.Theme;
+import app.fedilab.android.peertube.sqlite.AccountDAO;
+import app.fedilab.android.peertube.sqlite.Sqlite;
+import app.fedilab.android.peertube.viewmodel.ChannelsVM;
+import app.fedilab.android.peertube.viewmodel.PostActionsVM;
+import app.fedilab.android.peertube.viewmodel.RelationshipVM;
+import app.fedilab.android.peertube.viewmodel.TimelineVM;
+import es.dmoral.toasty.Toasty;
+
+
+public class ShowChannelActivity extends BaseActivity {
+
+
+ private Map relationship;
+ private Channel channel;
+ private action doAction;
+ private String channelAcct;
+ private boolean sepiaSearch;
+ private String peertubeInstance;
+ private ActivityShowChannelBinding binding;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Theme.setTheme(this, HelperInstance.getLiveInstance(this), false);
+ super.onCreate(savedInstanceState);
+ binding = ActivityShowChannelBinding.inflate(getLayoutInflater());
+ setContentView(binding.getRoot());
+ setTitle("");
+ Bundle b = getIntent().getExtras();
+ binding.accountFollow.setEnabled(false);
+ binding.accountPp.setBackgroundResource(R.drawable.account_pp_border);
+ if (b != null) {
+ channel = b.getParcelable("channel");
+ channelAcct = b.getString("channelId");
+ sepiaSearch = b.getBoolean("sepia_search", false);
+ peertubeInstance = b.getString("peertube_instance", null);
+
+ } else {
+ Toasty.error(ShowChannelActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show();
+ }
+
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+
+ ChannelsVM viewModel = new ViewModelProvider(ShowChannelActivity.this).get(ChannelsVM.class);
+ viewModel.get(sepiaSearch ? peertubeInstance : null, CHANNEL, channelAcct == null ? channel.getAcct() : channelAcct).observe(ShowChannelActivity.this, this::manageViewAccounts);
+ manageChannel();
+
+ if (MainActivity.typeOfConnection == MainActivity.TypeOfConnection.SURFING) {
+ binding.accountFollow.setText(getString(R.string.action_follow));
+ binding.accountFollow.setEnabled(true);
+ new Thread(() -> {
+ try {
+ SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
+ List accounts = new AccountDAO(ShowChannelActivity.this, db).getAllAccount();
+ runOnUiThread(() -> {
+ binding.accountFollow.setVisibility(View.VISIBLE);
+ binding.accountFollow.setOnClickListener(v -> {
+ AlertDialog.Builder builderSingle = new AlertDialog.Builder(ShowChannelActivity.this);
+ builderSingle.setTitle(getString(R.string.list_of_accounts));
+ if (accounts != null && accounts.size() > 0) {
+ if (accounts.size() > 1) {
+ final OwnAccountsAdapter accountsListAdapter = new OwnAccountsAdapter(ShowChannelActivity.this, accounts);
+ builderSingle.setAdapter(accountsListAdapter, (dialog, which) -> new Thread(() -> {
+ try {
+ RetrofitPeertubeAPI peertubeAPI = new RetrofitPeertubeAPI(ShowChannelActivity.this, accounts.get(which).getHost(), accounts.get(which).getToken());
+ peertubeAPI.post(FOLLOW, channel.getAcct(), null);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }).start());
+ } else {
+ RetrofitPeertubeAPI peertubeAPI = new RetrofitPeertubeAPI(ShowChannelActivity.this, accounts.get(0).getHost(), accounts.get(0).getToken());
+ peertubeAPI.post(FOLLOW, channel.getAcct(), null);
+ }
+ }
+ builderSingle.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
+ builderSingle.show();
+ });
+ });
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }).start();
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(@NotNull Menu menu) {
+ getMenuInflater().inflate(R.menu.main_account, menu);
+ if (!Helper.isLoggedIn(ShowChannelActivity.this) || sepiaSearch) {
+ menu.findItem(R.id.action_mute).setVisible(false);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ finish();
+ return true;
+ } else if (item.getItemId() == R.id.action_mute) {
+ PostActionsVM viewModel = new ViewModelProvider(ShowChannelActivity.this).get(PostActionsVM.class);
+ viewModel.post(MUTE, channel.getOwnerAccount().getAcct(), null).observe(ShowChannelActivity.this, apiResponse -> manageVIewPostActions(MUTE, apiResponse));
+ } else if (item.getItemId() == R.id.action_report) {
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(ShowChannelActivity.this);
+ LayoutInflater inflater1 = getLayoutInflater();
+ View dialogView = inflater1.inflate(R.layout.popup_report, new LinearLayout(ShowChannelActivity.this), false);
+ dialogBuilder.setView(dialogView);
+ EditText report_content = dialogView.findViewById(R.id.report_content);
+ dialogBuilder.setNeutralButton(R.string.cancel, (dialog, id) -> dialog.dismiss());
+ dialogBuilder.setPositiveButton(R.string.report, (dialog, id) -> {
+ if (report_content.getText().toString().trim().length() == 0) {
+ Toasty.info(ShowChannelActivity.this, getString(R.string.report_comment_size), Toasty.LENGTH_LONG).show();
+ } else {
+ PostActionsVM viewModel = new ViewModelProvider(ShowChannelActivity.this).get(PostActionsVM.class);
+ viewModel.post(REPORT_ACCOUNT, channel.getId(), report_content.getText().toString()).observe(ShowChannelActivity.this, apiResponse -> manageVIewPostActions(REPORT_ACCOUNT, apiResponse));
+ dialog.dismiss();
+ }
+ });
+ AlertDialog alertDialog = dialogBuilder.create();
+ alertDialog.show();
+ } else if (item.getItemId() == R.id.action_share && channel != null) {
+ Intent sendIntent = new Intent(Intent.ACTION_SEND);
+ sendIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.shared_via));
+ String extra_text = channel.getUrl();
+ sendIntent.putExtra(Intent.EXTRA_TEXT, extra_text);
+ sendIntent.setType("text/plain");
+ try {
+ startActivity(Intent.createChooser(sendIntent, getString(R.string.share_with)));
+ } catch (Exception e) {
+ Toasty.error(ShowChannelActivity.this, getString(R.string.toast_error), Toasty.LENGTH_LONG).show();
+ }
+ } else if (item.getItemId() == R.id.action_display_account) {
+ Bundle b = new Bundle();
+ Intent intent = new Intent(ShowChannelActivity.this, ShowAccountActivity.class);
+ b.putParcelable("account", channel.getOwnerAccount());
+ b.putString("accountAcct", channel.getOwnerAccount().getAcct());
+ intent.putExtras(b);
+ startActivity(intent);
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void manageChannel() {
+ SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
+
+ String accountIdRelation = channel.getAcct();
+ if (isLoggedIn(ShowChannelActivity.this)) {
+ RelationshipVM viewModel = new ViewModelProvider(ShowChannelActivity.this).get(RelationshipVM.class);
+ List uids = new ArrayList<>();
+ uids.add(accountIdRelation);
+ viewModel.get(uids).observe(ShowChannelActivity.this, this::manageVIewRelationship);
+ }
+
+ setTitle(channel.getAcct());
+
+ binding.accountTabLayout.addTab(binding.accountTabLayout.newTab().setText(getString(R.string.videos)));
+ binding.accountViewpager.setOffscreenPageLimit(1);
+
+
+ PagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
+ binding.accountViewpager.setAdapter(mPagerAdapter);
+ ViewGroup.LayoutParams params = binding.accountTabLayout.getLayoutParams();
+ params.height = 0;
+ binding.accountTabLayout.setLayoutParams(params);
+ binding.accountViewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ TabLayout.Tab tab = binding.accountTabLayout.getTabAt(position);
+ if (tab != null)
+ tab.select();
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+
+ }
+ });
+
+ binding.accountTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
+ @Override
+ public void onTabSelected(TabLayout.Tab tab) {
+ binding.accountViewpager.setCurrentItem(tab.getPosition());
+ }
+
+ @Override
+ public void onTabUnselected(TabLayout.Tab tab) {
+
+ }
+
+ @Override
+ public void onTabReselected(TabLayout.Tab tab) {
+ Fragment fragment = null;
+ if (binding.accountViewpager.getAdapter() != null)
+ fragment = (Fragment) binding.accountViewpager.getAdapter().instantiateItem(binding.accountViewpager, tab.getPosition());
+ switch (tab.getPosition()) {
+ case 0:
+ if (fragment != null) {
+ DisplayVideosFragment displayVideosFragment = ((DisplayVideosFragment) fragment);
+ displayVideosFragment.scrollToTop();
+ }
+ break;
+ case 1:
+ if (fragment != null) {
+ DisplayAccountsFragment displayAccountsFragment = ((DisplayAccountsFragment) fragment);
+ displayAccountsFragment.scrollToTop();
+ }
+ break;
+ }
+ }
+ });
+
+ binding.accountDn.setText(channel.getDisplayName());
+
+
+ manageNotes(channel);
+ Helper.loadAvatar(ShowChannelActivity.this, channel, binding.accountPp);
+ //Follow button
+ String target = channel.getAcct();
+
+ binding.accountFollow.setOnClickListener(v -> {
+ if (doAction == action.NOTHING) {
+ Toasty.info(ShowChannelActivity.this, getString(R.string.nothing_to_do), Toast.LENGTH_LONG).show();
+ } else if (doAction == action.FOLLOW) {
+ binding.accountFollow.setEnabled(false);
+ PostActionsVM viewModel = new ViewModelProvider(ShowChannelActivity.this).get(PostActionsVM.class);
+ viewModel.post(FOLLOW, target, null).observe(ShowChannelActivity.this, apiResponse -> manageVIewPostActions(FOLLOW, apiResponse));
+ } else if (doAction == action.UNFOLLOW) {
+ boolean confirm_unfollow = sharedpreferences.getBoolean(Helper.SET_UNFOLLOW_VALIDATION, true);
+ if (confirm_unfollow) {
+ AlertDialog.Builder unfollowConfirm = new AlertDialog.Builder(ShowChannelActivity.this);
+ unfollowConfirm.setTitle(getString(R.string.unfollow_confirm));
+ unfollowConfirm.setMessage(channel.getAcct());
+ unfollowConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
+ unfollowConfirm.setPositiveButton(R.string.yes, (dialog, which) -> {
+ binding.accountFollow.setEnabled(false);
+ PostActionsVM viewModel = new ViewModelProvider(ShowChannelActivity.this).get(PostActionsVM.class);
+ viewModel.post(UNFOLLOW, target, null).observe(ShowChannelActivity.this, apiResponse -> manageVIewPostActions(UNFOLLOW, apiResponse));
+ dialog.dismiss();
+ });
+ unfollowConfirm.show();
+ } else {
+ binding.accountFollow.setEnabled(false);
+ PostActionsVM viewModel = new ViewModelProvider(ShowChannelActivity.this).get(PostActionsVM.class);
+ viewModel.post(UNFOLLOW, target, null).observe(ShowChannelActivity.this, apiResponse -> manageVIewPostActions(UNFOLLOW, apiResponse));
+ }
+
+ }
+ });
+ }
+
+
+ public void manageVIewRelationship(APIResponse apiResponse) {
+
+ if (apiResponse.getError() != null) {
+ if (apiResponse.getError().getError().length() > 500) {
+ Toasty.info(ShowChannelActivity.this, getString(R.string.remote_account), Toast.LENGTH_LONG).show();
+ } else {
+ Toasty.error(ShowChannelActivity.this, apiResponse.getError().getError(), Toast.LENGTH_LONG).show();
+ }
+ return;
+ }
+ this.relationship = apiResponse.getRelationships();
+ manageButtonVisibility();
+
+ invalidateOptionsMenu();
+
+ }
+
+ //Manages the visibility of the button
+ private void manageButtonVisibility() {
+ if (relationship == null || MainActivity.typeOfConnection == SURFING || channel == null)
+ return;
+ binding.accountFollow.setEnabled(true);
+ Boolean isFollowing = relationship.get(channel.getAcct());
+ if (isFollowing != null && isFollowing) {
+ binding.accountFollow.setText(R.string.action_unfollow);
+ binding.accountFollow.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor(ShowChannelActivity.this, R.color.red_1)));
+ doAction = action.UNFOLLOW;
+ } else {
+ binding.accountFollow.setText(R.string.action_follow);
+ doAction = action.FOLLOW;
+ }
+ binding.accountFollow.setVisibility(View.VISIBLE);
+ }
+
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ }
+
+
+ public void manageVIewPostActions(RetrofitPeertubeAPI.ActionType statusAction, APIResponse apiResponse) {
+
+ if (apiResponse.getError() != null) {
+ Toasty.error(ShowChannelActivity.this, apiResponse.getError().getError(), Toast.LENGTH_LONG).show();
+ return;
+ }
+ String target = channel.getAcct();
+ //IF action is unfollow or mute, sends an intent to remove statuses
+ if (statusAction == RetrofitPeertubeAPI.ActionType.UNFOLLOW) {
+ Bundle b = new Bundle();
+ b.putString("receive_action", apiResponse.getTargetedId());
+ Intent intentBC = new Intent(Helper.RECEIVE_ACTION);
+ intentBC.putExtras(b);
+ }
+ if (statusAction == RetrofitPeertubeAPI.ActionType.UNFOLLOW || statusAction == RetrofitPeertubeAPI.ActionType.FOLLOW) {
+ RelationshipVM viewModel = new ViewModelProvider(ShowChannelActivity.this).get(RelationshipVM.class);
+ List uris = new ArrayList<>();
+ uris.add(target);
+ viewModel.get(uris).observe(ShowChannelActivity.this, this::manageVIewRelationship);
+ } else if (statusAction == RetrofitPeertubeAPI.ActionType.MUTE) {
+ Toasty.info(ShowChannelActivity.this, getString(R.string.muted_done), Toast.LENGTH_LONG).show();
+ }
+ }
+
+ public void manageViewAccounts(APIResponse apiResponse) {
+ if (apiResponse.getChannels() != null && apiResponse.getChannels().size() == 1) {
+ Channel channel = apiResponse.getChannels().get(0);
+ if (this.channel == null) {
+ this.channel = channel;
+ manageChannel();
+ }
+ if (channel.getOwnerAccount() != null) {
+ this.channel.setOwnerAccount(channel.getOwnerAccount());
+ }
+ binding.subscriberCount.setText(getString(R.string.followers_count, Helper.withSuffix(channel.getFollowersCount())));
+ binding.subscriberCount.setVisibility(View.VISIBLE);
+ manageNotes(channel);
+ }
+ }
+
+ private void manageNotes(Channel channel) {
+ if (channel.getDescription() != null && channel.getDescription().compareTo("null") != 0 && channel.getDescription().trim().length() > 0) {
+ SpannableString spannableString;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
+ spannableString = new SpannableString(Html.fromHtml(channel.getDescription(), FROM_HTML_MODE_LEGACY));
+ else
+ spannableString = new SpannableString(Html.fromHtml(channel.getDescription()));
+
+ binding.accountNote.setText(spannableString, TextView.BufferType.SPANNABLE);
+ binding.accountNote.setMovementMethod(LinkMovementMethod.getInstance());
+ binding.accountNote.setVisibility(View.VISIBLE);
+ } else {
+ binding.accountNote.setVisibility(View.GONE);
+ }
+ }
+
+ public enum action {
+ FOLLOW,
+ UNFOLLOW,
+ NOTHING
+ }
+
+ /**
+ * Pager adapter for the 2 fragments
+ */
+ private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
+
+ ScreenSlidePagerAdapter(FragmentManager fm) {
+ super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
+ }
+
+ @NotNull
+ @Override
+ public Fragment getItem(int position) {
+ DisplayVideosFragment displayVideosFragment = new DisplayVideosFragment();
+ Bundle bundle = new Bundle();
+ bundle.putSerializable(Helper.TIMELINE_TYPE, TimelineVM.TimelineType.CHANNEL_VIDEOS);
+ bundle.putParcelable("channel", channel);
+ bundle.putString("peertube_instance", channel.getHost());
+ bundle.putBoolean("sepia_search", sepiaSearch);
+ displayVideosFragment.setArguments(bundle);
+ return displayVideosFragment;
+ }
+
+
+ @Override
+ public int getCount() {
+ return 1;
+ }
+ }
+
+}
diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/VideosTimelineActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/VideosTimelineActivity.java
new file mode 100644
index 00000000..d5edb88e
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/peertube/activities/VideosTimelineActivity.java
@@ -0,0 +1,178 @@
+package app.fedilab.android.peertube.activities;
+/* Copyright 2020 Thomas Schneider
+ *
+ * This file is a part of TubeLab
+ *
+ * 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.
+ *
+ * TubeLab 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 TubeLab; if not,
+ * see . */
+
+import static app.fedilab.android.peertube.viewmodel.TimelineVM.TimelineType.HISTORY;
+
+import android.app.AlertDialog;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+
+import androidx.fragment.app.FragmentTransaction;
+import androidx.lifecycle.ViewModelProvider;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+
+import app.fedilab.android.peertube.R;
+import app.fedilab.android.peertube.client.APIResponse;
+import app.fedilab.android.peertube.databinding.ActivityVideosTimelineBinding;
+import app.fedilab.android.peertube.fragment.DisplayVideosFragment;
+import app.fedilab.android.peertube.helper.Helper;
+import app.fedilab.android.peertube.helper.HelperInstance;
+import app.fedilab.android.peertube.helper.Theme;
+import app.fedilab.android.peertube.viewmodel.TimelineVM;
+
+
+public class VideosTimelineActivity extends BaseActivity {
+
+ private TimelineVM.TimelineType type;
+ private DisplayVideosFragment displayVideosFragment;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Theme.setTheme(this, HelperInstance.getLiveInstance(this), false);
+ super.onCreate(savedInstanceState);
+ ActivityVideosTimelineBinding binding = ActivityVideosTimelineBinding.inflate(getLayoutInflater());
+ View mainView = binding.getRoot();
+ setContentView(mainView);
+
+ if (getSupportActionBar() != null)
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ Bundle b = getIntent().getExtras();
+ if (b != null)
+ type = (TimelineVM.TimelineType) b.get("type");
+ displayVideosFragment = null;
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+ if (savedInstanceState == null) {
+ displayVideosFragment = new DisplayVideosFragment();
+ Bundle bundle = new Bundle();
+ bundle.putSerializable(Helper.TIMELINE_TYPE, type);
+ displayVideosFragment.setArguments(bundle);
+ ft.add(R.id.container, displayVideosFragment).addToBackStack(null).commit();
+ }
+
+ if (type == TimelineVM.TimelineType.MY_VIDEOS) {
+ setTitle(R.string.my_videos);
+ } else if (type == HISTORY) {
+ setTitle(R.string.my_history);
+ //TODO: uncomment when available
+ // binding.historyFilter.setVisibility(View.VISIBLE);
+ binding.historyFilterAll.setOnClickListener(v -> historyFilter(null));
+ binding.historyFilterToday.setOnClickListener(v -> {
+ Calendar cal = GregorianCalendar.getInstance();
+ cal.set(Calendar.HOUR_OF_DAY, 0);
+ cal.set(Calendar.MINUTE, 0);
+ cal.set(Calendar.SECOND, 0);
+ cal.set(Calendar.MILLISECOND, 0);
+ cal.getTime();
+ historyFilter(cal.getTime());
+ });
+ binding.historyFilterLast7Days.setOnClickListener(v -> {
+ Calendar cal = GregorianCalendar.getInstance();
+ cal.setTime(new Date());
+ cal.add(Calendar.DAY_OF_YEAR, -7);
+ cal.getTime();
+ historyFilter(cal.getTime());
+ });
+
+ } else if (type == TimelineVM.TimelineType.MOST_LIKED) {
+ setTitle(R.string.title_most_liked);
+ }
+
+
+ }
+
+ private void historyFilter(Date date) {
+ String startDate = null;
+ if (date != null) {
+ SimpleDateFormat fmtOut = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH);
+ startDate = fmtOut.format(date);
+ }
+ if (displayVideosFragment != null) {
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+ displayVideosFragment = new DisplayVideosFragment();
+ Bundle bundle = new Bundle();
+ bundle.putSerializable(Helper.TIMELINE_TYPE, HISTORY);
+ bundle.putSerializable("startDate", startDate);
+ displayVideosFragment.setArguments(bundle);
+ ft.replace(R.id.container, displayVideosFragment);
+ ft.addToBackStack(null);
+ ft.commit();
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(@NotNull Menu menu) {
+ if (type == HISTORY) {
+ getMenuInflater().inflate(R.menu.main_history, menu);
+ return true;
+ } else {
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ }
+
+ @Override
+ public void onBackPressed() {
+ super.onBackPressed();
+ finish();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ finish();
+ return true;
+ } else if (item.getItemId() == R.id.action_delete) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(VideosTimelineActivity.this);
+ builder.setTitle(R.string.delete_history);
+ builder.setMessage(R.string.delete_history_confirm);
+ builder.setIcon(android.R.drawable.ic_dialog_alert)
+ .setPositiveButton(R.string.delete, (dialog, which) -> {
+ TimelineVM viewModelFeeds = new ViewModelProvider(VideosTimelineActivity.this).get(TimelineVM.class);
+ viewModelFeeds.deleterHistory().observe(VideosTimelineActivity.this, this::manageVIewVideos);
+
+ dialog.dismiss();
+ })
+ .setNegativeButton(R.string.no, (dialog, which) -> dialog.dismiss())
+ .show();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void manageVIewVideos(APIResponse apiResponse) {
+ if (type == HISTORY) {
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+ displayVideosFragment = new DisplayVideosFragment();
+ Bundle bundle = new Bundle();
+ bundle.putSerializable(Helper.TIMELINE_TYPE, HISTORY);
+ displayVideosFragment.setArguments(bundle);
+ ft.replace(R.id.container, displayVideosFragment);
+ ft.addToBackStack(null);
+ ft.commit();
+ }
+ }
+}
diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/WebviewActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/WebviewActivity.java
new file mode 100644
index 00000000..761bc104
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/peertube/activities/WebviewActivity.java
@@ -0,0 +1,183 @@
+package app.fedilab.android.peertube.activities;
+/* Copyright 2020 Thomas Schneider
+ *
+ * This file is a part of TubeLab
+ *
+ * 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.
+ *
+ * TubeLab 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 TubeLab; if not,
+ * see . */
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.Toast;
+
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+
+import org.jetbrains.annotations.NotNull;
+
+import app.fedilab.android.peertube.R;
+import app.fedilab.android.peertube.helper.Helper;
+import app.fedilab.android.peertube.helper.HelperInstance;
+import app.fedilab.android.peertube.helper.Theme;
+import app.fedilab.android.peertube.webview.CustomWebview;
+import app.fedilab.android.peertube.webview.MastalabWebChromeClient;
+import app.fedilab.android.peertube.webview.MastalabWebViewClient;
+import es.dmoral.toasty.Toasty;
+
+
+public class WebviewActivity extends BaseActivity {
+
+ private String url;
+ private boolean peertubeLink;
+ private CustomWebview webView;
+
+
+ @SuppressLint("SetJavaScriptEnabled")
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Theme.setTheme(this, HelperInstance.getLiveInstance(this), false);
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_webview);
+ Bundle b = getIntent().getExtras();
+ if (b != null) {
+ url = b.getString("url", null);
+ peertubeLink = b.getBoolean("peertubeLink", false);
+ }
+ if (url == null)
+ finish();
+ if (getSupportActionBar() != null)
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ webView = Helper.initializeWebview(WebviewActivity.this, R.id.webview, null);
+ setTitle("");
+ FrameLayout webview_container = findViewById(R.id.webview_container);
+ final ViewGroup videoLayout = findViewById(R.id.videoLayout); // Your own view, read class comments
+ webView.getSettings().setJavaScriptEnabled(true);
+
+
+ MastalabWebChromeClient mastalabWebChromeClient = new MastalabWebChromeClient(WebviewActivity.this, webView, webview_container, videoLayout);
+ mastalabWebChromeClient.setOnToggledFullscreen(fullscreen -> {
+
+ if (fullscreen) {
+ videoLayout.setVisibility(View.VISIBLE);
+ WindowManager.LayoutParams attrs = getWindow().getAttributes();
+ attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
+ attrs.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
+ getWindow().setAttributes(attrs);
+ getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
+ } else {
+ WindowManager.LayoutParams attrs = getWindow().getAttributes();
+ attrs.flags &= ~WindowManager.LayoutParams.FLAG_FULLSCREEN;
+ attrs.flags &= ~WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
+ getWindow().setAttributes(attrs);
+ getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
+ videoLayout.setVisibility(View.GONE);
+ }
+ });
+ webView.setWebChromeClient(mastalabWebChromeClient);
+ MastalabWebViewClient mastalabWebViewClient = new MastalabWebViewClient(WebviewActivity.this);
+ webView.setWebViewClient(mastalabWebViewClient);
+ webView.setDownloadListener((url, userAgent, contentDisposition, mimetype, contentLength) -> {
+
+ if (Build.VERSION.SDK_INT >= 23) {
+ if (ContextCompat.checkSelfPermission(WebviewActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(WebviewActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
+ ActivityCompat.requestPermissions(WebviewActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, Helper.EXTERNAL_STORAGE_REQUEST_CODE);
+ } else {
+ Helper.manageDownloads(WebviewActivity.this, url);
+ }
+ } else {
+ Helper.manageDownloads(WebviewActivity.this, url);
+ }
+ });
+ if (!url.toLowerCase().startsWith("http://") && !url.toLowerCase().startsWith("https://"))
+ url = "http://" + url;
+ webView.loadUrl(url);
+ }
+
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ return super.onPrepareOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(@NotNull Menu menu) {
+ getMenuInflater().inflate(R.menu.main_webview, menu);
+ if (peertubeLink) {
+ menu.findItem(R.id.action_go).setVisible(false);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ int itemId = item.getItemId();
+ if (itemId == android.R.id.home) {
+ finish();
+ return true;
+ } else if (itemId == R.id.action_go) {
+ Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ try {
+ startActivity(browserIntent);
+ } catch (Exception e) {
+ Toasty.error(WebviewActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
+ }
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ public void setUrl(String newUrl) {
+ this.url = newUrl;
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ if (webView != null)
+ webView.onPause();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (webView != null)
+ webView.onResume();
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (webView.canGoBack()) {
+ webView.goBack();
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (webView != null)
+ webView.destroy();
+ }
+
+}
diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/WebviewConnectActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/WebviewConnectActivity.java
new file mode 100644
index 00000000..03a602f9
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/peertube/activities/WebviewConnectActivity.java
@@ -0,0 +1,263 @@
+package app.fedilab.android.peertube.activities;
+/* Copyright 2020 Thomas Schneider
+ *
+ * This file is a part of TubeLab
+ *
+ * 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.
+ *
+ * TubeLab 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 TubeLab; if not,
+ * see . */
+
+import static app.fedilab.android.peertube.client.RetrofitPeertubeAPI.updateCredential;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.graphics.Bitmap;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.MenuItem;
+import android.webkit.CookieManager;
+import android.webkit.CookieSyncManager;
+import android.webkit.WebChromeClient;
+import android.webkit.WebResourceRequest;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.ProgressBar;
+
+import androidx.appcompat.app.AlertDialog;
+
+import java.net.URL;
+import java.util.regex.Matcher;
+
+import app.fedilab.android.peertube.BuildConfig;
+import app.fedilab.android.peertube.R;
+import app.fedilab.android.peertube.client.RetrofitPeertubeAPI;
+import app.fedilab.android.peertube.client.entities.OauthParams;
+import app.fedilab.android.peertube.client.entities.Token;
+import app.fedilab.android.peertube.helper.Helper;
+import app.fedilab.android.peertube.helper.HelperInstance;
+import app.fedilab.android.peertube.helper.Theme;
+
+
+public class WebviewConnectActivity extends BaseActivity {
+
+
+ private WebView webView;
+ private AlertDialog alert;
+ private String clientId, clientSecret;
+ private String url;
+
+ @SuppressWarnings("deprecation")
+ public static void clearCookies(Context context) {
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
+ CookieManager.getInstance().removeAllCookies(null);
+ CookieManager.getInstance().flush();
+ } else {
+ CookieSyncManager cookieSyncMngr = CookieSyncManager.createInstance(context);
+ cookieSyncMngr.startSync();
+ CookieManager cookieManager = CookieManager.getInstance();
+ cookieManager.removeAllCookie();
+ cookieManager.removeSessionCookie();
+ cookieSyncMngr.stopSync();
+ cookieSyncMngr.sync();
+ }
+ }
+
+
+ @SuppressLint("SetJavaScriptEnabled")
+ public void onCreate(Bundle savedInstanceState) {
+ Theme.setTheme(this, HelperInstance.getLiveInstance(this), false);
+ super.onCreate(savedInstanceState);
+ SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
+ WebView.setWebContentsDebuggingEnabled(true);
+ setContentView(R.layout.activity_webview_connect);
+ Bundle b = getIntent().getExtras();
+ if (b != null) {
+ url = b.getString("url");
+ }
+ if (url == null)
+ finish();
+
+ clientId = sharedpreferences.getString(Helper.CLIENT_ID, null);
+ clientSecret = sharedpreferences.getString(Helper.CLIENT_SECRET, null);
+
+ webView = findViewById(R.id.webviewConnect);
+ clearCookies(WebviewConnectActivity.this);
+ webView.getSettings().setJavaScriptEnabled(true);
+ CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true);
+ webView.getSettings().setUseWideViewPort(true);
+ webView.getSettings().setLoadWithOverviewMode(true);
+ webView.getSettings().setSupportZoom(true);
+ webView.getSettings().setDisplayZoomControls(false);
+ webView.getSettings().setBuiltInZoomControls(true);
+ webView.getSettings().setAllowContentAccess(true);
+ webView.getSettings().setLoadsImagesAutomatically(true);
+ webView.getSettings().setSupportMultipleWindows(false);
+ webView.getSettings().setAppCacheEnabled(true);
+ webView.getSettings().setDatabaseEnabled(true);
+ webView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);
+ webView.getSettings().setMediaPlaybackRequiresUserGesture(true);
+ if (getSupportActionBar() != null)
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ setTitle(R.string.login);
+
+ final ProgressBar pbar = findViewById(R.id.progress_bar);
+ webView.setWebChromeClient(new WebChromeClient() {
+ @Override
+ public void onProgressChanged(WebView view, int progress) {
+ if (progress < 100 && pbar.getVisibility() == ProgressBar.GONE) {
+ pbar.setVisibility(ProgressBar.VISIBLE);
+ }
+ pbar.setProgress(progress);
+ if (progress == 100) {
+ pbar.setVisibility(ProgressBar.GONE);
+ }
+ }
+ });
+
+ webView.setWebViewClient(new WebViewClient() {
+
+ @Override
+ public void onPageStarted(WebView view, String url, Bitmap favicon) {
+ super.onPageStarted(view, url, favicon);
+ //Avoid to load first page for academic instances & openid
+ if (!BuildConfig.full_instances && url.contains("/client")) {
+ view.stopLoading();
+ }
+ }
+
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
+ if (request.getUrl() != null) {
+ String url = request.getUrl().toString();
+ Matcher matcher = Helper.redirectPattern.matcher(url);
+ if (matcher.find()) {
+ String externalAuthToken = matcher.group(1);
+ String username = matcher.group(2);
+ new Thread(() -> {
+ try {
+ OauthParams oauthParams = new OauthParams();
+ oauthParams.setClient_id(sharedpreferences.getString(Helper.CLIENT_ID, null));
+ oauthParams.setClient_secret(sharedpreferences.getString(Helper.CLIENT_SECRET, null));
+ oauthParams.setGrant_type("password");
+ oauthParams.setScope("upload");
+ oauthParams.setResponse_type("code");
+ oauthParams.setUsername(username);
+ oauthParams.setExternalAuthToken(externalAuthToken);
+ oauthParams.setPassword(externalAuthToken);
+ String instance = new URL(url).getHost();
+ Token token = null;
+ try {
+ token = new RetrofitPeertubeAPI(WebviewConnectActivity.this, instance, null).manageToken(oauthParams);
+ } catch (Error error) {
+ error.printStackTrace();
+ Error.displayError(WebviewConnectActivity.this, error);
+ }
+ if (token != null) {
+ SharedPreferences.Editor editor = sharedpreferences.edit();
+ editor.putString(Helper.PREF_KEY_OAUTH_TOKEN, token.getAccess_token());
+ editor.putString(Helper.PREF_SOFTWARE, null);
+ editor.putString(Helper.PREF_REMOTE_INSTANCE, null);
+ editor.putString(Helper.PREF_INSTANCE, instance);
+ editor.apply();
+ updateCredential(WebviewConnectActivity.this, token.getAccess_token(), clientId, clientSecret, token.getRefresh_token(), new URL(url).getHost(), null);
+ finish();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }).start();
+ return true;
+ }
+ }
+ return super.shouldOverrideUrlLoading(view, request);
+ }
+
+ /* @Override
+ public void onPageFinished(WebView view, String url) {
+ Matcher matcher = Helper.redirectPattern.matcher(url);
+ if (matcher.find()) {
+ String externalAuthToken = matcher.group(1);
+ String username = matcher.group(2);
+ new Thread(() -> {
+ try {
+ OauthParams oauthParams = new OauthParams();
+ oauthParams.setClient_id(sharedpreferences.getString(Helper.CLIENT_ID, null));
+ oauthParams.setClient_secret(sharedpreferences.getString(Helper.CLIENT_SECRET, null));
+ oauthParams.setGrant_type("password");
+ oauthParams.setScope("upload");
+ oauthParams.setResponse_type("code");
+ oauthParams.setUsername(username);
+ oauthParams.setExternalAuthToken(externalAuthToken);
+ oauthParams.setPassword(externalAuthToken);
+ String instance = new URL(url).getHost();
+ Token token = null;
+ try {
+ token = new RetrofitPeertubeAPI(WebviewConnectActivity.this, instance, null).manageToken(oauthParams);
+ } catch (Error error) {
+ error.printStackTrace();
+ Error.displayError(WebviewConnectActivity.this, error);
+ }
+ if (token != null) {
+ SharedPreferences.Editor editor = sharedpreferences.edit();
+ editor.putString(Helper.PREF_KEY_OAUTH_TOKEN, token.getAccess_token());
+ editor.putString(Helper.PREF_SOFTWARE, null);
+ editor.putString(Helper.PREF_REMOTE_INSTANCE, null);
+ editor.putString(Helper.PREF_INSTANCE, instance);
+ editor.apply();
+ updateCredential(WebviewConnectActivity.this, token.getAccess_token(), clientId, clientSecret, token.getRefresh_token(), new URL(url).getHost(), null);
+ finish();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }).start();
+ }
+ super.onPageFinished(view, url);
+ }*/
+ });
+ webView.loadUrl(url);
+
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ int itemId = item.getItemId();
+ if (itemId == android.R.id.home) {
+ finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+
+ @Override
+ public void onBackPressed() {
+ if (webView != null && webView.canGoBack()) {
+ webView.goBack();
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (alert != null) {
+ alert.dismiss();
+ alert = null;
+ }
+ if (webView != null) {
+ webView.destroy();
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/fedilab/android/peertube/client/APIResponse.java b/app/src/main/java/app/fedilab/android/peertube/client/APIResponse.java
new file mode 100644
index 00000000..ca46e5c9
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/peertube/client/APIResponse.java
@@ -0,0 +1,258 @@
+package app.fedilab.android.peertube.client;
+/* Copyright 2020 Thomas Schneider
+ *
+ * This file is a part of TubeLab
+ *
+ * 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.
+ *
+ * TubeLab 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 TubeLab; if not,
+ * see . */
+
+import java.util.List;
+import java.util.Map;
+
+import app.fedilab.android.peertube.client.data.AccountData;
+import app.fedilab.android.peertube.client.data.BlockData.Block;
+import app.fedilab.android.peertube.client.data.CaptionData;
+import app.fedilab.android.peertube.client.data.ChannelData;
+import app.fedilab.android.peertube.client.data.CommentData;
+import app.fedilab.android.peertube.client.data.InstanceData;
+import app.fedilab.android.peertube.client.data.NotificationData;
+import app.fedilab.android.peertube.client.data.PlaylistData;
+import app.fedilab.android.peertube.client.data.VideoData;
+import app.fedilab.android.peertube.client.data.VideoPlaylistData.VideoPlaylist;
+import app.fedilab.android.peertube.client.entities.OverviewVideo;
+import app.fedilab.android.peertube.client.entities.PlaylistExist;
+import app.fedilab.android.peertube.client.entities.Rating;
+
+@SuppressWarnings({"unused", "RedundantSuppression"})
+public class APIResponse {
+
+ private List accounts = null;
+ private List channels = null;
+ private String targetedId = null;
+ private String actionReturn = null;
+ private Rating rating;
+ private OverviewVideo overviewVideo = null;
+ private Map> videoExistPlaylist = null;
+ private List peertubes = null;
+ private List comments = null;
+ private List muted;
+ private List videoPlaylist;
+ private CommentData.CommentThreadData commentThreadData;
+ private List peertubeNotifications = null;
+ private List playlists = null;
+ private List domains = null;
+ private Map relationships = null;
+ private List captions = null;
+ private Error error = null;
+ private String since_id, max_id;
+ private List instances;
+ private String stringData;
+ private int statusCode;
+ private String captionText;
+
+ public List getAccounts() {
+ return accounts;
+ }
+
+ public void setAccounts(List accounts) {
+ this.accounts = accounts;
+ }
+
+ public Error getError() {
+ return error;
+ }
+
+ public void setError(Error error) {
+ this.error = error;
+ }
+
+ public String getMax_id() {
+ return max_id;
+ }
+
+ public void setMax_id(String max_id) {
+ this.max_id = max_id;
+ }
+
+ public String getSince_id() {
+ return since_id;
+ }
+
+ public void setSince_id(String since_id) {
+ this.since_id = since_id;
+ }
+
+
+ public List getDomains() {
+ return domains;
+ }
+
+ public void setDomains(List domains) {
+ this.domains = domains;
+ }
+
+
+ public List getPeertubes() {
+ return peertubes;
+ }
+
+ public void setPeertubes(List peertubes) {
+ this.peertubes = peertubes;
+ }
+
+
+ public List getPeertubeNotifications() {
+ return peertubeNotifications;
+ }
+
+ public void setPeertubeNotifications(List peertubeNotifications) {
+ this.peertubeNotifications = peertubeNotifications;
+ }
+
+
+ public List getPlaylists() {
+ return playlists;
+ }
+
+ public void setPlaylists(List playlists) {
+ this.playlists = playlists;
+ }
+
+
+ public String getTargetedId() {
+ return targetedId;
+ }
+
+ public void setTargetedId(String targetedId) {
+ this.targetedId = targetedId;
+ }
+
+
+ public String getStringData() {
+ return stringData;
+ }
+
+ public void setStringData(String stringData) {
+ this.stringData = stringData;
+ }
+
+ public int getStatusCode() {
+ return statusCode;
+ }
+
+ public void setStatusCode(int statusCode) {
+ this.statusCode = statusCode;
+ }
+
+ public Map getRelationships() {
+ return relationships;
+ }
+
+ public void setRelationships(Map relationships) {
+ this.relationships = relationships;
+ }
+
+ public List getInstances() {
+ return instances;
+ }
+
+ public void setInstances(List instances) {
+ this.instances = instances;
+ }
+
+ public List getCaptions() {
+ return captions;
+ }
+
+ public void setCaptions(List captions) {
+ this.captions = captions;
+ }
+
+ public String getCaptionText() {
+ return captionText;
+ }
+
+ public void setCaptionText(String captionText) {
+ this.captionText = captionText;
+ }
+
+ public List getChannels() {
+ return channels;
+ }
+
+ public void setChannels(List channels) {
+ this.channels = channels;
+ }
+
+ public List getComments() {
+ return comments;
+ }
+
+ public void setComments(List comments) {
+ this.comments = comments;
+ }
+
+ public Rating getRating() {
+ return rating;
+ }
+
+ public void setRating(Rating rating) {
+ this.rating = rating;
+ }
+
+ public OverviewVideo getOverviewVideo() {
+ return overviewVideo;
+ }
+
+ public void setOverviewVideo(OverviewVideo overviewVideo) {
+ this.overviewVideo = overviewVideo;
+ }
+
+ public String getActionReturn() {
+ return actionReturn;
+ }
+
+ public void setActionReturn(String actionReturn) {
+ this.actionReturn = actionReturn;
+ }
+
+ public List getMuted() {
+ return muted;
+ }
+
+ public void setMuted(List muted) {
+ this.muted = muted;
+ }
+
+ public List getVideoPlaylist() {
+ return videoPlaylist;
+ }
+
+ public void setVideoPlaylist(List videoPlaylist) {
+ this.videoPlaylist = videoPlaylist;
+ }
+
+ public Map> getVideoExistPlaylist() {
+ return videoExistPlaylist;
+ }
+
+ public void setVideoExistPlaylist(Map> videoExistPlaylist) {
+ this.videoExistPlaylist = videoExistPlaylist;
+ }
+
+ public CommentData.CommentThreadData getCommentThreadData() {
+ return commentThreadData;
+ }
+
+ public void setCommentThreadData(CommentData.CommentThreadData commentThreadData) {
+ this.commentThreadData = commentThreadData;
+ }
+}
diff --git a/app/src/main/java/app/fedilab/android/peertube/client/MenuItemVideo.java b/app/src/main/java/app/fedilab/android/peertube/client/MenuItemVideo.java
new file mode 100644
index 00000000..e433c4a6
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/peertube/client/MenuItemVideo.java
@@ -0,0 +1,53 @@
+package app.fedilab.android.peertube.client;
+/* Copyright 2020 Thomas Schneider
+ *
+ * This file is a part of TubeLab
+ *
+ * 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.
+ *
+ * TubeLab 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 TubeLab; if not,
+ * see . */
+
+public class MenuItemVideo {
+
+ int icon;
+ String title;
+ actionType action;
+
+ public int getIcon() {
+ return icon;
+ }
+
+ public void setIcon(int icon) {
+ this.icon = icon;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public actionType getAction() {
+ return action;
+ }
+
+ public void setAction(actionType action) {
+ this.action = action;
+ }
+
+ public enum actionType {
+ RESOLUTION,
+ SPEED,
+ CAPTION,
+ AUTONEXT
+ }
+}
diff --git a/app/src/main/java/app/fedilab/android/peertube/client/PeertubeService.java b/app/src/main/java/app/fedilab/android/peertube/client/PeertubeService.java
new file mode 100644
index 00000000..b71c0146
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/peertube/client/PeertubeService.java
@@ -0,0 +1,529 @@
+package app.fedilab.android.peertube.client;
+/* Copyright 2020 Thomas Schneider
+ *
+ * This file is a part of TubeLab
+ *
+ * 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.
+ *
+ * TubeLab 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 TubeLab; if not,
+ * see . */
+
+import java.util.List;
+import java.util.Map;
+
+import app.fedilab.android.peertube.client.data.AccountData;
+import app.fedilab.android.peertube.client.data.BlockData;
+import app.fedilab.android.peertube.client.data.CaptionData;
+import app.fedilab.android.peertube.client.data.ChannelData;
+import app.fedilab.android.peertube.client.data.CommentData;
+import app.fedilab.android.peertube.client.data.InstanceData;
+import app.fedilab.android.peertube.client.data.NotificationData;
+import app.fedilab.android.peertube.client.data.PlaylistData;
+import app.fedilab.android.peertube.client.data.PluginData;
+import app.fedilab.android.peertube.client.data.VideoData;
+import app.fedilab.android.peertube.client.data.VideoPlaylistData;
+import app.fedilab.android.peertube.client.entities.CaptionsParams;
+import app.fedilab.android.peertube.client.entities.ChannelParams;
+import app.fedilab.android.peertube.client.entities.NotificationSettings;
+import app.fedilab.android.peertube.client.entities.Oauth;
+import app.fedilab.android.peertube.client.entities.OverviewVideo;
+import app.fedilab.android.peertube.client.entities.PlaylistExist;
+import app.fedilab.android.peertube.client.entities.Rating;
+import app.fedilab.android.peertube.client.entities.Report;
+import app.fedilab.android.peertube.client.entities.Token;
+import app.fedilab.android.peertube.client.entities.UserMe;
+import app.fedilab.android.peertube.client.entities.WellKnownNodeinfo;
+import okhttp3.MultipartBody;
+import okhttp3.RequestBody;
+import retrofit2.Call;
+import retrofit2.http.Body;
+import retrofit2.http.DELETE;
+import retrofit2.http.Field;
+import retrofit2.http.FormUrlEncoded;
+import retrofit2.http.GET;
+import retrofit2.http.Header;
+import retrofit2.http.Headers;
+import retrofit2.http.Multipart;
+import retrofit2.http.POST;
+import retrofit2.http.PUT;
+import retrofit2.http.Part;
+import retrofit2.http.Path;
+import retrofit2.http.Query;
+import retrofit2.http.QueryMap;
+
+@SuppressWarnings({"unused", "RedundantSuppression"})
+public interface PeertubeService {
+
+ @GET("instances")
+ Call getInstances(
+ @QueryMap Map params,
+ @Query("nsfwPolicy[]") String nsfwPolicy,
+ @Query("categoriesOr[]") List categories,
+ @Query("languagesOr[]") List languages);
+
+ //Server settings
+ @GET(".well-known/nodeinfo")
+ Call getWellKnownNodeinfo();
+
+ @GET("plugins/peertube-plugin-player-watermark/public-settings")
+ Call waterMark();
+
+ //Instance info
+ @GET("config/about")
+ Call configAbout();
+
+ //Instance config
+ @GET("config")
+ Call config();
+
+ @GET("{nodeInfoPath}")
+ Call getNodeinfo(@Path(value = "nodeInfoPath", encoded = true) String nodeInfoPath);
+
+ @GET("{captionContent}")
+ Call getCaptionContent(@Path("captionContent") String captionContent);
+
+ @GET("videos/categories")
+ Call