Split files

fix_reports
Thomas 2 years ago
parent e9571221be
commit d77dd2c349

@ -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()

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/castMiniController"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
android:visibility="gone">
<ImageView
android:id="@+id/cast_play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/play"
android:src="@drawable/ic_baseline_play_arrow_32"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cast_loader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<TextView
android:id="@+id/cast_loader_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/please_wait"
android:textColor="?colorAccent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/cast_loader_small"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.github.ybq.android.spinkit.SpinKitView
android:id="@+id/cast_loader_small"
android:layout_width="wrap_content"
android:layout_height="18dp"
android:layout_gravity="center"
android:layout_marginStart="5dp"
app:SpinKit_Color="?colorAccent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/cast_loader_text"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

@ -369,5 +369,122 @@
<action android:name="org.unifiedpush.android.connector.REGISTRATION_REFUSED" />
</intent-filter>
</receiver>
<activity
android:name=".peertube.activities.PeertubeActivity"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:launchMode="singleTask"
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
tools:targetApi="n" />
<activity
android:name=".peertube.activities.PeertubeEditUploadActivity"
android:configChanges="orientation|screenSize"
android:exported="false"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.ShowChannelActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.ShowAccountActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.AccountActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.MyAccountActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.SearchActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.AllPlaylistsActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.AllLocalPlaylistsActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.InstancePickerActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.PlaylistsActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.LocalPlaylistsActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.VideosTimelineActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.SepiaSearchActivity"
android:configChanges="orientation|screenSize"
android:label="@string/sepia_search"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.ManageInstancesActivity"
android:configChanges="orientation|screenSize"
android:label="@string/instances_picker"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.WebviewActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />
<activity
android:name=".peertube.activities.WebviewConnectActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />
<activity
android:name=".peertube.activities.MastodonWebviewConnectActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />
<activity
android:name=".peertube.activities.LoginActivity"
android:configChanges="orientation|screenSize"
android:exported="true"
android:windowSoftInputMode="stateAlwaysHidden">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="backtotubelab"
android:scheme="tubelab" />
</intent-filter>
</activity>
<activity
android:name=".peertube.activities.SettingsActivity"
android:configChanges="orientation|screenSize"
android:label="@string/settings"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.PeertubeRegisterActivity"
android:configChanges="orientation|screenSize"
android:label="@string/register_account"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.PeertubeUploadActivity"
android:configChanges="orientation|screenSize"
android:label="@string/upload_video"
android:windowSoftInputMode="stateAlwaysHidden" />
<service
android:name=".peertube.services.RetrieveInfoService"
android:exported="false" />
</application>
</manifest>

@ -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 <http://www.gnu.org/licenses>. */
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;
}
}
}

@ -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 <http://www.gnu.org/licenses>. */
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<Playlist> 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<VideoPlaylistData.VideoPlaylistExport> 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);
}
}

@ -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 <http://www.gnu.org/licenses>. */
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<Integer, String> privacyToSend;
private String idChannel;
private List<Playlist> playlists;
private Playlist playlistToEdit;
private List<ChannelData.Channel> 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<Integer, String> privaciesInit = new LinkedHashMap<>(peertubeInformation.getPrivacies());
if (privaciesInit.size() > 0) {
Map.Entry<Integer, String> 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<Integer, String> 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<String> adapterChannel = new ArrayAdapter<>(AllPlaylistsActivity.this,
android.R.layout.simple_spinner_dropdown_item, channelName);
bindingDialog.setUploadChannel.setAdapter(adapterChannel);
LinkedHashMap<String, String> translations = null;
if (peertubeInformation.getTranslations() != null)
translations = new LinkedHashMap<>(peertubeInformation.getTranslations());
LinkedHashMap<Integer, String> privaciesInit = new LinkedHashMap<>(peertubeInformation.getPlaylistPrivacies());
Map.Entry<Integer, String> entryInt = privaciesInit.entrySet().iterator().next();
privacyToSend = new HashMap<>();
privacyToSend.put(entryInt.getKey(), entryInt.getValue());
LinkedHashMap<Integer, String> privacies = new LinkedHashMap<>(peertubeInformation.getPlaylistPrivacies());
//Populate privacies
String[] privaciesA = new String[privacies.size()];
Iterator<Map.Entry<Integer, String>> it = privacies.entrySet().iterator();
i = 0;
while (it.hasNext()) {
Map.Entry<Integer, String> 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<String> 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<Integer, String> privaciesCheck = new LinkedHashMap<>(peertubeInformation.getPrivacies());
Iterator<Map.Entry<Integer, String>> it = privaciesCheck.entrySet().iterator();
int i = 0;
while (it.hasNext()) {
Map.Entry<Integer, String> 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);
}
}

@ -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 <http://www.gnu.org/licenses>. */
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<String> 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<String, String> 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<Map.Entry<String, String>> it = languages.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> 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<String> 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<Integer, String> 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<Map.Entry<Integer, String>> it = categories.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, String> 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<Integer> 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<InstanceData.Instance> 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));
}
}
}

@ -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 <http://www.gnu.org/licenses>. */
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);
}
}

@ -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 <http://www.gnu.org/licenses>. */
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 = 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<String> 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<PluginData.PluginInfo> 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();
}
}

@ -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 <http://www.gnu.org/licenses>. */
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<String> videoLanguageServer = userMe.getVideoLanguages();
if (videoLanguageServer != null) {
Set<String> videoLanguageServerSet = new TreeSet<>(videoLanguageServer);
videoLanguageServerSet.addAll(videoLanguageServer);
Set<String> 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<Account> 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 = 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;
}
}
}
}

@ -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 <http://www.gnu.org/licenses>. */
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<InstanceData.AboutInstance> 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<InstanceData.AboutInstance> 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);
}
}

@ -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 <http://www.gnu.org/licenses>. */
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();
}
}
}

@ -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 <http://www.gnu.org/licenses>. */
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<String> 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;
}
}

@ -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 <http://www.gnu.org/licenses>. */
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<String, String> 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<Integer, String> categories = new LinkedHashMap<>(peertubeInformation.getCategories());
LinkedHashMap<Integer, String> licences = new LinkedHashMap<>(peertubeInformation.getLicences());
LinkedHashMap<Integer, String> privacies = new LinkedHashMap<>(peertubeInformation.getPrivacies());
LinkedHashMap<String, String> languages = new LinkedHashMap<>(peertubeInformation.getLanguages());
LinkedHashMap<String, String> translations = null;
if (peertubeInformation.getTranslations() != null)
translations = new LinkedHashMap<>(peertubeInformation.getTranslations());
//Populate catgories
String[] categoriesA = new String[categories.size()];
Iterator<Map.Entry<Integer, String>> it = categories.entrySet().iterator();
int i = 0;
while (it.hasNext()) {
Map.Entry<Integer, String> 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<String> 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<Integer, String> 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<String> 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<Map.Entry<String, String>> itl = languages.entrySet().iterator();
i = 0;
while (itl.hasNext()) {
Map.Entry<String, String> 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<String> 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<Integer, String> 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<String> 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<String, String> languages = new LinkedHashMap<>(peertubeInformation.getLanguages());
Map.Entry<String, String> entryString = languages.entrySet().iterator().next();
languageToSend.setId(entryString.getKey());
languageToSend.setLabel(entryString.getValue());
}
if (licenseToSend == null) {
licenseToSend = new Item();
LinkedHashMap<Integer, String> licences = new LinkedHashMap<>(peertubeInformation.getLicences());
Map.Entry<Integer, String> entryInt = licences.entrySet().iterator().next();
licenseToSend.setId(entryInt.getKey());
licenseToSend.setLabel(entryInt.getValue());
}
if (categoryToSend == null) {
categoryToSend = new Item();
LinkedHashMap<Integer, String> categories = new LinkedHashMap<>(peertubeInformation.getCategories());
Map.Entry<Integer, String> entryInt = categories.entrySet().iterator().next();
categoryToSend.setId(entryInt.getKey());
categoryToSend.setLabel(entryInt.getValue());
}
if (privacyToSend == null) {
privacyToSend = new Item();
LinkedHashMap<Integer, String> privacies = new LinkedHashMap<>(peertubeInformation.getPrivacies());
Map.Entry<Integer, String> 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<Integer, String> categories = new LinkedHashMap<>(peertubeInformation.getCategories());
LinkedHashMap<Integer, String> licences = new LinkedHashMap<>(peertubeInformation.getLicences());
LinkedHashMap<Integer, String> privacies = new LinkedHashMap<>(peertubeInformation.getPrivacies());
LinkedHashMap<String, String> languages = new LinkedHashMap<>(peertubeInformation.getLanguages());
int languagePosition = 0;
if (languages.containsKey(language)) {
Iterator<Map.Entry<String, String>> itstr = languages.entrySet().iterator();
while (itstr.hasNext()) {
Map.Entry<String, String> pair = itstr.next();
if (pair.getKey().compareTo(language) == 0)
break;
itstr.remove();
languagePosition++;
}
}
int privacyPosition = 0;
if (privacies.containsKey(privacy)) {
Iterator<Map.Entry<Integer, String>> it = privacies.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, String> pair = it.next();
if (pair.getKey() == privacy)
break;
it.remove();
privacyPosition++;
}
}
int licensePosition = 0;
if (licences.containsKey(license)) {
Iterator<Map.Entry<Integer, String>> it = licences.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, String> pair = it.next();
if (pair.getKey() == license)
break;
it.remove();
licensePosition++;
}
}
int categoryPosition = 0;
if (categories.containsKey(category)) {
Iterator<Map.Entry<Integer, String>> it = categories.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, String> 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<String> 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<String> tags = video.getTags();
if (tags != null && tags.size() > 0) {
binding.pVideoTags.setTags(tags.toArray(new String[0]));
}
}
private void updateUploadChannel(int position) {
LinkedHashMap<String, String> channelsCheck = new LinkedHashMap<>(channels);
Iterator<Map.Entry<String, String>> it = channelsCheck.entrySet().iterator();
int i = 0;
while (it.hasNext()) {
Map.Entry<String, String> pair = it.next();
if (i == position) {
channelToSendId = pair.getValue();
break;
}
it.remove();
i++;
}
}
private void updateLanguagesPosition(int position) {
LinkedHashMap<String, String> languagesCheck = new LinkedHashMap<>(peertubeInformation.getLanguages());
Iterator<Map.Entry<String, String>> it = languagesCheck.entrySet().iterator();
int i = 0;
while (it.hasNext()) {
Map.Entry<String, String> pair = it.next();
if (i == position) {
languageToSend.setId(pair.getKey());
languageToSend.setLabel(pair.getValue());
break;
}
it.remove();
i++;
}
}
private void updateCategoryPosition(int position) {
LinkedHashMap<Integer, String> categoriesCheck = new LinkedHashMap<>(peertubeInformation.getCategories());
Iterator<Map.Entry<Integer, String>> it = categoriesCheck.entrySet().iterator();
int i = 0;
while (it.hasNext()) {
Map.Entry<Integer, String> pair = it.next();
if (i == position) {
categoryToSend.setId(pair.getKey());
categoryToSend.setLabel(pair.getValue());
break;
}
it.remove();
i++;
}
}
private void updateLicensePosition(int position) {
LinkedHashMap<Integer, String> licensesCheck = new LinkedHashMap<>(peertubeInformation.getLicences());
Iterator<Map.Entry<Integer, String>> it = licensesCheck.entrySet().iterator();
int i = 0;
while (it.hasNext()) {
Map.Entry<Integer, String> pair = it.next();
if (i == position) {
licenseToSend.setId(pair.getKey());
licenseToSend.setLabel(pair.getValue());
break;
}
it.remove();
i++;
}
}
private void updatePrivacyPosition(int position) {
LinkedHashMap<Integer, String> privaciesCheck = new LinkedHashMap<>(peertubeInformation.getPrivacies());
Iterator<Map.Entry<Integer, String>> it = privaciesCheck.entrySet().iterator();
int i = 0;
while (it.hasNext()) {
Map.Entry<Integer, String> 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<Channel> 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<String> 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<String, String> channelsIterator = new LinkedHashMap<>(channels);
Iterator<Map.Entry<String, String>> it = channelsIterator.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> 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();
}
}

@ -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 <http://www.gnu.org/licenses>. */
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,
"<a href='https://" + binding.loginInstance.getText().toString() + "/about/instance#terms-section' >" + tos + "</a>"
);
}
} else {
content_agreement = getString(R.string.agreement_check,
"<a href='https://apps.education.fr/cgu#peertube' >" + serverrules + "</a>",
"<a href='https://apps.education.fr/bonnes-pratiques/' >" + tos + "</a>"
);
}
agreement_text.setMovementMethod(LinkMovementMethod.getInstance());
if (content_agreement != null) {
agreement_text.setText(Html.fromHtml(content_agreement));
}
}
}

@ -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 <http://www.gnu.org/licenses>. */
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<String, String> channels;
private Uri uri;
private String filename;
private HashMap<Integer, String> privacyToSend;
private HashMap<String, String> 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<ChannelData.Channel> 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<String> adapterChannel = new ArrayAdapter<>(PeertubeUploadActivity.this,
android.R.layout.simple_spinner_dropdown_item, channelName);
binding.setUploadChannel.setAdapter(adapterChannel);
if (peertubeInformation == null) {
return;
}
LinkedHashMap<String, String> translations = null;
if (peertubeInformation.getTranslations() != null)
translations = new LinkedHashMap<>(peertubeInformation.getTranslations());
LinkedHashMap<Integer, String> privaciesInit = new LinkedHashMap<>(peertubeInformation.getPrivacies());
Map.Entry<Integer, String> entryInt = privaciesInit.entrySet().iterator().next();
privacyToSend = new HashMap<>();
privacyToSend.put(entryInt.getKey(), entryInt.getValue());
LinkedHashMap<Integer, String> privacies = new LinkedHashMap<>(peertubeInformation.getPrivacies());
//Populate privacies
String[] privaciesA = new String[privacies.size()];
Iterator<Map.Entry<Integer, String>> it = privacies.entrySet().iterator();
i = 0;
while (it.hasNext()) {
Map.Entry<Integer, String> 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<String> 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<Integer, String> privaciesCheck = new LinkedHashMap<>(peertubeInformation.getPrivacies());
Iterator<Map.Entry<Integer, String>> it = privaciesCheck.entrySet().iterator();
int i = 0;
while (it.hasNext()) {
Map.Entry<Integer, String> 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<String, String> channelsCheck = new LinkedHashMap<>(channels);
Iterator<Map.Entry<String, String>> it = channelsCheck.entrySet().iterator();
int i = 0;
while (it.hasNext()) {
Map.Entry<String, String> 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<String, String> channelM = channelToSend.entrySet().iterator().next();
String idChannel = channelM.getValue();
Map.Entry<Integer, String> 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<UploadNotificationAction> 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<UploadNotificationAction> 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);
}
}

@ -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 <http://www.gnu.org/licenses>. */
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);
}
}

@ -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 <http://www.gnu.org/licenses>. */
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;
}
}
}

@ -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 <http://www.gnu.org/licenses>. */
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<String> 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<Integer, String> categories = new LinkedHashMap<>(peertubeInformation.getCategories());
LinkedHashMap<Integer, String> licences = new LinkedHashMap<>(peertubeInformation.getLicences());
LinkedHashMap<String, String> languages = new LinkedHashMap<>(peertubeInformation.getLanguages());
LinkedHashMap<String, String> 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<Map.Entry<Integer, String>> it = categories.entrySet().iterator();
int i = 1;
while (it.hasNext()) {
Map.Entry<Integer, String> 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<String> 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<Integer, String> 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<String> 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<Map.Entry<String, String>> itl = languages.entrySet().iterator();
i = 1;
while (itl.hasNext()) {
Map.Entry<String, String> 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<String> 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<String, String> languagesCheck = new LinkedHashMap<>(peertubeInformation.getLanguages());
Iterator<Map.Entry<String, String>> it = languagesCheck.entrySet().iterator();
int i = 0;
while (it.hasNext()) {
Map.Entry<String, String> pair = it.next();
if (i == position && position > 0) {
List<String> languages = new ArrayList<>();
languages.add(pair.getKey());
sepiaSearchVideo.setBoostLanguages(languages);
break;
} else {
sepiaSearchVideo.setBoostLanguages(null);
}
it.remove();
i++;
}
}
private void updateCategoryPosition(int position) {
LinkedHashMap<Integer, String> categoriesCheck = new LinkedHashMap<>(peertubeInformation.getCategories());
Iterator<Map.Entry<Integer, String>> it = categoriesCheck.entrySet().iterator();
int i = 0;
while (it.hasNext()) {
Map.Entry<Integer, String> pair = it.next();
if (i == position && position > 0) {
List<Integer> categories = new ArrayList<>();
categories.add(pair.getKey());
sepiaSearchVideo.setCategoryOneOf(categories);
break;
} else {
sepiaSearchVideo.setCategoryOneOf(null);
}
it.remove();
i++;
}
}
private void updateLicensePosition(int position) {
LinkedHashMap<Integer, String> licensesCheck = new LinkedHashMap<>(peertubeInformation.getLicences());
Iterator<Map.Entry<Integer, String>> it = licensesCheck.entrySet().iterator();
int i = 0;
while (it.hasNext()) {
Map.Entry<Integer, String> pair = it.next();
if (i == position && position > 0) {
List<Integer> licenses = new ArrayList<>();
licenses.add(pair.getKey());
sepiaSearchVideo.setLicenceOneOf(licenses);
break;
} else {
sepiaSearchVideo.setLicenceOneOf(null);
}
it.remove();
i++;
}
}
}

@ -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 <http://www.gnu.org/licenses>. */
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);
}
}

@ -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 <http://www.gnu.org/licenses>. */
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;
}
}
}

@ -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 <http://www.gnu.org/licenses>. */
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<String, Boolean> 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<AccountData.Account> 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<String> 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<String> 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;
}
}
}

@ -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 <http://www.gnu.org/licenses>. */
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();
}
}
}

@ -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 <http://www.gnu.org/licenses>. */
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();
}
}

@ -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 <http://www.gnu.org/licenses>. */
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();
}
}
}

@ -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 <http://www.gnu.org/licenses>. */
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<AccountData.Account> accounts = null;
private List<ChannelData.Channel> channels = null;
private String targetedId = null;
private String actionReturn = null;
private Rating rating;
private OverviewVideo overviewVideo = null;
private Map<String, List<PlaylistExist>> videoExistPlaylist = null;
private List<VideoData.Video> peertubes = null;
private List<CommentData.Comment> comments = null;
private List<Block> muted;
private List<VideoPlaylist> videoPlaylist;
private CommentData.CommentThreadData commentThreadData;
private List<NotificationData.Notification> peertubeNotifications = null;
private List<PlaylistData.Playlist> playlists = null;
private List<String> domains = null;
private Map<String, Boolean> relationships = null;
private List<CaptionData.Caption> captions = null;
private Error error = null;
private String since_id, max_id;
private List<InstanceData.Instance> instances;
private String stringData;
private int statusCode;
private String captionText;
public List<AccountData.Account> getAccounts() {
return accounts;
}
public void setAccounts(List<AccountData.Account> 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<String> getDomains() {
return domains;
}
public void setDomains(List<String> domains) {
this.domains = domains;
}
public List<VideoData.Video> getPeertubes() {
return peertubes;
}
public void setPeertubes(List<VideoData.Video> peertubes) {
this.peertubes = peertubes;
}
public List<NotificationData.Notification> getPeertubeNotifications() {
return peertubeNotifications;
}
public void setPeertubeNotifications(List<NotificationData.Notification> peertubeNotifications) {
this.peertubeNotifications = peertubeNotifications;
}
public List<PlaylistData.Playlist> getPlaylists() {
return playlists;
}
public void setPlaylists(List<PlaylistData.Playlist> 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<String, Boolean> getRelationships() {
return relationships;
}
public void setRelationships(Map<String, Boolean> relationships) {
this.relationships = relationships;
}
public List<InstanceData.Instance> getInstances() {
return instances;
}
public void setInstances(List<InstanceData.Instance> instances) {
this.instances = instances;
}
public List<CaptionData.Caption> getCaptions() {
return captions;
}
public void setCaptions(List<CaptionData.Caption> captions) {
this.captions = captions;
}
public String getCaptionText() {
return captionText;
}
public void setCaptionText(String captionText) {
this.captionText = captionText;
}
public List<ChannelData.Channel> getChannels() {
return channels;
}
public void setChannels(List<ChannelData.Channel> channels) {
this.channels = channels;
}
public List<CommentData.Comment> getComments() {
return comments;
}
public void setComments(List<CommentData.Comment> 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<Block> getMuted() {
return muted;
}
public void setMuted(List<Block> muted) {
this.muted = muted;
}
public List<VideoPlaylist> getVideoPlaylist() {
return videoPlaylist;
}
public void setVideoPlaylist(List<VideoPlaylist> videoPlaylist) {
this.videoPlaylist = videoPlaylist;
}
public Map<String, List<PlaylistExist>> getVideoExistPlaylist() {
return videoExistPlaylist;
}
public void setVideoExistPlaylist(Map<String, List<PlaylistExist>> videoExistPlaylist) {
this.videoExistPlaylist = videoExistPlaylist;
}
public CommentData.CommentThreadData getCommentThreadData() {
return commentThreadData;
}
public void setCommentThreadData(CommentData.CommentThreadData commentThreadData) {
this.commentThreadData = commentThreadData;
}
}

@ -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 <http://www.gnu.org/licenses>. */
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
}
}

@ -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 <http://www.gnu.org/licenses>. */
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<InstanceData> getInstances(
@QueryMap Map<String, String> params,
@Query("nsfwPolicy[]") String nsfwPolicy,
@Query("categoriesOr[]") List<Integer> categories,
@Query("languagesOr[]") List<String> languages);
//Server settings
@GET(".well-known/nodeinfo")
Call<WellKnownNodeinfo> getWellKnownNodeinfo();
@GET("plugins/peertube-plugin-player-watermark/public-settings")
Call<PluginData.WaterMark> waterMark();
//Instance info
@GET("config/about")
Call<InstanceData.InstanceInfo> configAbout();
//Instance config
@GET("config")
Call<InstanceData.InstanceConfig> config();
@GET("{nodeInfoPath}")
Call<WellKnownNodeinfo.NodeInfo> getNodeinfo(@Path(value = "nodeInfoPath", encoded = true) String nodeInfoPath);
@GET("{captionContent}")
Call<String> getCaptionContent(@Path("captionContent") String captionContent);
@GET("videos/categories")
Call<Map<Integer, String>> getCategories();
@GET("videos/languages")
Call<Map<String, String>> getLanguages();
@GET("videos/privacies")
Call<Map<Integer, String>> getPrivacies();
@GET("video-playlists/privacies")
Call<Map<Integer, String>> getPlaylistsPrivacies();
@GET("videos/licences")
Call<Map<Integer, String>> getLicences();
//This one doesn't use api/v1 path
@GET("client/locales/{local}/server.json")
Call<Map<String, String>> getTranslations(@Path("local") String local);
//TOKEN
//Refresh
@FormUrlEncoded
@POST("users/token")
Call<Token> createOpenIdToken(
@Field("client_id") String client_id,
@Field("client_secret") String client_secret,
@Field("response_type") String response_type,
@Field("grant_type") String grant_type,
@Field("scope") String scope,
@Field("username") String username,
@Field("password") String password,
@Field("externalAuthToken") String externalAuthToken);
//TOKEN
//Refresh
@FormUrlEncoded
@POST("users/token")
Call<Token> createToken(
@Field("client_id") String client_id,
@Field("client_secret") String client_secret,
@Field("grant_type") String grant_type,
@Field("username") String username,
@Field("password") String password);
//TOKEN
//Refresh
@FormUrlEncoded
@POST("users/token")
Call<Token> refreshToken(
@Field("client_id") String client_id,
@Field("client_secret") String client_secret,
@Field("refresh_token") String refresh_token,
@Field("grant_type") String grant_type);
@GET("users/me")
Call<UserMe> verifyCredentials(@Header("Authorization") String credentials);
@GET("users/me/video-quota-used")
Call<UserMe.VideoQuota> getVideoQuota(@Header("Authorization") String credentials);
@FormUrlEncoded
@PUT("videos/{id}/watching")
Call<String> addToHistory(
@Header("Authorization") String credentials,
@Path("id") String id,
@Field("currentTime") long currentTime);
@FormUrlEncoded
@PUT("users/me")
Call<String> updateUser(
@Header("Authorization") String credentials,
@Field("videosHistoryEnabled") Boolean videosHistoryEnabled,
@Field("autoPlayVideo") Boolean autoPlayVideo,
@Field("autoPlayNextVideo") Boolean autoPlayNextVideo,
@Field("webTorrentEnabled") Boolean webTorrentEnabled,
@Field("videoLanguages") List<String> videoLanguages,
@Field("description") String description,
@Field("displayName") String displayName,
@Field("nsfwPolicy") String nsfwPolicy
);
@Multipart
@POST("video-channels/{channelHandle}/avatar/pick")
Call<UserMe.AvatarResponse> updateChannelProfilePicture(
@Header("Authorization") String credentials,
@Path("channelHandle") String channelHandle,
@Part MultipartBody.Part avatarfile);
@Multipart
@POST("users/me/avatar/pick")
Call<UserMe.AvatarResponse> updateProfilePicture(
@Header("Authorization") String credentials,
@Part MultipartBody.Part avatarfile);
//Timelines Authenticated
//Subscriber timeline
@GET("users/me/subscriptions/videos?sort=-publishedAt")
Call<VideoData> getSubscriptionVideos(
@Header("Authorization") String credentials,
@Query("start") String maxId,
@Query("count") String coun,
@Query("languageOneOf") List<String> languageOneOf);
//Overview videos
@GET("overviews/videos")
Call<OverviewVideo> getOverviewVideos(
@Header("Authorization") String credentials,
@Query("page") String page,
@Query("nsfw") String nsfw,
@Query("languageOneOf") List<String> languageOneOf);
//Most liked videos
@GET("videos?sort=-likes")
Call<VideoData> getMostLikedVideos(
@Header("Authorization") String credentials,
@Query("start") String maxId,
@Query("count") String count,
@Query("nsfw") String nsfw,
@Query("languageOneOf") List<String> languageOneOf);
//Trending videos
@GET("videos?sort=-trending")
Call<VideoData> getTrendingVideos(
@Header("Authorization") String credentials,
@Query("start") String maxId,
@Query("count") String count,
@Query("nsfw") String nsfw,
@Query("languageOneOf") List<String> languageOneOf);
//Recently added videos
@GET("videos?sort=-publishedAt")
Call<VideoData> getRecentlyAddedVideos(
@Header("Authorization") String credentials,
@Query("start") String maxId,
@Query("count") String count,
@Query("nsfw") String nsfw,
@Query("languageOneOf") List<String> languageOneOf);
//Local videos
@GET("videos?sort=-publishedAt&filter=local")
Call<VideoData> getLocalVideos(
@Header("Authorization") String credentials,
@Query("start") String maxId,
@Query("count") String count,
@Query("nsfw") String nsfw,
@Query("languageOneOf") List<String> languageOneOf);
//History
@GET("users/me/history/videos")
Call<VideoData> getHistory(
@Header("Authorization") String credentials,
@Query("start") String maxId,
@Query("count") String count,
@Query("nsfw") String nsfw,
@Query("startDate") String startDate,
@Query("endDate") String endDate
);
@POST("users/me/history/videos/remove")
Call<String> deleteHistory(
@Header("Authorization") String credentials);
//Search videos
@GET("search/videos")
Call<VideoData> searchVideos(
@Header("Authorization") String credentials,
@Query("search") String search,
@Query("start") String maxId,
@Query("count") String count,
@Query("nsfw") String nsfw);
//Search channels
@GET("search/video-channels")
Call<ChannelData> searchChannels(
@Header("Authorization") String credentials,
@Query("search") String search,
@Query("searcharget") String searchTarget,
@Query("start") String maxId,
@Query("count") String count);
//Search
@GET("search/videos")
Call<VideoData> searchNextVideo(
@Header("Authorization") String credentials,
@Query("tagsOneOf") List<String> tagsOneOf,
@Query("start") String maxId,
@Query("count") String count,
@Query("nsfw") String nsfw);
//Get notifications
@GET("users/me/notifications")
Call<NotificationData> getNotifications(@Header("Authorization") String credentials, @Query("start") String maxId, @Query("count") String count, @Query("since_id") String sinceId);
@GET("users/me/notifications?start=0&count=0&unread=true")
Call<NotificationData> countNotifications(@Header("Authorization") String credentials);
@POST("users/me/notifications/read-all")
Call<String> markAllAsRead(@Header("Authorization") String credentials);
@FormUrlEncoded
@POST("users/me/notifications/read")
Call<String> markAsRead(@Header("Authorization") String credentials, @Field("ids[]") List<String> ids);
//Update Notification settings
@PUT("users/me/notification-settings")
Call<String> updateNotifications(@Header("Authorization") String credentials, @Body NotificationSettings notificationSettings);
//Get/Post/Update/Delete video
//Get a video
@GET("videos/{id}")
Call<VideoData.Video> getVideo(@Path("id") String id);
//Get a video description
@GET("videos/{uuid}/description")
Call<VideoData.Description> getVideoDescription(@Path("uuid") String uuid);
@GET("videos/{id}")
Call<VideoData.Video> getMyVideo(@Header("Authorization") String credentials, @Path("id") String id);
//Get my video
@GET("users/me/videos?sort=-publishedAt")
Call<VideoData> getMyVideos(@Header("Authorization") String credentials, @Query("start") String maxId, @Query("count") String count);
//Get user videos
@GET("accounts/{name}/videos?sort=-publishedAt")
Call<VideoData> getVideosForAccount(
@Path("name") String name,
@Query("start") String maxId,
@Query("count") String count,
@Query("nsfw") String nsfw
);
@POST("videos/{id}/views")
Call<String> postView(@Path("id") String id);
@Multipart
@PUT("videos/{id}")
Call<String> updateVideo(
@Header("Authorization") String credentials,
@Path("id") String videoId,
@Part("channelId") RequestBody channelId,
@Part("name") RequestBody name,
@Part("category") int category,
@Part("commentsEnabled") boolean commentsEnabled,
@Part("description") RequestBody description,
@Part("downloadEnabled") boolean downloadEnabled,
@Part("language") RequestBody language,
@Part("licence") RequestBody licence,
@Part("nsfw") boolean nsfw,
@Part("privacy") int privacy,
@Part("support") RequestBody support,
@Part("tags[]") List<RequestBody> tags,
@Part("waitTranscoding") boolean waitTranscoding,
@Part MultipartBody.Part thumbnailfile,
@Part MultipartBody.Part previewfile);
@DELETE("videos/{id}")
Call<String> deleteVideo(@Header("Authorization") String credentials, @Path("id") String videoId);
@GET("oauth-clients/local")
Call<Oauth> getOauthAcad();
@GET("oauth-clients/local")
Call<Oauth> getOauth(@Query("client_name") String client_name, @Query("redirect_uris") String redirect_uris, @Query("scopes") String scopes, @Query("website") String website);
//Post/Update/Delete channel
//Channels for account
@GET("accounts/{accountId}/video-channels")
Call<ChannelData> getChannelsForAccount(@Path("accountId") String accountId);
//Get a channel
@GET("video-channels/{name}")
Call<ChannelData.Channel> getChannel(@Path("name") String name);
@GET("video-channels")
Call<ChannelData> getAllChannels();
@GET("video-channels/{channelHandle}/videos")
Call<VideoData> getChannelVideos(
@Path("channelHandle") String channelHandle,
@Query("start") String maxId,
@Query("count") String count,
@Query("nsfw") String nsfw);
@POST("video-channels")
Call<ChannelData.ChannelCreation> addChannel(@Header("Authorization") String credentials, @Body ChannelParams channelParams);
@PUT("video-channels/{channelHandle}")
Call<String> updateChannel(@Header("Authorization") String credentials, @Path("channelHandle") String channelHandle, @Body ChannelParams channelParams);
@DELETE("video-channels/{channelHandle}")
Call<String> deleteChannel(@Header("Authorization") String credentials, @Path("channelHandle") String channelHandle);
//Get/Post/Update/Delete playlist
@GET("video-playlists")
Call<PlaylistData> getPlaylists();
//Get a single account
@GET("accounts/{accountHandle}")
Call<AccountData.Account> getAccount(@Path("accountHandle") String accountHandle);
//Get/Post/Update/Delete playlist
@GET("accounts/{accountHandle}/video-playlists")
Call<PlaylistData> getPlaylistsForAccount(@Header("Authorization") String credentials, @Path("accountHandle") String accountHandle);
@GET("video-playlists/{id}")
Call<PlaylistData.Playlist> getPlaylist(@Path("id") String id);
@GET("video-playlists/{id}/videos")
Call<VideoPlaylistData> getVideosPlayList(@Header("Authorization") String credentials, @Path("id") String id, @Query("start") String maxId, @Query("count") String count);
@GET("users/me/video-playlists/videos-exist")
Call<Map<String, List<PlaylistExist>>> getVideoExistsInPlaylist(@Header("Authorization") String credentials, @Query("videoIds") List<String> videoIds);
@Multipart
@POST("video-playlists")
Call<VideoPlaylistData.VideoPlaylistCreation> addPlaylist(
@Header("Authorization") String credentials,
@Part("displayName") RequestBody displayName,
@Part("description") RequestBody description,
@Part("privacy") int privacy,
@Part("videoChannelId") RequestBody videoChannelId,
@Part MultipartBody.Part thumbnailfile);
@Multipart
@PUT("video-playlists/{id}")
Call<String> updatePlaylist(
@Header("Authorization") String credentials,
@Path("id") String videoId,
@Part("displayName") RequestBody displayName,
@Part("description") RequestBody description,
@Part("privacy") int privacy,
@Part("videoChannelId") RequestBody videoChannelId,
@Part MultipartBody.Part thumbnailfil);
@FormUrlEncoded
@POST("video-playlists/{id}/videos")
Call<VideoPlaylistData.PlaylistElement> addVideoInPlaylist(@Header("Authorization") String credentials, @Path("id") String id, @Field("videoId") String videoId);
@DELETE("video-playlists/{id}")
Call<String> deletePlaylist(@Header("Authorization") String credentials, @Path("id") String playlistId);
@DELETE("video-playlists/{id}/videos/{playlistElementId}")
Call<String> deleteVideoInPlaylist(@Header("Authorization") String credentials, @Path("id") String videoId, @Path("playlistElementId") String playlistElementId);
//Get/Update/Delete captions
@GET("videos/{id}/captions")
Call<CaptionData> getCaptions(@Path("id") String videoId);
@PUT("videos/{id}/captions/{captionLanguage}")
Call<String> updateCaptions(@Header("Authorization") String credentials, @Path("id") String videoId, @Path("captionLanguage") String captionLanguage, @Body CaptionsParams captionsParams, @Part MultipartBody.Part captionfile);
@DELETE("videos/{id}/captions/{captionLanguage}")
Call<String> deleteCaptions(@Header("Authorization") String credentials, @Path("id") String videoId, @Path("captionLanguage") String captionLanguage);
//Subscribe/Unsubscribe
//subscribers
@GET("users/me/subscriptions")
Call<ChannelData> getSubscription(@Header("Authorization") String credentials, @Query("start") String maxId, @Query("count") String count);
@GET("users/me/subscriptions/exist")
Call<Map<String, Boolean>> getSubscriptionsExist(@Header("Authorization") String credentials, @Query("uris") List<String> uris);
@FormUrlEncoded
@POST("users/me/subscriptions")
Call<String> follow(@Header("Authorization") String credentials, @Field("uri") String uri);
@DELETE("users/me/subscriptions/{subscriptionHandle}")
Call<String> unfollow(@Header("Authorization") String credentials, @Path("subscriptionHandle") String subscriptionHandle);
//Mute/Unmute
//Muted accounts
@GET("users/me/blocklist/accounts")
Call<BlockData> getMuted(@Header("Authorization") String credentials, @Query("start") String maxId, @Query("count") String count);
@FormUrlEncoded
@POST("users/me/blocklist/accounts")
Call<String> mute(@Header("Authorization") String credentials, @Field("accountName") String accountName);
@DELETE("users/me/blocklist/accounts/{accountName}")
Call<String> unmute(@Header("Authorization") String credentials, @Path("accountName") String accountName);
//Get video rating
@GET("users/me/videos/{id}/rating")
Call<Rating> getRating(@Header("Authorization") String credentials, @Path("id") String id);
//Like/unlike
@FormUrlEncoded
@PUT("videos/{id}/rate")
Call<String> rate(@Header("Authorization") String credentials, @Path("id") String id, @Field("rating") String rating);
//Comment
@GET("videos/{id}/comment-threads")
Call<CommentData> getComments(@Path("id") String id, @Query("start") String maxId, @Query("count") String count);
@GET("videos/{id}/comment-threads/{threadId}")
Call<CommentData.CommentThreadData> getReplies(@Path("id") String id, @Path("threadId") String threadId);
@FormUrlEncoded
@POST("videos/{id}/comment-threads")
Call<CommentData.CommentPosted> postComment(@Header("Authorization") String credentials, @Path("id") String id, @Field("text") String text);
@FormUrlEncoded
@POST("videos/{id}/comments/{commentId}")
Call<CommentData.CommentPosted> postReply(@Header("Authorization") String credentials, @Path("id") String id, @Path("commentId") String commentId, @Field("text") String text);
@DELETE("videos/{id}/comments/{commentId}")
Call<String> deleteComment(@Header("Authorization") String credentials, @Path("id") String id, @Path("commentId") String commentId);
@POST("bulk/remove-comments-of")
Call<String> deleteAllCommentForAccount(@Header("Authorization") String credentials, @Field("accountName") String accountName, @Field("scope") String scope);
@Headers({"Content-Type: application/json", "Cache-Control: max-age=640000"})
@POST("abuses")
Call<Report.ReportReturn> report(
@Header("Authorization") String credentials,
@Body Report report);
@FormUrlEncoded
@POST("users/register")
Call<String> register(
@Field("email") String email,
@Field("password") String password,
@Field("username") String username,
@Field("displayName") String displayName
);
}

@ -0,0 +1,86 @@
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 <http://www.gnu.org/licenses>. */
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Locale;
import app.fedilab.android.peertube.client.data.VideoData;
import app.fedilab.android.peertube.client.entities.SepiaSearch;
import retrofit2.Call;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class RetrofitSepiaSearchAPI {
private final String finalUrl;
public RetrofitSepiaSearchAPI() {
finalUrl = "https://search.joinpeertube.org/api/v1/";
}
private SepiaSearchService init() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(finalUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
return retrofit.create(SepiaSearchService.class);
}
/**
* Return videos for a sepia search
*
* @param sepiaSearch SepiaSearch
* @return VideoData
*/
public VideoData getVideos(SepiaSearch sepiaSearch) {
SepiaSearchService sepiaSearchService = init();
SimpleDateFormat fmtOut = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH);
String startDate = null;
if (sepiaSearch.getStartDate() != null) {
startDate = fmtOut.format(sepiaSearch.getStartDate());
}
Call<VideoData> videoDataCall = sepiaSearchService.getVideos(
sepiaSearch.getStart(),
sepiaSearch.getCount(),
sepiaSearch.getSearch(),
sepiaSearch.getDurationMin(),
sepiaSearch.getDurationMax(),
startDate,
sepiaSearch.getBoostLanguages(),
sepiaSearch.getCategoryOneOf(),
sepiaSearch.getLicenceOneOf(),
sepiaSearch.getTagsOneOf(),
sepiaSearch.getTagsAllOf(),
sepiaSearch.isNsfw(),
sepiaSearch.getSort());
try {
Response<VideoData> response = videoDataCall.execute();
if (response.isSuccessful() && response.body() != null) {
return response.body();
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}

@ -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 <http://www.gnu.org/licenses>. */
import java.util.List;
import app.fedilab.android.peertube.client.data.ChannelData;
import app.fedilab.android.peertube.client.data.VideoData;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
interface SepiaSearchService {
@GET("search/videos")
Call<VideoData> getVideos(
@Query("start") String maxId,
@Query("count") String count,
@Query("search") String search,
@Query("durationMin") int durationMin,
@Query("durationMax") int durationMax,
@Query("startDate") String startDate,
@Query("boostLanguages") List<String> languageOneOf,
@Query("categoryOneOf") List<Integer> categoryOneOf,
@Query("licenceOneOf") List<Integer> licenceOneOf,
@Query("tagsOneOf") List<String> tagsOneOf,
@Query("tagsAllOf") List<String> tagsAllOf,
@Query("nsfw") boolean nsfw,
@Query("sort") String sort
);
@GET("search/channels")
Call<ChannelData> getChannels(
@Query("search") String search,
@Query("start") String maxId,
@Query("count") String count
);
}

@ -0,0 +1,275 @@
package app.fedilab.android.peertube.client.data;
/* 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 <http://www.gnu.org/licenses>. */
import android.os.Parcel;
import android.os.Parcelable;
import com.google.gson.annotations.SerializedName;
import java.util.Date;
import java.util.List;
import app.fedilab.android.peertube.client.entities.Avatar;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class AccountData {
@SerializedName("total")
public int total;
@SerializedName("data")
public List<Account> data;
public static class Account implements Parcelable {
public static final Creator<Account> CREATOR = new Creator<Account>() {
@Override
public Account createFromParcel(Parcel source) {
return new Account(source);
}
@Override
public Account[] newArray(int size) {
return new Account[size];
}
};
@SerializedName("avatar")
private Avatar avatar;
@SerializedName("createdAt")
private Date createdAt;
@SerializedName("description")
private String description;
@SerializedName("displayName")
private String displayName;
@SerializedName("followersCount")
private int followersCount;
@SerializedName("followingCount")
private int followingCount;
@SerializedName("host")
private String host;
@SerializedName("hostRedundancyAllowed")
private boolean hostRedundancyAllowed;
@SerializedName("id")
private String id;
@SerializedName("name")
private String name;
@SerializedName("username")
private String username;
@SerializedName("updatedAt")
private Date updatedAt;
@SerializedName("url")
private String url;
@SerializedName("userId")
private String userId;
private String token;
private String client_id;
private String client_secret;
private String refresh_token;
private String software;
public Account() {
}
protected Account(Parcel in) {
this.avatar = in.readParcelable(Avatar.class.getClassLoader());
long tmpCreatedAt = in.readLong();
this.createdAt = tmpCreatedAt == -1 ? null : new Date(tmpCreatedAt);
this.description = in.readString();
this.displayName = in.readString();
this.followersCount = in.readInt();
this.followingCount = in.readInt();
this.host = in.readString();
this.hostRedundancyAllowed = in.readByte() != 0;
this.id = in.readString();
this.name = in.readString();
this.username = in.readString();
long tmpUpdatedAt = in.readLong();
this.updatedAt = tmpUpdatedAt == -1 ? null : new Date(tmpUpdatedAt);
this.url = in.readString();
this.userId = in.readString();
}
public Avatar getAvatar() {
return avatar;
}
public void setAvatar(Avatar avatar) {
this.avatar = avatar;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public int getFollowersCount() {
return followersCount;
}
public void setFollowersCount(int followersCount) {
this.followersCount = followersCount;
}
public int getFollowingCount() {
return followingCount;
}
public void setFollowingCount(int followingCount) {
this.followingCount = followingCount;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public boolean isHostRedundancyAllowed() {
return hostRedundancyAllowed;
}
public void setHostRedundancyAllowed(boolean hostRedundancyAllowed) {
this.hostRedundancyAllowed = hostRedundancyAllowed;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return name != null ? name : username;
}
public void setUsername(String name) {
this.name = name;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getAcct() {
return name + "@" + host;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getClient_id() {
return client_id;
}
public void setClient_id(String client_id) {
this.client_id = client_id;
}
public String getClient_secret() {
return client_secret;
}
public void setClient_secret(String client_secret) {
this.client_secret = client_secret;
}
public String getRefresh_token() {
return refresh_token;
}
public void setRefresh_token(String refresh_token) {
this.refresh_token = refresh_token;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getSoftware() {
return software;
}
public void setSoftware(String software) {
this.software = software;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(this.avatar, flags);
dest.writeLong(this.createdAt != null ? this.createdAt.getTime() : -1);
dest.writeString(this.description);
dest.writeString(this.displayName);
dest.writeInt(this.followersCount);
dest.writeInt(this.followingCount);
dest.writeString(this.host);
dest.writeByte(this.hostRedundancyAllowed ? (byte) 1 : (byte) 0);
dest.writeString(this.id);
dest.writeString(this.name);
dest.writeString(this.username);
dest.writeLong(this.updatedAt != null ? this.updatedAt.getTime() : -1);
dest.writeString(this.url);
dest.writeString(this.userId);
}
}
}

@ -0,0 +1,81 @@
package app.fedilab.android.peertube.client.data;
/* 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 <http://www.gnu.org/licenses>. */
import com.google.gson.annotations.SerializedName;
import java.util.Date;
import java.util.List;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class BlockData {
@SerializedName("total")
public int total;
@SerializedName("data")
public List<Block> data;
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public List<Block> getData() {
return data;
}
public void setData(List<Block> data) {
this.data = data;
}
@SuppressWarnings("unused")
public static class Block {
@SerializedName("blockedAccount")
private AccountData.Account blockedAccount;
@SerializedName("byAccount")
private AccountData.Account byAccount;
@SerializedName("createdAt")
private Date createdAt;
public AccountData.Account getBlockedAccount() {
return blockedAccount;
}
public void setBlockedAccount(AccountData.Account blockedAccount) {
this.blockedAccount = blockedAccount;
}
public AccountData.Account getByAccount() {
return byAccount;
}
public void setByAccount(AccountData.Account byAccount) {
this.byAccount = byAccount;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
}
}

@ -0,0 +1,53 @@
package app.fedilab.android.peertube.client.data;
/* 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 <http://www.gnu.org/licenses>. */
import com.google.gson.annotations.SerializedName;
import java.util.List;
import app.fedilab.android.peertube.client.entities.ItemStr;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class CaptionData {
@SerializedName("total")
public int total;
@SerializedName("data")
public List<Caption> data;
public static class Caption {
@SerializedName("captionPath")
private String captionPath;
@SerializedName("language")
private ItemStr language;
public String getCaptionPath() {
return captionPath;
}
public void setCaptionPath(String captionPath) {
this.captionPath = captionPath;
}
public ItemStr getLanguage() {
return language;
}
public void setLanguage(ItemStr language) {
this.language = language;
}
}
}

@ -0,0 +1,294 @@
package app.fedilab.android.peertube.client.data;
/* 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 <http://www.gnu.org/licenses>. */
import android.os.Parcel;
import android.os.Parcelable;
import com.google.gson.annotations.SerializedName;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import app.fedilab.android.peertube.client.entities.Avatar;
import app.fedilab.android.peertube.client.entities.ItemStr;
import app.fedilab.android.peertube.client.entities.ViewsPerDay;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class ChannelData {
@SerializedName("total")
public int total;
@SerializedName("data")
public List<Channel> data;
public static class Channel implements Parcelable {
public static final Creator<Channel> CREATOR = new Creator<Channel>() {
@Override
public Channel createFromParcel(Parcel source) {
return new Channel(source);
}
@Override
public Channel[] newArray(int size) {
return new Channel[size];
}
};
@SerializedName("avatar")
private Avatar avatar;
@SerializedName("createdAt")
private Date createdAt;
@SerializedName("description")
private String description;
@SerializedName("displayName")
private String displayName;
@SerializedName("followersCount")
private int followersCount;
@SerializedName("followingCount")
private int followingCount;
@SerializedName("host")
private String host;
@SerializedName("hostRedundancyAllowed")
private boolean hostRedundancyAllowed;
@SerializedName("id")
private String id;
@SerializedName("isLocal")
private boolean isLocal;
@SerializedName("name")
private String name;
@SerializedName("ownerAccount")
private AccountData.Account ownerAccount;
@SerializedName("support")
private String support;
@SerializedName("updatedAt")
private Date updatedAt;
@SerializedName("url")
private String url;
@SerializedName("viewsPerDay")
private List<ViewsPerDay> viewsPerDays;
private String acct;
private boolean selected;
public Channel() {
}
protected Channel(Parcel in) {
this.avatar = in.readParcelable(Avatar.class.getClassLoader());
long tmpCreatedAt = in.readLong();
this.createdAt = tmpCreatedAt == -1 ? null : new Date(tmpCreatedAt);
this.description = in.readString();
this.displayName = in.readString();
this.followersCount = in.readInt();
this.followingCount = in.readInt();
this.host = in.readString();
this.hostRedundancyAllowed = in.readByte() != 0;
this.id = in.readString();
this.isLocal = in.readByte() != 0;
this.name = in.readString();
this.ownerAccount = in.readParcelable(AccountData.Account.class.getClassLoader());
this.support = in.readString();
long tmpUpdatedAt = in.readLong();
this.updatedAt = tmpUpdatedAt == -1 ? null : new Date(tmpUpdatedAt);
this.url = in.readString();
this.viewsPerDays = new ArrayList<>();
in.readList(this.viewsPerDays, ViewsPerDay.class.getClassLoader());
this.acct = in.readString();
}
public Avatar getAvatar() {
return avatar;
}
public void setAvatar(Avatar avatar) {
this.avatar = avatar;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public int getFollowersCount() {
return followersCount;
}
public void setFollowersCount(int followersCount) {
this.followersCount = followersCount;
}
public int getFollowingCount() {
return followingCount;
}
public void setFollowingCount(int followingCount) {
this.followingCount = followingCount;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public boolean isHostRedundancyAllowed() {
return hostRedundancyAllowed;
}
public void setHostRedundancyAllowed(boolean hostRedundancyAllowed) {
this.hostRedundancyAllowed = hostRedundancyAllowed;
}
public String getAcct() {
return name + "@" + host;
}
public void setAcct(String acct) {
this.acct = acct;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public boolean isLocal() {
return isLocal;
}
public void setLocal(boolean local) {
isLocal = local;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public AccountData.Account getOwnerAccount() {
return ownerAccount;
}
public void setOwnerAccount(AccountData.Account ownerAccount) {
this.ownerAccount = ownerAccount;
}
public String getSupport() {
return support;
}
public void setSupport(String support) {
this.support = support;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public List<ViewsPerDay> getViewsPerDays() {
return viewsPerDays;
}
public void setViewsPerDays(List<ViewsPerDay> viewsPerDays) {
this.viewsPerDays = viewsPerDays;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(this.avatar, flags);
dest.writeLong(this.createdAt != null ? this.createdAt.getTime() : -1);
dest.writeString(this.description);
dest.writeString(this.displayName);
dest.writeInt(this.followersCount);
dest.writeInt(this.followingCount);
dest.writeString(this.host);
dest.writeByte(this.hostRedundancyAllowed ? (byte) 1 : (byte) 0);
dest.writeString(this.id);
dest.writeByte(this.isLocal ? (byte) 1 : (byte) 0);
dest.writeString(this.name);
dest.writeParcelable(this.ownerAccount, flags);
dest.writeString(this.support);
dest.writeLong(this.updatedAt != null ? this.updatedAt.getTime() : -1);
dest.writeString(this.url);
dest.writeList(this.viewsPerDays);
dest.writeString(this.acct);
}
}
public static class ChannelCreation {
@SerializedName("videoChannel")
private ItemStr videoChannel;
public ItemStr getVideoChannel() {
return videoChannel;
}
public void setVideoChannel(ItemStr videoChannel) {
this.videoChannel = videoChannel;
}
}
}

@ -0,0 +1,265 @@
package app.fedilab.android.peertube.client.data;
/* 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 <http://www.gnu.org/licenses>. */
import com.google.gson.annotations.SerializedName;
import java.util.Date;
import java.util.List;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class CommentData {
@SerializedName("total")
public int total;
@SerializedName("data")
public List<Comment> data;
public static class Comment {
@SerializedName("account")
private AccountData.Account account;
@SerializedName("createdAt")
private Date createdAt;
@SerializedName("deletedAt")
private Date deletedAt;
@SerializedName("id")
private String id;
@SerializedName("inReplyToCommentId")
private String inReplyToCommentId;
@SerializedName("isDeleted")
private boolean isDeleted;
@SerializedName("text")
private String text;
@SerializedName("threadId")
private String threadId;
@SerializedName("totalReplies")
private int totalReplies;
@SerializedName("totalRepliesFromVideoAuthor")
private int totalRepliesFromVideoAuthor;
@SerializedName("updatedAt")
private String updatedAt;
@SerializedName("url")
private String url;
@SerializedName("videoId")
private String videoId;
private boolean isReply = false;
private boolean isReplyViewOpen = false;
public AccountData.Account getAccount() {
return account;
}
public void setAccount(AccountData.Account account) {
this.account = account;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getDeletedAt() {
return deletedAt;
}
public void setDeletedAt(Date deletedAt) {
this.deletedAt = deletedAt;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getInReplyToCommentId() {
return inReplyToCommentId;
}
public void setInReplyToCommentId(String inReplyToCommentId) {
this.inReplyToCommentId = inReplyToCommentId;
}
public boolean isDeleted() {
return isDeleted;
}
public void setDeleted(boolean deleted) {
isDeleted = deleted;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getThreadId() {
return threadId;
}
public void setThreadId(String threadId) {
this.threadId = threadId;
}
public int getTotalReplies() {
return totalReplies;
}
public void setTotalReplies(int totalReplies) {
this.totalReplies = totalReplies;
}
public int getTotalRepliesFromVideoAuthor() {
return totalRepliesFromVideoAuthor;
}
public void setTotalRepliesFromVideoAuthor(int totalRepliesFromVideoAuthor) {
this.totalRepliesFromVideoAuthor = totalRepliesFromVideoAuthor;
}
public String getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(String updatedAt) {
this.updatedAt = updatedAt;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getVideoId() {
return videoId;
}
public void setVideoId(String videoId) {
this.videoId = videoId;
}
public boolean isReply() {
return isReply;
}
public void setReply(boolean reply) {
isReply = reply;
}
public boolean isReplyViewOpen() {
return isReplyViewOpen;
}
public void setReplyViewOpen(boolean replyViewOpen) {
isReplyViewOpen = replyViewOpen;
}
}
public static class CommentThreadData {
@SerializedName("comment")
public Comment comment;
@SerializedName("children")
public List<CommentThreadData> children;
public Comment getComment() {
return comment;
}
public void setComment(Comment comment) {
this.comment = comment;
}
public List<CommentThreadData> getChildren() {
return children;
}
public void setChildren(List<CommentThreadData> children) {
this.children = children;
}
}
public static class CommentPosted {
@SerializedName("comment")
private Comment comment;
public Comment getComment() {
return comment;
}
public void setComment(Comment comment) {
this.comment = comment;
}
}
public static class NotificationComment {
@SerializedName("id")
private String id;
@SerializedName("threadId")
private String threadId;
@SerializedName("video")
private VideoData.Video video;
@SerializedName("account")
private AccountData.Account account;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getThreadId() {
return threadId;
}
public void setThreadId(String threadId) {
this.threadId = threadId;
}
public VideoData.Video getVideo() {
return video;
}
public void setVideo(VideoData.Video video) {
this.video = video;
}
public AccountData.Account getAccount() {
return account;
}
public void setAccount(AccountData.Account account) {
this.account = account;
}
}
}

@ -0,0 +1,427 @@
package app.fedilab.android.peertube.client.data;
/* 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 <http://www.gnu.org/licenses>. */
import android.os.Parcel;
import android.os.Parcelable;
import android.text.SpannableStringBuilder;
import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class InstanceData {
@SerializedName("total")
public int total;
@SerializedName("data")
public List<Instance> data;
public static class Instance {
@SerializedName("autoBlacklistUserVideosEnabled")
private boolean autoBlacklistUserVideosEnabled;
@SerializedName("categories")
private List<Integer> categories;
@SerializedName("country")
private String country;
@SerializedName("createdAt")
private Date createdAt;
@SerializedName("defaultNSFWPolicy")
private String defaultNSFWPolicy;
@SerializedName("health")
private int health;
@SerializedName("host")
private String host;
@SerializedName("id")
private String id;
@SerializedName("languages")
private List<String> languages;
@SerializedName("name")
private String name;
@SerializedName("shortDescription")
private String shortDescription;
@SerializedName("signupAllowed")
private boolean signupAllowed;
@SerializedName("supportsIPv6")
private boolean supportsIPv6;
@SerializedName("totalInstanceFollowers")
private int totalInstanceFollowers;
@SerializedName("totalInstanceFollowing")
private int totalInstanceFollowing;
@SerializedName("totalLocalVideos")
private int totalLocalVideos;
@SerializedName("totalUsers")
private int totalUsers;
@SerializedName("totalVideos")
private int totalVideos;
@SerializedName("userVideoQuota")
private String userVideoQuota;
@SerializedName("version")
private String version;
@SerializedName("isNSFW")
private boolean isNSFW;
private SpannableStringBuilder spannableStringBuilder;
private boolean truncatedDescription = true;
public boolean isAutoBlacklistUserVideosEnabled() {
return autoBlacklistUserVideosEnabled;
}
public void setAutoBlacklistUserVideosEnabled(boolean autoBlacklistUserVideosEnabled) {
this.autoBlacklistUserVideosEnabled = autoBlacklistUserVideosEnabled;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public String getDefaultNSFWPolicy() {
return defaultNSFWPolicy;
}
public void setDefaultNSFWPolicy(String defaultNSFWPolicy) {
this.defaultNSFWPolicy = defaultNSFWPolicy;
}
public int getHealth() {
return health;
}
public void setHealth(int health) {
this.health = health;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getShortDescription() {
return shortDescription;
}
public void setShortDescription(String shortDescription) {
this.shortDescription = shortDescription;
}
public boolean isSignupAllowed() {
return signupAllowed;
}
public void setSignupAllowed(boolean signupAllowed) {
this.signupAllowed = signupAllowed;
}
public boolean isSupportsIPv6() {
return supportsIPv6;
}
public void setSupportsIPv6(boolean supportsIPv6) {
this.supportsIPv6 = supportsIPv6;
}
public int getTotalInstanceFollowers() {
return totalInstanceFollowers;
}
public void setTotalInstanceFollowers(int totalInstanceFollowers) {
this.totalInstanceFollowers = totalInstanceFollowers;
}
public int getTotalInstanceFollowing() {
return totalInstanceFollowing;
}
public void setTotalInstanceFollowing(int totalInstanceFollowing) {
this.totalInstanceFollowing = totalInstanceFollowing;
}
public int getTotalLocalVideos() {
return totalLocalVideos;
}
public void setTotalLocalVideos(int totalLocalVideos) {
this.totalLocalVideos = totalLocalVideos;
}
public int getTotalUsers() {
return totalUsers;
}
public void setTotalUsers(int totalUsers) {
this.totalUsers = totalUsers;
}
public int getTotalVideos() {
return totalVideos;
}
public void setTotalVideos(int totalVideos) {
this.totalVideos = totalVideos;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public boolean isNSFW() {
return isNSFW;
}
public void setNSFW(boolean NSFW) {
isNSFW = NSFW;
}
public String getUserVideoQuota() {
return userVideoQuota;
}
public void setUserVideoQuota(String userVideoQuota) {
this.userVideoQuota = userVideoQuota;
}
public SpannableStringBuilder getSpannableStringBuilder() {
return spannableStringBuilder;
}
public void setSpannableStringBuilder(SpannableStringBuilder spannableStringBuilder) {
this.spannableStringBuilder = spannableStringBuilder;
}
public List<String> getLanguages() {
return languages;
}
public void setLanguages(List<String> languages) {
this.languages = languages;
}
public List<Integer> getCategories() {
return categories;
}
public void setCategories(List<Integer> categories) {
this.categories = categories;
}
public boolean isTruncatedDescription() {
return truncatedDescription;
}
public void setTruncatedDescription(boolean truncatedDescription) {
this.truncatedDescription = truncatedDescription;
}
}
public static class InstanceInfo {
@SerializedName("instance")
private AboutInstance instance;
public AboutInstance getInstance() {
return instance;
}
public void setInstance(AboutInstance instance) {
this.instance = instance;
}
}
public static class AboutInstance implements Parcelable, Serializable {
public static final Creator<AboutInstance> CREATOR = new Creator<AboutInstance>() {
@Override
public AboutInstance createFromParcel(Parcel in) {
return new AboutInstance(in);
}
@Override
public AboutInstance[] newArray(int size) {
return new AboutInstance[size];
}
};
@SerializedName("name")
private String name;
@SerializedName("shortDescription")
private String shortDescription;
@SerializedName("description")
private String description;
@SerializedName("terms")
private String terms;
private String host;
private boolean truncatedDescription = true;
public AboutInstance() {
}
protected AboutInstance(Parcel in) {
name = in.readString();
shortDescription = in.readString();
description = in.readString();
terms = in.readString();
host = in.readString();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getShortDescription() {
return shortDescription;
}
public void setShortDescription(String shortDescription) {
this.shortDescription = shortDescription;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getTerms() {
return terms;
}
public void setTerms(String terms) {
this.terms = terms;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public boolean isTruncatedDescription() {
return truncatedDescription;
}
public void setTruncatedDescription(boolean truncatedDescription) {
this.truncatedDescription = truncatedDescription;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(name);
parcel.writeString(shortDescription);
parcel.writeString(description);
parcel.writeString(terms);
parcel.writeString(host);
}
}
public static class InstanceConfig {
@SerializedName("user")
private User user;
@SerializedName("plugin")
private PluginData.Plugin plugin;
public PluginData.Plugin getPlugin() {
return plugin;
}
public void setPlugin(PluginData.Plugin plugin) {
this.plugin = plugin;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
public static class User {
@SerializedName("videoQuota")
private long videoQuota;
@SerializedName("videoQuotaDaily")
private long videoQuotaDaily;
public long getVideoQuota() {
return videoQuota;
}
public void setVideoQuota(long videoQuota) {
this.videoQuota = videoQuota;
}
public long getVideoQuotaDaily() {
return videoQuotaDaily;
}
public void setVideoQuotaDaily(long videoQuotaDaily) {
this.videoQuotaDaily = videoQuotaDaily;
}
}
}

@ -0,0 +1,167 @@
package app.fedilab.android.peertube.client.data;
import com.google.gson.annotations.SerializedName;
import java.util.Date;
import java.util.List;
import app.fedilab.android.peertube.client.entities.ActorFollow;
import app.fedilab.android.peertube.client.entities.VideoAbuse;
import app.fedilab.android.peertube.client.entities.VideoBlacklist;
/* 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 <http://www.gnu.org/licenses>. */
@SuppressWarnings({"unused", "RedundantSuppression"})
public class NotificationData {
@SerializedName("total")
public int total;
@SerializedName("data")
public List<Notification> data;
public static class Notification {
@SerializedName("id")
private String id;
@SerializedName("type")
private int type;
@SerializedName("read")
private boolean read;
@SerializedName("video")
private VideoData.Video video;
@SerializedName("videoImport")
private VideoData.VideoImport videoImport;
@SerializedName("comment")
private CommentData.NotificationComment comment;
@SerializedName("videoAbuse")
private VideoAbuse videoAbuse;
@SerializedName("abuse")
private VideoAbuse.Abuse abuse;
@SerializedName("videoBlacklist")
private VideoBlacklist videoBlacklist;
@SerializedName("account")
private AccountData.Account account;
@SerializedName("actorFollow")
private ActorFollow actorFollow;
@SerializedName("createdAt")
private Date createdAt;
@SerializedName("updatedAt")
private Date updatedAt;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public boolean isRead() {
return read;
}
public void setRead(boolean read) {
this.read = read;
}
public VideoData.Video getVideo() {
return video;
}
public void setVideo(VideoData.Video video) {
this.video = video;
}
public VideoData.VideoImport getVideoImport() {
return videoImport;
}
public void setVideoImport(VideoData.VideoImport videoImport) {
this.videoImport = videoImport;
}
public CommentData.NotificationComment getComment() {
return comment;
}
public void setComment(CommentData.NotificationComment comment) {
this.comment = comment;
}
public VideoAbuse getVideoAbuse() {
return videoAbuse;
}
public void setVideoAbuse(VideoAbuse videoAbuse) {
this.videoAbuse = videoAbuse;
}
public VideoBlacklist getVideoBlacklist() {
return videoBlacklist;
}
public void setVideoBlacklist(VideoBlacklist videoBlacklist) {
this.videoBlacklist = videoBlacklist;
}
public AccountData.Account getAccount() {
return account;
}
public void setAccount(AccountData.Account account) {
this.account = account;
}
public ActorFollow getActorFollow() {
return actorFollow;
}
public void setActorFollow(ActorFollow actorFollow) {
this.actorFollow = actorFollow;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
public VideoAbuse.Abuse getAbuse() {
return abuse;
}
public void setAbuse(VideoAbuse.Abuse abuse) {
this.abuse = abuse;
}
}
}

@ -0,0 +1,222 @@
package app.fedilab.android.peertube.client.data;
/* 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 <http://www.gnu.org/licenses>. */
import android.os.Parcel;
import android.os.Parcelable;
import com.google.gson.annotations.SerializedName;
import java.util.Date;
import java.util.List;
import app.fedilab.android.peertube.client.entities.Item;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class PlaylistData {
@SerializedName("total")
public int total;
@SerializedName("data")
public List<Playlist> data;
public static class Playlist implements Parcelable {
public static final Creator<Playlist> CREATOR = new Creator<Playlist>() {
@Override
public Playlist createFromParcel(Parcel source) {
return new Playlist(source);
}
@Override
public Playlist[] newArray(int size) {
return new Playlist[size];
}
};
@SerializedName("id")
private String id;
@SerializedName("createdAt")
private Date createdAt;
@SerializedName("updatedAt")
private Date updatedAt;
@SerializedName("description")
private String description;
@SerializedName("uuid")
private String uuid;
@SerializedName("displayName")
private String displayName;
@SerializedName("isLocal")
private boolean isLocal;
@SerializedName("videoLength")
private long videoLength;
@SerializedName("thumbnailPath")
private String thumbnailPath;
@SerializedName("privacy")
private Item privacy;
@SerializedName("type")
private Item type;
@SerializedName("ownerAccount")
private AccountData.Account ownerAccount;
@SerializedName("videoChannel")
private ChannelData.Channel videoChannel;
public Playlist() {
}
protected Playlist(Parcel in) {
this.id = in.readString();
long tmpCreatedAt = in.readLong();
this.createdAt = tmpCreatedAt == -1 ? null : new Date(tmpCreatedAt);
long tmpUpdatedAt = in.readLong();
this.updatedAt = tmpUpdatedAt == -1 ? null : new Date(tmpUpdatedAt);
this.description = in.readString();
this.uuid = in.readString();
this.displayName = in.readString();
this.isLocal = in.readByte() != 0;
this.videoLength = in.readLong();
this.thumbnailPath = in.readString();
this.privacy = in.readParcelable(Item.class.getClassLoader());
this.type = in.readParcelable(Item.class.getClassLoader());
this.ownerAccount = in.readParcelable(AccountData.Account.class.getClassLoader());
this.videoChannel = in.readParcelable(ChannelData.Channel.class.getClassLoader());
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public boolean isLocal() {
return isLocal;
}
public void setLocal(boolean local) {
isLocal = local;
}
public long getVideoLength() {
return videoLength;
}
public void setVideoLength(long videoLength) {
this.videoLength = videoLength;
}
public String getThumbnailPath() {
return thumbnailPath;
}
public void setThumbnailPath(String thumbnailPath) {
this.thumbnailPath = thumbnailPath;
}
public Item getPrivacy() {
return privacy;
}
public void setPrivacy(Item privacy) {
this.privacy = privacy;
}
public Item getType() {
return type;
}
public void setType(Item type) {
this.type = type;
}
public AccountData.Account getOwnerAccount() {
return ownerAccount;
}
public void setOwnerAccount(AccountData.Account ownerAccount) {
this.ownerAccount = ownerAccount;
}
public ChannelData.Channel getVideoChannel() {
return videoChannel;
}
public void setVideoChannel(ChannelData.Channel videoChannel) {
this.videoChannel = videoChannel;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.id);
dest.writeLong(this.createdAt != null ? this.createdAt.getTime() : -1);
dest.writeLong(this.updatedAt != null ? this.updatedAt.getTime() : -1);
dest.writeString(this.description);
dest.writeString(this.uuid);
dest.writeString(this.displayName);
dest.writeByte(this.isLocal ? (byte) 1 : (byte) 0);
dest.writeLong(this.videoLength);
dest.writeString(this.thumbnailPath);
dest.writeParcelable(this.privacy, flags);
dest.writeParcelable(this.type, flags);
dest.writeParcelable(this.ownerAccount, flags);
dest.writeParcelable(this.videoChannel, flags);
}
}
}

@ -0,0 +1,125 @@
package app.fedilab.android.peertube.client.data;
import com.google.gson.annotations.SerializedName;
import java.util.List;
/* 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 <http://www.gnu.org/licenses>. */
@SuppressWarnings("ALL")
public class PluginData {
public static class Plugin {
@SerializedName("registered")
private List<PluginInfo> registered;
@SerializedName("registeredExternalAuths")
private List<PluginInfo> registeredExternalAuths;
@SerializedName("registeredIdAndPassAuths")
private List<PluginInfo> registeredIdAndPassAuths;
public List<PluginInfo> getRegistered() {
return registered;
}
public void setRegistered(List<PluginInfo> registered) {
this.registered = registered;
}
public List<PluginInfo> getRegisteredExternalAuths() {
return registeredExternalAuths;
}
public void setRegisteredExternalAuths(List<PluginInfo> registeredExternalAuths) {
this.registeredExternalAuths = registeredExternalAuths;
}
public List<PluginInfo> getRegisteredIdAndPassAuths() {
return registeredIdAndPassAuths;
}
public void setRegisteredIdAndPassAuths(List<PluginInfo> registeredIdAndPassAuths) {
this.registeredIdAndPassAuths = registeredIdAndPassAuths;
}
}
public static class WaterMark {
@SerializedName("publicSettings")
private PublicSettings description;
public PublicSettings getDescription() {
return description;
}
public void setDescription(PublicSettings description) {
this.description = description;
}
}
public static class PublicSettings {
@SerializedName("watermark-image-url")
private String watermarkImageUrl;
@SerializedName("watermark-target-url")
private String watermarkTargetUrl;
public String getWatermarkImageUrl() {
return watermarkImageUrl;
}
public void setWatermarkImageUrl(String watermarkImageUrl) {
this.watermarkImageUrl = watermarkImageUrl;
}
public String getWatermarkTargetUrl() {
return watermarkTargetUrl;
}
public void setWatermarkTargetUrl(String watermarkTargetUrl) {
this.watermarkTargetUrl = watermarkTargetUrl;
}
}
public static class PluginInfo {
@SerializedName("description")
private String description;
@SerializedName("name")
private String name;
@SerializedName("version")
private String version;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
}
}

@ -0,0 +1,885 @@
package app.fedilab.android.peertube.client.data;
/* 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 <http://www.gnu.org/licenses>. */
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Parcel;
import android.os.Parcelable;
import com.google.gson.annotations.SerializedName;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import app.fedilab.android.peertube.client.data.AccountData.Account;
import app.fedilab.android.peertube.client.entities.File;
import app.fedilab.android.peertube.client.entities.Item;
import app.fedilab.android.peertube.client.entities.ItemStr;
import app.fedilab.android.peertube.client.entities.PlaylistExist;
import app.fedilab.android.peertube.client.entities.StreamingPlaylists;
import app.fedilab.android.peertube.helper.Helper;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class VideoData {
@SerializedName("total")
public int total;
@SerializedName("data")
public List<Video> data;
public static class Video implements Parcelable {
public static final Creator<Video> CREATOR = new Creator<Video>() {
@Override
public Video createFromParcel(Parcel source) {
return new Video(source);
}
@Override
public Video[] newArray(int size) {
return new Video[size];
}
};
@SerializedName("account")
private Account account;
@SerializedName("blacklisted")
private boolean blacklisted;
@SerializedName("blacklistedReason")
private String blacklistedReason;
@SerializedName("category")
private Item category;
@SerializedName("channel")
private ChannelData.Channel channel;
@SerializedName("commentsEnabled")
private boolean commentsEnabled;
@SerializedName("createdAt")
private Date createdAt;
@SerializedName("description")
private String description;
@SerializedName("descriptionPath")
private String descriptionPath;
@SerializedName("dislikes")
private int dislikes;
@SerializedName("downloadEnabled")
private boolean downloadEnabled;
@SerializedName("duration")
private int duration;
@SerializedName("embedPath")
private String embedPath;
@SerializedName("embedUrl")
private String embedUrl;
@SerializedName("files")
private List<File> files;
@SerializedName("id")
private String id;
@SerializedName("isLive")
private boolean isLive = false;
@SerializedName("isLocal")
private boolean isLocal;
@SerializedName("language")
private ItemStr language;
@SerializedName("licence")
private Item licence;
@SerializedName("likes")
private int likes;
@SerializedName("name")
private String name;
@SerializedName("nsfw")
private boolean nsfw;
@SerializedName("originallyPublishedAt")
private Date originallyPublishedAt;
@SerializedName("previewPath")
private String previewPath;
@SerializedName("privacy")
private Item privacy;
@SerializedName("publishedAt")
private Date publishedAt;
@SerializedName("state")
private Item state;
@SerializedName("streamingPlaylists")
private List<StreamingPlaylists> streamingPlaylists;
@SerializedName("support")
private String support;
@SerializedName("tags")
private List<String> tags;
@SerializedName("thumbnailPath")
private String thumbnailPath;
@SerializedName("trackerUrls")
private List<String> trackerUrls;
@SerializedName("updatedAt")
private Date updatedAt;
@SerializedName("userHistory")
private UserHistory userHistory;
@SerializedName("uuid")
private String uuid;
@SerializedName("views")
private int views;
@SerializedName("waitTranscoding")
private boolean waitTranscoding;
private String myRating;
private String originUrl;
private int errorCode;
private String errorMessage;
//Dedicated to overview videos to reuse the logic of videos
private boolean hasTitle = false;
private String title;
private titleType titleType;
private List<PlaylistExist> playlistExists;
public Video() {
}
protected Video(Parcel in) {
this.account = in.readParcelable(Account.class.getClassLoader());
this.blacklisted = in.readByte() != 0;
this.blacklistedReason = in.readString();
this.category = in.readParcelable(Item.class.getClassLoader());
this.channel = in.readParcelable(ChannelData.Channel.class.getClassLoader());
this.commentsEnabled = in.readByte() != 0;
long tmpCreatedAt = in.readLong();
this.createdAt = tmpCreatedAt == -1 ? null : new Date(tmpCreatedAt);
this.description = in.readString();
this.descriptionPath = in.readString();
this.dislikes = in.readInt();
this.downloadEnabled = in.readByte() != 0;
this.duration = in.readInt();
this.embedPath = in.readString();
this.embedUrl = in.readString();
this.files = new ArrayList<>();
in.readList(this.files, File.class.getClassLoader());
this.id = in.readString();
this.isLive = in.readByte() != 0;
this.isLocal = in.readByte() != 0;
this.language = in.readParcelable(ItemStr.class.getClassLoader());
this.licence = in.readParcelable(Item.class.getClassLoader());
this.likes = in.readInt();
this.name = in.readString();
this.nsfw = in.readByte() != 0;
long tmpOriginallyPublishedAt = in.readLong();
this.originallyPublishedAt = tmpOriginallyPublishedAt == -1 ? null : new Date(tmpOriginallyPublishedAt);
this.previewPath = in.readString();
this.privacy = in.readParcelable(Item.class.getClassLoader());
long tmpPublishedAt = in.readLong();
this.publishedAt = tmpPublishedAt == -1 ? null : new Date(tmpPublishedAt);
this.state = in.readParcelable(Item.class.getClassLoader());
this.streamingPlaylists = new ArrayList<>();
in.readList(this.streamingPlaylists, StreamingPlaylists.class.getClassLoader());
this.support = in.readString();
this.tags = in.createStringArrayList();
this.thumbnailPath = in.readString();
this.trackerUrls = in.createStringArrayList();
long tmpUpdatedAt = in.readLong();
this.updatedAt = tmpUpdatedAt == -1 ? null : new Date(tmpUpdatedAt);
this.userHistory = in.readParcelable(UserHistory.class.getClassLoader());
this.uuid = in.readString();
this.views = in.readInt();
this.waitTranscoding = in.readByte() != 0;
this.myRating = in.readString();
}
public String getFileUrl(String resolution, Context context) {
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
int mode = sharedpreferences.getInt(Helper.SET_VIDEO_MODE, Helper.VIDEO_MODE_NORMAL);
List<File> files = getAllFile(context);
if (files != null && files.size() > 0) {
return getFile(context, files, resolution, mode);
}
return null;
}
public List<File> getAllFile(Context context) {
if (files != null && files.size() > 0) {
return files;
} else if (streamingPlaylists != null) {
List<File> files = new ArrayList<>();
for (StreamingPlaylists streamingPlaylists : streamingPlaylists) {
if (streamingPlaylists.getFiles().size() > 0) {
files.addAll(streamingPlaylists.getFiles());
} else {
File file = new File();
file.setFileUrl(streamingPlaylists.getPlaylistUrl());
file.setFileDownloadUrl(streamingPlaylists.getPlaylistUrl());
files.add(file);
}
}
return files;
}
return files;
}
private String getFile(Context context, List<File> files, String resolution, int mode) {
if (resolution != null) {
for (File file : files) {
if (file.getResolutions().getLabel().compareTo(resolution) == 0) {
return file.getFileUrl();
}
}
}
File file = Helper.defaultFile(context, files);
if (file != null) {
return file.getFileUrl();
} else {
return null;
}
}
public String getTorrentUrl(String resolution, Context context) {
for (File file : files) {
if (file.getResolutions().getLabel().compareTo(resolution) == 0) {
return file.getTorrentUrl();
}
}
return Helper.defaultFile(context, files).getTorrentUrl();
}
public String getTorrentDownloadUrl(String resolution) {
for (File file : files) {
if (file.getResolutions().getLabel().compareTo(resolution) == 0) {
return file.getTorrentDownloadUrl();
}
}
return files.get(0).getTorrentDownloadUrl();
}
public String getFileDownloadUrl(String resolution) {
if (resolution != null) {
for (File file : files) {
if (file.getResolutions().getLabel().compareTo(resolution) == 0) {
return file.getFileDownloadUrl();
}
}
}
return files != null && files.size() > 0 ? files.get(0).getFileDownloadUrl() : null;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public boolean isBlacklisted() {
return blacklisted;
}
public void setBlacklisted(boolean blacklisted) {
this.blacklisted = blacklisted;
}
public String getBlacklistedReason() {
return blacklistedReason;
}
public void setBlacklistedReason(String blacklistedReason) {
this.blacklistedReason = blacklistedReason;
}
public Item getCategory() {
return category;
}
public void setCategory(Item category) {
this.category = category;
}
public ChannelData.Channel getChannel() {
return channel;
}
public void setChannel(ChannelData.Channel channel) {
this.channel = channel;
}
public boolean isCommentsEnabled() {
return commentsEnabled;
}
public void setCommentsEnabled(boolean commentsEnabled) {
this.commentsEnabled = commentsEnabled;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getDescriptionPath() {
return descriptionPath;
}
public void setDescriptionPath(String descriptionPath) {
this.descriptionPath = descriptionPath;
}
public int getDislikes() {
return dislikes;
}
public void setDislikes(int dislikes) {
this.dislikes = dislikes;
}
public boolean isDownloadEnabled() {
return downloadEnabled;
}
public void setDownloadEnabled(boolean downloadEnabled) {
this.downloadEnabled = downloadEnabled;
}
public int getDuration() {
return duration;
}
public void setDuration(int duration) {
this.duration = duration;
}
public String getEmbedPath() {
return embedPath;
}
public void setEmbedPath(String embedPath) {
this.embedPath = embedPath;
}
public String getEmbedUrl() {
return embedUrl;
}
public void setEmbedUrl(String embedUrl) {
this.embedUrl = embedUrl;
}
public List<File> getFiles() {
return files;
}
public void setFiles(List<File> files) {
this.files = files;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public boolean isLive() {
return isLive;
}
public void setLive(boolean live) {
isLive = live;
}
public boolean isLocal() {
return isLocal;
}
public void setLocal(boolean local) {
isLocal = local;
}
public ItemStr getLanguage() {
return language;
}
public void setLanguage(ItemStr language) {
this.language = language;
}
public Item getLicence() {
return licence;
}
public void setLicence(Item licence) {
this.licence = licence;
}
public int getLikes() {
return likes;
}
public void setLikes(int likes) {
this.likes = likes;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isNsfw() {
return nsfw;
}
public void setNsfw(boolean nsfw) {
this.nsfw = nsfw;
}
public Date getOriginallyPublishedAt() {
return originallyPublishedAt;
}
public void setOriginallyPublishedAt(Date originallyPublishedAt) {
this.originallyPublishedAt = originallyPublishedAt;
}
public String getPreviewPath() {
return previewPath;
}
public void setPreviewPath(String previewPath) {
this.previewPath = previewPath;
}
public Item getPrivacy() {
return privacy;
}
public void setPrivacy(Item privacy) {
this.privacy = privacy;
}
public Date getPublishedAt() {
return publishedAt;
}
public void setPublishedAt(Date publishedAt) {
this.publishedAt = publishedAt;
}
public Item getState() {
return state;
}
public void setState(Item state) {
this.state = state;
}
public List<StreamingPlaylists> getStreamingPlaylists() {
return streamingPlaylists;
}
public void setStreamingPlaylists(List<StreamingPlaylists> streamingPlaylists) {
this.streamingPlaylists = streamingPlaylists;
}
public String getSupport() {
return support;
}
public void setSupport(String support) {
this.support = support;
}
public List<String> getTags() {
return tags;
}
public void setTags(List<String> tags) {
this.tags = tags;
}
public String getThumbnailPath() {
return thumbnailPath;
}
public void setThumbnailPath(String thumbnailPath) {
this.thumbnailPath = thumbnailPath;
}
public List<String> getTrackerUrls() {
return trackerUrls;
}
public void setTrackerUrls(List<String> trackerUrls) {
this.trackerUrls = trackerUrls;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public int getViews() {
return views;
}
public void setViews(int views) {
this.views = views;
}
public boolean isWaitTranscoding() {
return waitTranscoding;
}
public void setWaitTranscoding(boolean waitTranscoding) {
this.waitTranscoding = waitTranscoding;
}
public String getOriginUrl() {
return originUrl;
}
public void setOriginUrl(String originUrl) {
this.originUrl = originUrl;
}
public int getErrorCode() {
return errorCode;
}
public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}
public String getMyRating() {
return myRating;
}
public void setMyRating(String myRating) {
this.myRating = myRating;
}
public boolean isHasTitle() {
return hasTitle;
}
public void setHasTitle(boolean hasTitle) {
this.hasTitle = hasTitle;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Video.titleType getTitleType() {
return titleType;
}
public void setTitleType(Video.titleType titleType) {
this.titleType = titleType;
}
public List<PlaylistExist> getPlaylistExists() {
return playlistExists;
}
public void setPlaylistExists(List<PlaylistExist> playlistExists) {
this.playlistExists = playlistExists;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public UserHistory getUserHistory() {
return userHistory;
}
public void setUserHistory(UserHistory userHistory) {
this.userHistory = userHistory;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(this.account, flags);
dest.writeByte(this.blacklisted ? (byte) 1 : (byte) 0);
dest.writeString(this.blacklistedReason);
dest.writeParcelable(this.category, flags);
dest.writeParcelable(this.channel, flags);
dest.writeByte(this.commentsEnabled ? (byte) 1 : (byte) 0);
dest.writeLong(this.createdAt != null ? this.createdAt.getTime() : -1);
dest.writeString(this.description);
dest.writeString(this.descriptionPath);
dest.writeInt(this.dislikes);
dest.writeByte(this.downloadEnabled ? (byte) 1 : (byte) 0);
dest.writeInt(this.duration);
dest.writeString(this.embedPath);
dest.writeString(this.embedUrl);
dest.writeList(this.files);
dest.writeString(this.id);
dest.writeByte(this.isLive ? (byte) 1 : (byte) 0);
dest.writeByte(this.isLocal ? (byte) 1 : (byte) 0);
dest.writeParcelable(this.language, flags);
dest.writeParcelable(this.licence, flags);
dest.writeInt(this.likes);
dest.writeString(this.name);
dest.writeByte(this.nsfw ? (byte) 1 : (byte) 0);
dest.writeLong(this.originallyPublishedAt != null ? this.originallyPublishedAt.getTime() : -1);
dest.writeString(this.previewPath);
dest.writeParcelable(this.privacy, flags);
dest.writeLong(this.publishedAt != null ? this.publishedAt.getTime() : -1);
dest.writeParcelable(this.state, flags);
dest.writeList(this.streamingPlaylists);
dest.writeString(this.support);
dest.writeStringList(this.tags);
dest.writeString(this.thumbnailPath);
dest.writeStringList(this.trackerUrls);
dest.writeLong(this.updatedAt != null ? this.updatedAt.getTime() : -1);
dest.writeParcelable(this.userHistory, flags);
dest.writeString(this.uuid);
dest.writeInt(this.views);
dest.writeByte(this.waitTranscoding ? (byte) 1 : (byte) 0);
dest.writeString(this.myRating);
}
public enum titleType {
TAG,
CATEGORY,
CHANNEL
}
}
public static class VideoImport {
@SerializedName("id")
private String id;
@SerializedName("video")
private Video video;
@SerializedName("torrentName")
private String torrentName;
@SerializedName("magnetUri")
private String magnetUri;
@SerializedName("targetUri")
private String targetUri;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Video getVideo() {
return video;
}
public void setVideo(Video video) {
this.video = video;
}
public String getTorrentName() {
return torrentName;
}
public void setTorrentName(String torrentName) {
this.torrentName = torrentName;
}
public String getMagnetUri() {
return magnetUri;
}
public void setMagnetUri(String magnetUri) {
this.magnetUri = magnetUri;
}
public String getTargetUri() {
return targetUri;
}
public void setTargetUri(String targetUri) {
this.targetUri = targetUri;
}
}
public static class UserHistory implements Parcelable {
public static final Creator<UserHistory> CREATOR = new Creator<UserHistory>() {
@Override
public UserHistory createFromParcel(Parcel in) {
return new UserHistory(in);
}
@Override
public UserHistory[] newArray(int size) {
return new UserHistory[size];
}
};
@SerializedName("currentTime")
long currentTime;
public UserHistory() {
}
protected UserHistory(Parcel in) {
this.currentTime = in.readLong();
}
public long getCurrentTime() {
return currentTime;
}
public void setCurrentTime(long currentTime) {
this.currentTime = currentTime;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeLong(currentTime);
}
}
public static class Description {
@SerializedName("description")
private String description;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
public static class VideoExport implements Parcelable {
public static final Creator<VideoExport> CREATOR = new Creator<VideoExport>() {
@Override
public VideoExport createFromParcel(Parcel in) {
return new VideoExport(in);
}
@Override
public VideoExport[] newArray(int size) {
return new VideoExport[size];
}
};
private int id;
private String uuid;
private Video videoData;
private int playlistDBid;
public VideoExport() {
}
protected VideoExport(Parcel in) {
id = in.readInt();
uuid = in.readString();
videoData = in.readParcelable(Video.class.getClassLoader());
playlistDBid = in.readInt();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public Video getVideoData() {
return videoData;
}
public void setVideoData(Video videoData) {
this.videoData = videoData;
}
public int getPlaylistDBid() {
return playlistDBid;
}
public void setPlaylistDBid(int playlistDBid) {
this.playlistDBid = playlistDBid;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(id);
parcel.writeString(uuid);
parcel.writeParcelable(videoData, i);
parcel.writeInt(playlistDBid);
}
}
}

@ -0,0 +1,247 @@
package app.fedilab.android.peertube.client.data;
/* 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 <http://www.gnu.org/licenses>. */
import android.os.Parcel;
import android.os.Parcelable;
import com.google.gson.annotations.SerializedName;
import java.util.List;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class VideoPlaylistData {
@SerializedName("total")
public int total;
@SerializedName("data")
public List<VideoPlaylist> data;
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public List<VideoPlaylist> getData() {
return data;
}
public void setData(List<VideoPlaylist> data) {
this.data = data;
}
public static class VideoPlaylist {
@SerializedName("id")
private String id;
@SerializedName("position")
private String position;
@SerializedName("startTimestamp")
private long startTimestamp;
@SerializedName("stopTimestamp")
private long stopTimestamp;
@SerializedName("type")
private int type;
@SerializedName("video")
private VideoData.Video video;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
public long getStartTimestamp() {
return startTimestamp;
}
public void setStartTimestamp(long startTimestamp) {
this.startTimestamp = startTimestamp;
}
public long getStopTimestamp() {
return stopTimestamp;
}
public void setStopTimestamp(long stopTimestamp) {
this.stopTimestamp = stopTimestamp;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public VideoData.Video getVideo() {
return video;
}
public void setVideo(VideoData.Video video) {
this.video = video;
}
}
public static class VideoPlaylistCreation {
@SerializedName("videoPlaylist")
private VideoPlaylistCreationItem videoPlaylist;
public VideoPlaylistCreationItem getVideoPlaylist() {
return videoPlaylist;
}
public void setVideoPlaylist(VideoPlaylistCreationItem videoPlaylist) {
this.videoPlaylist = videoPlaylist;
}
}
public static class PlaylistElement {
@SerializedName("videoPlaylistElement")
private VideoPlaylistCreationItem videoPlaylistElement;
public VideoPlaylistCreationItem getVideoPlaylistElement() {
return videoPlaylistElement;
}
public void setVideoPlaylistElement(VideoPlaylistCreationItem videoPlaylistElement) {
this.videoPlaylistElement = videoPlaylistElement;
}
}
public static class VideoPlaylistCreationItem {
@SerializedName("id")
String id;
@SerializedName("uuid")
String uuid;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
}
public static class VideoPlaylistExport implements Parcelable {
public static final Creator<VideoPlaylistExport> CREATOR = new Creator<VideoPlaylistExport>() {
@Override
public VideoPlaylistExport createFromParcel(Parcel in) {
return new VideoPlaylistExport(in);
}
@Override
public VideoPlaylistExport[] newArray(int size) {
return new VideoPlaylistExport[size];
}
};
private long playlistDBkey;
private String acct;
private String uuid;
private PlaylistData.Playlist playlist;
private List<VideoPlaylist> videos;
public VideoPlaylistExport() {
}
protected VideoPlaylistExport(Parcel in) {
playlistDBkey = in.readLong();
acct = in.readString();
uuid = in.readString();
playlist = in.readParcelable(PlaylistData.Playlist.class.getClassLoader());
in.readList(this.videos, VideoPlaylist.class.getClassLoader());
}
public String getAcct() {
return acct;
}
public void setAcct(String acct) {
this.acct = acct;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public PlaylistData.Playlist getPlaylist() {
return playlist;
}
public void setPlaylist(PlaylistData.Playlist playlist) {
this.playlist = playlist;
}
public long getPlaylistDBkey() {
return playlistDBkey;
}
public void setPlaylistDBkey(long playlistDBkey) {
this.playlistDBkey = playlistDBkey;
}
public List<VideoPlaylist> getVideos() {
return videos;
}
public void setVideos(List<VideoPlaylist> videos) {
this.videos = videos;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeLong(playlistDBkey);
parcel.writeString(acct);
parcel.writeString(uuid);
parcel.writeParcelable(playlist, i);
parcel.writeList(videos);
}
}
}

@ -0,0 +1,71 @@
package app.fedilab.android.peertube.client.entities;
/* Copyright 2021 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 <http://www.gnu.org/licenses>. */
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.helper.HelperAcadInstance;
public class AcadInstances {
private String name;
private String url;
private boolean openId;
public static boolean isOpenId(String domain) {
List<AcadInstances> instances = getInstances();
for (AcadInstances acadInstance : instances) {
if (acadInstance.getUrl().compareTo(domain) == 0) {
return acadInstance.isOpenId();
}
}
return false;
}
public static List<AcadInstances> getInstances() {
List<AcadInstances> acadInstances = new ArrayList<>();
LinkedHashMap<String, String> instancesMap = new LinkedHashMap<>(HelperAcadInstance.instances_themes);
Iterator<Map.Entry<String, String>> it = instancesMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> pair = it.next();
AcadInstances acadInstance = new AcadInstances();
acadInstance.name = pair.getKey();
acadInstance.openId = true;
acadInstance.url = pair.getValue();
acadInstances.add(acadInstance);
it.remove();
}
return acadInstances;
}
public String getName() {
return name;
}
public String getUrl() {
return url;
}
public boolean isOpenId() {
return openId;
}
}

@ -0,0 +1,74 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
@SuppressWarnings({"unused", "RedundantSuppression"})
public class AccountCreation {
private String username;
private String displayName;
private String email;
private String password;
private String passwordConfirm;
private String instance;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPasswordConfirm() {
return passwordConfirm;
}
public void setPasswordConfirm(String passwordConfirm) {
this.passwordConfirm = passwordConfirm;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public String getInstance() {
return instance;
}
public void setInstance(String instance) {
this.instance = instance;
}
}

@ -0,0 +1,71 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
import com.google.gson.annotations.SerializedName;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class Actor {
@SerializedName("type")
private String type;
@SerializedName("name")
private String name;
@SerializedName("displayName")
private String displayName;
@SerializedName("host")
private String host;
@SerializedName("avatar")
private Avatar avatar;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public Avatar getAvatar() {
return avatar;
}
public void setAvatar(Avatar avatar) {
this.avatar = avatar;
}
}

@ -0,0 +1,62 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
import com.google.gson.annotations.SerializedName;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class ActorFollow {
@SerializedName("id")
private String id;
@SerializedName("follower")
private Actor follower;
@SerializedName("following")
private Actor following;
@SerializedName("state")
private String state;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Actor getFollower() {
return follower;
}
public void setFollower(Actor follower) {
this.follower = follower;
}
public Actor getFollowing() {
return following;
}
public void setFollowing(Actor following) {
this.following = following;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}

@ -0,0 +1,92 @@
package app.fedilab.android.peertube.client.entities;
import android.os.Parcel;
import android.os.Parcelable;
import com.google.gson.annotations.SerializedName;
import java.util.Date;
/* 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 <http://www.gnu.org/licenses>. */
@SuppressWarnings({"unused", "RedundantSuppression"})
public class Avatar implements Parcelable {
public static final Creator<Avatar> CREATOR = new Creator<Avatar>() {
@Override
public Avatar createFromParcel(Parcel source) {
return new Avatar(source);
}
@Override
public Avatar[] newArray(int size) {
return new Avatar[size];
}
};
@SerializedName("createdAt")
private Date createdAt;
@SerializedName("path")
private String path;
@SerializedName("updatedAt")
private Date updatedAt;
public Avatar() {
}
protected Avatar(Parcel in) {
long tmpCreatedAt = in.readLong();
this.createdAt = tmpCreatedAt == -1 ? null : new Date(tmpCreatedAt);
this.path = in.readString();
long tmpUpdatedAt = in.readLong();
this.updatedAt = tmpUpdatedAt == -1 ? null : new Date(tmpUpdatedAt);
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(this.createdAt != null ? this.createdAt.getTime() : -1);
dest.writeString(this.path);
dest.writeLong(this.updatedAt != null ? this.updatedAt.getTime() : -1);
}
}

@ -0,0 +1,42 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
import com.google.gson.annotations.SerializedName;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class CaptionsParams {
@SerializedName("captionLanguage")
private String captionLanguage;
@SerializedName("id")
private String id;
public String getCaptionLanguage() {
return captionLanguage;
}
public void setCaptionLanguage(String captionLanguage) {
this.captionLanguage = captionLanguage;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}

@ -0,0 +1,72 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
import com.google.gson.annotations.SerializedName;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class ChannelParams {
@SerializedName("displayName")
private String displayName;
@SerializedName("name")
private String name;
@SerializedName("description")
private String description;
@SerializedName("support")
private String support;
@SerializedName("bulkVideosSupportUpdate")
private boolean bulkVideosSupportUpdate;
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getSupport() {
return support;
}
public void setSupport(String support) {
this.support = support;
}
public boolean isBulkVideosSupportUpdate() {
return bulkVideosSupportUpdate;
}
public void setBulkVideosSupportUpdate(boolean bulkVideosSupportUpdate) {
this.bulkVideosSupportUpdate = bulkVideosSupportUpdate;
}
}

@ -0,0 +1,61 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.widget.Toast;
import app.fedilab.android.peertube.R;
import es.dmoral.toasty.Toasty;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class Error extends Throwable {
private String error = null;
private int statusCode = -1;
public static void displayError(Context context, Error error) {
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> {
String message;
if (error.getError() != null && error.getError().trim().length() > 0)
message = error.getError();
else
message = context.getString(R.string.toast_error);
Toasty.error(context, message, Toast.LENGTH_LONG).show();
};
mainHandler.post(myRunnable);
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public int getStatusCode() {
return statusCode;
}
public void setStatusCode(int statusCode) {
this.statusCode = statusCode;
}
}

@ -0,0 +1,146 @@
package app.fedilab.android.peertube.client.entities;
import android.os.Parcel;
import android.os.Parcelable;
import com.google.gson.annotations.SerializedName;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class File implements Parcelable {
public static final Creator<File> CREATOR = new Creator<File>() {
@Override
public File createFromParcel(Parcel source) {
return new File(source);
}
@Override
public File[] newArray(int size) {
return new File[size];
}
};
@SerializedName("fileDownloadUrl")
private String fileDownloadUrl;
@SerializedName("fileUrl")
private String fileUrl;
@SerializedName("fps")
private int fps;
@SerializedName("magnetUri")
private String magnetUri;
@SerializedName("metadataUrl")
private String metadataUrl;
@SerializedName("resolution")
private Item resolutions;
@SerializedName("size")
private long size;
@SerializedName("torrentDownloadUrl")
private String torrentDownloadUrl;
@SerializedName("torrentUrl")
private String torrentUrl;
public File() {
}
protected File(Parcel in) {
this.fileDownloadUrl = in.readString();
this.fileUrl = in.readString();
this.fps = in.readInt();
this.magnetUri = in.readString();
this.metadataUrl = in.readString();
this.resolutions = in.readParcelable(Item.class.getClassLoader());
this.size = in.readLong();
this.torrentDownloadUrl = in.readString();
this.torrentUrl = in.readString();
}
public String getFileDownloadUrl() {
return fileDownloadUrl;
}
public void setFileDownloadUrl(String fileDownloadUrl) {
this.fileDownloadUrl = fileDownloadUrl;
}
public String getFileUrl() {
return fileUrl;
}
public void setFileUrl(String fileUrl) {
this.fileUrl = fileUrl;
}
public int getFps() {
return fps;
}
public void setFps(int fps) {
this.fps = fps;
}
public String getMagnetUri() {
return magnetUri;
}
public void setMagnetUri(String magnetUri) {
this.magnetUri = magnetUri;
}
public String getMetadataUrl() {
return metadataUrl;
}
public void setMetadataUrl(String metadataUrl) {
this.metadataUrl = metadataUrl;
}
public Item getResolutions() {
return resolutions;
}
public void setResolutions(Item resolutions) {
this.resolutions = resolutions;
}
public long getSize() {
return size;
}
public void setSize(long size) {
this.size = size;
}
public String getTorrentDownloadUrl() {
return torrentDownloadUrl;
}
public void setTorrentDownloadUrl(String torrentDownloadUrl) {
this.torrentDownloadUrl = torrentDownloadUrl;
}
public String getTorrentUrl() {
return torrentUrl;
}
public void setTorrentUrl(String torrentUrl) {
this.torrentUrl = torrentUrl;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.fileDownloadUrl);
dest.writeString(this.fileUrl);
dest.writeInt(this.fps);
dest.writeString(this.magnetUri);
dest.writeString(this.metadataUrl);
dest.writeParcelable(this.resolutions, flags);
dest.writeLong(this.size);
dest.writeString(this.torrentDownloadUrl);
dest.writeString(this.torrentUrl);
}
}

@ -0,0 +1,78 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
import java.util.List;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class InstanceParams {
private boolean healthy = true;
private boolean signup = true;
private List<Integer> categoriesOr;
private String nsfwPolicy = "do_not_list";
private List<String> languagesOr;
private String minUserQuota = "5000000000";
public boolean isHealthy() {
return healthy;
}
public void setHealthy(boolean healthy) {
this.healthy = healthy;
}
public boolean isSignup() {
return signup;
}
public void setSignup(boolean signup) {
this.signup = signup;
}
public List<Integer> getCategoriesOr() {
return categoriesOr;
}
public void setCategoriesOr(List<Integer> categoriesOr) {
this.categoriesOr = categoriesOr;
}
public String getNsfwPolicy() {
return nsfwPolicy;
}
public void setNsfwPolicy(String nsfwPolicy) {
this.nsfwPolicy = nsfwPolicy;
}
public String getMinUserQuota() {
return minUserQuota;
}
public void setMinUserQuota(String minUserQuota) {
this.minUserQuota = minUserQuota;
}
public List<String> getLanguagesOr() {
return languagesOr;
}
public void setLanguagesOr(List<String> languagesOr) {
this.languagesOr = languagesOr;
}
}

@ -0,0 +1,75 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
import android.os.Parcel;
import android.os.Parcelable;
import com.google.gson.annotations.SerializedName;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class Item implements Parcelable {
public static final Creator<Item> CREATOR = new Creator<Item>() {
@Override
public Item createFromParcel(Parcel source) {
return new Item(source);
}
@Override
public Item[] newArray(int size) {
return new Item[size];
}
};
@SerializedName("id")
private int id;
@SerializedName("label")
private String label;
public Item() {
}
protected Item(Parcel in) {
this.id = in.readInt();
this.label = in.readString();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.id);
dest.writeString(this.label);
}
}

@ -0,0 +1,76 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
import android.os.Parcel;
import android.os.Parcelable;
import com.google.gson.annotations.SerializedName;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class ItemStr implements Parcelable {
public static final Creator<ItemStr> CREATOR = new Creator<ItemStr>() {
@Override
public ItemStr createFromParcel(Parcel source) {
return new ItemStr(source);
}
@Override
public ItemStr[] newArray(int size) {
return new ItemStr[size];
}
};
@SerializedName("id")
private String id;
@SerializedName("label")
private String label;
public ItemStr() {
}
protected ItemStr(Parcel in) {
this.id = in.readString();
this.label = in.readString();
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.id);
dest.writeString(this.label);
}
}

@ -0,0 +1,92 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
@SuppressWarnings({"unused", "RedundantSuppression"})
public class MenuItemView {
private int id;
private String strId;
private String label;
private boolean selected;
public MenuItemView() {
}
public MenuItemView(int id, String label) {
this.id = id;
this.label = label;
selected = false;
}
public MenuItemView(int id, String label, boolean selected) {
this.id = id;
this.label = label;
this.selected = selected;
}
public MenuItemView(String strId, String label, boolean selected) {
this.strId = strId;
this.label = label;
this.selected = selected;
}
public MenuItemView(String strId, String label) {
this.strId = strId;
this.label = label;
this.selected = false;
}
public MenuItemView(int id, String strId, String label, boolean selected) {
this.id = id;
this.strId = strId;
this.label = label;
this.selected = selected;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
public String getStrId() {
return strId;
}
public void setStrId(String strId) {
this.strId = strId;
}
}

@ -0,0 +1,163 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
import com.google.gson.annotations.SerializedName;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class NotificationSettings {
@SerializedName("abuseAsModerator")
private int abuseAsModerator;
@SerializedName("abuseNewMessage")
private int abuseNewMessage;
@SerializedName("abuseStateChange")
private int abuseStateChange;
@SerializedName("autoInstanceFollowing")
private int autoInstanceFollowing;
@SerializedName("blacklistOnMyVideo")
private int blacklistOnMyVideo;
@SerializedName("commentMention")
private int commentMention;
@SerializedName("myVideoImportFinished")
private int myVideoImportFinished;
@SerializedName("myVideoPublished")
private int myVideoPublished;
@SerializedName("newCommentOnMyVideo")
private int newCommentOnMyVideo;
@SerializedName("newFollow")
private int newFollow;
@SerializedName("newInstanceFollower")
private int newInstanceFollower;
@SerializedName("newUserRegistration")
private int newUserRegistration;
@SerializedName("newVideoFromSubscription")
private int newVideoFromSubscription;
@SerializedName("videoAutoBlacklistAsModerator")
private int videoAutoBlacklistAsModerator;
public int getAbuseAsModerator() {
return abuseAsModerator;
}
public void setAbuseAsModerator(int abuseAsModerator) {
this.abuseAsModerator = abuseAsModerator;
}
public int getAbuseNewMessage() {
return abuseNewMessage;
}
public void setAbuseNewMessage(int abuseNewMessage) {
this.abuseNewMessage = abuseNewMessage;
}
public int getAbuseStateChange() {
return abuseStateChange;
}
public void setAbuseStateChange(int abuseStateChange) {
this.abuseStateChange = abuseStateChange;
}
public int getAutoInstanceFollowing() {
return autoInstanceFollowing;
}
public void setAutoInstanceFollowing(int autoInstanceFollowing) {
this.autoInstanceFollowing = autoInstanceFollowing;
}
public int getBlacklistOnMyVideo() {
return blacklistOnMyVideo;
}
public void setBlacklistOnMyVideo(int blacklistOnMyVideo) {
this.blacklistOnMyVideo = blacklistOnMyVideo;
}
public int getCommentMention() {
return commentMention;
}
public void setCommentMention(int commentMention) {
this.commentMention = commentMention;
}
public int getMyVideoImportFinished() {
return myVideoImportFinished;
}
public void setMyVideoImportFinished(int myVideoImportFinished) {
this.myVideoImportFinished = myVideoImportFinished;
}
public int getMyVideoPublished() {
return myVideoPublished;
}
public void setMyVideoPublished(int myVideoPublished) {
this.myVideoPublished = myVideoPublished;
}
public int getNewCommentOnMyVideo() {
return newCommentOnMyVideo;
}
public void setNewCommentOnMyVideo(int newCommentOnMyVideo) {
this.newCommentOnMyVideo = newCommentOnMyVideo;
}
public int getNewFollow() {
return newFollow;
}
public void setNewFollow(int newFollow) {
this.newFollow = newFollow;
}
public int getNewInstanceFollower() {
return newInstanceFollower;
}
public void setNewInstanceFollower(int newInstanceFollower) {
this.newInstanceFollower = newInstanceFollower;
}
public int getNewUserRegistration() {
return newUserRegistration;
}
public void setNewUserRegistration(int newUserRegistration) {
this.newUserRegistration = newUserRegistration;
}
public int getNewVideoFromSubscription() {
return newVideoFromSubscription;
}
public void setNewVideoFromSubscription(int newVideoFromSubscription) {
this.newVideoFromSubscription = newVideoFromSubscription;
}
public int getVideoAutoBlacklistAsModerator() {
return videoAutoBlacklistAsModerator;
}
public void setVideoAutoBlacklistAsModerator(int videoAutoBlacklistAsModerator) {
this.videoAutoBlacklistAsModerator = videoAutoBlacklistAsModerator;
}
}

@ -0,0 +1,44 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
import com.google.gson.annotations.SerializedName;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class Oauth {
@SerializedName("client_id")
private String client_id;
@SerializedName("client_secret")
private String client_secret;
public String getClient_id() {
return client_id;
}
public void setClient_id(String client_id) {
this.client_id = client_id;
}
public String getClient_secret() {
return client_secret;
}
public void setClient_secret(String client_secret) {
this.client_secret = client_secret;
}
}

@ -0,0 +1,143 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
import com.google.gson.annotations.SerializedName;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class OauthParams {
@SerializedName("client_id")
private String client_id;
@SerializedName("client_secret")
private String client_secret;
@SerializedName("grant_type")
private String grant_type;
@SerializedName("username")
private String username;
@SerializedName("password")
private String password;
@SerializedName("scope")
private String scope = "user";
@SerializedName("externalAuthToken")
private String externalAuthToken;
@SerializedName("refresh_token")
private String refresh_token;
@SerializedName("access_token")
private String access_token;
@SerializedName("response_type")
private String response_type;
@SerializedName("code")
private String code;
@SerializedName("redirect_uri")
private String redirect_uri;
public String getClient_secret() {
return client_secret;
}
public void setClient_secret(String client_secret) {
this.client_secret = client_secret;
}
public String getClient_id() {
return client_id;
}
public void setClient_id(String client_id) {
this.client_id = client_id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public String getGrant_type() {
return grant_type;
}
public void setGrant_type(String grant_type) {
this.grant_type = grant_type;
}
public String getExternalAuthToken() {
return externalAuthToken;
}
public void setExternalAuthToken(String externalAuthToken) {
this.externalAuthToken = externalAuthToken;
}
public String getRefresh_token() {
return refresh_token;
}
public void setRefresh_token(String refresh_token) {
this.refresh_token = refresh_token;
}
public String getAccess_token() {
return access_token;
}
public void setAccess_token(String access_token) {
this.access_token = access_token;
}
public String getResponse_type() {
return response_type;
}
public void setResponse_type(String response_type) {
this.response_type = response_type;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getRedirect_uri() {
return redirect_uri;
}
public void setRedirect_uri(String redirect_uri) {
this.redirect_uri = redirect_uri;
}
}

@ -0,0 +1,127 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
import com.google.gson.annotations.SerializedName;
import java.util.List;
import app.fedilab.android.peertube.client.data.ChannelData.Channel;
import app.fedilab.android.peertube.client.data.VideoData.Video;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class OverviewVideo {
@SerializedName("categories")
private List<Categories> categories;
@SerializedName("channels")
private List<Channels> channels;
@SerializedName("tags")
private List<Tags> tags;
public List<Categories> getCategories() {
return categories;
}
public void setCategories(List<Categories> categories) {
this.categories = categories;
}
public List<Channels> getChannels() {
return channels;
}
public void setChannels(List<Channels> channels) {
this.channels = channels;
}
public List<Tags> getTags() {
return tags;
}
public void setTags(List<Tags> tags) {
this.tags = tags;
}
public static class Categories {
@SerializedName("category")
private Item category;
@SerializedName("videos")
private List<Video> videos;
public Item getCategory() {
return category;
}
public void setCategory(Item category) {
this.category = category;
}
public List<Video> getVideos() {
return videos;
}
public void setVideos(List<Video> videos) {
this.videos = videos;
}
}
public static class Channels {
@SerializedName("channels")
private Channel channels;
@SerializedName("videos")
private List<Video> videos;
public Channel getChannels() {
return channels;
}
public void setChannels(Channel channels) {
this.channels = channels;
}
public List<Video> getVideos() {
return videos;
}
public void setVideos(List<Video> videos) {
this.videos = videos;
}
}
public static class Tags {
@SerializedName("tag")
private String tag;
@SerializedName("videos")
private List<Video> videos;
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
public List<Video> getVideos() {
return videos;
}
public void setVideos(List<Video> videos) {
this.videos = videos;
}
}
}

@ -0,0 +1,77 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
import java.util.HashMap;
import java.util.Map;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class PeertubeInformation {
private Map<Integer, String> categories = new HashMap<>();
private Map<String, String> languages = new HashMap<>();
private Map<Integer, String> licences = new HashMap<>();
private Map<Integer, String> privacies = new HashMap<>();
private Map<Integer, String> playlistPrivacies = new HashMap<>();
private Map<String, String> translations = new HashMap<>();
public Map<String, String> getTranslations() {
return translations;
}
public void setTranslations(Map<String, String> translations) {
this.translations = translations;
}
public Map<Integer, String> getCategories() {
return categories;
}
public void setCategories(Map<Integer, String> categories) {
this.categories = categories;
}
public Map<String, String> getLanguages() {
return languages;
}
public void setLanguages(Map<String, String> languages) {
this.languages = languages;
}
public Map<Integer, String> getLicences() {
return licences;
}
public void setLicences(Map<Integer, String> licences) {
this.licences = licences;
}
public Map<Integer, String> getPrivacies() {
return privacies;
}
public void setPrivacies(Map<Integer, String> privacies) {
this.privacies = privacies;
}
public Map<Integer, String> getPlaylistPrivacies() {
return playlistPrivacies;
}
public void setPlaylistPrivacies(Map<Integer, String> playlistPrivacies) {
this.playlistPrivacies = playlistPrivacies;
}
}

@ -0,0 +1,63 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
import com.google.gson.annotations.SerializedName;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class PlaylistExist {
@SerializedName("playlistElementId")
private String playlistElementId;
@SerializedName("playlistId")
private String playlistId;
@SerializedName("startTimestamp")
private long startTimestamp;
@SerializedName("stopTimestamp")
private long stopTimestamp;
public String getPlaylistElementId() {
return playlistElementId;
}
public void setPlaylistElementId(String playlistElementId) {
this.playlistElementId = playlistElementId;
}
public String getPlaylistId() {
return playlistId;
}
public void setPlaylistId(String playlistId) {
this.playlistId = playlistId;
}
public long getStartTimestamp() {
return startTimestamp;
}
public void setStartTimestamp(long startTimestamp) {
this.startTimestamp = startTimestamp;
}
public long getStopTimestamp() {
return stopTimestamp;
}
public void setStopTimestamp(long stopTimestamp) {
this.stopTimestamp = stopTimestamp;
}
}

@ -0,0 +1,63 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
import com.google.gson.annotations.SerializedName;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class PlaylistParams {
@SerializedName("displayName")
private String displayName;
@SerializedName("description")
private String description;
@SerializedName("privacy")
private int privacy;
@SerializedName("videoChannelId")
private String videoChannelId;
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public int getPrivacy() {
return privacy;
}
public void setPrivacy(int privacy) {
this.privacy = privacy;
}
public String getVideoChannelId() {
return videoChannelId;
}
public void setVideoChannelId(String videoChannelId) {
this.videoChannelId = videoChannelId;
}
}

@ -0,0 +1,42 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
import com.google.gson.annotations.SerializedName;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class Rating {
@SerializedName("videoId")
private String videoId;
@SerializedName("rating")
private String rating;
public String getVideoId() {
return videoId;
}
public void setVideoId(String videoId) {
this.videoId = videoId;
}
public String getRating() {
return rating;
}
public void setRating(String rating) {
this.rating = rating;
}
}

@ -0,0 +1,147 @@
package app.fedilab.android.peertube.client.entities;
import com.google.gson.annotations.SerializedName;
import java.util.List;
/* 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 <http://www.gnu.org/licenses>. */
@SuppressWarnings({"unused", "RedundantSuppression"})
public class Report {
@SerializedName("reason")
private String reason;
@SerializedName("predefinedReasons")
private List<String> predefinedReasons;
@SerializedName("video")
private VideoReport video;
@SerializedName("comment")
private CommentReport comment;
@SerializedName("account")
private AccountReport account;
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
public List<String> getPredefinedReasons() {
return predefinedReasons;
}
public void setPredefinedReasons(List<String> predefinedReasons) {
this.predefinedReasons = predefinedReasons;
}
public VideoReport getVideo() {
return video;
}
public void setVideo(VideoReport video) {
this.video = video;
}
public CommentReport getComment() {
return comment;
}
public void setComment(CommentReport comment) {
this.comment = comment;
}
public AccountReport getAccount() {
return account;
}
public void setAccount(AccountReport account) {
this.account = account;
}
public static class VideoReport {
@SerializedName("id")
private String id;
@SerializedName("startAt")
private long startAt;
@SerializedName("endAt")
private long endAt;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public long getStartAt() {
return startAt;
}
public void setStartAt(long startAt) {
this.startAt = startAt;
}
public long getEndAt() {
return endAt;
}
public void setEndAt(long endAt) {
this.endAt = endAt;
}
}
public static class CommentReport {
@SerializedName("id")
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
public static class AccountReport {
@SerializedName("id")
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
public static class ReportReturn {
@SerializedName("abuse")
private ItemStr reply;
public ItemStr getItemStr() {
return reply;
}
public void setItemStr(ItemStr itemStr) {
this.reply = itemStr;
}
}
}

@ -0,0 +1,214 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
import android.os.Parcel;
import android.os.Parcelable;
import com.google.gson.annotations.SerializedName;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@SuppressWarnings("unused")
public class SepiaSearch implements Parcelable {
public static final Creator<SepiaSearch> CREATOR = new Creator<SepiaSearch>() {
@Override
public SepiaSearch createFromParcel(Parcel source) {
return new SepiaSearch(source);
}
@Override
public SepiaSearch[] newArray(int size) {
return new SepiaSearch[size];
}
};
@SerializedName("start")
private String start;
@SerializedName("count")
private String count;
@SerializedName("search")
private String search;
@SerializedName("durationMax")
private int durationMax;
@SerializedName("durationMin")
private int durationMin;
@SerializedName("startDate")
private Date startDate;
@SerializedName("boostLanguages")
private List<String> boostLanguages;
@SerializedName("categoryOneOf")
private List<Integer> categoryOneOf;
@SerializedName("licenceOneOf")
private List<Integer> licenceOneOf;
@SerializedName("tagsOneOf")
private List<String> tagsOneOf;
@SerializedName("tagsAllOf")
private List<String> tagsAllOf;
@SerializedName("nsfw")
private boolean nsfw;
@SerializedName("sort")
private String sort;
public SepiaSearch() {
}
protected SepiaSearch(Parcel in) {
this.start = in.readString();
this.count = in.readString();
this.search = in.readString();
this.durationMax = in.readInt();
this.durationMin = in.readInt();
long tmpStartDate = in.readLong();
this.startDate = tmpStartDate == -1 ? null : new Date(tmpStartDate);
this.boostLanguages = in.createStringArrayList();
this.categoryOneOf = new ArrayList<>();
in.readList(this.categoryOneOf, Integer.class.getClassLoader());
this.licenceOneOf = new ArrayList<>();
in.readList(this.licenceOneOf, Integer.class.getClassLoader());
this.tagsOneOf = in.createStringArrayList();
this.tagsAllOf = in.createStringArrayList();
this.nsfw = in.readByte() != 0;
this.sort = in.readString();
}
public String getStart() {
return start;
}
public void setStart(String start) {
this.start = start;
}
public String getCount() {
return count;
}
public void setCount(String count) {
this.count = count;
}
public String getSearch() {
return search;
}
public void setSearch(String search) {
this.search = search;
}
public int getDurationMax() {
return durationMax;
}
public void setDurationMax(int durationMax) {
this.durationMax = durationMax;
}
public int getDurationMin() {
return durationMin;
}
public void setDurationMin(int durationMin) {
this.durationMin = durationMin;
}
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public List<String> getBoostLanguages() {
return boostLanguages;
}
public void setBoostLanguages(List<String> boostLanguages) {
this.boostLanguages = boostLanguages;
}
public List<Integer> getCategoryOneOf() {
return categoryOneOf;
}
public void setCategoryOneOf(List<Integer> categoryOneOf) {
this.categoryOneOf = categoryOneOf;
}
public List<Integer> getLicenceOneOf() {
return licenceOneOf;
}
public void setLicenceOneOf(List<Integer> licenceOneOf) {
this.licenceOneOf = licenceOneOf;
}
public List<String> getTagsOneOf() {
return tagsOneOf;
}
public void setTagsOneOf(List<String> tagsOneOf) {
this.tagsOneOf = tagsOneOf;
}
public List<String> getTagsAllOf() {
return tagsAllOf;
}
public void setTagsAllOf(List<String> tagsAllOf) {
this.tagsAllOf = tagsAllOf;
}
public boolean isNsfw() {
return nsfw;
}
public void setNsfw(boolean nsfw) {
this.nsfw = nsfw;
}
public String getSort() {
return sort;
}
public void setSort(String sort) {
this.sort = sort;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.start);
dest.writeString(this.count);
dest.writeString(this.search);
dest.writeInt(this.durationMax);
dest.writeInt(this.durationMin);
dest.writeLong(this.startDate != null ? this.startDate.getTime() : -1);
dest.writeStringList(this.boostLanguages);
dest.writeList(this.categoryOneOf);
dest.writeList(this.licenceOneOf);
dest.writeStringList(this.tagsOneOf);
dest.writeStringList(this.tagsAllOf);
dest.writeByte(this.nsfw ? (byte) 1 : (byte) 0);
dest.writeString(this.sort);
}
}

@ -0,0 +1,161 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
import android.os.Parcel;
import android.os.Parcelable;
import com.google.gson.annotations.SerializedName;
import java.util.List;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class StreamingPlaylists implements Parcelable {
public static final Creator<StreamingPlaylists> CREATOR = new Creator<StreamingPlaylists>() {
@Override
public StreamingPlaylists createFromParcel(Parcel in) {
return new StreamingPlaylists(in);
}
@Override
public StreamingPlaylists[] newArray(int size) {
return new StreamingPlaylists[size];
}
};
@SerializedName("id")
private String id;
@SerializedName("type")
private int type;
@SerializedName("playlistUrl")
private String playlistUrl;
@SerializedName("segmentsSha256Url")
private String segmentsSha256Url;
@SerializedName("files")
private List<File> files;
@SerializedName("redundancies")
private List<Redundancies> redundancies;
protected StreamingPlaylists(Parcel in) {
id = in.readString();
type = in.readInt();
playlistUrl = in.readString();
segmentsSha256Url = in.readString();
files = in.createTypedArrayList(File.CREATOR);
redundancies = in.createTypedArrayList(Redundancies.CREATOR);
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public String getPlaylistUrl() {
return playlistUrl;
}
public void setPlaylistUrl(String playlistUrl) {
this.playlistUrl = playlistUrl;
}
public String getSegmentsSha256Url() {
return segmentsSha256Url;
}
public void setSegmentsSha256Url(String segmentsSha256Url) {
this.segmentsSha256Url = segmentsSha256Url;
}
public List<File> getFiles() {
return files;
}
public void setFiles(List<File> files) {
this.files = files;
}
public List<Redundancies> getRedundancies() {
return redundancies;
}
public void setRedundancies(List<Redundancies> redundancies) {
this.redundancies = redundancies;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(id);
parcel.writeInt(type);
parcel.writeString(playlistUrl);
parcel.writeString(segmentsSha256Url);
parcel.writeTypedList(files);
parcel.writeTypedList(redundancies);
}
public static class Redundancies implements Parcelable {
public static final Creator<Redundancies> CREATOR = new Creator<Redundancies>() {
@Override
public Redundancies createFromParcel(Parcel in) {
return new Redundancies(in);
}
@Override
public Redundancies[] newArray(int size) {
return new Redundancies[size];
}
};
@SerializedName("baseUrl")
private String baseUrl;
protected Redundancies(Parcel in) {
baseUrl = in.readString();
}
public String getBaseUrl() {
return baseUrl;
}
public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(baseUrl);
}
}
}

@ -0,0 +1,62 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
import com.google.gson.annotations.SerializedName;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class Token {
@SerializedName("access_token")
private String access_token;
@SerializedName("expires_in")
private long expires_in;
@SerializedName("refresh_token")
private String refresh_token;
@SerializedName("token_type")
private String token_type;
public String getAccess_token() {
return access_token;
}
public void setAccess_token(String access_token) {
this.access_token = access_token;
}
public String getRefresh_token() {
return refresh_token;
}
public void setRefresh_token(String refresh_token) {
this.refresh_token = refresh_token;
}
public long getExpires_in() {
return expires_in;
}
public void setExpires_in(long expires_in) {
this.expires_in = expires_in;
}
public String getToken_type() {
return token_type;
}
public void setToken_type(String token_type) {
this.token_type = token_type;
}
}

@ -0,0 +1,305 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
import com.google.gson.annotations.SerializedName;
import java.util.Date;
import java.util.List;
import app.fedilab.android.peertube.client.data.AccountData.Account;
import app.fedilab.android.peertube.client.data.ChannelData;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class UserMe {
@SerializedName("account")
private Account account;
@SerializedName("autoPlayNextVideo")
private boolean autoPlayNextVideo;
@SerializedName("autoPlayNextVideoPlaylist")
private boolean autoPlayNextVideoPlaylist;
@SerializedName("autoPlayVideo")
private boolean autoPlayVideo;
@SerializedName("blocked")
private boolean blocked;
@SerializedName("blockedReason")
private String blockedReason;
@SerializedName("createdAt")
private Date createdAt;
@SerializedName("email")
private String email;
@SerializedName("emailVerified")
private String emailVerified;
@SerializedName("id")
private String id;
@SerializedName("lastLoginDate")
private Date lastLoginDate;
@SerializedName("noInstanceConfigWarningModal")
private boolean noInstanceConfigWarningModal;
@SerializedName("noWelcomeModal")
private boolean noWelcomeModal;
@SerializedName("notificationSettings")
private NotificationSettings notificationSettings;
@SerializedName("nsfwPolicy")
private String nsfwPolicy;
@SerializedName("role")
private int role;
@SerializedName("roleLabel")
private String roleLabel;
@SerializedName("username")
private String username;
@SerializedName("videoChannels")
private List<ChannelData.Channel> videoChannels;
@SerializedName("videoLanguages")
private List<String> videoLanguages;
@SerializedName("videoQuota")
private long videoQuota;
@SerializedName("videoQuotaDaily")
private long videoQuotaDaily;
@SerializedName("videosHistoryEnabled")
private boolean videosHistoryEnabled;
@SerializedName("webTorrentEnabled")
private boolean webTorrentEnabled;
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public boolean isAutoPlayNextVideo() {
return autoPlayNextVideo;
}
public void setAutoPlayNextVideo(boolean autoPlayNextVideo) {
this.autoPlayNextVideo = autoPlayNextVideo;
}
public boolean isAutoPlayNextVideoPlaylist() {
return autoPlayNextVideoPlaylist;
}
public void setAutoPlayNextVideoPlaylist(boolean autoPlayNextVideoPlaylist) {
this.autoPlayNextVideoPlaylist = autoPlayNextVideoPlaylist;
}
public boolean isBlocked() {
return blocked;
}
public void setBlocked(boolean blocked) {
this.blocked = blocked;
}
public String getBlockedReason() {
return blockedReason;
}
public void setBlockedReason(String blockedReason) {
this.blockedReason = blockedReason;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getEmailVerified() {
return emailVerified;
}
public void setEmailVerified(String emailVerified) {
this.emailVerified = emailVerified;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Date getLastLoginDate() {
return lastLoginDate;
}
public void setLastLoginDate(Date lastLoginDate) {
this.lastLoginDate = lastLoginDate;
}
public boolean isNoInstanceConfigWarningModal() {
return noInstanceConfigWarningModal;
}
public void setNoInstanceConfigWarningModal(boolean noInstanceConfigWarningModal) {
this.noInstanceConfigWarningModal = noInstanceConfigWarningModal;
}
public boolean isNoWelcomeModal() {
return noWelcomeModal;
}
public void setNoWelcomeModal(boolean noWelcomeModal) {
this.noWelcomeModal = noWelcomeModal;
}
public NotificationSettings getNotificationSettings() {
return notificationSettings;
}
public void setNotificationSettings(NotificationSettings notificationSettings) {
this.notificationSettings = notificationSettings;
}
public String getNsfwPolicy() {
return nsfwPolicy;
}
public void setNsfwPolicy(String nsfwPolicy) {
this.nsfwPolicy = nsfwPolicy;
}
public int getRole() {
return role;
}
public void setRole(int role) {
this.role = role;
}
public String getRoleLabel() {
return roleLabel;
}
public void setRoleLabel(String roleLabel) {
this.roleLabel = roleLabel;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public List<ChannelData.Channel> getVideoChannels() {
return videoChannels;
}
public void setVideoChannels(List<ChannelData.Channel> videoChannels) {
this.videoChannels = videoChannels;
}
public List<String> getVideoLanguages() {
return videoLanguages;
}
public void setVideoLanguages(List<String> videoLanguages) {
this.videoLanguages = videoLanguages;
}
public long getVideoQuota() {
return videoQuota;
}
public void setVideoQuota(long videoQuota) {
this.videoQuota = videoQuota;
}
public long getVideoQuotaDaily() {
return videoQuotaDaily;
}
public void setVideoQuotaDaily(long videoQuotaDaily) {
this.videoQuotaDaily = videoQuotaDaily;
}
public boolean isVideosHistoryEnabled() {
return videosHistoryEnabled;
}
public void setVideosHistoryEnabled(boolean videosHistoryEnabled) {
this.videosHistoryEnabled = videosHistoryEnabled;
}
public boolean isWebTorrentEnabled() {
return webTorrentEnabled;
}
public void setWebTorrentEnabled(boolean webTorrentEnabled) {
this.webTorrentEnabled = webTorrentEnabled;
}
public boolean isAutoPlayVideo() {
return autoPlayVideo;
}
public void setAutoPlayVideo(boolean autoPlayVideo) {
this.autoPlayVideo = autoPlayVideo;
}
public static class AvatarResponse {
@SerializedName("avatar")
private Avatar avatar;
public Avatar getAvatar() {
return avatar;
}
public void setAvatar(Avatar avatar) {
this.avatar = avatar;
}
}
public static class VideoQuota {
@SerializedName("videoQuotaUsed")
private long videoQuotaUsed;
@SerializedName("videoQuotaUsedDaily")
private long videoQuotaUsedDaily;
public long getVideoQuotaUsed() {
return videoQuotaUsed;
}
public void setVideoQuotaUsed(long videoQuotaUsed) {
this.videoQuotaUsed = videoQuotaUsed;
}
public long getVideoQuotaUsedDaily() {
return videoQuotaUsedDaily;
}
public void setVideoQuotaUsedDaily(long videoQuotaUsedDaily) {
this.videoQuotaUsedDaily = videoQuotaUsedDaily;
}
}
}

@ -0,0 +1,144 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
import android.net.Uri;
import java.util.List;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class UserSettings {
private Boolean videosHistoryEnabled;
private Boolean autoPlayVideo;
private Boolean webTorrentEnabled;
private Boolean autoPlayNextVideo;
private List<String> videoLanguages;
private String description;
private String displayName;
private Uri avatarfile;
private String fileName;
private NotificationSettings notificationSettings;
private String nsfwPolicy;
public Boolean isVideosHistoryEnabled() {
return videosHistoryEnabled;
}
public Boolean isAutoPlayVideo() {
return autoPlayVideo;
}
public Boolean isWebTorrentEnabled() {
return webTorrentEnabled;
}
public List<String> getVideoLanguages() {
return videoLanguages;
}
public void setVideoLanguages(List<String> videoLanguages) {
this.videoLanguages = videoLanguages;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public Uri getAvatarfile() {
return avatarfile;
}
public void setAvatarfile(Uri avatarfile) {
this.avatarfile = avatarfile;
}
public Boolean getVideosHistoryEnabled() {
return videosHistoryEnabled;
}
public void setVideosHistoryEnabled(Boolean videosHistoryEnabled) {
this.videosHistoryEnabled = videosHistoryEnabled;
}
public Boolean getAutoPlayVideo() {
return autoPlayVideo;
}
public void setAutoPlayVideo(Boolean autoPlayVideo) {
this.autoPlayVideo = autoPlayVideo;
}
public Boolean getWebTorrentEnabled() {
return webTorrentEnabled;
}
public void setWebTorrentEnabled(Boolean webTorrentEnabled) {
this.webTorrentEnabled = webTorrentEnabled;
}
public Boolean isAutoPlayNextVideo() {
return autoPlayNextVideo;
}
public Boolean getAutoPlayNextVideo() {
return autoPlayNextVideo;
}
public void setAutoPlayNextVideo(Boolean autoPlayNextVideo) {
this.autoPlayNextVideo = autoPlayNextVideo;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
if (fileName == null) {
this.fileName = "avatar.png";
} else {
this.fileName = fileName;
}
}
public NotificationSettings getNotificationSettings() {
return notificationSettings;
}
public void setNotificationSettings(NotificationSettings notificationSettings) {
this.notificationSettings = notificationSettings;
}
public String getNsfwPolicy() {
return nsfwPolicy;
}
public void setNsfwPolicy(String nsfwPolicy) {
this.nsfwPolicy = nsfwPolicy;
}
}

@ -0,0 +1,79 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
import com.google.gson.annotations.SerializedName;
import app.fedilab.android.peertube.client.data.CommentData;
import app.fedilab.android.peertube.client.data.VideoData;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class VideoAbuse {
@SerializedName("id")
private String id;
@SerializedName("video")
private VideoData.Video video;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public VideoData.Video getVideo() {
return video;
}
public void setVideo(VideoData.Video video) {
this.video = video;
}
public static class Abuse {
@SerializedName("comment")
private CommentData.Comment comment;
@SerializedName("threadId")
private String threadId;
@SerializedName("id")
private String id;
public CommentData.Comment getComment() {
return comment;
}
public void setComment(CommentData.Comment comment) {
this.comment = comment;
}
public String getThreadId() {
return threadId;
}
public void setThreadId(String threadId) {
this.threadId = threadId;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
}

@ -0,0 +1,44 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
import com.google.gson.annotations.SerializedName;
import app.fedilab.android.peertube.client.data.VideoData;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class VideoBlacklist {
@SerializedName("id")
private String id;
@SerializedName("video")
private VideoData.Video video;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public VideoData.Video getVideo() {
return video;
}
public void setVideo(VideoData.Video video) {
this.video = video;
}
}

@ -0,0 +1,176 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
import com.google.gson.annotations.SerializedName;
import org.jetbrains.annotations.NotNull;
import java.util.Date;
import java.util.List;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class VideoParams {
@SerializedName("channelId")
private String channelId;
@SerializedName("name")
private String name;
@SerializedName("category")
private int category;
@SerializedName("commentsEnabled")
private boolean commentsEnabled;
@SerializedName("description")
private String description;
@SerializedName("downloadEnabled")
private boolean downloadEnabled;
@SerializedName("language")
private String language;
@SerializedName("licence")
private String licence;
@SerializedName("nsfw")
private boolean nsfw;
@SerializedName("originallyPublishedAt")
private Date originallyPublishedAt;
@SerializedName("privacy")
private int privacy;
@SerializedName("support")
private String support;
@SerializedName("tags")
private List<String> tags;
@SerializedName("waitTranscoding")
private boolean waitTranscoding;
@NotNull
@Override
public String toString() {
return "channelId: " + channelId + "\nname: " + name + "\ncategory: " + category + "\ncommentsEnabled: " + commentsEnabled
+ "\ndescription: " + description + "\ndownloadEnabled: " + downloadEnabled + "\nlanguage: " + language
+ "\nlicence: " + licence + "\nnsfw: " + nsfw + "\noriginallyPublishedAt: " + originallyPublishedAt
+ "\nprivacy: " + privacy + "\nsupport: " + support + "\ntags: " + tags + "\nwaitTranscoding: " + waitTranscoding;
}
public String getChannelId() {
return channelId;
}
public void setChannelId(String channelId) {
this.channelId = channelId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getCategory() {
return category;
}
public void setCategory(int category) {
this.category = category;
}
public boolean isCommentsEnabled() {
return commentsEnabled;
}
public void setCommentsEnabled(boolean commentsEnabled) {
this.commentsEnabled = commentsEnabled;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public boolean isDownloadEnabled() {
return downloadEnabled;
}
public void setDownloadEnabled(boolean downloadEnabled) {
this.downloadEnabled = downloadEnabled;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public String getLicence() {
return licence;
}
public void setLicence(String licence) {
this.licence = licence;
}
public boolean isNsfw() {
return nsfw;
}
public void setNsfw(boolean nsfw) {
this.nsfw = nsfw;
}
public Date getOriginallyPublishedAt() {
return originallyPublishedAt;
}
public void setOriginallyPublishedAt(Date originallyPublishedAt) {
this.originallyPublishedAt = originallyPublishedAt;
}
public int getPrivacy() {
return privacy;
}
public void setPrivacy(int privacy) {
this.privacy = privacy;
}
public String getSupport() {
return support;
}
public void setSupport(String support) {
this.support = support;
}
public List<String> getTags() {
return tags;
}
public void setTags(List<String> tags) {
this.tags = tags;
}
public boolean isWaitTranscoding() {
return waitTranscoding;
}
public void setWaitTranscoding(boolean waitTranscoding) {
this.waitTranscoding = waitTranscoding;
}
}

@ -0,0 +1,75 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
import android.os.Parcel;
import android.os.Parcelable;
import com.google.gson.annotations.SerializedName;
import java.util.Date;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class ViewsPerDay implements Parcelable {
public static final Creator<ViewsPerDay> CREATOR = new Creator<ViewsPerDay>() {
@Override
public ViewsPerDay createFromParcel(Parcel in) {
return new ViewsPerDay(in);
}
@Override
public ViewsPerDay[] newArray(int size) {
return new ViewsPerDay[size];
}
};
@SerializedName("date")
private Date date;
@SerializedName("views")
private int views;
protected ViewsPerDay(Parcel in) {
long tmpDate = in.readLong();
this.date = tmpDate == -1 ? null : new Date(tmpDate);
views = in.readInt();
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public int getViews() {
return views;
}
public void setViews(int views) {
this.views = views;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeLong(this.date != null ? this.date.getTime() : -1);
parcel.writeInt(views);
}
}

@ -0,0 +1,105 @@
package app.fedilab.android.peertube.client.entities;
/* 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 <http://www.gnu.org/licenses>. */
import com.google.gson.annotations.SerializedName;
import java.util.List;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class WellKnownNodeinfo {
@SerializedName("links")
private List<NodeInfoLinks> links;
public List<NodeInfoLinks> getLinks() {
return links;
}
public void setLinks(List<NodeInfoLinks> links) {
this.links = links;
}
public static class NodeInfoLinks {
@SerializedName("reel")
private String reel;
@SerializedName("href")
private String href;
public String getReel() {
return reel;
}
public void setReel(String reel) {
this.reel = reel;
}
public String getHref() {
return href;
}
public void setHref(String href) {
this.href = href;
}
}
public static class NodeInfo {
@SerializedName("version")
private String version;
@SerializedName("software")
private Software software;
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public Software getSoftware() {
return software;
}
public void setSoftware(Software software) {
this.software = software;
}
}
public static class Software {
@SerializedName("name")
private String name;
@SerializedName("version")
private String version;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
}
}

@ -0,0 +1,255 @@
package app.fedilab.android.peertube.client.mastodon;
/* 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 <http://www.gnu.org/licenses>. */
import android.os.Parcel;
import android.os.Parcelable;
import com.google.gson.annotations.SerializedName;
import java.util.Date;
import app.fedilab.android.peertube.client.data.AccountData;
import app.fedilab.android.peertube.client.entities.Avatar;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class MastodonAccount {
public static AccountData.Account convertToPeertubeAccount(Account initialAccount) {
AccountData.Account account = new AccountData.Account();
Avatar avatar = new Avatar();
avatar.setPath(initialAccount.getAvatar());
account.setAvatar(avatar);
account.setDescription(initialAccount.getDescription());
account.setDisplayName(initialAccount.getDisplayName());
account.setUsername(initialAccount.getUsername());
account.setHost(initialAccount.getHost());
return account;
}
public static class Account implements Parcelable {
public static final Creator<Account> CREATOR = new Creator<Account>() {
@Override
public Account createFromParcel(Parcel source) {
return new Account(source);
}
@Override
public Account[] newArray(int size) {
return new Account[size];
}
};
@SerializedName("avatar")
private String avatar;
@SerializedName("created_at")
private Date createdAt;
@SerializedName("note")
private String description;
@SerializedName("display_name")
private String displayName;
@SerializedName("followers_count")
private int followersCount;
@SerializedName("following_count")
private int followingCount;
@SerializedName("id")
private String id;
@SerializedName("username")
private String username;
@SerializedName("updated_at")
private Date updatedAt;
@SerializedName("url")
private String url;
private String token;
private String client_id;
private String client_secret;
private String refresh_token;
private String software;
private String host;
public Account() {
}
protected Account(Parcel in) {
this.avatar = in.readString();
long tmpCreatedAt = in.readLong();
this.createdAt = tmpCreatedAt == -1 ? null : new Date(tmpCreatedAt);
this.description = in.readString();
this.displayName = in.readString();
this.followersCount = in.readInt();
this.followingCount = in.readInt();
this.id = in.readString();
this.username = in.readString();
long tmpUpdatedAt = in.readLong();
this.updatedAt = tmpUpdatedAt == -1 ? null : new Date(tmpUpdatedAt);
this.url = in.readString();
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public int getFollowersCount() {
return followersCount;
}
public void setFollowersCount(int followersCount) {
this.followersCount = followersCount;
}
public int getFollowingCount() {
return followingCount;
}
public void setFollowingCount(int followingCount) {
this.followingCount = followingCount;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getAcct() {
return username + "@" + host;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getClient_id() {
return client_id;
}
public void setClient_id(String client_id) {
this.client_id = client_id;
}
public String getClient_secret() {
return client_secret;
}
public void setClient_secret(String client_secret) {
this.client_secret = client_secret;
}
public String getRefresh_token() {
return refresh_token;
}
public void setRefresh_token(String refresh_token) {
this.refresh_token = refresh_token;
}
public String getSoftware() {
return software;
}
public void setSoftware(String software) {
this.software = software;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.avatar);
dest.writeLong(this.createdAt != null ? this.createdAt.getTime() : -1);
dest.writeString(this.description);
dest.writeString(this.displayName);
dest.writeInt(this.followersCount);
dest.writeInt(this.followingCount);
dest.writeString(this.id);
dest.writeString(this.username);
dest.writeLong(this.updatedAt != null ? this.updatedAt.getTime() : -1);
dest.writeString(this.url);
}
}
}

@ -0,0 +1,104 @@
package app.fedilab.android.peertube.client.mastodon;
/* 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 <http://www.gnu.org/licenses>. */
import app.fedilab.android.peertube.client.entities.Oauth;
import app.fedilab.android.peertube.client.entities.Token;
import retrofit2.Call;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.Header;
import retrofit2.http.POST;
import retrofit2.http.Path;
import retrofit2.http.Query;
interface MastodonService {
@FormUrlEncoded
@POST("apps")
Call<Oauth> getOauth(
@Field("client_name") String client_name,
@Field("redirect_uris") String redirect_uris,
@Field("scopes") String scopes,
@Field("website") String website);
@FormUrlEncoded
@POST("oauth/token")
Call<Token> createToken(
@Field("grant_type") String grant_type,
@Field("client_id") String client_id,
@Field("client_secret") String client_secret,
@Field("redirect_uri") String redirect_uri,
@Field("code") String code);
@GET("accounts/verify_credentials")
Call<MastodonAccount.Account> verifyCredentials(@Header("Authorization") String credentials);
@GET("search?type=statuses&resolve=true")
Call<Results> searchMessage(
@Header("Authorization") String credentials,
@Query("q") String messageURL
);
@FormUrlEncoded
@POST("statuses")
Call<Status> postReply(
@Header("Authorization") String credentials,
@Field("in_reply_to_id") String inReplyToId,
@Field("status") String content,
@Field("visibility") String visibility
);
@POST("statuses/{id}/reblog")
Call<Status> boost(
@Header("Authorization") String credentials,
@Path("id") String id
);
@POST("statuses/{id}/unreblog")
Call<Status> unBoost(
@Header("Authorization") String credentials,
@Path("id") String id
);
@POST("statuses/{id}/favourite")
Call<Status> favourite(
@Header("Authorization") String credentials,
@Path("id") String id
);
@POST("statuses/{id}/unfavourite")
Call<Status> unfavourite(
@Header("Authorization") String credentials,
@Path("id") String id
);
@POST("statuses/{id}/bookmark")
Call<Status> bookmark(
@Header("Authorization") String credentials,
@Path("id") String id
);
@POST("statuses/{id}/unbookmark")
Call<Status> unbookmark(
@Header("Authorization") String credentials,
@Path("id") String id
);
}

@ -0,0 +1,44 @@
package app.fedilab.android.peertube.client.mastodon;
/* Copyright 2021 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 <http://www.gnu.org/licenses>. */
import com.google.gson.annotations.SerializedName;
import java.util.List;
public class Results {
@SerializedName("accounts")
private List<MastodonAccount.Account> accounts;
@SerializedName("statuses")
private List<Status> statuses;
public List<MastodonAccount.Account> getAccounts() {
return accounts;
}
public void setAccounts(List<MastodonAccount.Account> accounts) {
this.accounts = accounts;
}
public List<Status> getStatuses() {
return statuses;
}
public void setStatuses(List<Status> statuses) {
this.statuses = statuses;
}
}

@ -0,0 +1,356 @@
package app.fedilab.android.peertube.client.mastodon;
/* 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 <http://www.gnu.org/licenses>. */
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.sqlite.SQLiteDatabase;
import android.os.Handler;
import android.os.Looper;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.concurrent.TimeUnit;
import app.fedilab.android.peertube.R;
import app.fedilab.android.peertube.activities.MainActivity;
import app.fedilab.android.peertube.client.APIResponse;
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.helper.Helper;
import app.fedilab.android.peertube.sqlite.MastodonAccountDAO;
import app.fedilab.android.peertube.sqlite.Sqlite;
import okhttp3.OkHttpClient;
import retrofit2.Call;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class RetrofitMastodonAPI {
final OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(60, TimeUnit.SECONDS)
.connectTimeout(60, TimeUnit.SECONDS)
.build();
private final String finalUrl;
private final String finalUrl2;
private final Context _context;
private String instance;
private String token;
public RetrofitMastodonAPI(Context context) {
_context = context;
SharedPreferences sharedpreferences = _context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
this.instance = sharedpreferences.getString(Helper.PREF_REMOTE_INSTANCE, null);
this.token = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null);
finalUrl = "https://" + this.instance + "/api/v1/";
finalUrl2 = "https://" + this.instance + "/api/v2/";
}
public RetrofitMastodonAPI(Context context, String instance, String token) {
_context = context;
this.instance = instance;
this.token = token;
finalUrl = "https://" + instance + "/api/v1/";
finalUrl2 = "https://" + this.instance + "/api/v2/";
}
public Status search(String url) throws Error {
MastodonService mastodonService2 = init2();
Call<Results> statusCall = mastodonService2.searchMessage(getToken(), url);
Response<Results> response;
try {
response = statusCall.execute();
if (response.isSuccessful() && response.body() != null && response.body().getStatuses() != null && response.body().getStatuses().size() > 0) {
return response.body().getStatuses().get(0);
} else {
Error error = new Error();
error.setStatusCode(response.code());
if (response.errorBody() != null) {
error.setError(response.errorBody().string());
} else {
error.setError(_context.getString(R.string.toast_error));
}
throw error;
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public void updateCredential(Activity activity, String client_id, String client_secret, String refresh_token, String software) {
new Thread(() -> {
MastodonAccount.Account account;
try {
account = new RetrofitMastodonAPI(activity, instance, token).verifyCredentials();
} catch (Error error) {
Error.displayError(activity, error);
error.printStackTrace();
return;
}
try {
//At the state the instance can be encoded
instance = URLDecoder.decode(instance, "utf-8");
} catch (UnsupportedEncodingException ignored) {
}
SharedPreferences sharedpreferences = activity.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
account.setToken(token);
account.setClient_id(client_id);
account.setClient_secret(client_secret);
account.setRefresh_token(refresh_token);
account.setHost(instance);
account.setSoftware(software);
SQLiteDatabase db = Sqlite.getInstance(activity.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
boolean userExists = new MastodonAccountDAO(activity, db).userExist(account);
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putString(Helper.PREF_KEY_ID, account.getId());
editor.putString(Helper.PREF_KEY_NAME, account.getUsername());
if (token != null) {
editor.putString(Helper.PREF_KEY_OAUTH_TOKEN, token);
}
editor.putString(Helper.PREF_REMOTE_INSTANCE, account.getHost());
editor.putString(Helper.PREF_SOFTWARE, software);
editor.apply();
if (userExists) {
new MastodonAccountDAO(activity, db).updateAccountCredential(account);
} else {
if (account.getUsername() != null && account.getCreatedAt() != null) {
new MastodonAccountDAO(activity, db).insertAccount(account);
}
}
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> {
Intent mainActivity = new Intent(activity, MainActivity.class);
mainActivity.putExtra(Helper.INTENT_ACTION, Helper.ADD_USER_INTENT);
activity.startActivity(mainActivity);
activity.finish();
};
mainHandler.post(myRunnable);
}).start();
}
private MastodonService init_no_api() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://" + instance)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
SharedPreferences sharedpreferences = _context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
if (token == null) {
token = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null);
}
return retrofit.create(MastodonService.class);
}
private MastodonService init() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(finalUrl)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
SharedPreferences sharedpreferences = _context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
if (token == null) {
token = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null);
}
return retrofit.create(MastodonService.class);
}
private MastodonService init2() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(finalUrl2)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
SharedPreferences sharedpreferences = _context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
if (token == null) {
token = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null);
}
return retrofit.create(MastodonService.class);
}
/**
* Get Oauth
*
* @return APIResponse
*/
public Oauth oauthClient(String client_name, String redirect_uris, String scopes, String website) {
MastodonService mastodonService = init();
try {
Call<Oauth> oauth;
oauth = mastodonService.getOauth(client_name, redirect_uris, scopes, website);
Response<Oauth> response = oauth.execute();
if (response.isSuccessful() && response.body() != null) {
return response.body();
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/***
* Verifiy credential of the authenticated user *synchronously*
* @return Account
*/
public MastodonAccount.Account verifyCredentials() throws Error {
MastodonService mastodonService = init();
Call<MastodonAccount.Account> accountCall = mastodonService.verifyCredentials("Bearer " + token);
APIResponse apiResponse = new APIResponse();
try {
Response<MastodonAccount.Account> response = accountCall.execute();
if (response.isSuccessful() && response.body() != null) {
return response.body();
} else {
Error error = new Error();
error.setStatusCode(response.code());
if (response.errorBody() != null) {
error.setError(response.errorBody().string());
} else {
error.setError(_context.getString(R.string.toast_error));
}
throw error;
}
} catch (IOException e) {
Error error = new Error();
error.setError(_context.getString(R.string.toast_error));
apiResponse.setError(error);
e.printStackTrace();
}
return null;
}
/***
* Verifiy credential of the authenticated user *synchronously*
* @return Account
*/
public Token manageToken(OauthParams oauthParams) throws Error {
MastodonService mastodonService = init_no_api();
Call<Token> createToken = mastodonService.createToken(
oauthParams.getGrant_type(),
oauthParams.getClient_id(),
oauthParams.getClient_secret(),
oauthParams.getRedirect_uri(),
oauthParams.getCode()
);
if (createToken != null) {
try {
Response<Token> response = createToken.execute();
if (response.isSuccessful()) {
return response.body();
} else {
Error error = new Error();
error.setStatusCode(response.code());
if (response.errorBody() != null) {
error.setError(response.errorBody().string());
} else {
error.setError(_context.getString(R.string.toast_error));
}
throw error;
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
public Status commentAction(String url, String content) throws Error {
MastodonService mastodonService = init();
Status status = search(url);
if (status != null) {
Call<Status> postReplyCall = mastodonService.postReply(getToken(), status.getId(), content, null);
try {
Response<Status> responsePost = postReplyCall.execute();
if (responsePost.isSuccessful()) {
Status statusReturned = responsePost.body();
if (statusReturned != null && statusReturned.getAccount() != null) {
statusReturned.getAccount().setHost(instance);
}
return statusReturned;
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
public Status postAction(actionType type, Status status) {
MastodonService mastodonService = init();
Call<Status> postAction = null;
if (status != null) {
switch (type) {
case BOOST:
postAction = mastodonService.boost(getToken(), status.getId());
break;
case UNBOOST:
postAction = mastodonService.unBoost(getToken(), status.getId());
break;
case FAVOURITE:
postAction = mastodonService.favourite(getToken(), status.getId());
break;
case UNFAVOURITE:
postAction = mastodonService.unfavourite(getToken(), status.getId());
break;
case BOOKMARK:
postAction = mastodonService.bookmark(getToken(), status.getId());
break;
case UNBOOKMARK:
postAction = mastodonService.unbookmark(getToken(), status.getId());
break;
}
try {
if (postAction != null) {
Response<Status> responsePost = postAction.execute();
if (responsePost.isSuccessful()) {
Status statusReturned = responsePost.body();
if (statusReturned != null && statusReturned.getAccount() != null) {
statusReturned.getAccount().setHost(instance);
}
return statusReturned;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
private String getToken() {
if (token != null) {
return "Bearer " + token;
} else {
return null;
}
}
public enum actionType {
BOOST,
UNBOOST,
FAVOURITE,
UNFAVOURITE,
BOOKMARK,
UNBOOKMARK
}
}

@ -0,0 +1,149 @@
package app.fedilab.android.peertube.client.mastodon;
/* Copyright 2021 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 <http://www.gnu.org/licenses>. */
import com.google.gson.annotations.SerializedName;
import java.util.Date;
import app.fedilab.android.peertube.client.data.CommentData;
@SuppressWarnings("unused")
public class Status {
@SerializedName("id")
private String id;
@SerializedName("in_reply_to_id")
private String inReplyToCommentId;
@SerializedName("account")
private MastodonAccount.Account account;
@SerializedName("url")
private String url;
@SerializedName("content")
private String text;
@SerializedName("created_at")
private Date createdAt;
@SerializedName("reblogs_count")
private int reblogsCount;
@SerializedName("favourites_count")
private int favouritesCount;
@SerializedName("favourited")
private boolean favourited;
@SerializedName("reblogged")
private boolean reblogged;
@SerializedName("bookmarked")
private boolean bookmarked;
public static CommentData.Comment convertStatusToComment(Status status) {
CommentData.Comment comment = new CommentData.Comment();
comment.setAccount(MastodonAccount.convertToPeertubeAccount(status.getAccount()));
comment.setCreatedAt(status.getCreatedAt());
comment.setText(status.getText());
return comment;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getInReplyToCommentId() {
return inReplyToCommentId;
}
public void setInReplyToCommentId(String inReplyToCommentId) {
this.inReplyToCommentId = inReplyToCommentId;
}
public MastodonAccount.Account getAccount() {
return account;
}
public void setAccount(MastodonAccount.Account account) {
this.account = account;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public int getReblogsCount() {
return reblogsCount;
}
public void setReblogsCount(int reblogsCount) {
this.reblogsCount = reblogsCount;
}
public int getFavouriteCount() {
return favouritesCount;
}
public boolean isFavourited() {
return favourited;
}
public void setFavourited(boolean favourited) {
this.favourited = favourited;
}
public boolean isReblogged() {
return reblogged;
}
public void setReblogged(boolean reblogged) {
this.reblogged = reblogged;
}
public int getFavouritesCount() {
return favouritesCount;
}
public void setFavouritesCount(int favouritesCount) {
this.favouritesCount = favouritesCount;
}
public boolean isBookmarked() {
return bookmarked;
}
public void setBookmarked(boolean bookmarked) {
this.bookmarked = bookmarked;
}
}

@ -0,0 +1,187 @@
package app.fedilab.android.peertube.drawer;
/* 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 <http://www.gnu.org/licenses>. */
import static androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.sqlite.SQLiteDatabase;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.text.Html;
import android.text.SpannableString;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.PopupMenu;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import app.fedilab.android.peertube.R;
import app.fedilab.android.peertube.client.data.InstanceData;
import app.fedilab.android.peertube.databinding.DrawerAboutInstanceBinding;
import app.fedilab.android.peertube.helper.Helper;
import app.fedilab.android.peertube.sqlite.Sqlite;
import app.fedilab.android.peertube.sqlite.StoredInstanceDAO;
public class AboutInstanceAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final List<InstanceData.AboutInstance> aboutInstances;
public AllInstancesRemoved allInstancesRemoved;
private Context context;
public AboutInstanceAdapter(List<InstanceData.AboutInstance> aboutInstances) {
this.aboutInstances = aboutInstances;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getItemCount() {
return aboutInstances.size();
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
DrawerAboutInstanceBinding itemBinding = DrawerAboutInstanceBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
return new ViewHolder(itemBinding);
}
@SuppressLint("ApplySharedPref")
@Override
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder viewHolder, int i) {
context = viewHolder.itemView.getContext();
final ViewHolder holder = (ViewHolder) viewHolder;
final InstanceData.AboutInstance aboutInstance = aboutInstances.get(i);
holder.binding.aboutInstanceHost.setText(aboutInstance.getHost());
SpannableString spannableString;
if (aboutInstance.getShortDescription() != null && aboutInstance.getShortDescription().trim().length() > 0) {
if (aboutInstance.isTruncatedDescription()) {
holder.binding.aboutInstanceDescription.setMaxLines(3);
holder.binding.aboutInstanceDescription.setEllipsize(TextUtils.TruncateAt.END);
} else {
holder.binding.aboutInstanceDescription.setMaxLines(Integer.MAX_VALUE);
holder.binding.aboutInstanceDescription.setEllipsize(null);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
spannableString = new SpannableString(Html.fromHtml(aboutInstance.getShortDescription(), FROM_HTML_MODE_LEGACY));
else
spannableString = new SpannableString(Html.fromHtml(aboutInstance.getShortDescription()));
holder.binding.aboutInstanceDescription.setText(spannableString, TextView.BufferType.SPANNABLE);
holder.binding.aboutInstanceDescription.setOnClickListener(v -> {
aboutInstance.setTruncatedDescription(!aboutInstance.isTruncatedDescription());
notifyItemChanged(i);
});
holder.binding.aboutInstanceDescription.setVisibility(View.VISIBLE);
} else {
holder.binding.aboutInstanceDescription.setVisibility(View.GONE);
}
if (aboutInstance.getShortDescription() != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
spannableString = new SpannableString(Html.fromHtml(aboutInstance.getShortDescription(), FROM_HTML_MODE_LEGACY));
else
spannableString = new SpannableString(Html.fromHtml(aboutInstance.getShortDescription()));
holder.binding.aboutInstanceDescription.setText(spannableString, TextView.BufferType.SPANNABLE);
}
holder.binding.aboutInstanceName.setText(aboutInstance.getName());
holder.binding.instanceContainer.setOnClickListener(v -> {
final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putString(Helper.PREF_INSTANCE, aboutInstance.getHost());
editor.commit();
Helper.logoutNoRemoval((Activity) context);
});
holder.binding.instanceMore.setOnClickListener(v -> {
PopupMenu popup = new PopupMenu(context, holder.binding.instanceMore);
popup.getMenuInflater()
.inflate(R.menu.instance_menu, popup.getMenu());
popup.setOnMenuItemClickListener(item -> {
int itemId = item.getItemId();
if (itemId == R.id.action_delete) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.delete_instance);
builder.setMessage(R.string.delete_instance_confirm);
builder.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(R.string.delete, (dialog, which) -> {
new Thread(() -> {
SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
new StoredInstanceDAO(context, db).removeInstance(aboutInstance.getHost());
aboutInstances.remove(aboutInstance);
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> {
notifyItemRemoved(i);
if (aboutInstances.size() == 0) {
allInstancesRemoved.onAllInstancesRemoved();
}
};
mainHandler.post(myRunnable);
}).start();
dialog.dismiss();
})
.setNegativeButton(R.string.no, (dialog, which) -> dialog.dismiss())
.show();
}
return true;
});
popup.show();
});
}
public interface AllInstancesRemoved {
void onAllInstancesRemoved();
}
static class ViewHolder extends RecyclerView.ViewHolder {
DrawerAboutInstanceBinding binding;
ViewHolder(DrawerAboutInstanceBinding itemView) {
super(itemView.getRoot());
binding = itemView;
}
}
}

@ -0,0 +1,110 @@
package app.fedilab.android.peertube.drawer;
/* 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 <http://www.gnu.org/licenses>. */
import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.core.graphics.ColorUtils;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import app.fedilab.android.peertube.client.data.ChannelData;
import app.fedilab.android.peertube.databinding.DrawerHorizontalAccountBinding;
import app.fedilab.android.peertube.helper.Helper;
public class AccountsHorizontalListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final List<ChannelData.Channel> channels;
EventListener listener;
private Context context;
public AccountsHorizontalListAdapter(List<ChannelData.Channel> channels, EventListener listener) {
this.channels = channels;
this.listener = listener;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
DrawerHorizontalAccountBinding itemBinding = DrawerHorizontalAccountBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
return new ViewHolder(itemBinding);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
final ViewHolder holder = (ViewHolder) viewHolder;
final ChannelData.Channel channel = channels.get(position);
if (channel.getDisplayName() != null && !channel.getDisplayName().trim().equals(""))
holder.binding.accountDn.setText(channel.getDisplayName());
else
holder.binding.accountDn.setText(channel.getName().replace("@", ""));
//Profile picture
Helper.loadAvatar(context, channel, holder.binding.accountPp);
if (channel.isSelected()) {
holder.binding.mainContainer.setBackgroundColor(ColorUtils.setAlphaComponent(Helper.fetchAccentColor(context), 50));
} else {
holder.binding.mainContainer.setBackgroundColor(Color.TRANSPARENT);
}
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getItemCount() {
return channels.size();
}
public interface EventListener {
void click(ChannelData.Channel channel);
}
private class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
DrawerHorizontalAccountBinding binding;
ViewHolder(DrawerHorizontalAccountBinding itemView) {
super(itemView.getRoot());
binding = itemView;
itemView.getRoot().setOnClickListener(this);
}
@Override
public void onClick(View v) {
ChannelData.Channel channel = channels.get(getAdapterPosition());
listener.click(channel);
for (ChannelData.Channel acc : channels) {
acc.setSelected(acc.getId().compareTo(channel.getId()) == 0);
}
notifyItemRangeChanged(0, channels.size());
}
}
}

@ -0,0 +1,162 @@
package app.fedilab.android.peertube.drawer;
/* 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 <http://www.gnu.org/licenses>. */
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.List;
import app.fedilab.android.peertube.R;
import app.fedilab.android.peertube.activities.ShowAccountActivity;
import app.fedilab.android.peertube.client.APIResponse;
import app.fedilab.android.peertube.client.RetrofitPeertubeAPI;
import app.fedilab.android.peertube.client.data.AccountData.Account;
import app.fedilab.android.peertube.helper.Helper;
import app.fedilab.android.peertube.viewmodel.PostActionsVM;
import es.dmoral.toasty.Toasty;
public class AccountsListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final List<Account> accounts;
private final AccountsListAdapter accountsListAdapter;
private final RetrofitPeertubeAPI.DataType type;
public AllAccountsRemoved allAccountsRemoved;
private Context context;
public AccountsListAdapter(RetrofitPeertubeAPI.DataType type, List<Account> accounts) {
this.accounts = accounts;
this.accountsListAdapter = this;
this.type = type;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
LayoutInflater layoutInflater = LayoutInflater.from(context);
return new ViewHolder(layoutInflater.inflate(R.layout.drawer_account, parent, false));
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
final ViewHolder holder = (ViewHolder) viewHolder;
final Account account = accounts.get(position);
if (type == RetrofitPeertubeAPI.DataType.MUTED) {
holder.account_action.setOnClickListener(v -> {
PostActionsVM viewModel = new ViewModelProvider((ViewModelStoreOwner) context).get(PostActionsVM.class);
viewModel.post(RetrofitPeertubeAPI.ActionType.UNMUTE, account.getAcct(), null).observe((LifecycleOwner) context, apiResponse -> manageVIewPostActions(RetrofitPeertubeAPI.ActionType.UNMUTE, apiResponse, account.getAcct()));
});
} else {
holder.account_action.hide();
}
holder.account_dn.setText(account.getDisplayName());
holder.account_ac.setText(String.format("@%s", account.getAcct()));
if (account.getDescription() == null) {
account.setDescription("");
}
//Profile picture
Helper.loadAvatar(context, account, holder.account_pp);
//Follow button
if (type == RetrofitPeertubeAPI.DataType.MUTED) {
holder.account_action.show();
holder.account_action.setImageResource(R.drawable.ic_baseline_volume_mute_24);
}
holder.account_pp.setOnClickListener(v -> {
Intent intent = new Intent(context, ShowAccountActivity.class);
Bundle b = new Bundle();
b.putParcelable("account", account);
b.putString("accountAcct", account.getAcct());
intent.putExtras(b);
context.startActivity(intent);
});
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getItemCount() {
return accounts.size();
}
public void manageVIewPostActions(RetrofitPeertubeAPI.ActionType statusAction, APIResponse apiResponse, String elementTargeted) {
if (apiResponse.getError() != null) {
Toasty.error(context, apiResponse.getError().getError(), Toast.LENGTH_LONG).show();
return;
}
if (statusAction == RetrofitPeertubeAPI.ActionType.UNMUTE) {
int position = 0;
for (Account account : accounts) {
if (account.getAcct().equals(elementTargeted)) {
accounts.remove(position);
accountsListAdapter.notifyItemRemoved(position);
break;
}
position++;
}
if (accounts.size() == 0 && allAccountsRemoved != null) {
allAccountsRemoved.onAllAccountsRemoved();
}
}
}
public interface AllAccountsRemoved {
void onAllAccountsRemoved();
}
private static class ViewHolder extends RecyclerView.ViewHolder {
ImageView account_pp;
TextView account_ac;
TextView account_dn;
FloatingActionButton account_action;
LinearLayout account_container;
ViewHolder(View itemView) {
super(itemView);
account_pp = itemView.findViewById(R.id.account_pp);
account_dn = itemView.findViewById(R.id.account_dn);
account_ac = itemView.findViewById(R.id.account_ac);
account_action = itemView.findViewById(R.id.account_action);
account_container = itemView.findViewById(R.id.account_container);
}
}
}

@ -0,0 +1,184 @@
package app.fedilab.android.peertube.drawer;
/* 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 <http://www.gnu.org/licenses>. */
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.PopupMenu;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import app.fedilab.android.peertube.R;
import app.fedilab.android.peertube.activities.AccountActivity;
import app.fedilab.android.peertube.activities.ShowChannelActivity;
import app.fedilab.android.peertube.client.RetrofitPeertubeAPI;
import app.fedilab.android.peertube.client.data.ChannelData.Channel;
import app.fedilab.android.peertube.helper.Helper;
public class ChannelListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final List<Channel> channels;
public AllChannelRemoved allChannelRemoved;
public EditAlertDialog editAlertDialog;
private Context context;
public ChannelListAdapter(List<Channel> channels) {
this.channels = channels;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
LayoutInflater layoutInflater = LayoutInflater.from(context);
return new ViewHolder(layoutInflater.inflate(R.layout.drawer_channel, parent, false));
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
final ViewHolder holder = (ViewHolder) viewHolder;
final Channel channel = channels.get(position);
holder.account_dn.setText(channel.getDisplayName());
holder.account_ac.setText(String.format("@%s", channel.getAcct()));
if (channel.getDescription() == null) {
channel.setDescription("");
}
//Profile picture
Helper.loadAvatar(context, channel, holder.account_pp);
if (!isMyChannel(channel)) {
holder.more_actions.setVisibility(View.GONE);
}
holder.more_actions.setOnClickListener(view -> {
PopupMenu popup = new PopupMenu(context, holder.more_actions);
popup.getMenuInflater()
.inflate(R.menu.playlist_menu, popup.getMenu());
if (channels.size() == 1) {
popup.getMenu().findItem(R.id.action_delete).setEnabled(false);
}
popup.setOnMenuItemClickListener(item -> {
int itemId = item.getItemId();
if (itemId == R.id.action_delete) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(context.getString(R.string.delete_channel) + ": " + channel.getName());
builder.setMessage(context.getString(R.string.action_channel_confirm_delete));
builder.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(R.string.yes, (dialog, which) -> {
new Thread(() -> {
new RetrofitPeertubeAPI(context).post(RetrofitPeertubeAPI.ActionType.DELETE_CHANNEL, channel.getName(), null);
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> {
channels.remove(channel);
notifyDataSetChanged();
if (channels.size() == 0) {
allChannelRemoved.onAllChannelRemoved();
}
};
mainHandler.post(myRunnable);
}).start();
dialog.dismiss();
})
.setNegativeButton(R.string.no, (dialog, which) -> dialog.dismiss())
.show();
} else if (itemId == R.id.action_edit) {
if (context instanceof AccountActivity) {
editAlertDialog.show(channel);
}
}
return true;
});
popup.show();
});
holder.account_pp.setOnClickListener(v -> {
Intent intent = new Intent(context, ShowChannelActivity.class);
Bundle b = new Bundle();
b.putParcelable("channel", channel);
intent.putExtras(b);
context.startActivity(intent);
});
}
private boolean isMyChannel(Channel channel) {
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
String channeIdOwner = channel.getOwnerAccount().getId();
String channeInstanceOwner = channel.getOwnerAccount().getHost();
String instanceShar = sharedpreferences.getString(Helper.PREF_INSTANCE, null);
String userIdShar = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
if (channeIdOwner != null && channeInstanceOwner != null && instanceShar != null && userIdShar != null) {
return channeIdOwner.compareTo(userIdShar) == 0 && channeInstanceOwner.compareTo(instanceShar) == 0;
} else {
return false;
}
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getItemCount() {
return channels.size();
}
public interface AllChannelRemoved {
void onAllChannelRemoved();
}
public interface EditAlertDialog {
void show(Channel channel);
}
private static class ViewHolder extends RecyclerView.ViewHolder {
ImageView account_pp;
TextView account_ac;
TextView account_dn;
ImageButton more_actions;
LinearLayout account_container;
ViewHolder(View itemView) {
super(itemView);
account_pp = itemView.findViewById(R.id.account_pp);
account_dn = itemView.findViewById(R.id.account_dn);
account_ac = itemView.findViewById(R.id.account_ac);
more_actions = itemView.findViewById(R.id.more_actions);
account_container = itemView.findViewById(R.id.account_container);
}
}
}

@ -0,0 +1,386 @@
package app.fedilab.android.peertube.drawer;
/* 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 <http://www.gnu.org/licenses>. */
import static app.fedilab.android.peertube.client.RetrofitPeertubeAPI.ActionType.MUTE;
import static app.fedilab.android.peertube.client.RetrofitPeertubeAPI.ActionType.REPLY;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.Html;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.text.style.ForegroundColorSpan;
import android.view.LayoutInflater;
import android.view.MotionEvent;
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.annotation.NonNull;
import androidx.appcompat.widget.PopupMenu;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import app.fedilab.android.peertube.R;
import app.fedilab.android.peertube.activities.PeertubeActivity;
import app.fedilab.android.peertube.activities.ShowAccountActivity;
import app.fedilab.android.peertube.client.APIResponse;
import app.fedilab.android.peertube.client.RetrofitPeertubeAPI;
import app.fedilab.android.peertube.client.data.CommentData.Comment;
import app.fedilab.android.peertube.client.entities.Report;
import app.fedilab.android.peertube.databinding.DrawerCommentBinding;
import app.fedilab.android.peertube.helper.CommentDecorationHelper;
import app.fedilab.android.peertube.helper.EmojiHelper;
import app.fedilab.android.peertube.helper.Helper;
import app.fedilab.android.peertube.viewmodel.PostActionsVM;
import es.dmoral.toasty.Toasty;
public class CommentListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final List<Comment> comments;
private final CommentListAdapter commentListAdapter;
private final boolean isThread;
private final String instance;
private final boolean sepiaSearch;
public AllCommentRemoved allCommentRemoved;
boolean isVideoOwner;
private Context context;
public CommentListAdapter(List<Comment> comments, boolean isVideoOwner, boolean isThread, String instance, boolean sepiaSearch) {
this.comments = comments;
commentListAdapter = this;
this.isVideoOwner = isVideoOwner;
this.isThread = isThread;
this.instance = instance;
this.sepiaSearch = sepiaSearch;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getItemCount() {
return comments.size();
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
DrawerCommentBinding itemBinding = DrawerCommentBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
return new ViewHolder(itemBinding);
}
@SuppressLint({"SetJavaScriptEnabled", "ClickableViewAccessibility"})
@Override
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder viewHolder, int i) {
context = viewHolder.itemView.getContext();
final ViewHolder holder = (ViewHolder) viewHolder;
final Comment comment = comments.get(i);
if (comment == null)
return;
holder.binding.mainContainer.setTag(i);
holder.binding.decorationContainer.removeAllViews();
if (comment.isReply()) {
int ident = CommentDecorationHelper.getIndentation(comment.getInReplyToCommentId(), comments);
for (int j = 0; j <= ident; j++) {
View view = new View(context);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(1, LinearLayout.LayoutParams.MATCH_PARENT);
params.setMargins((int) Helper.convertDpToPixel(5, context), 0, 0, 0);
view.setBackgroundResource(R.color.colorAccent);
view.setLayoutParams(params);
holder.binding.decorationContainer.addView(view, 0);
}
}
holder.binding.moreActions.setOnClickListener(view -> {
PopupMenu popup = new PopupMenu(context, holder.binding.moreActions);
popup.getMenuInflater()
.inflate(R.menu.comment_menu, popup.getMenu());
if (!Helper.isOwner(context, comment.getAccount())) {
popup.getMenu().findItem(R.id.action_delete).setVisible(false);
} else {
popup.getMenu().findItem(R.id.action_mute).setVisible(false);
popup.getMenu().findItem(R.id.action_remove_comments).setVisible(false);
popup.getMenu().findItem(R.id.action_report).setVisible(false);
}
if (!isVideoOwner) {
popup.getMenu().findItem(R.id.action_remove_comments).setVisible(false);
}
if (!Helper.isLoggedIn(context)) {
popup.getMenu().findItem(R.id.action_mute).setVisible(false);
popup.getMenu().findItem(R.id.action_remove_comments).setVisible(false);
popup.getMenu().findItem(R.id.action_delete).setVisible(false);
}
popup.setOnMenuItemClickListener(item -> {
int itemId = item.getItemId();
if (itemId == R.id.action_delete) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.delete_comment);
builder.setMessage(R.string.delete_comment_confirm);
builder.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(R.string.delete, (dialog, which) -> {
new Thread(() -> {
new RetrofitPeertubeAPI(context).post(RetrofitPeertubeAPI.ActionType.PEERTUBEDELETECOMMENT, comment.getVideoId(), comment.getId());
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> {
comments.remove(comment);
notifyDataSetChanged();
if (comments.size() == 0) {
allCommentRemoved.onAllCommentRemoved();
}
};
mainHandler.post(myRunnable);
}).start();
dialog.dismiss();
})
.setNegativeButton(R.string.no, (dialog, which) -> dialog.dismiss())
.show();
} else if (itemId == R.id.action_report) {
reportComment(comment);
} else if (itemId == R.id.action_mute) {
PostActionsVM viewModel = new ViewModelProvider((ViewModelStoreOwner) context).get(PostActionsVM.class);
viewModel.post(MUTE, comment.getAccount().getAcct(), null).observe((LifecycleOwner) context, apiResponse -> manageVIewPostActions(MUTE, 0, apiResponse));
comments.remove(comment);
notifyDataSetChanged();
if (comments.size() == 0) {
allCommentRemoved.onAllCommentRemoved();
}
} else if (itemId == R.id.action_remove_comments) {
AlertDialog.Builder builder;
builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.delete_account_comment);
builder.setMessage(R.string.delete_account_comment_confirm);
builder.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(R.string.delete, (dialog, which) -> {
new Thread(() -> {
new RetrofitPeertubeAPI(context).post(RetrofitPeertubeAPI.ActionType.PEERTUBE_DELETE_ALL_COMMENT_FOR_ACCOUNT, comment.getAccount().getAcct(), "my-videos");
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> {
comments.remove(comment);
notifyDataSetChanged();
if (comments.size() == 0) {
allCommentRemoved.onAllCommentRemoved();
}
};
mainHandler.post(myRunnable);
}).start();
dialog.dismiss();
})
.setNegativeButton(R.string.no, (dialog, which) -> dialog.dismiss())
.show();
}
return true;
});
popup.show();
});
holder.binding.commentContent.setOnTouchListener((view, motionEvent) -> {
if (motionEvent.getAction() == MotionEvent.ACTION_UP && !view.hasFocus()) {
try {
view.requestFocus();
} catch (Exception ignored) {
}
}
return false;
});
Spanned commentSpan;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
commentSpan = Html.fromHtml(EmojiHelper.shortnameToUnicode(comment.getText()), Html.FROM_HTML_MODE_COMPACT);
else
commentSpan = Html.fromHtml(EmojiHelper.shortnameToUnicode(comment.getText()));
holder.binding.commentContent.setText(commentSpan, TextView.BufferType.SPANNABLE);
holder.binding.commentContent.setMovementMethod(LinkMovementMethod.getInstance());
holder.binding.commentAccountDisplayname.setText(comment.getAccount().getDisplayName());
if (context instanceof PeertubeActivity && !isThread) {
holder.binding.mainContainer.setOnClickListener(v -> ((PeertubeActivity) context).openCommentThread(comment));
holder.binding.commentContent.setOnClickListener(v -> ((PeertubeActivity) context).openCommentThread(comment));
holder.binding.replyButton.setOnClickListener(v -> ((PeertubeActivity) context).openCommentThread(comment));
} else if (context instanceof PeertubeActivity) {
holder.binding.replyButton.setOnClickListener(v -> ((PeertubeActivity) context).openPostComment(comment, i));
}
if (comment.getTotalReplies() > 0) {
holder.binding.numberOfReplies.setVisibility(View.VISIBLE);
holder.binding.numberOfReplies.setText(context.getResources().getQuantityString(R.plurals.number_of_replies, comment.getTotalReplies(), comment.getTotalReplies()));
} else {
holder.binding.numberOfReplies.setVisibility(View.GONE);
}
if (comment.getAccount() != null) {
Spannable wordtoSpan;
Pattern hashAcct;
wordtoSpan = new SpannableString("@" + comment.getAccount().getAcct());
hashAcct = Pattern.compile("(@" + comment.getAccount().getAcct() + ")");
Matcher matcherAcct = hashAcct.matcher(wordtoSpan);
while (matcherAcct.find()) {
int matchStart = matcherAcct.start(1);
int matchEnd = matcherAcct.end();
if (wordtoSpan.length() >= matchEnd && matchStart < matchEnd) {
wordtoSpan.setSpan(new ForegroundColorSpan(ContextCompat.getColor(context, android.R.color.darker_gray)), matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
}
holder.binding.commentAccountUsername.setText(wordtoSpan);
}
holder.binding.commentDate.setText(Helper.dateDiff(context, comment.getCreatedAt()));
Helper.loadAvatar(context, comment.getAccount(), holder.binding.commentAccountProfile);
holder.binding.commentAccountProfile.setOnClickListener(v -> {
Bundle b = new Bundle();
Intent intent = new Intent(context, ShowAccountActivity.class);
b.putParcelable("account", comment.getAccount());
b.putString("accountAcct", comment.getAccount().getAcct());
intent.putExtras(b);
context.startActivity(intent);
});
if (comment.isReply()) {
holder.binding.replyButton.setVisibility(View.VISIBLE);
} else {
holder.binding.replyButton.setVisibility(View.GONE);
}
if (i == 0 && isThread) {
holder.binding.postReplyButton.setVisibility(View.VISIBLE);
} else {
holder.binding.postReplyButton.setVisibility(View.GONE);
}
holder.binding.postReplyButton.setOnClickListener(v -> {
if (Helper.canMakeAction(context) && !sepiaSearch) {
((PeertubeActivity) context).openPostComment(comment, i);
} else {
if (sepiaSearch) {
Toasty.info(context, context.getString(R.string.federation_issue), Toasty.LENGTH_SHORT).show();
} else {
Toasty.error(context, context.getString(R.string.not_logged_in), Toast.LENGTH_SHORT).show();
}
}
});
if (Helper.canMakeAction(context) && !sepiaSearch) {
holder.binding.replyButton.setVisibility(View.VISIBLE);
} else {
holder.binding.replyButton.setVisibility(View.GONE);
}
}
public void manageVIewPostActions(RetrofitPeertubeAPI.ActionType statusAction, int i, APIResponse apiResponse) {
if (apiResponse.getError() != null) {
Toasty.error(context, apiResponse.getError().getError(), Toast.LENGTH_LONG).show();
return;
}
if (statusAction == RetrofitPeertubeAPI.ActionType.PEERTUBEDELETECOMMENT) {
int position = 0;
for (Comment comment : comments) {
if (comment.getId().equals(apiResponse.getTargetedId())) {
comments.remove(comment);
commentListAdapter.notifyItemRemoved(position);
break;
}
position++;
}
} else if (statusAction == REPLY) {
if (apiResponse.getComments() != null && apiResponse.getComments().size() > 0) {
comments.add(i + 1, apiResponse.getComments().get(0));
notifyItemInserted(i + 1);
}
} else if (statusAction == RetrofitPeertubeAPI.ActionType.REPORT_COMMENT) {
Toasty.success(context, context.getString(R.string.successful_report_comment), Toasty.LENGTH_LONG).show();
} else if (statusAction == MUTE) {
Toasty.info(context, context.getString(R.string.muted_done), Toast.LENGTH_LONG).show();
}
}
private void reportComment(Comment comment) {
androidx.appcompat.app.AlertDialog.Builder dialogBuilder = new androidx.appcompat.app.AlertDialog.Builder(context);
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
View dialogView = inflater.inflate(R.layout.popup_report, new LinearLayout(context), 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(context, context.getString(R.string.report_comment_size), Toasty.LENGTH_LONG).show();
} else {
PostActionsVM viewModel = new ViewModelProvider((ViewModelStoreOwner) context).get(PostActionsVM.class);
Report report = new Report();
Report.CommentReport commentReport = new Report.CommentReport();
commentReport.setId(comment.getId());
report.setComment(commentReport);
report.setReason(report_content.getText().toString());
viewModel.report(report).observe((LifecycleOwner) context, apiResponse -> manageVIewPostActions(RetrofitPeertubeAPI.ActionType.REPORT_COMMENT, 0, apiResponse));
dialog.dismiss();
}
});
androidx.appcompat.app.AlertDialog alertDialog2 = dialogBuilder.create();
alertDialog2.show();
}
public interface AllCommentRemoved {
void onAllCommentRemoved();
}
static class ViewHolder extends RecyclerView.ViewHolder {
DrawerCommentBinding binding;
ViewHolder(DrawerCommentBinding itemView) {
super(itemView.getRoot());
binding = itemView;
}
}
}

@ -0,0 +1,177 @@
package app.fedilab.android.peertube.drawer;
/* 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 <http://www.gnu.org/licenses>. */
import static android.app.Activity.RESULT_OK;
import static androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY;
import static app.fedilab.android.peertube.helper.Helper.peertubeInformation;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.text.Html;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.LinkedHashMap;
import java.util.List;
import app.fedilab.android.peertube.R;
import app.fedilab.android.peertube.client.data.InstanceData.Instance;
import app.fedilab.android.peertube.databinding.DrawerInstanceBinding;
import app.fedilab.android.peertube.helper.RoundedBackgroundSpan;
public class InstanceAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final List<Instance> instances;
private Context context;
public InstanceAdapter(List<Instance> instances) {
this.instances = instances;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
DrawerInstanceBinding itemBinding = DrawerInstanceBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
return new ViewHolder(itemBinding);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
final Instance instance = instances.get(position);
final ViewHolder holder = (ViewHolder) viewHolder;
if (instance.getShortDescription() != null && instance.getShortDescription().trim().length() > 0) {
if (instance.isTruncatedDescription()) {
holder.binding.description.setText(instance.getShortDescription());
holder.binding.description.setMaxLines(3);
holder.binding.description.setEllipsize(TextUtils.TruncateAt.END);
} else {
holder.binding.description.setText(instance.getShortDescription());
holder.binding.description.setMaxLines(Integer.MAX_VALUE);
holder.binding.description.setEllipsize(null);
}
SpannableString spannableString;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
spannableString = new SpannableString(Html.fromHtml(instance.getShortDescription(), FROM_HTML_MODE_LEGACY));
else
spannableString = new SpannableString(Html.fromHtml(instance.getShortDescription()));
holder.binding.description.setText(spannableString, TextView.BufferType.SPANNABLE);
holder.binding.description.setOnClickListener(v -> {
instance.setTruncatedDescription(!instance.isTruncatedDescription());
notifyItemChanged(position);
});
holder.binding.description.setVisibility(View.VISIBLE);
} else {
holder.binding.description.setVisibility(View.GONE);
}
holder.binding.name.setText(instance.getName());
holder.binding.host.setText(instance.getHost());
SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
String between = "";
if (peertubeInformation != null && peertubeInformation.getCategories() != null) {
LinkedHashMap<Integer, String> info_cat = new LinkedHashMap<>(peertubeInformation.getCategories());
if (instance.getCategories() != null && instance.getCategories().size() > 0 && instance.getSpannableStringBuilder() == null) {
for (int category : instance.getCategories()) {
String cat = info_cat.get(category);
stringBuilder.append(between);
if (cat != null && cat.trim().toLowerCase().compareTo("null") != 0) {
if (between.length() == 0) between = " ";
String tag = " " + cat + " ";
stringBuilder.append(tag);
stringBuilder.setSpan(new RoundedBackgroundSpan(context), stringBuilder.length() - tag.length(), stringBuilder.length() - tag.length() + tag.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
}
instance.setSpannableStringBuilder(stringBuilder);
}
}
if (instance.getSpannableStringBuilder() != null) {
holder.binding.tags.setText(instance.getSpannableStringBuilder());
}
if (peertubeInformation != null && peertubeInformation.getLanguages() != null) {
LinkedHashMap<String, String> info_lang = new LinkedHashMap<>(peertubeInformation.getLanguages());
StringBuilder languages = new StringBuilder();
if (instance.getLanguages() != null && instance.getLanguages().size() > 0) {
for (String language : instance.getLanguages()) {
languages.append(info_lang.get(language)).append(" ");
}
}
if (languages.toString().trim().length() == 0) {
holder.binding.languages.setVisibility(View.GONE);
} else {
holder.binding.languages.setText(languages);
holder.binding.languages.setVisibility(View.VISIBLE);
}
}
if (instance.getDefaultNSFWPolicy().compareTo("do_not_list") != 0) {
holder.binding.sensitiveContent.setText(context.getString(R.string.sensitive_content, instance.getDefaultNSFWPolicy()));
holder.binding.sensitiveContent.setVisibility(View.VISIBLE);
} else {
holder.binding.sensitiveContent.setVisibility(View.GONE);
}
holder.binding.followersInstance.setText(context.getString(R.string.followers_instance, String.valueOf(instance.getTotalInstanceFollowers())));
holder.binding.pickup.setOnClickListener(v -> {
Intent data = new Intent();
String instanceHost = instance.getHost();
data.setData(Uri.parse(instanceHost));
((Activity) context).setResult(RESULT_OK, data);
((Activity) context).finish();
});
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getItemCount() {
return instances.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
DrawerInstanceBinding binding;
ViewHolder(DrawerInstanceBinding itemView) {
super(itemView.getRoot());
binding = itemView;
}
}
}

@ -0,0 +1,87 @@
package app.fedilab.android.peertube.drawer;
/* 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 <http://www.gnu.org/licenses>. */
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import app.fedilab.android.peertube.client.MenuItemVideo;
import app.fedilab.android.peertube.databinding.DrawerMenuBinding;
public class MenuAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final List<MenuItemVideo> menuItemVideos;
public ItemClicked itemClicked;
public MenuAdapter(List<MenuItemVideo> menuItemVideos) {
this.menuItemVideos = menuItemVideos;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getItemCount() {
return menuItemVideos.size();
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
DrawerMenuBinding itemBinding = DrawerMenuBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
return new ViewHolder(itemBinding);
}
@Override
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder viewHolder, int i) {
final ViewHolder holder = (ViewHolder) viewHolder;
final MenuItemVideo menuItemVideo = menuItemVideos.get(i);
holder.binding.menuIcon.setImageResource(menuItemVideo.getIcon());
holder.binding.title.setText(menuItemVideo.getTitle());
holder.binding.itemMenuContainer.setOnClickListener(v -> itemClicked.onItemClicked(menuItemVideo.getAction()));
}
public interface ItemClicked {
void onItemClicked(MenuItemVideo.actionType actionType);
}
static class ViewHolder extends RecyclerView.ViewHolder {
DrawerMenuBinding binding;
ViewHolder(DrawerMenuBinding itemView) {
super(itemView.getRoot());
binding = itemView;
}
}
}

@ -0,0 +1,89 @@
package app.fedilab.android.peertube.drawer;
/* 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 <http://www.gnu.org/licenses>. */
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import app.fedilab.android.peertube.client.MenuItemVideo;
import app.fedilab.android.peertube.client.entities.MenuItemView;
import app.fedilab.android.peertube.databinding.DrawerMenuItemBinding;
public class MenuItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final List<MenuItemView> items;
public ItemAction itemAction;
MenuItemVideo.actionType actionType;
public MenuItemAdapter(MenuItemVideo.actionType actionType, List<MenuItemView> items) {
this.items = items;
this.actionType = actionType;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getItemCount() {
return items.size();
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
DrawerMenuItemBinding itemBinding = DrawerMenuItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
return new ViewHolder(itemBinding);
}
@Override
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder viewHolder, int i) {
final ViewHolder holder = (ViewHolder) viewHolder;
final MenuItemView item = items.get(i);
holder.binding.title.setText(item.getLabel());
holder.binding.radio.setChecked(item.isSelected());
holder.binding.itemMenuContainer.setOnClickListener(v -> itemAction.which(actionType, item));
holder.binding.radio.setOnClickListener(v -> itemAction.which(actionType, item));
}
public interface ItemAction {
void which(MenuItemVideo.actionType actionType, MenuItemView item);
}
static class ViewHolder extends RecyclerView.ViewHolder {
DrawerMenuItemBinding binding;
ViewHolder(DrawerMenuItemBinding itemView) {
super(itemView.getRoot());
binding = itemView;
}
}
}

@ -0,0 +1,94 @@
package app.fedilab.android.peertube.drawer;
/* 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 <http://www.gnu.org/licenses>. */
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import java.util.List;
import app.fedilab.android.peertube.R;
import app.fedilab.android.peertube.client.data.AccountData.Account;
import app.fedilab.android.peertube.helper.Helper;
public class OwnAccountsAdapter extends ArrayAdapter<Account> {
private final List<Account> accounts;
private final LayoutInflater layoutInflater;
public OwnAccountsAdapter(Context context, List<Account> accounts) {
super(context, android.R.layout.simple_list_item_1, accounts);
this.accounts = accounts;
layoutInflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return accounts.size();
}
@Override
public Account getItem(int position) {
return accounts.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@NonNull
@Override
public View getView(final int position, View convertView, @NonNull ViewGroup parent) {
final Account account = accounts.get(position);
final ViewHolder holder;
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.drawer_account_owner, parent, false);
holder = new ViewHolder();
holder.account_pp = convertView.findViewById(R.id.account_pp);
holder.account_un = convertView.findViewById(R.id.account_un);
holder.account_container = convertView.findViewById(R.id.account_container);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.account_un.setText(String.format("@%s", account.getAcct()));
//Profile picture
Helper.loadAvatar(holder.account_pp.getContext(), account, holder.account_pp);
return convertView;
}
private static class ViewHolder {
ImageView account_pp;
TextView account_un;
LinearLayout account_container;
}
}

@ -0,0 +1,490 @@
package app.fedilab.android.peertube.drawer;
/* 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 <http://www.gnu.org/licenses>. */
import static app.fedilab.android.peertube.client.RetrofitPeertubeAPI.ActionType.FOLLOW;
import static app.fedilab.android.peertube.client.RetrofitPeertubeAPI.ActionType.UNFOLLOW;
import static app.fedilab.android.peertube.viewmodel.TimelineVM.TimelineType.MY_VIDEOS;
import static app.fedilab.android.peertube.viewmodel.TimelineVM.TimelineType.SEPIA_SEARCH;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.PopupMenu;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestBuilder;
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestOptions;
import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import app.fedilab.android.peertube.R;
import app.fedilab.android.peertube.activities.PeertubeActivity;
import app.fedilab.android.peertube.activities.PeertubeEditUploadActivity;
import app.fedilab.android.peertube.activities.ShowChannelActivity;
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;
import app.fedilab.android.peertube.client.data.PlaylistData;
import app.fedilab.android.peertube.client.data.VideoData;
import app.fedilab.android.peertube.client.entities.PlaylistExist;
import app.fedilab.android.peertube.client.entities.Report;
import app.fedilab.android.peertube.databinding.DrawerPeertubeBinding;
import app.fedilab.android.peertube.helper.Helper;
import app.fedilab.android.peertube.helper.HelperInstance;
import app.fedilab.android.peertube.viewmodel.PlaylistsVM;
import app.fedilab.android.peertube.viewmodel.PostActionsVM;
import app.fedilab.android.peertube.viewmodel.TimelineVM;
import es.dmoral.toasty.Toasty;
import jp.wasabeef.glide.transformations.BlurTransformation;
public class PeertubeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final List<VideoData.Video> videos;
public RelationShipListener relationShipListener;
public PlaylistListener playlistListener;
private Context context;
private TimelineVM.TimelineType timelineType;
private boolean sepiaSearch;
private ChannelData.Channel forChannel;
private AccountData.Account forAccount;
public PeertubeAdapter(List<VideoData.Video> videos, TimelineVM.TimelineType timelineType, boolean sepiaSearch, ChannelData.Channel forChannel, AccountData.Account forAccount) {
this.videos = videos;
this.timelineType = timelineType;
this.sepiaSearch = sepiaSearch || timelineType == SEPIA_SEARCH;
this.forChannel = forChannel;
this.forAccount = forAccount;
}
public PeertubeAdapter(List<VideoData.Video> videos) {
this.videos = videos;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
DrawerPeertubeBinding itemBinding = DrawerPeertubeBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
return new ViewHolder(itemBinding);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
final ViewHolder holder = (ViewHolder) viewHolder;
final VideoData.Video video = videos.get(position);
if (video == null) {
return;
}
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, "");
assert userId != null;
boolean videoInList = sharedpreferences.getBoolean(context.getString(R.string.set_video_in_list_choice), false);
boolean ownVideos;
if (timelineType == TimelineVM.TimelineType.MY_VIDEOS) {
ownVideos = true;
} else {
ownVideos = Helper.isVideoOwner(context, video);
}
String instance = null;
if (sepiaSearch) {
instance = video.getAccount().getHost();
} else if (forChannel != null) {
instance = forChannel.getHost();
} else if (forAccount != null) {
instance = forAccount.getHost();
}
holder.binding.peertubeAccountName.setText(video.getChannel().getAcct());
Helper.loadAvatar(context, video.getChannel(), holder.binding.peertubeProfile);
holder.binding.peertubeTitle.setText(video.getName());
if (video.isLive()) {
holder.binding.peertubeDuration.setText(R.string.live);
holder.binding.peertubeDuration.setBackgroundResource(R.drawable.rounded_live);
} else {
holder.binding.peertubeDuration.setText(Helper.secondsToString(video.getDuration()));
holder.binding.peertubeDuration.setBackgroundResource(R.drawable.rounded_corner);
}
holder.binding.peertubeDate.setText(String.format(" - %s", Helper.dateDiff(context, video.getCreatedAt())));
holder.binding.peertubeViews.setText(context.getString(R.string.number_view_video, Helper.withSuffix(video.getViews())));
boolean blur = sharedpreferences.getString(context.getString(R.string.set_video_sensitive_choice), Helper.BLUR).compareTo("blur") == 0 && video.isNsfw();
if (videoInList) {
Helper.loadGiF(context, instance, video.getThumbnailPath(), holder.binding.peertubeVideoImageSmall, blur);
holder.binding.peertubeVideoImageSmall.setVisibility(View.VISIBLE);
holder.binding.previewContainer.setVisibility(View.GONE);
} else {
loadImage(holder.binding.peertubeVideoImage, instance, video.getPreviewPath(), video.getThumbnailPath(), blur);
holder.binding.peertubeVideoImageSmall.setVisibility(View.GONE);
holder.binding.previewContainer.setVisibility(View.VISIBLE);
}
//For Overview Videos: boolean values for displaying title is managed in the fragment
if (video.isHasTitle()) {
holder.binding.headerTitle.setVisibility(View.VISIBLE);
switch (video.getTitleType()) {
case TAG:
holder.binding.headerTitle.setText(String.format("#%s", video.getTitle()));
break;
case CHANNEL:
case CATEGORY:
holder.binding.headerTitle.setText(String.format("%s", video.getTitle()));
break;
}
} else {
holder.binding.headerTitle.setVisibility(View.GONE);
}
if (!ownVideos) {
holder.binding.peertubeProfile.setOnClickListener(v -> {
Intent intent = new Intent(context, ShowChannelActivity.class);
Bundle b = new Bundle();
b.putParcelable("channel", video.getChannel());
b.putBoolean("sepia_search", sepiaSearch || forChannel != null);
if (sepiaSearch || forChannel != null) {
b.putString("peertube_instance", video.getAccount().getHost());
}
intent.putExtras(b);
context.startActivity(intent);
});
}
holder.binding.moreActions.setOnClickListener(view -> {
PopupMenu popup = new PopupMenu(context, holder.binding.moreActions);
popup.getMenuInflater()
.inflate(R.menu.video_drawer_menu, popup.getMenu());
if (timelineType == MY_VIDEOS) {
popup.getMenu().findItem(R.id.action_report).setVisible(false);
popup.getMenu().findItem(R.id.action_follow).setVisible(false);
} else {
popup.getMenu().findItem(R.id.action_edit).setVisible(false);
if (relationShipListener == null || relationShipListener.getRelationShip() == null || relationShipListener.getRelationShip().size() == 0) {
popup.getMenu().findItem(R.id.action_follow).setVisible(false);
} else {
popup.getMenu().findItem(R.id.action_follow).setVisible(true);
if (relationShipListener.getRelationShip().containsKey(video.getChannel().getAcct()) && relationShipListener.getRelationShip().get(video.getChannel().getAcct())) {
popup.getMenu().findItem(R.id.action_follow).setTitle(context.getString(R.string.action_unfollow));
} else {
popup.getMenu().findItem(R.id.action_follow).setTitle(context.getString(R.string.action_follow));
}
}
}
popup.getMenu().findItem(R.id.action_playlist).setVisible(playlistListener != null && playlistListener.getPlaylist() != null && playlistListener.getPlaylist().size() != 0);
popup.setOnMenuItemClickListener(item -> {
int itemId = item.getItemId();
if (itemId == R.id.action_follow) {
if (relationShipListener.getRelationShip().containsKey(video.getChannel().getAcct()) && relationShipListener.getRelationShip().get(video.getChannel().getAcct())) {
relationShipListener.getRelationShip().put(video.getChannel().getAcct(), false);
popup.getMenu().findItem(R.id.action_follow).setTitle(context.getString(R.string.action_follow));
boolean confirm_unfollow = sharedpreferences.getBoolean(Helper.SET_UNFOLLOW_VALIDATION, true);
if (confirm_unfollow) {
AlertDialog.Builder unfollowConfirm = new AlertDialog.Builder(context);
unfollowConfirm.setTitle(context.getString(R.string.unfollow_confirm));
unfollowConfirm.setMessage(video.getChannel().getAcct());
unfollowConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
unfollowConfirm.setPositiveButton(R.string.yes, (dialog, which) -> {
PostActionsVM viewModel = new ViewModelProvider((ViewModelStoreOwner) context).get(PostActionsVM.class);
viewModel.post(UNFOLLOW, video.getChannel().getAcct(), null).observe((LifecycleOwner) context, apiResponse -> manageVIewPostActions(UNFOLLOW, apiResponse));
dialog.dismiss();
});
unfollowConfirm.show();
} else {
PostActionsVM viewModel = new ViewModelProvider((ViewModelStoreOwner) context).get(PostActionsVM.class);
viewModel.post(UNFOLLOW, video.getChannel().getAcct(), null).observe((LifecycleOwner) context, apiResponse -> manageVIewPostActions(UNFOLLOW, apiResponse));
}
} else {
relationShipListener.getRelationShip().put(video.getChannel().getAcct(), true);
popup.getMenu().findItem(R.id.action_follow).setTitle(context.getString(R.string.action_unfollow));
PostActionsVM viewModel = new ViewModelProvider((ViewModelStoreOwner) context).get(PostActionsVM.class);
viewModel.post(FOLLOW, video.getChannel().getAcct(), null).observe((LifecycleOwner) context, apiResponse -> manageVIewPostActions(FOLLOW, apiResponse));
}
} else if (itemId == R.id.action_playlist) {
PlaylistsVM viewModelOwnerPlaylist = new ViewModelProvider((ViewModelStoreOwner) context).get(PlaylistsVM.class);
viewModelOwnerPlaylist.manage(PlaylistsVM.action.GET_PLAYLISTS, null, null).observe((LifecycleOwner) context, apiResponse -> manageVIewPlaylists(video, apiResponse));
} else if (itemId == R.id.action_edit) {
Intent intent = new Intent(context, PeertubeEditUploadActivity.class);
Bundle b = new Bundle();
b.putString("video_id", video.getUuid());
intent.putExtras(b);
context.startActivity(intent);
} else if (itemId == R.id.action_report) {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context);
LayoutInflater inflater1 = ((Activity) context).getLayoutInflater();
View dialogView = inflater1.inflate(R.layout.popup_report, new LinearLayout(context), false);
dialogBuilder.setView(dialogView);
EditText report_content = dialogView.findViewById(R.id.report_content);
dialogBuilder.setNeutralButton(R.string.cancel, (dialog2, id) -> dialog2.dismiss());
dialogBuilder.setPositiveButton(R.string.report, (dialog2, id) -> {
if (report_content.getText().toString().trim().length() == 0) {
Toasty.info(context, context.getString(R.string.report_comment_size), Toasty.LENGTH_LONG).show();
} else {
PostActionsVM viewModel = new ViewModelProvider((ViewModelStoreOwner) context).get(PostActionsVM.class);
Report report = new Report();
Report.VideoReport videoReport = new Report.VideoReport();
videoReport.setId(video.getId());
report.setVideo(videoReport);
report.setReason(report_content.getText().toString());
viewModel.report(report).observe((LifecycleOwner) context, apiResponse -> manageVIewPostActions(RetrofitPeertubeAPI.ActionType.REPORT_VIDEO, apiResponse));
dialog2.dismiss();
}
});
AlertDialog alertDialog2 = dialogBuilder.create();
alertDialog2.show();
}
return true;
});
popup.show();
});
holder.binding.bottomContainer.setOnClickListener(v -> {
Intent intent = new Intent(context, PeertubeActivity.class);
Bundle b = new Bundle();
b.putString("video_id", video.getId());
b.putString("video_uuid", video.getUuid());
b.putBoolean("isMyVideo", ownVideos);
b.putBoolean("sepia_search", sepiaSearch);
b.putParcelable("video", video);
if (sepiaSearch) {
b.putString("peertube_instance", video.getAccount().getHost());
}
intent.putExtras(b);
context.startActivity(intent);
});
holder.binding.peertubeVideoImage.setOnClickListener(v -> {
Intent intent = new Intent(context, PeertubeActivity.class);
Bundle b = new Bundle();
b.putString("video_id", video.getId());
b.putParcelable("video", video);
b.putString("video_uuid", video.getUuid());
b.putBoolean("isMyVideo", ownVideos);
b.putBoolean("sepia_search", sepiaSearch);
if (sepiaSearch) {
b.putString("peertube_instance", video.getAccount().getHost());
}
intent.putExtras(b);
context.startActivity(intent);
});
}
public void manageVIewPlaylists(VideoData.Video video, APIResponse apiResponse) {
if (apiResponse.getError() != null) {
return;
}
if (apiResponse.getPlaylists() != null && apiResponse.getPlaylists().size() > 0) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.modify_playlists);
List<PlaylistData.Playlist> ownerPlaylists = apiResponse.getPlaylists();
if (ownerPlaylists == null) {
return;
}
String[] label = new String[ownerPlaylists.size()];
boolean[] checked = new boolean[ownerPlaylists.size()];
int i = 0;
List<PlaylistExist> playlistsForVideo = playlistListener.getPlaylist().get(video.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((ViewModelStoreOwner) context).get(PlaylistsVM.class);
if (isChecked) { //Add to playlist
playlistsViewModel.manage(PlaylistsVM.action.ADD_VIDEOS, ownerPlaylists.get(which), video.getUuid()).observe((LifecycleOwner) context, apiResponse3 -> addElement(ownerPlaylists.get(which).getId(), video.getId(), apiResponse3));
} else { //Remove from playlist
String elementInPlaylistId = null;
for (PlaylistExist playlistExist : video.getPlaylistExists()) {
if (playlistExist.getPlaylistId().compareTo(ownerPlaylists.get(which).getId()) == 0) {
elementInPlaylistId = playlistExist.getPlaylistElementId();
}
}
playlistsViewModel.manage(PlaylistsVM.action.DELETE_VIDEOS, ownerPlaylists.get(which), elementInPlaylistId);
playlistListener.getPlaylist().remove(video.getId());
}
});
builder.setPositiveButton(R.string.close, (dialog, which) -> dialog.dismiss());
AlertDialog dialog = builder.create();
dialog.show();
}
}
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<PlaylistExist> playlistExistList = playlistListener.getPlaylist().get(videoId);
if (playlistExistList == null) {
playlistExistList = new ArrayList<>();
}
playlistExistList.add(playlistExist);
playlistListener.getPlaylist().put(videoId, playlistExistList);
}
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getItemCount() {
return videos.size();
}
public void manageVIewPostActions(RetrofitPeertubeAPI.ActionType statusAction, APIResponse apiResponse) {
if (statusAction == RetrofitPeertubeAPI.ActionType.REPORT_VIDEO) {
Toasty.success(context, context.getString(R.string.successful_video_report), Toasty.LENGTH_LONG).show();
} else 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);
}
}
@SuppressLint("CheckResult")
private void loadImage(ImageView target, String instance, String urlPreview, String thumbnail, boolean blur) {
if (urlPreview == null || urlPreview.startsWith("null")) {
urlPreview = thumbnail;
}
if (instance != null) {
urlPreview = "https://" + instance + urlPreview;
thumbnail = "https://" + instance + thumbnail;
} else {
urlPreview = "https://" + HelperInstance.getLiveInstance(context) + urlPreview;
thumbnail = "https://" + HelperInstance.getLiveInstance(context) + thumbnail;
}
RequestBuilder<Drawable> requestBuilder = Glide.with(context)
.asDrawable();
if (blur) {
requestBuilder.apply(new RequestOptions().transform(new BlurTransformation(50, 3), new CenterCrop(), new RoundedCorners(10)));
} else {
requestBuilder.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10)));
}
final Drawable[] initialResource = new Drawable[1];
String finalUrlPreview = urlPreview;
requestBuilder.load(thumbnail).into(
new CustomTarget<Drawable>() {
@Override
public void onResourceReady(@NonNull final Drawable resource, Transition<? super Drawable> transition) {
target.setImageDrawable(resource);
initialResource[0] = resource;
RequestBuilder<Drawable> requestBuilder = Glide.with(context)
.asDrawable();
if (blur) {
requestBuilder.apply(new RequestOptions().transform(new BlurTransformation(50, 3), new CenterCrop(), new RoundedCorners(10)));
} else {
requestBuilder.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10)));
}
requestBuilder.load(finalUrlPreview).into(
new CustomTarget<Drawable>() {
@Override
public void onResourceReady(@NonNull final Drawable resource, Transition<? super Drawable> transition) {
target.setImageDrawable(resource);
}
@Override
public void onLoadCleared(@Nullable Drawable placeholder) {
}
@Override
public void onLoadFailed(@Nullable Drawable errorDrawable) {
target.setImageDrawable(initialResource[0]);
}
}
);
}
@Override
public void onLoadCleared(@Nullable Drawable placeholder) {
}
}
);
}
public interface RelationShipListener {
Map<String, Boolean> getRelationShip();
}
public interface PlaylistListener {
Map<String, List<PlaylistExist>> getPlaylist();
}
static class ViewHolder extends RecyclerView.ViewHolder {
DrawerPeertubeBinding binding;
ViewHolder(DrawerPeertubeBinding itemView) {
super(itemView.getRoot());
binding = itemView;
}
}
}

@ -0,0 +1,257 @@
package app.fedilab.android.peertube.drawer;
/* 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 <http://www.gnu.org/licenses>. */
import static app.fedilab.android.peertube.activities.MainActivity.badgeCount;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import app.fedilab.android.peertube.R;
import app.fedilab.android.peertube.activities.AccountActivity;
import app.fedilab.android.peertube.activities.PeertubeActivity;
import app.fedilab.android.peertube.activities.ShowAccountActivity;
import app.fedilab.android.peertube.activities.ShowChannelActivity;
import app.fedilab.android.peertube.client.RetrofitPeertubeAPI;
import app.fedilab.android.peertube.client.data.AccountData;
import app.fedilab.android.peertube.client.data.ChannelData;
import app.fedilab.android.peertube.client.data.NotificationData.Notification;
import app.fedilab.android.peertube.client.entities.Actor;
import app.fedilab.android.peertube.fragment.DisplayNotificationsFragment;
import app.fedilab.android.peertube.helper.Helper;
import app.fedilab.android.peertube.helper.HelperInstance;
public class PeertubeNotificationsListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final List<Notification> notifications;
private Context context;
public PeertubeNotificationsListAdapter(List<Notification> notifications) {
this.notifications = notifications;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
LayoutInflater layoutInflater = LayoutInflater.from(this.context);
return new ViewHolder(layoutInflater.inflate(R.layout.drawer_peertube_notification, parent, false));
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
ViewHolder holder = (ViewHolder) viewHolder;
Notification notification = notifications.get(position);
//Follow Notification
boolean clickableNotification = true;
holder.peertube_notif_pp.setVisibility(View.VISIBLE);
AccountData.Account accountAction = null;
ChannelData.Channel channelAction = null;
if (notification.isRead()) {
holder.unread.setVisibility(View.INVISIBLE);
} else {
holder.unread.setVisibility(View.VISIBLE);
}
if (notification.getActorFollow() != null) {
String profileUrl = notification.getActorFollow().getFollower().getAvatar() != null ? notification.getActorFollow().getFollower().getAvatar().getPath() : null;
Helper.loadGiF(context, profileUrl, holder.peertube_notif_pp);
Actor accountActionFollow = notification.getActorFollow().getFollower();
String type = notification.getActorFollow().getFollowing().getType();
String message;
if (type != null && type.compareTo("channel") == 0) {
message = context.getString(R.string.peertube_follow_channel, notification.getActorFollow().getFollower().getDisplayName(), notification.getActorFollow().getFollowing().getDisplayName());
} else {
message = context.getString(R.string.peertube_follow_account, accountActionFollow.getDisplayName());
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
holder.peertube_notif_message.setText(Html.fromHtml(message, Html.FROM_HTML_MODE_LEGACY));
else
holder.peertube_notif_message.setText(Html.fromHtml(message));
Actor actor = notification.getActorFollow().getFollower();
accountAction = new AccountData.Account();
accountAction.setAvatar(actor.getAvatar());
accountAction.setDisplayName(actor.getDisplayName());
accountAction.setHost(actor.getHost());
accountAction.setUsername(actor.getName());
holder.peertube_notif_message.setOnClickListener(v -> markAsRead(notification, position));
} else if (notification.getComment() != null) { //Comment Notification
Helper.loadAvatar(context, notification.getComment().getAccount(), holder.peertube_notif_pp);
accountAction = notification.getComment().getAccount();
String message = context.getString(R.string.peertube_comment_on_video, accountAction.getDisplayName(), accountAction.getUsername());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
holder.peertube_notif_message.setText(Html.fromHtml(message, Html.FROM_HTML_MODE_LEGACY));
else
holder.peertube_notif_message.setText(Html.fromHtml(message));
AccountData.Account finalAccountAction1 = accountAction;
holder.peertube_notif_message.setOnClickListener(v -> {
Intent intent = new Intent(context, PeertubeActivity.class);
Bundle b = new Bundle();
b.putParcelable("video", notification.getVideo());
b.putString("peertube_instance", finalAccountAction1.getHost());
b.putString("video_id", notification.getComment().getVideo().getId());
b.putString("video_uuid", notification.getComment().getVideo().getUuid());
intent.putExtras(b);
markAsRead(notification, position);
context.startActivity(intent);
});
} else {
String profileUrl = notification.getVideo() != null && notification.getVideo().getChannel().getAvatar() != null ? notification.getVideo().getChannel().getAvatar().getPath() : null;
Helper.loadGiF(context, profileUrl, holder.peertube_notif_pp);
String message = "";
boolean myVideo = false;
holder.peertube_notif_pp.setVisibility(View.INVISIBLE);
if (notification.getVideo() != null) {
if (notification.getType() == DisplayNotificationsFragment.MY_VIDEO_PUBLISHED) {
message = context.getString(R.string.peertube_video_published, notification.getVideo().getName());
myVideo = true;
} else if (notification.getType() == DisplayNotificationsFragment.MY_VIDEO_IMPORT_ERROR) {
message = context.getString(R.string.peertube_video_import_error, notification.getVideo().getName());
myVideo = true;
} else if (notification.getType() == DisplayNotificationsFragment.MY_VIDEO_IMPORT_SUCCESS) {
message = context.getString(R.string.peertube_video_import_success, notification.getVideo().getName());
myVideo = true;
} else if (notification.getType() == DisplayNotificationsFragment.NEW_VIDEO_FROM_SUBSCRIPTION) {
channelAction = notification.getVideo().getChannel();
message = context.getString(R.string.peertube_video_from_subscription, channelAction.getDisplayName(), notification.getVideo().getName());
holder.peertube_notif_pp.setVisibility(View.VISIBLE);
} else if (notification.getType() == DisplayNotificationsFragment.BLACKLIST_ON_MY_VIDEO) {
message = context.getString(R.string.peertube_video_blacklist, notification.getVideo().getName());
} else if (notification.getType() == DisplayNotificationsFragment.UNBLACKLIST_ON_MY_VIDEO) {
message = context.getString(R.string.peertube_video_unblacklist, notification.getVideo().getName());
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
holder.peertube_notif_message.setText(Html.fromHtml(message, Html.FROM_HTML_MODE_LEGACY));
else
holder.peertube_notif_message.setText(Html.fromHtml(message));
boolean finalMyVideo = myVideo;
holder.peertube_notif_message.setOnClickListener(v -> {
Intent intent = new Intent(context, PeertubeActivity.class);
Bundle b = new Bundle();
b.putParcelable("video", notification.getVideo());
b.putString("peertube_instance", HelperInstance.getLiveInstance(context));
b.putBoolean("isMyVideo", finalMyVideo);
b.putString("video_id", notification.getVideo().getId());
b.putString("video_uuid", notification.getVideo().getUuid());
intent.putExtras(b);
context.startActivity(intent);
markAsRead(notification, position);
});
} else if (notification.getVideoAbuse() != null && notification.getVideoAbuse().getVideo() != null) {
message = context.getString(R.string.peertube_video_abuse, notification.getVideoAbuse().getVideo().getName());
clickableNotification = false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
holder.peertube_notif_message.setText(Html.fromHtml(message, Html.FROM_HTML_MODE_LEGACY));
else
holder.peertube_notif_message.setText(Html.fromHtml(message));
holder.peertube_notif_message.setOnClickListener(v -> markAsRead(notification, position));
} else if (notification.getAbuse() != null) {
clickableNotification = false;
if (notification.getType() == DisplayNotificationsFragment.MY_VIDEO_REPPORT_SUCCESS) {
message = context.getString(R.string.peertube_video_report_success, notification.getAbuse().getId());
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
holder.peertube_notif_message.setText(Html.fromHtml(message, Html.FROM_HTML_MODE_LEGACY));
else
holder.peertube_notif_message.setText(Html.fromHtml(message));
holder.peertube_notif_message.setOnClickListener(v -> markAsRead(notification, position));
}
}
holder.peertube_notif_date.setText(Helper.dateDiff(context, notification.getCreatedAt()));
AccountData.Account finalAccountAction = accountAction;
ChannelData.Channel finalChannelAction = channelAction;
if (clickableNotification) {
holder.peertube_notif_pp.setOnClickListener(v -> {
Bundle b = new Bundle();
Intent intent = null;
if (finalAccountAction != null) {
intent = new Intent(context, ShowAccountActivity.class);
b.putParcelable("account", finalAccountAction);
b.putString("accountAcct", finalAccountAction.getUsername() + "@" + finalAccountAction.getHost());
} else if (finalChannelAction != null) {
intent = new Intent(context, ShowChannelActivity.class);
b.putParcelable("channel", finalChannelAction);
}
if (intent != null) {
intent.putExtras(b);
context.startActivity(intent);
}
});
}
}
private void markAsRead(Notification notification, int position) {
if (!notification.isRead()) {
notification.setRead(true);
badgeCount--;
if (context instanceof AccountActivity) {
((AccountActivity) context).updateCounter();
}
notifyItemChanged(position);
new Thread(() -> new RetrofitPeertubeAPI(context).markAsRead(notification.getId())).start();
}
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getItemCount() {
return notifications.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
ImageView peertube_notif_pp;
TextView peertube_notif_message, peertube_notif_date, unread;
RelativeLayout main_container_trans;
public ViewHolder(View itemView) {
super(itemView);
peertube_notif_pp = itemView.findViewById(R.id.peertube_notif_pp);
peertube_notif_message = itemView.findViewById(R.id.peertube_notif_message);
peertube_notif_date = itemView.findViewById(R.id.peertube_notif_date);
main_container_trans = itemView.findViewById(R.id.container_trans);
unread = itemView.findViewById(R.id.unread);
}
public View getView() {
return itemView;
}
}
}

@ -0,0 +1,288 @@
package app.fedilab.android.peertube.drawer;
/* 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 <http://www.gnu.org/licenses>. */
import static app.fedilab.android.peertube.viewmodel.PlaylistsVM.action.GET_LIST_VIDEOS;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.PopupMenu;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.FutureTarget;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutionException;
import app.fedilab.android.peertube.BuildConfig;
import app.fedilab.android.peertube.R;
import app.fedilab.android.peertube.activities.AllPlaylistsActivity;
import app.fedilab.android.peertube.activities.LocalPlaylistsActivity;
import app.fedilab.android.peertube.activities.MainActivity;
import app.fedilab.android.peertube.activities.PlaylistsActivity;
import app.fedilab.android.peertube.client.APIResponse;
import app.fedilab.android.peertube.client.RetrofitPeertubeAPI;
import app.fedilab.android.peertube.client.data.PlaylistData.Playlist;
import app.fedilab.android.peertube.client.data.VideoPlaylistData;
import app.fedilab.android.peertube.databinding.DrawerPlaylistBinding;
import app.fedilab.android.peertube.helper.Helper;
import app.fedilab.android.peertube.helper.HelperInstance;
import app.fedilab.android.peertube.helper.NotificationHelper;
import app.fedilab.android.peertube.helper.PlaylistExportHelper;
import app.fedilab.android.peertube.sqlite.ManagePlaylistsDAO;
import app.fedilab.android.peertube.sqlite.Sqlite;
import app.fedilab.android.peertube.viewmodel.PlaylistsVM;
import es.dmoral.toasty.Toasty;
public class PlaylistAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final List<Playlist> playlists;
private final boolean locale;
public AllPlaylistRemoved allPlaylistRemoved;
private Context context;
public PlaylistAdapter(List<Playlist> lists, boolean locale) {
this.playlists = lists;
this.locale = locale;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
DrawerPlaylistBinding itemBinding = DrawerPlaylistBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
return new ViewHolder(itemBinding);
}
@SuppressLint({"SetJavaScriptEnabled", "ClickableViewAccessibility"})
@Override
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder viewHolder, int position) {
context = viewHolder.itemView.getContext();
final ViewHolder holder = (ViewHolder) viewHolder;
final Playlist playlist = playlists.get(position);
String imgUrl;
if (locale) {
imgUrl = "https://" + playlist.getOwnerAccount().getHost() + playlist.getThumbnailPath();
} else {
imgUrl = playlist.getThumbnailPath();
}
Helper.loadGiF(context, imgUrl, holder.binding.previewPlaylist);
holder.binding.previewTitle.setText(playlist.getDisplayName());
if (playlist.getDescription() != null && playlist.getDescription().trim().compareTo("null") != 0 && playlist.getDescription().length() > 0) {
holder.binding.previewDescription.setText(playlist.getDescription());
holder.binding.previewDescription.setVisibility(View.VISIBLE);
} else {
holder.binding.previewDescription.setVisibility(View.GONE);
}
holder.binding.previewVisibility.setText(playlist.getPrivacy().getLabel());
holder.binding.playlistContainer.setOnClickListener(v -> {
Intent intent = new Intent(context, locale ? LocalPlaylistsActivity.class : PlaylistsActivity.class);
Bundle b = new Bundle();
b.putParcelable("playlist", playlist);
intent.putExtras(b);
context.startActivity(intent);
});
if (playlist.getDisplayName().compareTo("Watch later") == 0) {
holder.binding.playlistMore.setVisibility(View.GONE);
} else {
holder.binding.playlistMore.setVisibility(View.VISIBLE);
}
holder.binding.playlistMore.setOnClickListener(v -> {
PopupMenu popup = new PopupMenu(context, holder.binding.playlistMore);
popup.getMenuInflater()
.inflate(R.menu.playlist_menu, popup.getMenu());
if (!BuildConfig.full_instances) {
popup.getMenu().findItem(R.id.action_export).setVisible(true);
}
if (locale) {
popup.getMenu().findItem(R.id.action_export).setVisible(false);
popup.getMenu().findItem(R.id.action_edit).setVisible(false);
}
popup.setOnMenuItemClickListener(item -> {
int itemId = item.getItemId();
if (itemId == R.id.action_delete) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(context.getString(R.string.action_lists_delete) + ": " + playlist.getDisplayName());
builder.setMessage(context.getString(R.string.action_lists_confirm_delete));
builder.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(R.string.yes, (dialog, which) -> {
playlists.remove(playlist);
notifyDataSetChanged();
if (!locale) {
PlaylistsVM viewModel = new ViewModelProvider((ViewModelStoreOwner) context).get(PlaylistsVM.class);
viewModel.manage(PlaylistsVM.action.DELETE_PLAYLIST, playlist, null).observe((LifecycleOwner) context, apiResponse -> manageVIewPlaylists(PlaylistsVM.action.DELETE_PLAYLIST, apiResponse));
} else {
new Thread(() -> {
SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
new ManagePlaylistsDAO(context, db).removePlaylist(playlist.getUuid());
}).start();
}
if (playlists.size() == 0) {
allPlaylistRemoved.onAllPlaylistRemoved();
}
dialog.dismiss();
})
.setNegativeButton(R.string.no, (dialog, which) -> dialog.dismiss())
.show();
} else if (itemId == R.id.action_edit) {
if (context instanceof AllPlaylistsActivity) {
((AllPlaylistsActivity) context).manageAlert(playlist);
}
} else if (itemId == R.id.action_export) {
if (Build.VERSION.SDK_INT >= 23) {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, Helper.EXTERNAL_STORAGE_REQUEST_CODE);
} else {
doExport(playlist);
}
} else {
doExport(playlist);
}
}
return true;
});
popup.show();
});
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getItemCount() {
return playlists.size();
}
private void doExport(Playlist playlist) {
new Thread(() -> {
File file;
RetrofitPeertubeAPI retrofitPeertubeAPI = new RetrofitPeertubeAPI(context);
APIResponse apiResponse = retrofitPeertubeAPI.playlistAction(GET_LIST_VIDEOS, playlist.getId(), null, null, null);
if (apiResponse != null) {
List<VideoPlaylistData.VideoPlaylist> videos = apiResponse.getVideoPlaylist();
VideoPlaylistData.VideoPlaylistExport videoPlaylistExport = new VideoPlaylistData.VideoPlaylistExport();
videoPlaylistExport.setPlaylist(playlist);
videoPlaylistExport.setUuid(playlist.getUuid());
videoPlaylistExport.setAcct(MainActivity.userMe.getAccount().getAcct());
videoPlaylistExport.setVideos(videos);
String data = PlaylistExportHelper.playlistToStringStorage(videoPlaylistExport);
File root = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "");
if (!root.exists()) {
//noinspection ResultOfMethodCallIgnored
root.mkdirs();
}
String fileName = "playlist_" + playlist.getUuid() + ".tubelab";
file = new File(root, fileName);
FileWriter writer;
try {
writer = new FileWriter(file);
writer.append(data);
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> Toasty.error(context, context.getString(R.string.toast_error), Toasty.LENGTH_LONG).show();
mainHandler.post(myRunnable);
return;
}
String urlAvatar = playlist.getThumbnailPath() != null ? HelperInstance.getLiveInstance(context) + playlist.getThumbnailPath() : null;
FutureTarget<Bitmap> futureBitmapChannel = Glide.with(context.getApplicationContext())
.asBitmap()
.load(urlAvatar != null ? urlAvatar : R.drawable.missing_peertube).submit();
Bitmap icon = null;
try {
icon = futureBitmapChannel.get();
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
Intent mailIntent = new Intent(Intent.ACTION_SEND);
mailIntent.setType("message/rfc822");
Uri contentUri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileProvider", file);
mailIntent.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.export_notification_subjet));
mailIntent.putExtra(Intent.EXTRA_TEXT, context.getString(R.string.export_notification_body));
mailIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
mailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
NotificationHelper.notify_user(context.getApplicationContext(),
playlist.getOwnerAccount(), mailIntent, icon,
context.getString(R.string.export_notification_title),
context.getString(R.string.export_notification_content));
}
}).start();
}
@SuppressWarnings({"unused", "RedundantSuppression"})
public void manageVIewPlaylists(PlaylistsVM.action actionType, APIResponse apiResponse) {
}
public interface AllPlaylistRemoved {
void onAllPlaylistRemoved();
}
static class ViewHolder extends RecyclerView.ViewHolder {
DrawerPlaylistBinding binding;
ViewHolder(DrawerPlaylistBinding itemView) {
super(itemView.getRoot());
binding = itemView;
}
}
}

@ -0,0 +1,219 @@
package app.fedilab.android.peertube.fragment;
/* 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 <http://www.gnu.org/licenses>. */
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import java.util.ArrayList;
import java.util.List;
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.Account;
import app.fedilab.android.peertube.client.data.BlockData;
import app.fedilab.android.peertube.drawer.AccountsListAdapter;
import app.fedilab.android.peertube.viewmodel.AccountsVM;
import es.dmoral.toasty.Toasty;
public class DisplayAccountsFragment extends Fragment implements AccountsListAdapter.AllAccountsRemoved {
private boolean flag_loading;
private Context context;
private AccountsListAdapter accountsListAdapter;
private String max_id;
private List<Account> accounts;
private RelativeLayout mainLoader, nextElementLoader, textviewNoAction;
private boolean firstLoad;
private SwipeRefreshLayout swipeRefreshLayout;
private RecyclerView lv_accounts;
private View rootView;
private RetrofitPeertubeAPI.DataType accountFetch;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_recyclerview, container, false);
context = getContext();
Bundle bundle = this.getArguments();
accounts = new ArrayList<>();
if (bundle != null) {
if (bundle.containsKey("accountFetch")) {
accountFetch = (RetrofitPeertubeAPI.DataType) bundle.getSerializable("accountFetch");
}
}
max_id = null;
firstLoad = true;
flag_loading = true;
swipeRefreshLayout = rootView.findViewById(R.id.swipeContainer);
lv_accounts = rootView.findViewById(R.id.lv_elements);
lv_accounts.addItemDecoration(new DividerItemDecoration(context, DividerItemDecoration.VERTICAL));
mainLoader = rootView.findViewById(R.id.loader);
nextElementLoader = rootView.findViewById(R.id.loading_next);
textviewNoAction = rootView.findViewById(R.id.no_action);
mainLoader.setVisibility(View.VISIBLE);
nextElementLoader.setVisibility(View.GONE);
accountsListAdapter = new AccountsListAdapter(accountFetch, this.accounts);
accountsListAdapter.allAccountsRemoved = this;
lv_accounts.setAdapter(accountsListAdapter);
TextView no_action_text = rootView.findViewById(R.id.no_action_text);
if (accountFetch == RetrofitPeertubeAPI.DataType.MUTED) {
no_action_text.setText(context.getString(R.string.no_muted));
}
final LinearLayoutManager mLayoutManager;
mLayoutManager = new LinearLayoutManager(context);
lv_accounts.setLayoutManager(mLayoutManager);
lv_accounts.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) {
flag_loading = true;
AccountsVM viewModel = new ViewModelProvider(DisplayAccountsFragment.this).get(AccountsVM.class);
viewModel.getAccounts(accountFetch, max_id).observe(DisplayAccountsFragment.this.requireActivity(), apiResponse -> manageViewAccounts(apiResponse));
nextElementLoader.setVisibility(View.VISIBLE);
}
} else {
nextElementLoader.setVisibility(View.GONE);
}
}
}
});
swipeRefreshLayout.setOnRefreshListener(this::pullToRefresh);
AccountsVM viewModel = new ViewModelProvider(this).get(AccountsVM.class);
viewModel.getAccounts(RetrofitPeertubeAPI.DataType.MUTED, max_id).observe(DisplayAccountsFragment.this.requireActivity(), this::manageViewAccounts);
return rootView;
}
@Override
public void onResume() {
super.onResume();
if (getActivity() != null && getActivity() != null) {
View action_button = getActivity().findViewById(R.id.action_button);
if (action_button != null) {
action_button.setVisibility(View.GONE);
}
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
rootView = null;
}
@Override
public void onCreate(Bundle saveInstance) {
super.onCreate(saveInstance);
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
this.context = context;
}
@Override
public void onDestroy() {
super.onDestroy();
}
public void scrollToTop() {
if (lv_accounts != null)
lv_accounts.setAdapter(accountsListAdapter);
}
private void manageViewAccounts(APIResponse apiResponse) {
mainLoader.setVisibility(View.GONE);
nextElementLoader.setVisibility(View.GONE);
if (apiResponse.getError() != null) {
Toasty.error(context, apiResponse.getError().getError(), Toast.LENGTH_LONG).show();
swipeRefreshLayout.setRefreshing(false);
flag_loading = false;
return;
}
flag_loading = (apiResponse.getMax_id() == null);
List<Account> accounts = apiResponse.getAccounts();
if (accountFetch == RetrofitPeertubeAPI.DataType.MUTED) {
accounts = new ArrayList<>();
List<BlockData.Block> blockList = apiResponse.getMuted();
for (BlockData.Block block : blockList) {
accounts.add(block.getBlockedAccount());
}
}
if (max_id == null) {
max_id = "0";
}
if (firstLoad && (accounts == null || accounts.size() == 0))
textviewNoAction.setVisibility(View.VISIBLE);
else
textviewNoAction.setVisibility(View.GONE);
max_id = String.valueOf(Integer.parseInt(max_id) + 20);
if (accounts != null && accounts.size() > 0) {
int previousPosition = this.accounts.size();
int currentPosition = this.accounts.size();
this.accounts.addAll(accounts);
if (previousPosition == 0) {
accountsListAdapter = new AccountsListAdapter(accountFetch, this.accounts);
accountsListAdapter.allAccountsRemoved = this;
lv_accounts.setAdapter(accountsListAdapter);
} else
accountsListAdapter.notifyItemRangeChanged(currentPosition, accounts.size());
}
swipeRefreshLayout.setRefreshing(false);
firstLoad = false;
}
public void pullToRefresh() {
max_id = null;
accounts = new ArrayList<>();
firstLoad = true;
flag_loading = true;
swipeRefreshLayout.setRefreshing(true);
AccountsVM viewModel = new ViewModelProvider(this).get(AccountsVM.class);
viewModel.getAccounts(RetrofitPeertubeAPI.DataType.MUTED, null).observe(DisplayAccountsFragment.this.requireActivity(), this::manageViewAccounts);
}
@Override
public void onAllAccountsRemoved() {
textviewNoAction.setVisibility(View.VISIBLE);
}
}

@ -0,0 +1,357 @@
package app.fedilab.android.peertube.fragment;
/* 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 <http://www.gnu.org/licenses>. */
import static app.fedilab.android.peertube.activities.PeertubeUploadActivity.MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE;
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.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.DividerItemDecoration;
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 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.APIResponse;
import app.fedilab.android.peertube.client.RetrofitPeertubeAPI;
import app.fedilab.android.peertube.client.data.ChannelData;
import app.fedilab.android.peertube.client.entities.ChannelParams;
import app.fedilab.android.peertube.databinding.AddChannelBinding;
import app.fedilab.android.peertube.databinding.FragmentRecyclerviewBinding;
import app.fedilab.android.peertube.drawer.ChannelListAdapter;
import app.fedilab.android.peertube.helper.Helper;
import app.fedilab.android.peertube.helper.HelperInstance;
import app.fedilab.android.peertube.viewmodel.ChannelsVM;
import app.fedilab.android.peertube.viewmodel.SearchVM;
import es.dmoral.toasty.Toasty;
public class DisplayChannelsFragment extends Fragment implements ChannelListAdapter.AllChannelRemoved, ChannelListAdapter.EditAlertDialog {
private static final int PICK_AVATAR = 467;
private Context context;
private ChannelListAdapter channelListAdapter;
private List<ChannelData.Channel> channels;
private String name;
private View rootView;
private FloatingActionButton action_button;
private FragmentRecyclerviewBinding binding;
private AddChannelBinding bindingDialog;
private Uri inputData;
private String search_peertube;
private String max_id;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
binding = FragmentRecyclerviewBinding.inflate(LayoutInflater.from(context));
rootView = binding.getRoot();
context = getContext();
Bundle bundle = this.getArguments();
channels = new ArrayList<>();
max_id = "0";
if (bundle != null) {
name = bundle.getString("name", null);
search_peertube = bundle.getString("search_peertube", null);
}
if (getActivity() != null) {
action_button = getActivity().findViewById(R.id.action_button);
if (action_button != null) {
action_button.setVisibility(View.VISIBLE);
action_button.setOnClickListener(view -> manageAlert(null));
}
}
binding.lvElements.addItemDecoration(new DividerItemDecoration(context, DividerItemDecoration.VERTICAL));
channelListAdapter = new ChannelListAdapter(this.channels);
channelListAdapter.allChannelRemoved = this;
channelListAdapter.editAlertDialog = this;
binding.lvElements.setAdapter(channelListAdapter);
final LinearLayoutManager mLayoutManager;
mLayoutManager = new LinearLayoutManager(context);
binding.lvElements.setLayoutManager(mLayoutManager);
binding.swipeContainer.setOnRefreshListener(this::pullToRefresh);
loadChannels(max_id);
return rootView;
}
private void loadChannels(String max_id) {
if (search_peertube == null) {
ChannelsVM viewModel = new ViewModelProvider(this).get(ChannelsVM.class);
if (name != null) {
viewModel.get(RetrofitPeertubeAPI.DataType.CHANNELS_FOR_ACCOUNT, name).observe(DisplayChannelsFragment.this.requireActivity(), this::manageViewChannels);
} else {
viewModel.get(RetrofitPeertubeAPI.DataType.MY_CHANNELS, null).observe(DisplayChannelsFragment.this.requireActivity(), this::manageViewChannels);
}
} else {
SearchVM viewModelSearch = new ViewModelProvider(this).get(SearchVM.class);
viewModelSearch.getChannels(max_id, search_peertube).observe(this.requireActivity(), this::manageViewChannels);
}
}
@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(context, getString(R.string.toot_select_image_error), Toast.LENGTH_LONG).show();
return;
}
inputData = data.getData();
Glide.with(context)
.load(inputData)
.thumbnail(0.1f)
.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10)))
.into(bindingDialog.profilePicture);
}
}
@Override
public void onResume() {
super.onResume();
if (getActivity() != null && getActivity() != null) {
View action_button = getActivity().findViewById(R.id.action_button);
if (action_button != null) {
action_button.setVisibility(View.VISIBLE);
}
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
rootView = null;
}
@Override
public void onCreate(Bundle saveInstance) {
super.onCreate(saveInstance);
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
this.context = context;
}
@Override
public void onDestroy() {
super.onDestroy();
}
public void scrollToTop() {
binding.lvElements.setAdapter(channelListAdapter);
}
private void manageViewChannels(APIResponse apiResponse) {
binding.loader.setVisibility(View.GONE);
binding.loadingNext.setVisibility(View.GONE);
if (apiResponse.getError() != null) {
Toasty.error(context, apiResponse.getError().getError(), Toast.LENGTH_LONG).show();
binding.swipeContainer.setRefreshing(false);
return;
}
List<ChannelData.Channel> channels = apiResponse.getChannels();
if ((channels == null || channels.size() == 0))
binding.noAction.setVisibility(View.VISIBLE);
else
binding.noAction.setVisibility(View.GONE);
max_id = String.valueOf(Integer.parseInt(max_id) + 20);
if (channels != null && channels.size() > 0) {
int currentPosition = this.channels.size();
this.channels.addAll(channels);
if (currentPosition == 0) {
channelListAdapter = new ChannelListAdapter(this.channels);
channelListAdapter.allChannelRemoved = DisplayChannelsFragment.this;
channelListAdapter.editAlertDialog = DisplayChannelsFragment.this;
binding.lvElements.setAdapter(channelListAdapter);
} else {
channelListAdapter.notifyItemRangeChanged(currentPosition, channels.size());
}
}
binding.swipeContainer.setRefreshing(false);
}
public void pullToRefresh() {
channels = new ArrayList<>();
binding.swipeContainer.setRefreshing(true);
loadChannels("0");
}
@Override
public void onAllChannelRemoved() {
binding.noAction.setVisibility(View.VISIBLE);
}
public void manageAlert(ChannelParams oldChannelValues) {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context);
bindingDialog = AddChannelBinding.inflate(LayoutInflater.from(context), null, false);
dialogBuilder.setView(bindingDialog.getRoot());
if (oldChannelValues != null) {
bindingDialog.displayName.setText(oldChannelValues.getDisplayName());
bindingDialog.name.setText(oldChannelValues.getName());
bindingDialog.description.setText(oldChannelValues.getDescription());
bindingDialog.name.setEnabled(false);
}
dialogBuilder.setPositiveButton(R.string.validate, null);
dialogBuilder.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss());
AlertDialog alertDialog = dialogBuilder.create();
int position;
if (oldChannelValues == null) {
position = -1;
} else {
position = 0;
for (ChannelData.Channel channel : channels) {
if (channel.getName().compareTo(oldChannelValues.getName()) == 0) {
break;
}
position++;
}
}
bindingDialog.selectFile.setOnClickListener(v -> {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) !=
PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions((Activity) context,
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.loadAvatar(context, channels.get(position), bindingDialog.profilePicture);
int finalPosition = position;
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 && bindingDialog.name.getText() != null && bindingDialog.name.getText().toString().trim().length() > 0) {
ChannelParams channelCreation = new ChannelParams();
channelCreation.setDisplayName(bindingDialog.displayName.getText().toString().trim());
channelCreation.setName(bindingDialog.name.getText().toString().trim());
if (bindingDialog.description.getText() != null && bindingDialog.description.getText().toString().trim().length() > 0) {
channelCreation.setDescription(bindingDialog.description.getText().toString().trim());
}
new Thread(() -> {
APIResponse apiResponse;
if (oldChannelValues == null) {
apiResponse = new RetrofitPeertubeAPI(context).createOrUpdateChannel(ChannelsVM.action.CREATE_CHANNEL, null, channelCreation, inputData);
} else {
apiResponse = new RetrofitPeertubeAPI(context).createOrUpdateChannel(ChannelsVM.action.UPDATE_CHANNEL, channelCreation.getName() + "@" + HelperInstance.getLiveInstance(context), channelCreation, inputData);
}
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> {
if (oldChannelValues == null) {
ChannelData.Channel channel = new ChannelData.Channel();
channel.setId(apiResponse.getActionReturn());
channel.setName(channelCreation.getName());
channel.setDisplayName(channelCreation.getDisplayName());
channel.setDescription(channelCreation.getDescription());
channels.add(0, channel);
channelListAdapter.notifyItemInserted(0);
} else {
channels.get(finalPosition).setName(channelCreation.getName());
channels.get(finalPosition).setDisplayName(channelCreation.getDisplayName());
channels.get(finalPosition).setDescription(channelCreation.getDescription());
channelListAdapter.notifyItemChanged(finalPosition);
}
if (action_button != null) {
action_button.setEnabled(true);
}
};
mainHandler.post(myRunnable);
}).start();
alertDialog.dismiss();
if (action_button != null) {
action_button.setEnabled(false);
}
} else {
Toasty.error(context, context.getString(R.string.error_display_name_channel), Toast.LENGTH_LONG).show();
}
});
});
if (oldChannelValues == null) {
alertDialog.setTitle(getString(R.string.action_channel_create));
} else {
alertDialog.setTitle(getString(R.string.action_channel_edit));
}
alertDialog.setOnDismissListener(dialogInterface -> {
//Hide keyboard
InputMethodManager imm = (InputMethodManager) context.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 show(ChannelData.Channel channel) {
ChannelParams oldChannelValues = new ChannelParams();
oldChannelValues.setName(channel.getName());
oldChannelValues.setDescription(channel.getDescription());
oldChannelValues.setDisplayName(channel.getDisplayName());
manageAlert(oldChannelValues);
}
}

@ -0,0 +1,232 @@
package app.fedilab.android.peertube.fragment;
/* 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 <http://www.gnu.org/licenses>. */
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import java.util.ArrayList;
import java.util.List;
import app.fedilab.android.peertube.R;
import app.fedilab.android.peertube.client.APIResponse;
import app.fedilab.android.peertube.client.data.NotificationData.Notification;
import app.fedilab.android.peertube.drawer.PeertubeNotificationsListAdapter;
import app.fedilab.android.peertube.viewmodel.NotificationsVM;
import es.dmoral.toasty.Toasty;
@SuppressWarnings({"unused", "RedundantSuppression"})
public class DisplayNotificationsFragment extends Fragment {
//Peertube notification type
public final static int NEW_VIDEO_FROM_SUBSCRIPTION = 1;
public final static int NEW_COMMENT_ON_MY_VIDEO = 2;
public final static int NEW_ABUSE_FOR_MODERATORS = 3;
public final static int BLACKLIST_ON_MY_VIDEO = 4;
public final static int UNBLACKLIST_ON_MY_VIDEO = 5;
public final static int MY_VIDEO_PUBLISHED = 6;
public final static int MY_VIDEO_IMPORT_SUCCESS = 7;
public final static int MY_VIDEO_IMPORT_ERROR = 8;
public final static int NEW_USER_REGISTRATION = 9;
public final static int NEW_FOLLOW = 10;
public final static int COMMENT_MENTION = 11;
public final static int VIDEO_AUTO_BLACKLIST_FOR_MODERATORS = 12;
public final static int NEW_INSTANCE_FOLLOWER = 13;
public final static int AUTO_INSTANCE_FOLLOWING = 14;
public final static int MY_VIDEO_REPPORT_SUCCESS = 15;
public final static int ABUSE_NEW_MESSAGE = 16;
private boolean flag_loading;
private Context context;
private PeertubeNotificationsListAdapter peertubeNotificationsListAdapter;
private String max_id;
private List<Notification> notifications;
private RelativeLayout mainLoader, nextElementLoader, textviewNoAction;
private boolean firstLoad;
private SwipeRefreshLayout swipeRefreshLayout;
private RecyclerView lv_notifications;
private View rootView;
private NotificationsVM viewModel;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_recyclerview, container, false);
context = getContext();
notifications = new ArrayList<>();
max_id = "0";
firstLoad = true;
flag_loading = true;
swipeRefreshLayout = rootView.findViewById(R.id.swipeContainer);
viewModel = new ViewModelProvider(this).get(NotificationsVM.class);
lv_notifications = rootView.findViewById(R.id.lv_elements);
lv_notifications.addItemDecoration(new DividerItemDecoration(context, DividerItemDecoration.VERTICAL));
mainLoader = rootView.findViewById(R.id.loader);
nextElementLoader = rootView.findViewById(R.id.loading_next);
textviewNoAction = rootView.findViewById(R.id.no_action);
TextView no_action_text = rootView.findViewById(R.id.no_action_text);
no_action_text.setText(context.getString(R.string.no_notifications));
mainLoader.setVisibility(View.VISIBLE);
nextElementLoader.setVisibility(View.GONE);
peertubeNotificationsListAdapter = new PeertubeNotificationsListAdapter(this.notifications);
lv_notifications.setAdapter(peertubeNotificationsListAdapter);
final LinearLayoutManager mLayoutManager;
mLayoutManager = new LinearLayoutManager(context);
lv_notifications.setLayoutManager(mLayoutManager);
lv_notifications.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) {
flag_loading = true;
viewModel.getNotifications(null, max_id).observe(DisplayNotificationsFragment.this.requireActivity(), apiResponse -> manageVIewNotifications(apiResponse));
nextElementLoader.setVisibility(View.VISIBLE);
}
} else {
nextElementLoader.setVisibility(View.GONE);
}
}
}
});
swipeRefreshLayout.setOnRefreshListener(this::pullToRefresh);
viewModel.getNotifications(null, "0").observe(DisplayNotificationsFragment.this.requireActivity(), this::manageVIewNotifications);
return rootView;
}
@Override
public void onDestroyView() {
super.onDestroyView();
rootView = null;
}
@Override
public void onPause() {
super.onPause();
if (swipeRefreshLayout != null) {
swipeRefreshLayout.setEnabled(false);
swipeRefreshLayout.setRefreshing(false);
swipeRefreshLayout.clearAnimation();
}
}
@Override
public void onResume() {
super.onResume();
swipeRefreshLayout.setEnabled(true);
if (getActivity() != null && getActivity() != null) {
View action_button = getActivity().findViewById(R.id.action_button);
if (action_button != null) {
action_button.setVisibility(View.GONE);
}
}
}
@Override
public void onCreate(Bundle saveInstance) {
super.onCreate(saveInstance);
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
this.context = context;
}
public void onDestroy() {
super.onDestroy();
}
public void scrollToTop() {
if (lv_notifications != null)
lv_notifications.setAdapter(peertubeNotificationsListAdapter);
}
public void pullToRefresh() {
int size = notifications.size();
notifications.clear();
notifications = new ArrayList<>();
max_id = "0";
peertubeNotificationsListAdapter.notifyItemRangeRemoved(0, size);
firstLoad = true;
flag_loading = true;
swipeRefreshLayout.setRefreshing(true);
viewModel.getNotifications(null, "0").observe(DisplayNotificationsFragment.this.requireActivity(), this::manageVIewNotifications);
}
private void manageVIewNotifications(APIResponse apiResponse) {
mainLoader.setVisibility(View.GONE);
nextElementLoader.setVisibility(View.GONE);
if (apiResponse.getError() != null) {
Toasty.error(context, apiResponse.getError().getError(), Toast.LENGTH_LONG).show();
flag_loading = false;
swipeRefreshLayout.setRefreshing(false);
return;
}
int previousPosition = notifications.size();
max_id = String.valueOf(Integer.parseInt(max_id) + 20);
List<Notification> notifications = apiResponse.getPeertubeNotifications();
if (firstLoad && (notifications == null || notifications.size() == 0))
textviewNoAction.setVisibility(View.VISIBLE);
else
textviewNoAction.setVisibility(View.GONE);
if (notifications != null && notifications.size() > 0) {
this.notifications.addAll(notifications);
if (previousPosition == 0) {
peertubeNotificationsListAdapter = new PeertubeNotificationsListAdapter(this.notifications);
lv_notifications.setAdapter(peertubeNotificationsListAdapter);
} else
peertubeNotificationsListAdapter.notifyItemRangeInserted(previousPosition, notifications.size());
} else {
if (firstLoad)
textviewNoAction.setVisibility(View.VISIBLE);
}
swipeRefreshLayout.setRefreshing(false);
firstLoad = false;
//The initial call comes from a classic tab refresh
flag_loading = (max_id == null);
}
}

@ -0,0 +1,442 @@
package app.fedilab.android.peertube.fragment;
/* 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 <http://www.gnu.org/licenses>. */
import static app.fedilab.android.peertube.client.data.VideoData.Video.titleType.CATEGORY;
import static app.fedilab.android.peertube.client.data.VideoData.Video.titleType.CHANNEL;
import static app.fedilab.android.peertube.client.data.VideoData.Video.titleType.TAG;
import android.content.Context;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.HashMap;
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.VideoData;
import app.fedilab.android.peertube.client.entities.OverviewVideo;
import app.fedilab.android.peertube.client.entities.PlaylistExist;
import app.fedilab.android.peertube.drawer.PeertubeAdapter;
import app.fedilab.android.peertube.helper.Helper;
import app.fedilab.android.peertube.viewmodel.PlaylistsVM;
import app.fedilab.android.peertube.viewmodel.RelationshipVM;
import app.fedilab.android.peertube.viewmodel.TimelineVM;
import es.dmoral.toasty.Toasty;
public class DisplayOverviewFragment extends Fragment implements PeertubeAdapter.RelationShipListener, PeertubeAdapter.PlaylistListener {
private LinearLayoutManager mLayoutManager;
private GridLayoutManager gLayoutManager;
private boolean flag_loading;
private Context context;
private PeertubeAdapter peertubeAdapater;
private int page;
private List<VideoData.Video> peertubes;
private RelativeLayout mainLoader, nextElementLoader, textviewNoAction;
private boolean firstLoad;
private SwipeRefreshLayout swipeRefreshLayout;
private TextView textviewNoActionText;
private View rootView;
private RecyclerView lv_status;
private TimelineVM viewModelFeeds;
private Map<String, Boolean> relationship;
private Map<String, List<PlaylistExist>> playlists;
public DisplayOverviewFragment() {
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_overview, container, false);
peertubes = new ArrayList<>();
context = getContext();
lv_status = rootView.findViewById(R.id.lv_status);
page = 1;
flag_loading = true;
firstLoad = true;
swipeRefreshLayout = rootView.findViewById(R.id.swipeContainer);
mainLoader = rootView.findViewById(R.id.loader);
nextElementLoader = rootView.findViewById(R.id.loading_next_status);
textviewNoAction = rootView.findViewById(R.id.no_action);
textviewNoActionText = rootView.findViewById(R.id.no_action_text);
mainLoader.setVisibility(View.VISIBLE);
nextElementLoader.setVisibility(View.GONE);
peertubeAdapater = new PeertubeAdapter(this.peertubes);
peertubeAdapater.playlistListener = this;
peertubeAdapater.relationShipListener = this;
lv_status.setAdapter(peertubeAdapater);
if (!Helper.isTablet(context)) {
mLayoutManager = new LinearLayoutManager(context);
lv_status.setLayoutManager(mLayoutManager);
} else {
gLayoutManager = new GridLayoutManager(context, 2);
int spanCount = (int) Helper.convertDpToPixel(2, context);
int spacing = (int) Helper.convertDpToPixel(5, context);
lv_status.addItemDecoration(new GridSpacingItemDecoration(spanCount, spacing, true));
lv_status.setLayoutManager(gLayoutManager);
}
viewModelFeeds = new ViewModelProvider(DisplayOverviewFragment.this).get(TimelineVM.class);
swipeRefreshLayout.setOnRefreshListener(this::pullToRefresh);
loadTimeline(page);
lv_status.addOnScrollListener(new RecyclerView.OnScrollListener() {
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
if (mLayoutManager != null) {
int firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();
if (dy > 0) {
int visibleItemCount = mLayoutManager.getChildCount();
int totalItemCount = mLayoutManager.getItemCount();
if (firstVisibleItem + visibleItemCount == totalItemCount && context != null) {
if (!flag_loading) {
flag_loading = true;
loadTimeline(page);
nextElementLoader.setVisibility(View.VISIBLE);
}
} else {
nextElementLoader.setVisibility(View.GONE);
}
}
} else if (gLayoutManager != null) {
int firstVisibleItem = gLayoutManager.findFirstVisibleItemPosition();
if (dy > 0) {
int visibleItemCount = gLayoutManager.getChildCount();
int totalItemCount = gLayoutManager.getItemCount();
if (firstVisibleItem + visibleItemCount == totalItemCount && context != null) {
if (!flag_loading) {
flag_loading = true;
loadTimeline(page);
nextElementLoader.setVisibility(View.VISIBLE);
}
} else {
nextElementLoader.setVisibility(View.GONE);
}
}
}
}
});
return rootView;
}
@Override
public void onPause() {
super.onPause();
if (swipeRefreshLayout != null) {
swipeRefreshLayout.setEnabled(false);
swipeRefreshLayout.setRefreshing(false);
swipeRefreshLayout.clearAnimation();
}
if (getActivity() != null) {
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null && getView() != null) {
imm.hideSoftInputFromWindow(getView().getWindowToken(), 0);
}
}
}
@Override
public void onCreate(Bundle saveInstance) {
super.onCreate(saveInstance);
}
@Override
public void onAttach(@NotNull Context context) {
super.onAttach(context);
this.context = context;
}
@Override
public void onStop() {
super.onStop();
}
@Override
public void onDestroy() {
super.onDestroy();
}
private void manageVIewVideos(APIResponse apiResponse) {
//hide loaders
mainLoader.setVisibility(View.GONE);
nextElementLoader.setVisibility(View.GONE);
//handle other API error
if (this.peertubes == null || apiResponse == null || apiResponse.getOverviewVideo() == null || (apiResponse.getError() != null)) {
if (apiResponse == null || apiResponse.getError() == null)
Toasty.error(context, context.getString(R.string.toast_error), Toast.LENGTH_LONG).show();
else {
Toasty.error(context, apiResponse.getError().getError(), Toast.LENGTH_LONG).show();
}
swipeRefreshLayout.setRefreshing(false);
flag_loading = false;
return;
}
OverviewVideo overviewVideos = apiResponse.getOverviewVideo();
int totalAdded = 0;
int previousPosition = this.peertubes.size();
apiResponse.setPeertubes(new ArrayList<>());
if (overviewVideos.getCategories().size() > 0 && overviewVideos.getCategories() != null) {
String categoryTitle = overviewVideos.getCategories().get(0).getCategory().getLabel();
List<VideoData.Video> videoCategories = overviewVideos.getCategories().get(0).getVideos();
int i = 0;
for (VideoData.Video video : videoCategories) {
if (i == 0) {
video.setTitle(categoryTitle);
video.setHasTitle(true);
video.setTitleType(CATEGORY);
}
i++;
peertubes.add(video);
apiResponse.getPeertubes().add(video);
totalAdded++;
}
}
if (overviewVideos.getTags().size() > 0 && overviewVideos.getTags().get(0) != null) {
String tagTitle = overviewVideos.getTags().get(0).getTag();
List<VideoData.Video> videoTags = overviewVideos.getTags().get(0).getVideos();
int i = 0;
for (VideoData.Video video : videoTags) {
if (i == 0) {
video.setTitle(tagTitle);
video.setHasTitle(true);
video.setTitleType(TAG);
}
i++;
peertubes.add(video);
apiResponse.getPeertubes().add(video);
totalAdded++;
}
}
if (overviewVideos.getChannels().size() > 0 && overviewVideos.getChannels().get(0).getChannels() != null) {
String channelTitle = overviewVideos.getChannels().get(0).getChannels().getAcct();
List<VideoData.Video> videoChannels = overviewVideos.getChannels().get(0).getVideos();
int i = 0;
for (VideoData.Video video : videoChannels) {
if (i == 0) {
video.setTitle(channelTitle);
video.setHasTitle(true);
video.setTitleType(CHANNEL);
}
i++;
peertubes.add(video);
apiResponse.getPeertubes().add(video);
totalAdded++;
}
}
if (Helper.isLoggedIn(context)) {
List<String> uids = new ArrayList<>();
for (VideoData.Video video : apiResponse.getPeertubes()) {
uids.add(video.getChannel().getName() + "@" + video.getChannel().getHost());
}
if (uids.size() > 0 && !DisplayOverviewFragment.this.isDetached()) {
try {
RelationshipVM viewModel = new ViewModelProvider(this).get(RelationshipVM.class);
viewModel.get(uids).observe(DisplayOverviewFragment.this.requireActivity(), this::manageVIewRelationship);
} catch (Exception ignored) {
}
}
List<String> videoIds = new ArrayList<>();
for (VideoData.Video video : apiResponse.getPeertubes()) {
videoIds.add(video.getId());
}
if (videoIds.size() > 0 && !DisplayOverviewFragment.this.isDetached()) {
try {
PlaylistsVM viewModel = new ViewModelProvider(this).get(PlaylistsVM.class);
viewModel.videoExists(videoIds).observe(DisplayOverviewFragment.this.requireActivity(), this::manageVIewPlaylist);
} catch (Exception ignored) {
}
}
}
//max_id needs to work like an offset
page++;
//If no item were inserted previously the adapter is created
if (previousPosition == 0) {
peertubeAdapater = new PeertubeAdapter(this.peertubes);
peertubeAdapater.playlistListener = DisplayOverviewFragment.this;
peertubeAdapater.relationShipListener = DisplayOverviewFragment.this;
lv_status.setAdapter(peertubeAdapater);
} else
peertubeAdapater.notifyItemRangeInserted(previousPosition, totalAdded);
//remove handlers
swipeRefreshLayout.setRefreshing(false);
textviewNoAction.setVisibility(View.GONE);
if (firstLoad && (this.peertubes == null || this.peertubes.size() == 0)) {
textviewNoActionText.setText(R.string.no_video_to_display);
textviewNoAction.setVisibility(View.VISIBLE);
}
flag_loading = false;
firstLoad = false;
}
@Override
public void onDestroyView() {
if (lv_status != null) {
try {
lv_status.setAdapter(null);
} catch (Exception ignored) {
}
}
super.onDestroyView();
rootView = null;
}
@Override
public void onResume() {
super.onResume();
swipeRefreshLayout.setEnabled(true);
}
public void scrollToTop() {
if (mLayoutManager != null) {
mLayoutManager.scrollToPositionWithOffset(0, 0);
} else if (gLayoutManager != null) {
gLayoutManager.scrollToPositionWithOffset(0, 0);
}
}
public void pullToRefresh() {
int size = peertubes.size();
peertubes.clear();
peertubes = new ArrayList<>();
page = 1;
peertubeAdapater.notifyItemRangeRemoved(0, size);
loadTimeline(page);
}
/**
* Manage timeline load
*
* @param page String pagination
*/
private void loadTimeline(int page) {
viewModelFeeds.getOverviewVideos(String.valueOf(page)).observe(DisplayOverviewFragment.this.requireActivity(), this::manageVIewVideos);
}
public void manageVIewPlaylist(APIResponse apiResponse) {
if (apiResponse.getError() != null || apiResponse.getVideoExistPlaylist() == null) {
return;
}
if (playlists == null) {
playlists = new HashMap<>();
}
playlists.putAll(apiResponse.getVideoExistPlaylist());
for (VideoData.Video video : peertubes) {
video.setPlaylistExists(playlists.get(video.getId()));
}
}
public void manageVIewRelationship(APIResponse apiResponse) {
if (apiResponse.getError() != null) {
return;
}
if (relationship == null) {
relationship = new HashMap<>();
}
relationship.putAll(apiResponse.getRelationships());
}
@Override
public Map<String, Boolean> getRelationShip() {
return relationship;
}
@Override
public Map<String, List<PlaylistExist>> getPlaylist() {
return playlists;
}
static class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
private final int spanCount;
private final int spacing;
private final boolean includeEdge;
public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) {
this.spanCount = spanCount;
this.spacing = spacing;
this.includeEdge = includeEdge;
}
@Override
public void getItemOffsets(@NotNull Rect outRect, @NotNull View view, RecyclerView parent, @NotNull RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view);
int column = position % spanCount;
if (includeEdge) {
outRect.left = spacing - column * spacing / spanCount;
outRect.right = (column + 1) * spacing / spanCount;
if (position < spanCount) {
outRect.top = spacing;
}
outRect.bottom = spacing;
} else {
outRect.left = column * spacing / spanCount;
outRect.right = spacing - (column + 1) * spacing / spanCount;
if (position >= spanCount) {
outRect.top = spacing;
}
}
}
}
}

@ -0,0 +1,367 @@
package app.fedilab.android.peertube.fragment;
/* 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 <http://www.gnu.org/licenses>. */
import static app.fedilab.android.peertube.client.RetrofitPeertubeAPI.DataType.MY_CHANNELS;
import static app.fedilab.android.peertube.helper.Helper.peertubeInformation;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.InputFilter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.Spinner;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import org.jetbrains.annotations.NotNull;
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.activities.PlaylistsActivity;
import app.fedilab.android.peertube.client.APIResponse;
import app.fedilab.android.peertube.client.RetrofitPeertubeAPI;
import app.fedilab.android.peertube.client.data.AccountData.Account;
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.drawer.PlaylistAdapter;
import app.fedilab.android.peertube.viewmodel.ChannelsVM;
import app.fedilab.android.peertube.viewmodel.PlaylistsVM;
import es.dmoral.toasty.Toasty;
public class DisplayPlaylistsFragment extends Fragment {
private Context context;
private List<Playlist> playlists;
private RelativeLayout mainLoader;
private FloatingActionButton add_new;
private PlaylistAdapter playlistAdapter;
private RelativeLayout textviewNoAction;
private HashMap<Integer, String> privacyToSend;
private HashMap<String, String> channelToSend;
private Spinner set_upload_channel;
private Spinner set_upload_privacy;
private HashMap<String, String> channels;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//View for fragment is the same that fragment accounts
View rootView = inflater.inflate(R.layout.fragment_playlists, container, false);
context = getContext();
playlists = new ArrayList<>();
RecyclerView lv_playlist = rootView.findViewById(R.id.lv_playlist);
textviewNoAction = rootView.findViewById(R.id.no_action);
mainLoader = rootView.findViewById(R.id.loader);
RelativeLayout nextElementLoader = rootView.findViewById(R.id.loading_next_items);
mainLoader.setVisibility(View.VISIBLE);
nextElementLoader.setVisibility(View.GONE);
playlists = new ArrayList<>();
playlistAdapter = new PlaylistAdapter(playlists, false);
lv_playlist.setAdapter(playlistAdapter);
PlaylistsVM viewModel = new ViewModelProvider(this).get(PlaylistsVM.class);
viewModel.manage(PlaylistsVM.action.GET_PLAYLISTS, null, null).observe(DisplayPlaylistsFragment.this.requireActivity(), apiResponse -> manageVIewPlaylists(PlaylistsVM.action.GET_PLAYLISTS, apiResponse));
add_new = rootView.findViewById(R.id.add_new);
LinkedHashMap<Integer, String> privaciesInit = new LinkedHashMap<>(peertubeInformation.getPrivacies());
Map.Entry<Integer, String> entryInt = privaciesInit.entrySet().iterator().next();
privacyToSend = new HashMap<>();
privacyToSend.put(entryInt.getKey(), entryInt.getValue());
LinkedHashMap<Integer, String> privacies = new LinkedHashMap<>(peertubeInformation.getPrivacies());
//Populate privacies
Iterator<Map.Entry<Integer, String>> it = privacies.entrySet().iterator();
while (it.hasNext()) {
it.remove();
}
if (add_new != null) {
add_new.setOnClickListener(view -> {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context);
LayoutInflater inflater1 = ((Activity) context).getLayoutInflater();
View dialogView = inflater1.inflate(R.layout.add_playlist, new LinearLayout(context), false);
dialogBuilder.setView(dialogView);
EditText display_name = dialogView.findViewById(R.id.display_name);
EditText description = dialogView.findViewById(R.id.description);
set_upload_channel = dialogView.findViewById(R.id.set_upload_channel);
set_upload_privacy = dialogView.findViewById(R.id.set_upload_privacy);
ChannelsVM viewModelC = new ViewModelProvider(this).get(ChannelsVM.class);
viewModelC.get(MY_CHANNELS, null).observe(DisplayPlaylistsFragment.this.requireActivity(), this::manageVIewChannels);
display_name.setFilters(new InputFilter[]{new InputFilter.LengthFilter(120)});
description.setFilters(new InputFilter[]{new InputFilter.LengthFilter(1000)});
dialogBuilder.setPositiveButton(R.string.validate, (dialog, id) -> {
if (display_name.getText() != null && display_name.getText().toString().trim().length() > 0) {
Playlist playlist = new Playlist();
playlist.setDisplayName(display_name.getText().toString().trim());
if (description.getText() != null && description.getText().toString().trim().length() > 0) {
playlist.setDescription(description.getText().toString().trim());
}
String idChannel = null;
if (channelToSend != null) {
Map.Entry<String, String> channelM = channelToSend.entrySet().iterator().next();
idChannel = channelM.getValue();
}
Map.Entry<Integer, String> privacyM = privacyToSend.entrySet().iterator().next();
Item privacyItem = new Item();
privacyItem.setLabel(privacyM.getValue());
privacyItem.setId(privacyM.getKey());
if (privacyItem.getLabel().equals("Public") && (playlist.getVideoChannel() == null)) {
Toasty.error(context, context.getString(R.string.error_channel_mandatory), Toast.LENGTH_LONG).show();
} else {
if (privacyToSend != null) {
playlist.setPrivacy(privacyItem);
}
PlaylistParams playlistParams = new PlaylistParams();
playlistParams.setVideoChannelId(idChannel);
playlistParams.setDisplayName(playlist.getDisplayName());
playlistParams.setDescription(playlist.getDescription());
new Thread(() -> {
APIResponse apiResponse = new RetrofitPeertubeAPI(context).createOrUpdatePlaylist(PlaylistsVM.action.CREATE_PLAYLIST, null, playlistParams, null);
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> {
if (getActivity() == null)
return;
playlist.setId(apiResponse.getActionReturn());
playlists.add(0, playlist);
playlistAdapter.notifyDataSetChanged();
};
mainHandler.post(myRunnable);
add_new.setEnabled(true);
}).start();
dialog.dismiss();
add_new.setEnabled(false);
}
} else {
Toasty.error(context, context.getString(R.string.error_display_name), Toast.LENGTH_LONG).show();
}
});
dialogBuilder.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss());
AlertDialog alertDialog = dialogBuilder.create();
alertDialog.setTitle(getString(R.string.action_playlist_create));
alertDialog.setOnDismissListener(dialogInterface -> {
//Hide keyboard
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
assert imm != null;
imm.hideSoftInputFromWindow(display_name.getWindowToken(), 0);
});
if (alertDialog.getWindow() != null)
alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
alertDialog.show();
});
}
return rootView;
}
@Override
public void onCreate(Bundle saveInstance) {
super.onCreate(saveInstance);
}
@Override
public void onAttach(@NotNull Context context) {
super.onAttach(context);
this.context = context;
}
@Override
public void onDestroy() {
super.onDestroy();
}
public void manageVIewPlaylists(PlaylistsVM.action actionType, APIResponse apiResponse) {
mainLoader.setVisibility(View.GONE);
add_new.setEnabled(true);
if (apiResponse.getError() != null) {
Toasty.error(context, apiResponse.getError().getError(), Toast.LENGTH_LONG).show();
return;
}
if (actionType == PlaylistsVM.action.GET_PLAYLISTS) {
if (apiResponse.getPlaylists() != null && apiResponse.getPlaylists().size() > 0) {
this.playlists.addAll(apiResponse.getPlaylists());
playlistAdapter.notifyDataSetChanged();
textviewNoAction.setVisibility(View.GONE);
} else {
textviewNoAction.setVisibility(View.VISIBLE);
}
} else if (actionType == PlaylistsVM.action.CREATE_PLAYLIST) {
if (apiResponse.getPlaylists() != null && apiResponse.getPlaylists().size() > 0) {
Intent intent = new Intent(context, PlaylistsActivity.class);
Bundle b = new Bundle();
b.putParcelable("playlist", apiResponse.getPlaylists().get(0));
intent.putExtras(b);
context.startActivity(intent);
this.playlists.add(0, apiResponse.getPlaylists().get(0));
playlistAdapter.notifyDataSetChanged();
textviewNoAction.setVisibility(View.GONE);
} else {
Toasty.error(context, apiResponse.getError().getError(), Toast.LENGTH_LONG).show();
}
} else if (actionType == PlaylistsVM.action.DELETE_PLAYLIST) {
if (this.playlists.size() == 0)
textviewNoAction.setVisibility(View.VISIBLE);
}
}
public void manageVIewChannels(APIResponse apiResponse) {
if (apiResponse.getError() != null || apiResponse.getAccounts() == null || apiResponse.getAccounts().size() == 0) {
if (apiResponse.getError() != null && apiResponse.getError().getError() != null)
Toasty.error(context, apiResponse.getError().getError(), Toast.LENGTH_LONG).show();
else
Toasty.error(context, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
return;
}
//Populate channels
List<Account> accounts = apiResponse.getAccounts();
String[] channelName = new String[accounts.size() + 1];
String[] channelId = new String[accounts.size() + 1];
int i = 1;
channelName[0] = "";
channelId[0] = "";
channels = new HashMap<>();
for (Account account : accounts) {
channels.put(account.getUsername(), account.getId());
channelName[i] = account.getUsername();
channelId[i] = account.getId();
i++;
}
channelToSend = new HashMap<>();
channelToSend.put(channelName[0], channelId[0]);
ArrayAdapter<String> adapterChannel = new ArrayAdapter<>(context,
android.R.layout.simple_spinner_dropdown_item, channelName);
set_upload_channel.setAdapter(adapterChannel);
LinkedHashMap<String, String> translations = null;
if (peertubeInformation.getTranslations() != null)
translations = new LinkedHashMap<>(peertubeInformation.getTranslations());
LinkedHashMap<Integer, String> privaciesInit = new LinkedHashMap<>(peertubeInformation.getPlaylistPrivacies());
Map.Entry<Integer, String> entryInt = privaciesInit.entrySet().iterator().next();
privacyToSend = new HashMap<>();
privacyToSend.put(entryInt.getKey(), entryInt.getValue());
LinkedHashMap<Integer, String> privacies = new LinkedHashMap<>(peertubeInformation.getPlaylistPrivacies());
//Populate privacies
String[] privaciesA = new String[privacies.size()];
Iterator<Map.Entry<Integer, String>> it = privacies.entrySet().iterator();
i = 0;
while (it.hasNext()) {
Map.Entry<Integer, String> 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<String> adapterPrivacies = new ArrayAdapter<>(context,
android.R.layout.simple_spinner_dropdown_item, privaciesA);
set_upload_privacy.setAdapter(adapterPrivacies);
//Manage privacies
set_upload_privacy.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
LinkedHashMap<Integer, String> privaciesCheck = new LinkedHashMap<>(peertubeInformation.getPrivacies());
Iterator<Map.Entry<Integer, String>> it = privaciesCheck.entrySet().iterator();
int i = 0;
while (it.hasNext()) {
Map.Entry<Integer, String> 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) {
}
});
//Manage languages
set_upload_channel.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
LinkedHashMap<String, String> channelsCheck = new LinkedHashMap<>(channels);
Iterator<Map.Entry<String, String>> it = channelsCheck.entrySet().iterator();
int i = 0;
while (it.hasNext()) {
Map.Entry<String, String> 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) {
}
});
}
}

@ -0,0 +1,319 @@
package app.fedilab.android.peertube.fragment;
/* 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 <http://www.gnu.org/licenses>. */
import static app.fedilab.android.peertube.viewmodel.TimelineVM.TimelineType.SEPIA_SEARCH;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import app.fedilab.android.peertube.BuildConfig;
import app.fedilab.android.peertube.R;
import app.fedilab.android.peertube.client.data.ChannelData;
import app.fedilab.android.peertube.client.data.VideoData;
import app.fedilab.android.peertube.client.entities.SepiaSearch;
import app.fedilab.android.peertube.databinding.FragmentVideoBinding;
import app.fedilab.android.peertube.drawer.AccountsHorizontalListAdapter;
import app.fedilab.android.peertube.drawer.PeertubeAdapter;
import app.fedilab.android.peertube.helper.Helper;
import app.fedilab.android.peertube.viewmodel.SepiaSearchVM;
import es.dmoral.toasty.Toasty;
public class DisplaySepiaSearchFragment extends Fragment implements AccountsHorizontalListAdapter.EventListener {
private LinearLayoutManager mLayoutManager;
private GridLayoutManager gLayoutManager;
private boolean flag_loading;
private Context context;
private PeertubeAdapter peertubeAdapater;
private List<VideoData.Video> peertubes;
private boolean firstLoad;
private SharedPreferences sharedpreferences;
private SepiaSearchVM viewModelSearch;
private SepiaSearch sepiaSearchVideo;
private FragmentVideoBinding binding;
public DisplaySepiaSearchFragment() {
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
binding = FragmentVideoBinding.inflate(inflater, container, false);
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
peertubes = new ArrayList<>();
context = getContext();
Bundle bundle = this.getArguments();
if (bundle != null) {
sepiaSearchVideo = bundle.getParcelable("sepiaSearchVideo");
}
flag_loading = true;
firstLoad = true;
assert context != null;
sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
binding.loader.setVisibility(View.VISIBLE);
binding.loadingNextVideos.setVisibility(View.GONE);
peertubeAdapater = new PeertubeAdapter(this.peertubes, SEPIA_SEARCH, true, null, null);
binding.lvVideos.setAdapter(peertubeAdapater);
if (!Helper.isTablet(context)) {
mLayoutManager = new LinearLayoutManager(context);
binding.lvVideos.setLayoutManager(mLayoutManager);
} else {
gLayoutManager = new GridLayoutManager(context, 2);
int spanCount = (int) Helper.convertDpToPixel(2, context);
int spacing = (int) Helper.convertDpToPixel(5, context);
binding.lvVideos.addItemDecoration(new GridSpacingItemDecoration(spanCount, spacing, true));
binding.lvVideos.setLayoutManager(gLayoutManager);
}
viewModelSearch = new ViewModelProvider(DisplaySepiaSearchFragment.this).get(SepiaSearchVM.class);
binding.swipeContainer.setOnRefreshListener(this::pullToRefresh);
binding.lvVideos.addOnScrollListener(new RecyclerView.OnScrollListener() {
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
if (mLayoutManager != null) {
int firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();
if (dy > 0) {
int visibleItemCount = mLayoutManager.getChildCount();
int totalItemCount = mLayoutManager.getItemCount();
if (firstVisibleItem + visibleItemCount == totalItemCount && context != null) {
if (!flag_loading) {
flag_loading = true;
loadTimeline();
binding.loadingNextVideos.setVisibility(View.VISIBLE);
}
} else {
binding.loadingNextVideos.setVisibility(View.GONE);
}
}
} else if (gLayoutManager != null) {
int firstVisibleItem = gLayoutManager.findFirstVisibleItemPosition();
if (dy > 0) {
int visibleItemCount = gLayoutManager.getChildCount();
int totalItemCount = gLayoutManager.getItemCount();
if (firstVisibleItem + visibleItemCount == totalItemCount && context != null) {
if (!flag_loading) {
flag_loading = true;
loadTimeline();
binding.loadingNextVideos.setVisibility(View.VISIBLE);
}
} else {
binding.loadingNextVideos.setVisibility(View.GONE);
}
}
}
}
});
loadTimeline();
}
@Override
public void onPause() {
super.onPause();
if (binding.swipeContainer != null) {
binding.swipeContainer.setEnabled(false);
binding.swipeContainer.setRefreshing(false);
binding.swipeContainer.clearAnimation();
}
if (getActivity() != null) {
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null && getView() != null) {
imm.hideSoftInputFromWindow(getView().getWindowToken(), 0);
}
}
}
@Override
public void onCreate(Bundle saveInstance) {
super.onCreate(saveInstance);
}
@Override
public void onAttach(@NotNull Context context) {
super.onAttach(context);
this.context = context;
}
@Override
public void onStop() {
super.onStop();
}
@Override
public void onDestroy() {
super.onDestroy();
}
private void manageVIewVideos(VideoData videoData) {
//hide loaders
binding.loader.setVisibility(View.GONE);
binding.loadingNextVideos.setVisibility(View.GONE);
//handle other API error
if (videoData == null || videoData.data == null) {
Toasty.error(context, context.getString(R.string.toast_error), Toast.LENGTH_LONG).show();
binding.swipeContainer.setRefreshing(false);
flag_loading = false;
return;
}
int previousPosition = this.peertubes.size();
int videoPerPage = sharedpreferences.getInt(Helper.SET_VIDEOS_PER_PAGE, Helper.VIDEOS_PER_PAGE);
sepiaSearchVideo.setStart(String.valueOf(Integer.parseInt(sepiaSearchVideo.getStart()) + videoPerPage));
if (!BuildConfig.google_restriction) {
this.peertubes.addAll(videoData.data);
} else {
for (VideoData.Video video : videoData.data) {
if (video.getName() == null || !video.getName().toLowerCase().contains("youtube") || !video.getName().toLowerCase().contains("download")) {
this.peertubes.add(video);
}
}
}
//If no item were inserted previously the adapter is created
if (previousPosition == 0) {
peertubeAdapater = new PeertubeAdapter(this.peertubes, SEPIA_SEARCH, true, null, null);
binding.lvVideos.setAdapter(peertubeAdapater);
} else
peertubeAdapater.notifyItemRangeInserted(previousPosition, videoData.data.size());
//remove handlers
binding.swipeContainer.setRefreshing(false);
binding.noAction.setVisibility(View.GONE);
if (firstLoad && (videoData.data == null || videoData.data.size() == 0)) {
binding.noActionText.setText(R.string.no_video_to_display);
binding.noAction.setVisibility(View.VISIBLE);
}
flag_loading = false;
firstLoad = false;
}
@Override
public void onDestroyView() {
if (binding.lvVideos != null) {
try {
binding.lvVideos.setAdapter(null);
} catch (Exception ignored) {
}
}
super.onDestroyView();
}
@Override
public void onResume() {
super.onResume();
binding.swipeContainer.setEnabled(true);
}
public void scrollToTop() {
if (mLayoutManager != null) {
mLayoutManager.scrollToPositionWithOffset(0, 0);
} else if (gLayoutManager != null) {
gLayoutManager.scrollToPositionWithOffset(0, 0);
}
}
public void pullToRefresh() {
int size = peertubes.size();
peertubes.clear();
peertubes = new ArrayList<>();
peertubeAdapater.notifyItemRangeRemoved(0, size);
loadTimeline();
}
@Override
public void click(ChannelData.Channel forChannel) {
pullToRefresh();
}
private void loadTimeline() {
viewModelSearch.sepiaSearch(sepiaSearchVideo).observe(this.requireActivity(), this::manageVIewVideos);
}
static class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
private final int spanCount;
private final int spacing;
private final boolean includeEdge;
public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) {
this.spanCount = spanCount;
this.spacing = spacing;
this.includeEdge = includeEdge;
}
@Override
public void getItemOffsets(@NotNull Rect outRect, @NotNull View view, RecyclerView parent, @NotNull RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view);
int column = position % spanCount;
if (includeEdge) {
outRect.left = spacing - column * spacing / spanCount;
outRect.right = (column + 1) * spacing / spanCount;
if (position < spanCount) {
outRect.top = spacing;
}
outRect.bottom = spacing;
} else {
outRect.left = column * spacing / spanCount;
outRect.right = spacing - (column + 1) * spacing / spanCount;
if (position >= spanCount) {
outRect.top = spacing;
}
}
}
}
}

@ -0,0 +1,559 @@
package app.fedilab.android.peertube.fragment;
/* 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 <http://www.gnu.org/licenses>. */
import static app.fedilab.android.peertube.viewmodel.TimelineVM.TimelineType.VIDEOS_IN_LOCAL_PLAYLIST;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import app.fedilab.android.peertube.BuildConfig;
import app.fedilab.android.peertube.R;
import app.fedilab.android.peertube.activities.MainActivity;
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;
import app.fedilab.android.peertube.client.data.VideoData;
import app.fedilab.android.peertube.client.data.VideoPlaylistData;
import app.fedilab.android.peertube.client.entities.PlaylistExist;
import app.fedilab.android.peertube.databinding.FragmentVideoBinding;
import app.fedilab.android.peertube.drawer.AccountsHorizontalListAdapter;
import app.fedilab.android.peertube.drawer.PeertubeAdapter;
import app.fedilab.android.peertube.helper.Helper;
import app.fedilab.android.peertube.viewmodel.AccountsVM;
import app.fedilab.android.peertube.viewmodel.PlaylistsVM;
import app.fedilab.android.peertube.viewmodel.RelationshipVM;
import app.fedilab.android.peertube.viewmodel.SearchVM;
import app.fedilab.android.peertube.viewmodel.TimelineVM;
import es.dmoral.toasty.Toasty;
public class DisplayVideosFragment extends Fragment implements AccountsHorizontalListAdapter.EventListener, PeertubeAdapter.RelationShipListener, PeertubeAdapter.PlaylistListener {
private LinearLayoutManager mLayoutManager;
private GridLayoutManager gLayoutManager;
private boolean flag_loading, flag_loading_account;
private Context context;
private PeertubeAdapter peertubeAdapater;
private AccountsHorizontalListAdapter accountsHorizontalListAdapter;
private String max_id, max_id_accounts;
private List<VideoData.Video> peertubes;
private List<ChannelData.Channel> channels;
private TimelineVM.TimelineType type;
private boolean firstLoad;
private String search_peertube;
private boolean check_ScrollingUp;
private ChannelData.Channel forChannel;
private TimelineVM viewModelFeeds;
private SearchVM viewModelSearch;
private AccountsVM viewModelAccounts;
private ChannelData.Channel channel;
private AccountData.Account account;
private Map<String, Boolean> relationship;
private Map<String, List<PlaylistExist>> playlists;
private String playlistId;
private String remoteInstance;
private boolean sepiaSearch;
private String startDate, endDate;
private FragmentVideoBinding binding;
private String channelId;
public DisplayVideosFragment() {
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
binding = FragmentVideoBinding.inflate(inflater, container, false);
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
peertubes = new ArrayList<>();
channels = new ArrayList<>();
context = getContext();
startDate = null;
endDate = null;
Bundle bundle = this.getArguments();
if (bundle != null) {
search_peertube = bundle.getString("search_peertube", null);
channel = bundle.getParcelable("channel");
account = bundle.getParcelable("account");
remoteInstance = bundle.getString("peertube_instance", null);
sepiaSearch = bundle.getBoolean("sepia_search", false);
type = (TimelineVM.TimelineType) bundle.get(Helper.TIMELINE_TYPE);
playlistId = bundle.getString("playlistId", null);
startDate = bundle.getString("startDate", null);
endDate = bundle.getString("endDate", null);
}
if (channel != null) {
channelId = channel.getAcct();
} else if (account != null) {
channelId = account.getAcct();
}
max_id = "0";
//forChannel = type == TimelineVM.TimelineType.ACCOUNT_VIDEOS ? channelId : null;
max_id_accounts = null;
flag_loading = true;
flag_loading_account = false;
firstLoad = true;
check_ScrollingUp = false;
binding.loader.setVisibility(View.VISIBLE);
binding.loadingNextVideos.setVisibility(View.GONE);
peertubeAdapater = new PeertubeAdapter(this.peertubes, type, sepiaSearch, forChannel, account);
peertubeAdapater.playlistListener = this;
peertubeAdapater.relationShipListener = this;
binding.lvVideos.setAdapter(peertubeAdapater);
accountsHorizontalListAdapter = new AccountsHorizontalListAdapter(this.channels, this);
LinearLayoutManager layoutManager
= new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
binding.lvAccounts.setLayoutManager(layoutManager);
binding.lvAccounts.setAdapter(accountsHorizontalListAdapter);
if (!Helper.isTablet(context)) {
mLayoutManager = new LinearLayoutManager(context);
binding.lvVideos.setLayoutManager(mLayoutManager);
} else {
gLayoutManager = new GridLayoutManager(context, 2);
int spanCount = (int) Helper.convertDpToPixel(2, context);
int spacing = (int) Helper.convertDpToPixel(5, context);
binding.lvVideos.addItemDecoration(new GridSpacingItemDecoration(spanCount, spacing, true));
binding.lvVideos.setLayoutManager(gLayoutManager);
}
viewModelAccounts = new ViewModelProvider(DisplayVideosFragment.this).get(AccountsVM.class);
viewModelFeeds = new ViewModelProvider(DisplayVideosFragment.this).get(TimelineVM.class);
viewModelSearch = new ViewModelProvider(DisplayVideosFragment.this).get(SearchVM.class);
binding.swipeContainer.setOnRefreshListener(() -> pullToRefresh(true));
binding.lvAccounts.addOnScrollListener(new RecyclerView.OnScrollListener() {
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
int firstVisibleItem = layoutManager.findFirstVisibleItemPosition();
if (dx > 0) {
int visibleItemCount = layoutManager.getChildCount();
int totalItemCount = layoutManager.getItemCount();
if (firstVisibleItem + visibleItemCount == totalItemCount && context != null && !flag_loading_account) {
flag_loading_account = true;
viewModelAccounts.getAccounts(RetrofitPeertubeAPI.DataType.SUBSCRIBER, max_id_accounts).observe(DisplayVideosFragment.this.requireActivity(), apiResponse -> manageViewAccounts(apiResponse));
}
}
}
});
if (type != VIDEOS_IN_LOCAL_PLAYLIST) {
binding.lvVideos.addOnScrollListener(new RecyclerView.OnScrollListener() {
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
if (type == TimelineVM.TimelineType.SUBSCRIBTIONS) {
if (dy > 0) {
if (check_ScrollingUp) {
binding.topAccountContainer.setVisibility(View.GONE);
final Handler handler = new Handler();
handler.postDelayed(() -> check_ScrollingUp = false, 300);
}
} else {
if (!check_ScrollingUp) {
binding.topAccountContainer.setVisibility(View.VISIBLE);
final Handler handler = new Handler();
handler.postDelayed(() -> check_ScrollingUp = true, 300);
}
}
}
if (mLayoutManager != null) {
int firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();
if (dy > 0) {
int visibleItemCount = mLayoutManager.getChildCount();
int totalItemCount = mLayoutManager.getItemCount();
if (firstVisibleItem + visibleItemCount == totalItemCount && context != null) {
if (!flag_loading) {
flag_loading = true;
loadTimeline(max_id);
binding.loadingNextVideos.setVisibility(View.VISIBLE);
}
} else {
binding.loadingNextVideos.setVisibility(View.GONE);
}
}
} else if (gLayoutManager != null) {
int firstVisibleItem = gLayoutManager.findFirstVisibleItemPosition();
if (dy > 0) {
int visibleItemCount = gLayoutManager.getChildCount();
int totalItemCount = gLayoutManager.getItemCount();
if (firstVisibleItem + visibleItemCount == totalItemCount && context != null) {
if (!flag_loading) {
flag_loading = true;
loadTimeline(max_id);
binding.loadingNextVideos.setVisibility(View.VISIBLE);
}
} else {
binding.loadingNextVideos.setVisibility(View.GONE);
}
}
}
}
});
}
if (type == TimelineVM.TimelineType.SUBSCRIBTIONS) {
AccountsVM viewModel = new ViewModelProvider(this).get(AccountsVM.class);
viewModel.getAccounts(RetrofitPeertubeAPI.DataType.SUBSCRIBER, max_id).observe(DisplayVideosFragment.this.requireActivity(), this::manageViewAccounts);
}
loadTimeline(max_id);
binding.displayAll.setOnClickListener(v -> {
forChannel = null;
pullToRefresh(false);
});
}
@Override
public void onResume() {
super.onResume();
binding.swipeContainer.setEnabled(true);
}
@Override
public void onPause() {
super.onPause();
binding.swipeContainer.setEnabled(false);
binding.swipeContainer.setRefreshing(false);
binding.swipeContainer.clearAnimation();
if (getActivity() != null) {
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null && getView() != null) {
imm.hideSoftInputFromWindow(getView().getWindowToken(), 0);
}
}
}
@Override
public void onCreate(Bundle saveInstance) {
super.onCreate(saveInstance);
}
@Override
public void onAttach(@NotNull Context context) {
super.onAttach(context);
this.context = context;
}
@Override
public void onStop() {
super.onStop();
}
@Override
public void onDestroy() {
super.onDestroy();
}
private void manageViewAccounts(APIResponse apiResponse) {
flag_loading_account = false;
if (apiResponse != null && apiResponse.getChannels() != null && apiResponse.getChannels().size() > 0) {
if (binding.topAccountContainer.getVisibility() == View.GONE) {
binding.topAccountContainer.setVisibility(View.VISIBLE);
}
int previousPosition = channels.size();
channels.addAll(apiResponse.getChannels());
accountsHorizontalListAdapter.notifyItemRangeInserted(previousPosition, apiResponse.getChannels().size());
if (max_id_accounts == null) {
max_id_accounts = "0";
}
final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
//max_id_accounts needs to work like an offset
int tootPerPage = sharedpreferences.getInt(Helper.SET_VIDEOS_PER_PAGE, Helper.VIDEOS_PER_PAGE);
max_id_accounts = String.valueOf(Integer.parseInt(max_id_accounts) + tootPerPage);
}
}
private void manageVIewVideos(APIResponse apiResponse) {
//hide loaders
binding.loader.setVisibility(View.GONE);
binding.loadingNextVideos.setVisibility(View.GONE);
//handle other API error
if (this.peertubes == null || apiResponse == null || (apiResponse.getError() != null)) {
if (apiResponse == null)
Toasty.error(context, context.getString(R.string.toast_error), Toast.LENGTH_LONG).show();
else if (apiResponse.getError() != null) {
if (apiResponse.getError().getError().length() > 500) {
Toasty.info(context, getString(R.string.remote_account), Toast.LENGTH_LONG).show();
} else {
Toasty.error(context, apiResponse.getError().getError(), Toast.LENGTH_LONG).show();
}
}
binding.swipeContainer.setRefreshing(false);
flag_loading = false;
return;
}
int previousPosition = this.peertubes.size();
if (max_id == null)
max_id = "0";
//max_id needs to work like an offset
final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
int videoPerPage = sharedpreferences.getInt(Helper.SET_VIDEOS_PER_PAGE, Helper.VIDEOS_PER_PAGE);
max_id = String.valueOf(Integer.parseInt(max_id) + videoPerPage);
if (apiResponse.getPeertubes() == null && apiResponse.getVideoPlaylist() == null) {
return;
}
if (apiResponse.getVideoPlaylist() != null) {
apiResponse.setPeertubes(new ArrayList<>());
for (VideoPlaylistData.VideoPlaylist v : apiResponse.getVideoPlaylist()) {
apiResponse.getPeertubes().add(v.getVideo());
}
}
if (!BuildConfig.google_restriction) {
this.peertubes.addAll(apiResponse.getPeertubes());
} else {
for (VideoData.Video video : apiResponse.getPeertubes()) {
if (video.getName() == null || !video.getName().toLowerCase().contains("youtube") || !video.getName().toLowerCase().contains("download")) {
this.peertubes.add(video);
}
}
}
//If no item were inserted previously the adapter is created
if (previousPosition == 0) {
peertubeAdapater = new PeertubeAdapter(this.peertubes, type, sepiaSearch, forChannel, account);
peertubeAdapater.playlistListener = DisplayVideosFragment.this;
peertubeAdapater.relationShipListener = DisplayVideosFragment.this;
binding.lvVideos.setAdapter(peertubeAdapater);
} else
peertubeAdapater.notifyItemRangeInserted(previousPosition, apiResponse.getPeertubes().size());
//remove handlers
binding.swipeContainer.setRefreshing(false);
binding.noAction.setVisibility(View.GONE);
if (firstLoad && (apiResponse.getPeertubes() == null || apiResponse.getPeertubes().size() == 0)) {
binding.noActionText.setText(R.string.no_video_to_display);
binding.noAction.setVisibility(View.VISIBLE);
}
flag_loading = false;
firstLoad = false;
if (Helper.isLoggedIn(context)) {
List<String> uids = new ArrayList<>();
for (VideoData.Video video : apiResponse.getPeertubes()) {
if (video != null) {
uids.add(video.getChannel().getName() + "@" + video.getChannel().getHost());
}
}
if (uids.size() > 0 && !DisplayVideosFragment.this.isDetached()) {
try {
RelationshipVM viewModel = new ViewModelProvider(this).get(RelationshipVM.class);
viewModel.get(uids).observe(DisplayVideosFragment.this.requireActivity(), this::manageVIewRelationship);
} catch (Exception ignored) {
}
}
List<String> videoIds = new ArrayList<>();
for (VideoData.Video video : apiResponse.getPeertubes()) {
if (video != null) {
videoIds.add(video.getId());
}
}
if (videoIds.size() > 0 && !DisplayVideosFragment.this.isDetached()) {
try {
PlaylistsVM viewModel = new ViewModelProvider(this).get(PlaylistsVM.class);
viewModel.videoExists(videoIds).observe(DisplayVideosFragment.this.requireActivity(), this::manageVIewPlaylist);
} catch (Exception ignored) {
}
}
}
}
public void manageVIewPlaylist(APIResponse apiResponse) {
if (apiResponse.getError() != null || apiResponse.getVideoExistPlaylist() == null) {
return;
}
if (playlists == null) {
playlists = new HashMap<>();
}
playlists.putAll(apiResponse.getVideoExistPlaylist());
for (VideoData.Video video : peertubes) {
if (video != null) {
video.setPlaylistExists(playlists.get(video.getId()));
}
}
}
public void manageVIewRelationship(APIResponse apiResponse) {
if (apiResponse.getError() != null || apiResponse.getRelationships() == null) {
return;
}
if (relationship == null) {
relationship = new HashMap<>();
}
relationship.putAll(apiResponse.getRelationships());
}
@Override
public void onDestroyView() {
try {
binding.lvVideos.setAdapter(null);
} catch (Exception ignored) {
}
super.onDestroyView();
}
public void scrollToTop() {
if (mLayoutManager != null) {
mLayoutManager.scrollToPositionWithOffset(0, 0);
} else if (gLayoutManager != null) {
gLayoutManager.scrollToPositionWithOffset(0, 0);
}
}
public void pullToRefresh(boolean reload) {
if (type == TimelineVM.TimelineType.SUBSCRIBTIONS && reload) {
DisplayVideosFragment subscriptionFragment = ((MainActivity) context).getSubscriptionFragment();
if (subscriptionFragment != null) {
FragmentTransaction ft = ((MainActivity) context).getSupportFragmentManager().beginTransaction();
ft.detach(subscriptionFragment).attach(subscriptionFragment).commit();
}
} else {
int size = peertubes.size();
peertubes.clear();
peertubes = new ArrayList<>();
max_id = "0";
peertubeAdapater.notifyItemRangeRemoved(0, size);
if (forChannel == null) {
for (ChannelData.Channel channel : channels) {
channel.setSelected(false);
}
accountsHorizontalListAdapter.notifyItemRangeRemoved(0, channels.size());
}
loadTimeline("0");
}
}
@Override
public void click(ChannelData.Channel forChannel) {
this.forChannel = forChannel;
pullToRefresh(false);
}
/**
* Manage timeline load
*
* @param max_id String pagination
*/
private void loadTimeline(String max_id) {
if (search_peertube == null) { //Not a Peertube search
if (type == TimelineVM.TimelineType.CHANNEL_VIDEOS) {
viewModelFeeds.getVideosInChannel(sepiaSearch ? remoteInstance : null, channelId, max_id).observe(this.requireActivity(), this::manageVIewVideos);
} else if (type == TimelineVM.TimelineType.VIDEOS_IN_PLAYLIST) {
viewModelFeeds.loadVideosInPlaylist(playlistId, max_id).observe(this.requireActivity(), this::manageVIewVideos);
} else if (type == VIDEOS_IN_LOCAL_PLAYLIST) {
viewModelFeeds.loadVideosInLocalPlaylist(playlistId).observe(this.requireActivity(), this::manageVIewVideos);
} else if (type == TimelineVM.TimelineType.HISTORY) {
viewModelFeeds.getVideoHistory(max_id, startDate, endDate).observe(this.requireActivity(), this::manageVIewVideos);
} else {
viewModelFeeds.getVideos(type, max_id, forChannel, account).observe(this.requireActivity(), this::manageVIewVideos);
}
} else {
viewModelSearch.getVideos(max_id, search_peertube).observe(this.requireActivity(), this::manageVIewVideos);
}
}
@Override
public Map<String, Boolean> getRelationShip() {
return relationship;
}
@Override
public Map<String, List<PlaylistExist>> getPlaylist() {
return playlists;
}
static class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
private final int spanCount;
private final int spacing;
private final boolean includeEdge;
public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) {
this.spanCount = spanCount;
this.spacing = spacing;
this.includeEdge = includeEdge;
}
@Override
public void getItemOffsets(@NotNull Rect outRect, @NotNull View view, RecyclerView parent, @NotNull RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view);
int column = position % spanCount;
if (includeEdge) {
outRect.left = spacing - column * spacing / spanCount;
outRect.right = (column + 1) * spacing / spanCount;
if (position < spanCount) {
outRect.top = spacing;
}
outRect.bottom = spacing;
} else {
outRect.left = column * spacing / spanCount;
outRect.right = spacing - (column + 1) * spacing / spanCount;
if (position >= spanCount) {
outRect.top = spacing;
}
}
}
}
}

@ -0,0 +1,480 @@
package app.fedilab.android.peertube.fragment;
import static app.fedilab.android.peertube.activities.MainActivity.userMe;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.res.ResourcesCompat;
import androidx.fragment.app.FragmentActivity;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.preference.ListPreference;
import androidx.preference.MultiSelectListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceScreen;
import androidx.preference.SeekBarPreference;
import androidx.preference.SwitchPreference;
import com.avatarfirst.avatargenlib.AvatarGenerator;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import app.fedilab.android.peertube.BuildConfig;
import app.fedilab.android.peertube.R;
import app.fedilab.android.peertube.activities.MainActivity;
import app.fedilab.android.peertube.activities.MyAccountActivity;
import app.fedilab.android.peertube.client.RetrofitPeertubeAPI;
import app.fedilab.android.peertube.client.entities.UserSettings;
import app.fedilab.android.peertube.helper.Helper;
import app.fedilab.android.peertube.helper.HelperInstance;
import app.fedilab.android.peertube.helper.ThemeHelper;
import es.dmoral.toasty.Toasty;
/* 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 <http://www.gnu.org/licenses>. */
public class SettingsFragment extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener {
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.main_preferences);
createPref();
}
@Override
public void onResume() {
super.onResume();
getPreferenceScreen().getSharedPreferences()
.registerOnSharedPreferenceChangeListener(this);
}
@Override
public void onPause() {
super.onPause();
getPreferenceScreen().getSharedPreferences()
.unregisterOnSharedPreferenceChangeListener(this);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
requireActivity();
SharedPreferences sharedpreferences = requireActivity().getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedpreferences.edit();
if (key.compareTo(getString(R.string.set_video_mode_choice)) == 0) {
ListPreference set_video_mode_choice = findPreference(getString(R.string.set_video_mode_choice));
if (set_video_mode_choice != null) {
switch (set_video_mode_choice.getValue()) {
case "0":
editor.putInt(Helper.SET_VIDEO_MODE, Helper.VIDEO_MODE_NORMAL);
break;
case "1":
editor.putInt(Helper.SET_VIDEO_MODE, Helper.VIDEO_MODE_MAGNET);
break;
case "2":
editor.putInt(Helper.SET_VIDEO_MODE, Helper.VIDEO_MODE_WEBVIEW);
break;
case "3":
editor.putInt(Helper.SET_VIDEO_MODE, Helper.VIDEO_MODE_TORRENT);
break;
}
}
}
if (key.compareTo(getString(R.string.set_theme_choice)) == 0) {
ListPreference set_theme_choice = findPreference(getString(R.string.set_theme_choice));
if (set_theme_choice != null) {
int choice;
switch (set_theme_choice.getValue()) {
case "0":
choice = Helper.LIGHT_MODE;
break;
case "1":
choice = Helper.DARK_MODE;
break;
default:
choice = Helper.DEFAULT_MODE;
}
editor.putInt(Helper.SET_THEME, choice);
editor.apply();
ThemeHelper.switchTo(choice);
}
}
if (key.compareTo(getString(R.string.set_video_sensitive_choice)) == 0) {
ListPreference set_video_sensitive_choice = findPreference(getString(R.string.set_video_sensitive_choice));
if (set_video_sensitive_choice != null) {
editor.putString(getString(R.string.set_video_sensitive_choice), set_video_sensitive_choice.getValue());
editor.apply();
if (Helper.isLoggedIn(getActivity())) {
new Thread(() -> {
UserSettings userSettings = new UserSettings();
userSettings.setNsfwPolicy(set_video_sensitive_choice.getValue());
try {
RetrofitPeertubeAPI api = new RetrofitPeertubeAPI(getActivity());
api.updateUser(userSettings);
userMe.setNsfwPolicy(set_video_sensitive_choice.getValue());
} catch (Exception | Error e) {
e.printStackTrace();
}
}).start();
}
}
}
if (key.compareTo(getString(R.string.set_video_quality_choice)) == 0) {
ListPreference set_video_quality_choice = findPreference(getString(R.string.set_video_quality_choice));
if (set_video_quality_choice != null) {
switch (set_video_quality_choice.getValue()) {
case "0":
editor.putInt(Helper.SET_QUALITY_MODE, Helper.QUALITY_HIGH);
break;
case "1":
editor.putInt(Helper.SET_QUALITY_MODE, Helper.QUALITY_MEDIUM);
break;
case "2":
editor.putInt(Helper.SET_QUALITY_MODE, Helper.QUALITY_LOW);
break;
}
}
}
if (key.compareTo(getString(R.string.set_video_cache_choice)) == 0) {
SeekBarPreference set_video_cache_choice = findPreference(getString(R.string.set_video_cache_choice));
assert set_video_cache_choice != null;
final int progress = set_video_cache_choice.getValue();
set_video_cache_choice.setSummary(requireActivity().getString(R.string.video_cache_value, progress * 10));
editor.putInt(Helper.SET_VIDEO_CACHE, progress * 10);
}
if (key.compareTo(getString(R.string.set_video_minimize_choice)) == 0) {
SwitchPreference set_video_minimize_choice = findPreference(
getString(R.string.set_video_minimize_choice));
assert set_video_minimize_choice != null;
editor.putBoolean(getString(R.string.set_video_minimize_choice), set_video_minimize_choice.isChecked());
}
if (key.compareTo(getString(R.string.set_autoplay_choice)) == 0) {
SwitchPreference set_autoplay_choice = findPreference(getString(R.string.set_autoplay_choice));
assert set_autoplay_choice != null;
editor.putBoolean(getString(R.string.set_autoplay_choice), set_autoplay_choice.isChecked());
if (Helper.isLoggedIn(getActivity())) {
new Thread(() -> {
UserSettings userSettings = new UserSettings();
userSettings.setAutoPlayVideo(set_autoplay_choice.isChecked());
try {
RetrofitPeertubeAPI api = new RetrofitPeertubeAPI(getActivity());
api.updateUser(userSettings);
} catch (Exception | Error e) {
e.printStackTrace();
}
}).start();
}
}
if (key.compareTo(getString(R.string.set_fullscreen_choice)) == 0) {
SwitchPreference set_fullscreen_choice = findPreference(getString(R.string.set_fullscreen_choice));
assert set_fullscreen_choice != null;
editor.putBoolean(getString(R.string.set_fullscreen_choice), set_fullscreen_choice.isChecked());
}
if (key.compareTo(getString(R.string.set_autoplay_next_video_choice)) == 0) {
SwitchPreference set_autoplay_next_video_choice = findPreference(getString(R.string.set_autoplay_next_video_choice));
assert set_autoplay_next_video_choice != null;
editor.putBoolean(getString(R.string.set_autoplay_next_video_choice), set_autoplay_next_video_choice.isChecked());
if (Helper.isLoggedIn(getActivity())) {
new Thread(() -> {
UserSettings userSettings = new UserSettings();
userSettings.setAutoPlayNextVideo(set_autoplay_next_video_choice.isChecked());
try {
RetrofitPeertubeAPI api = new RetrofitPeertubeAPI(getActivity());
api.updateUser(userSettings);
} catch (Exception | Error e) {
e.printStackTrace();
}
}).start();
}
}
if (key.compareTo(getString(R.string.set_play_screen_lock_choice)) == 0) {
SwitchPreference set_play_screen_lock_choice = findPreference(getString(R.string.set_play_screen_lock_choice));
assert set_play_screen_lock_choice != null;
editor.putBoolean(getString(R.string.set_play_screen_lock_choice), set_play_screen_lock_choice.isChecked());
}
if (key.compareTo(getString(R.string.set_video_in_list_choice)) == 0) {
SwitchPreference set_video_in_list_choice = findPreference(getString(R.string.set_video_in_list_choice));
assert set_video_in_list_choice != null;
editor.putBoolean(getString(R.string.set_video_in_list_choice), set_video_in_list_choice.isChecked());
Intent intent = new Intent(requireActivity(), MainActivity.class);
startActivity(intent);
}
if (key.compareTo(getString(R.string.set_cast_choice)) == 0) {
SwitchPreference set_cast_choice = findPreference(getString(R.string.set_cast_choice));
assert set_cast_choice != null;
editor.putInt(getString(R.string.set_cast_choice), set_cast_choice.isChecked() ? 1 : 0);
Intent intentBC = new Intent(Helper.RECEIVE_CAST_SETTINGS);
Bundle b = new Bundle();
b.putInt("state_asked", set_cast_choice.isChecked() ? 1 : 0);
intentBC.putExtras(b);
LocalBroadcastManager.getInstance(requireActivity()).sendBroadcast(intentBC);
}
if (key.compareTo(getString(R.string.set_video_language_choice)) == 0) {
MultiSelectListPreference set_video_language_choice = findPreference(getString(R.string.set_video_language_choice));
assert set_video_language_choice != null;
editor.putStringSet(getString(R.string.set_video_language_choice), set_video_language_choice.getValues());
if (Helper.isLoggedIn(getActivity())) {
new Thread(() -> {
UserSettings userSettings = new UserSettings();
Set<String> language_choiceValues = set_video_language_choice.getValues();
userSettings.setVideoLanguages(new ArrayList<>(language_choiceValues));
try {
RetrofitPeertubeAPI api = new RetrofitPeertubeAPI(getActivity());
api.updateUser(userSettings);
} catch (Exception | Error e) {
e.printStackTrace();
}
}).start();
}
}
editor.apply();
}
private void createPref() {
getPreferenceScreen().removeAll();
addPreferencesFromResource(R.xml.main_preferences);
PreferenceScreen preferenceScreen = getPreferenceScreen();
FragmentActivity context = requireActivity();
if (preferenceScreen == null) {
Toasty.error(requireActivity(), getString(R.string.toast_error), Toasty.LENGTH_SHORT).show();
return;
}
//****** My Account ******
Preference my_account = findPreference("my_account");
if (my_account != null) {
my_account.setOnPreferenceClickListener(preference -> {
startActivity(new Intent(requireActivity(), MyAccountActivity.class));
return false;
});
if (!Helper.isLoggedIn(getActivity()) || userMe == null) {
my_account.setVisible(false);
} else {
my_account.setTitle(userMe.getUsername());
my_account.setSummary(userMe.getEmail());
Resources resources = getResources();
Drawable defaultAvatar = ResourcesCompat.getDrawable(resources, R.drawable.missing_peertube, null);
my_account.setIcon(defaultAvatar);
String avatarUrl = null;
BitmapDrawable avatar = null;
if (userMe.getAccount().getAvatar() != null) {
avatarUrl = "https://" + HelperInstance.getLiveInstance(context) + userMe.getAccount().getAvatar().getPath();
} else {
avatar = new AvatarGenerator.AvatarBuilder(context)
.setLabel(userMe.getAccount().getAcct())
.setAvatarSize(120)
.setTextSize(30)
.toSquare()
.setBackgroundColor(Helper.fetchAccentColor(context))
.build();
}
Glide.with(requireActivity())
.asDrawable()
.load(avatarUrl != null ? avatarUrl : avatar)
.into(new CustomTarget<Drawable>() {
@Override
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
my_account.setIcon(resource);
}
@Override
public void onLoadCleared(@Nullable Drawable placeholder) {
}
});
}
}
//****** App theme *******
ListPreference set_theme_choice = findPreference(getString(R.string.set_theme_choice));
List<String> arrayTheme = Arrays.asList(getResources().getStringArray(R.array.settings_theme));
CharSequence[] entriesTheme = arrayTheme.toArray(new CharSequence[0]);
CharSequence[] entryValuesTheme = new CharSequence[3];
final SharedPreferences sharedpref = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
int currentTheme = sharedpref.getInt(Helper.SET_THEME, BuildConfig.default_theme);
entryValuesTheme[0] = String.valueOf(Helper.LIGHT_MODE);
entryValuesTheme[1] = String.valueOf(Helper.DARK_MODE);
entryValuesTheme[2] = String.valueOf(Helper.DEFAULT_MODE);
if (set_theme_choice != null) {
set_theme_choice.setEntries(entriesTheme);
set_theme_choice.setEntryValues(entryValuesTheme);
set_theme_choice.setValueIndex(currentTheme);
}
//****** Video mode *******
ListPreference set_video_mode_choice = findPreference(getString(R.string.set_video_mode_choice));
List<String> array = Arrays.asList(getResources().getStringArray(R.array.settings_video_mode));
CharSequence[] entries = array.toArray(new CharSequence[0]);
CharSequence[] entryValues = new CharSequence[2];
int video_mode = sharedpref.getInt(Helper.SET_VIDEO_MODE, Helper.VIDEO_MODE_NORMAL);
entryValues[0] = String.valueOf(Helper.VIDEO_MODE_NORMAL);
entryValues[1] = String.valueOf(Helper.VIDEO_MODE_WEBVIEW);
if (set_video_mode_choice != null) {
set_video_mode_choice.setEntries(entries);
set_video_mode_choice.setEntryValues(entryValues);
if (video_mode > Helper.VIDEO_MODE_WEBVIEW) {
video_mode = Helper.VIDEO_MODE_NORMAL;
}
set_video_mode_choice.setValueIndex(video_mode);
}
//****** Video quality *******
ListPreference set_video_quality_choice = findPreference(getString(R.string.set_video_quality_choice));
List<String> arrayQuality = Arrays.asList(getResources().getStringArray(R.array.settings_video_quality));
CharSequence[] entriesQuality = arrayQuality.toArray(new CharSequence[0]);
CharSequence[] entryValuesQuality = new CharSequence[3];
int video_quality = sharedpref.getInt(Helper.SET_QUALITY_MODE, Helper.QUALITY_HIGH);
entryValuesQuality[0] = String.valueOf(Helper.QUALITY_HIGH);
entryValuesQuality[1] = String.valueOf(Helper.QUALITY_MEDIUM);
entryValuesQuality[2] = String.valueOf(Helper.QUALITY_LOW);
if (set_video_quality_choice != null) {
set_video_quality_choice.setEntries(entriesQuality);
set_video_quality_choice.setEntryValues(entryValuesQuality);
set_video_quality_choice.setValueIndex(video_quality);
}
//****** Video cache *******
SeekBarPreference set_video_cache_choice = findPreference(getString(R.string.set_video_cache_choice));
int video_cache = sharedpref.getInt(Helper.SET_VIDEO_CACHE, Helper.DEFAULT_VIDEO_CACHE_MB);
assert set_video_cache_choice != null;
set_video_cache_choice.setValue(video_cache / 10);
//****** Minimized videos *******
boolean minimized = sharedpref.getBoolean(getString(R.string.set_video_minimize_choice), true);
SwitchPreference set_video_minimize_choice = findPreference(getString(R.string.set_video_minimize_choice));
assert set_video_minimize_choice != null;
set_video_minimize_choice.setChecked(minimized);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O
|| !requireActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)) {
set_video_minimize_choice.setVisible(false);
}
//****** Autoplay videos *******
boolean autoplay = sharedpref.getBoolean(getString(R.string.set_autoplay_choice), true);
SwitchPreference set_autoplay_choice = findPreference(getString(R.string.set_autoplay_choice));
assert set_autoplay_choice != null;
set_autoplay_choice.setChecked(autoplay);
//****** Fullscreen videos *******
boolean fullscreen = sharedpref.getBoolean(getString(R.string.set_fullscreen_choice), false);
SwitchPreference set_fullscreen_choice = findPreference(getString(R.string.set_fullscreen_choice));
assert set_fullscreen_choice != null;
set_fullscreen_choice.setChecked(fullscreen);
//****** Autoplay next videos *******
boolean autoplayNextVideo = sharedpref.getBoolean(getString(R.string.set_autoplay_next_video_choice), false);
SwitchPreference set_autoplay_next_video_choice = findPreference(getString(R.string.set_autoplay_next_video_choice));
assert set_autoplay_next_video_choice != null;
set_autoplay_next_video_choice.setChecked(autoplayNextVideo);
//****** Screen lock *******
boolean playScreenLock = sharedpref.getBoolean(getString(R.string.set_play_screen_lock_choice), false);
SwitchPreference set_play_screen_lock_choice = findPreference(getString(R.string.set_play_screen_lock_choice));
assert set_play_screen_lock_choice != null;
set_play_screen_lock_choice.setChecked(playScreenLock);
//****** Display videos in a list *******
boolean videosInList = sharedpref.getBoolean(getString(R.string.set_video_in_list_choice), false);
SwitchPreference set_video_in_list_choice = findPreference(getString(R.string.set_video_in_list_choice));
assert set_video_in_list_choice != null;
set_video_in_list_choice.setChecked(videosInList);
//****** Allow Chromecast *******
int cast = sharedpref.getInt(getString(R.string.set_cast_choice), BuildConfig.cast_enabled);
SwitchPreference set_cast_choice = findPreference(getString(R.string.set_cast_choice));
assert set_cast_choice != null;
set_cast_choice.setChecked(cast == 1);
//****** Language filter *********
LinkedHashMap<String, String> languages = new LinkedHashMap<>(Helper.peertubeInformation.getLanguages());
List<CharSequence> entriesLanguages = new ArrayList<>();
List<CharSequence> valuesLanguages = new ArrayList<>();
Iterator<Map.Entry<String, String>> it = languages.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> pair = it.next();
entriesLanguages.add(pair.getValue());
valuesLanguages.add(pair.getKey());
it.remove();
}
MultiSelectListPreference set_video_language_choice = findPreference(getString(R.string.set_video_language_choice));
Set<String> selection = sharedpref.getStringSet(getString(R.string.set_video_language_choice), null);
assert set_video_language_choice != null;
set_video_language_choice.setEntries(entriesLanguages.toArray(new CharSequence[]{}));
set_video_language_choice.setEntryValues(valuesLanguages.toArray(new CharSequence[]{}));
if (selection != null) {
set_video_language_choice.setValues(selection);
}
//****** Display sensitive content *******
ListPreference set_video_sensitive_choice = findPreference(getString(R.string.set_video_sensitive_choice));
List<String> arraySensitive = new ArrayList<>();
arraySensitive.add(getString(R.string.do_not_list));
arraySensitive.add(getString(R.string.blur));
arraySensitive.add(getString(R.string.display));
CharSequence[] entriesSensitive = arraySensitive.toArray(new CharSequence[0]);
CharSequence[] entryValuesSensitive = new CharSequence[3];
String currentSensitive = sharedpref.getString(getString(R.string.set_video_sensitive_choice), Helper.BLUR);
entryValuesSensitive[0] = Helper.DO_NOT_LIST.toLowerCase();
entryValuesSensitive[1] = Helper.BLUR.toLowerCase();
entryValuesSensitive[2] = Helper.DISPLAY.toLowerCase();
int currentSensitivePosition = 0;
for (CharSequence val : entryValuesSensitive) {
if (val.equals(currentSensitive)) {
break;
}
currentSensitivePosition++;
}
if (set_video_sensitive_choice != null) {
set_video_sensitive_choice.setEntries(entriesSensitive);
set_video_sensitive_choice.setEntryValues(entryValuesSensitive);
set_video_sensitive_choice.setValueIndex(currentSensitivePosition);
}
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save