|
@ -62,7 +62,8 @@ dependencies {
|
||||||
exclude module: 'support-annotations'
|
exclude module: 'support-annotations'
|
||||||
})
|
})
|
||||||
|
|
||||||
implementation 'com.github.TeamNewPipe:NewPipeExtractor:b6d3252'
|
implementation 'com.github.TeamNewPipe:NewPipeExtractor:43b54cc'
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
testImplementation 'org.mockito:mockito-core:2.23.0'
|
testImplementation 'org.mockito:mockito-core:2.23.0'
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,9 @@ import android.app.NotificationChannel;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.nostra13.universalimageloader.cache.memory.impl.LRULimitedMemoryCache;
|
import com.nostra13.universalimageloader.cache.memory.impl.LRULimitedMemoryCache;
|
||||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||||
|
@ -29,6 +29,7 @@ import org.schabi.newpipe.report.UserAction;
|
||||||
import org.schabi.newpipe.settings.SettingsActivity;
|
import org.schabi.newpipe.settings.SettingsActivity;
|
||||||
import org.schabi.newpipe.util.ExtractorHelper;
|
import org.schabi.newpipe.util.ExtractorHelper;
|
||||||
import org.schabi.newpipe.util.Localization;
|
import org.schabi.newpipe.util.Localization;
|
||||||
|
import org.schabi.newpipe.util.ServiceHelper;
|
||||||
import org.schabi.newpipe.util.StateSaver;
|
import org.schabi.newpipe.util.StateSaver;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -103,6 +104,8 @@ public class App extends Application {
|
||||||
StateSaver.init(this);
|
StateSaver.init(this);
|
||||||
initNotificationChannel();
|
initNotificationChannel();
|
||||||
|
|
||||||
|
ServiceHelper.initServices(this);
|
||||||
|
|
||||||
// Initialize image loader
|
// Initialize image loader
|
||||||
ImageLoader.getInstance().init(getImageLoaderConfigurations(10, 50));
|
ImageLoader.getInstance().init(getImageLoaderConfigurations(10, 50));
|
||||||
|
|
||||||
|
|
|
@ -29,14 +29,18 @@ import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
import android.widget.Spinner;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
@ -47,12 +51,15 @@ import androidx.appcompat.widget.Toolbar;
|
||||||
import androidx.core.view.GravityCompat;
|
import androidx.core.view.GravityCompat;
|
||||||
import androidx.drawerlayout.widget.DrawerLayout;
|
import androidx.drawerlayout.widget.DrawerLayout;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
import com.google.android.material.navigation.NavigationView;
|
import com.google.android.material.navigation.NavigationView;
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
|
import org.schabi.newpipe.extractor.ServiceList;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
|
import org.schabi.newpipe.extractor.services.peertube.PeertubeInstance;
|
||||||
import org.schabi.newpipe.fragments.BackPressable;
|
import org.schabi.newpipe.fragments.BackPressable;
|
||||||
import org.schabi.newpipe.fragments.MainFragment;
|
import org.schabi.newpipe.fragments.MainFragment;
|
||||||
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
|
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
|
||||||
|
@ -61,11 +68,15 @@ import org.schabi.newpipe.report.ErrorActivity;
|
||||||
import org.schabi.newpipe.util.Constants;
|
import org.schabi.newpipe.util.Constants;
|
||||||
import org.schabi.newpipe.util.KioskTranslator;
|
import org.schabi.newpipe.util.KioskTranslator;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
|
import org.schabi.newpipe.util.PeertubeHelper;
|
||||||
import org.schabi.newpipe.util.PermissionHelper;
|
import org.schabi.newpipe.util.PermissionHelper;
|
||||||
import org.schabi.newpipe.util.ServiceHelper;
|
import org.schabi.newpipe.util.ServiceHelper;
|
||||||
import org.schabi.newpipe.util.StateSaver;
|
import org.schabi.newpipe.util.StateSaver;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends AppCompatActivity {
|
||||||
private static final String TAG = "MainActivity";
|
private static final String TAG = "MainActivity";
|
||||||
public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");
|
public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");
|
||||||
|
@ -300,13 +311,57 @@ public class MainActivity extends AppCompatActivity {
|
||||||
final String title = s.getServiceInfo().getName() +
|
final String title = s.getServiceInfo().getName() +
|
||||||
(ServiceHelper.isBeta(s) ? " (beta)" : "");
|
(ServiceHelper.isBeta(s) ? " (beta)" : "");
|
||||||
|
|
||||||
drawerItems.getMenu()
|
MenuItem menuItem = drawerItems.getMenu()
|
||||||
.add(R.id.menu_services_group, s.getServiceId(), ORDER, title)
|
.add(R.id.menu_services_group, s.getServiceId(), ORDER, title)
|
||||||
.setIcon(ServiceHelper.getIcon(s.getServiceId()));
|
.setIcon(ServiceHelper.getIcon(s.getServiceId()));
|
||||||
|
|
||||||
|
// peertube specifics
|
||||||
|
if(s.getServiceId() == 3){
|
||||||
|
enhancePeertubeMenu(s, menuItem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(true);
|
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void enhancePeertubeMenu(StreamingService s, MenuItem menuItem) {
|
||||||
|
PeertubeInstance currentInstace = PeertubeHelper.getCurrentInstance();
|
||||||
|
menuItem.setTitle(currentInstace.getName() + (ServiceHelper.isBeta(s) ? " (beta)" : ""));
|
||||||
|
Spinner spinner = (Spinner) LayoutInflater.from(this).inflate(R.layout.instance_spinner_layout, null);
|
||||||
|
List<PeertubeInstance> instances = PeertubeHelper.getInstanceList(this);
|
||||||
|
List<String> items = new ArrayList<>();
|
||||||
|
int defaultSelect = 0;
|
||||||
|
for(PeertubeInstance instance: instances){
|
||||||
|
items.add(instance.getName());
|
||||||
|
if(instance.getUrl().equals(currentInstace.getUrl())){
|
||||||
|
defaultSelect = items.size()-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, R.layout.instance_spinner_item, items);
|
||||||
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
spinner.setAdapter(adapter);
|
||||||
|
spinner.setSelection(defaultSelect, false);
|
||||||
|
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
PeertubeInstance newInstance = instances.get(position);
|
||||||
|
if(newInstance.getUrl().equals(PeertubeHelper.getCurrentInstance().getUrl())) return;
|
||||||
|
PeertubeHelper.selectInstance(newInstance, getApplicationContext());
|
||||||
|
changeService(menuItem);
|
||||||
|
drawer.closeDrawers();
|
||||||
|
new Handler(Looper.getMainLooper()).postDelayed(() -> {
|
||||||
|
getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
|
||||||
|
recreate();
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNothingSelected(AdapterView<?> parent) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
menuItem.setActionView(spinner);
|
||||||
|
}
|
||||||
|
|
||||||
private void showTabs() throws ExtractionException {
|
private void showTabs() throws ExtractionException {
|
||||||
serviceArrow.setImageResource(R.drawable.ic_arrow_down_white);
|
serviceArrow.setImageResource(R.drawable.ic_arrow_down_white);
|
||||||
|
|
||||||
|
@ -367,6 +422,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
String selectedServiceName = NewPipe.getService(
|
String selectedServiceName = NewPipe.getService(
|
||||||
ServiceHelper.getSelectedServiceId(this)).getServiceInfo().getName();
|
ServiceHelper.getSelectedServiceId(this)).getServiceInfo().getName();
|
||||||
headerServiceView.setText(selectedServiceName);
|
headerServiceView.setText(selectedServiceName);
|
||||||
|
headerServiceView.post(() -> headerServiceView.setSelected(true));
|
||||||
toggleServiceButton.setContentDescription(
|
toggleServiceButton.setContentDescription(
|
||||||
getString(R.string.drawer_header_description) + selectedServiceName);
|
getString(R.string.drawer_header_description) + selectedServiceName);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.schabi.newpipe.extractor.InfoItem;
|
||||||
import org.schabi.newpipe.extractor.ListExtractor;
|
import org.schabi.newpipe.extractor.ListExtractor;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
|
||||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||||
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
||||||
|
@ -98,7 +99,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||||
@Override
|
@Override
|
||||||
public void setUserVisibleHint(boolean isVisibleToUser) {
|
public void setUserVisibleHint(boolean isVisibleToUser) {
|
||||||
super.setUserVisibleHint(isVisibleToUser);
|
super.setUserVisibleHint(isVisibleToUser);
|
||||||
if(activity != null
|
if (activity != null
|
||||||
&& useAsFrontPage
|
&& useAsFrontPage
|
||||||
&& isVisibleToUser) {
|
&& isVisibleToUser) {
|
||||||
setTitle(currentInfo != null ? currentInfo.getName() : name);
|
setTitle(currentInfo != null ? currentInfo.getName() : name);
|
||||||
|
@ -152,7 +153,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
ActionBar supportActionBar = activity.getSupportActionBar();
|
ActionBar supportActionBar = activity.getSupportActionBar();
|
||||||
if(useAsFrontPage && supportActionBar != null) {
|
if (useAsFrontPage && supportActionBar != null) {
|
||||||
supportActionBar.setDisplayHomeAsUpEnabled(false);
|
supportActionBar.setDisplayHomeAsUpEnabled(false);
|
||||||
} else {
|
} else {
|
||||||
inflater.inflate(R.menu.menu_channel, menu);
|
inflater.inflate(R.menu.menu_channel, menu);
|
||||||
|
@ -165,7 +166,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||||
|
|
||||||
private void openRssFeed() {
|
private void openRssFeed() {
|
||||||
final ChannelInfo info = currentInfo;
|
final ChannelInfo info = currentInfo;
|
||||||
if(info != null) {
|
if (info != null) {
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(info.getFeedUrl()));
|
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(info.getFeedUrl()));
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
|
@ -178,10 +179,14 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||||
openRssFeed();
|
openRssFeed();
|
||||||
break;
|
break;
|
||||||
case R.id.menu_item_openInBrowser:
|
case R.id.menu_item_openInBrowser:
|
||||||
ShareUtils.openUrlInBrowser(this.getContext(), currentInfo.getOriginalUrl());
|
if (currentInfo != null) {
|
||||||
|
ShareUtils.openUrlInBrowser(this.getContext(), currentInfo.getOriginalUrl());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case R.id.menu_item_share:
|
case R.id.menu_item_share:
|
||||||
ShareUtils.shareUrl(this.getContext(), name, currentInfo.getOriginalUrl());
|
if (currentInfo != null) {
|
||||||
|
ShareUtils.shareUrl(this.getContext(), name, currentInfo.getOriginalUrl());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
|
@ -218,7 +223,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||||
.debounce(100, TimeUnit.MILLISECONDS)
|
.debounce(100, TimeUnit.MILLISECONDS)
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe((List<SubscriptionEntity> subscriptionEntities) ->
|
.subscribe((List<SubscriptionEntity> subscriptionEntities) ->
|
||||||
updateSubscribeButton(!subscriptionEntities.isEmpty())
|
updateSubscribeButton(!subscriptionEntities.isEmpty())
|
||||||
, onError));
|
, onError));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -359,9 +364,9 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||||
|
|
||||||
headerRootLayout.setVisibility(View.VISIBLE);
|
headerRootLayout.setVisibility(View.VISIBLE);
|
||||||
imageLoader.displayImage(result.getBannerUrl(), headerChannelBanner,
|
imageLoader.displayImage(result.getBannerUrl(), headerChannelBanner,
|
||||||
ImageDisplayConstants.DISPLAY_BANNER_OPTIONS);
|
ImageDisplayConstants.DISPLAY_BANNER_OPTIONS);
|
||||||
imageLoader.displayImage(result.getAvatarUrl(), headerAvatarView,
|
imageLoader.displayImage(result.getAvatarUrl(), headerAvatarView,
|
||||||
ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS);
|
ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS);
|
||||||
|
|
||||||
headerSubscribersTextView.setVisibility(View.VISIBLE);
|
headerSubscribersTextView.setVisibility(View.VISIBLE);
|
||||||
if (result.getSubscriberCount() >= 0) {
|
if (result.getSubscriberCount() >= 0) {
|
||||||
|
@ -397,8 +402,8 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||||
|
|
||||||
private PlayQueue getPlayQueue(final int index) {
|
private PlayQueue getPlayQueue(final int index) {
|
||||||
final List<StreamInfoItem> streamItems = new ArrayList<>();
|
final List<StreamInfoItem> streamItems = new ArrayList<>();
|
||||||
for(InfoItem i : infoListAdapter.getItemsList()) {
|
for (InfoItem i : infoListAdapter.getItemsList()) {
|
||||||
if(i instanceof StreamInfoItem) {
|
if (i instanceof StreamInfoItem) {
|
||||||
streamItems.add((StreamInfoItem) i);
|
streamItems.add((StreamInfoItem) i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -432,12 +437,16 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||||
protected boolean onError(Throwable exception) {
|
protected boolean onError(Throwable exception) {
|
||||||
if (super.onError(exception)) return true;
|
if (super.onError(exception)) return true;
|
||||||
|
|
||||||
int errorId = exception instanceof ExtractionException ? R.string.parsing_error : R.string.general_error;
|
if (exception instanceof ContentNotAvailableException) {
|
||||||
onUnrecoverableError(exception,
|
showError(getString(R.string.content_not_available), false);
|
||||||
UserAction.REQUESTED_CHANNEL,
|
} else {
|
||||||
NewPipe.getNameOfService(serviceId),
|
int errorId = exception instanceof ExtractionException ? R.string.parsing_error : R.string.general_error;
|
||||||
url,
|
onUnrecoverableError(exception,
|
||||||
errorId);
|
UserAction.REQUESTED_CHANNEL,
|
||||||
|
NewPipe.getNameOfService(serviceId),
|
||||||
|
url,
|
||||||
|
errorId);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,417 @@
|
||||||
|
package org.schabi.newpipe.settings;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.RadioButton;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.appcompat.content.res.AppCompatResources;
|
||||||
|
import androidx.appcompat.widget.AppCompatImageView;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
|
import com.grack.nanojson.JsonStringWriter;
|
||||||
|
import com.grack.nanojson.JsonWriter;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.extractor.services.peertube.PeertubeInstance;
|
||||||
|
import org.schabi.newpipe.util.Constants;
|
||||||
|
import org.schabi.newpipe.util.PeertubeHelper;
|
||||||
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import io.reactivex.Single;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.disposables.CompositeDisposable;
|
||||||
|
import io.reactivex.disposables.Disposable;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
|
||||||
|
public class PeertubeInstanceListFragment extends Fragment {
|
||||||
|
|
||||||
|
private List<PeertubeInstance> instanceList = new ArrayList<>();
|
||||||
|
private PeertubeInstance selectedInstance;
|
||||||
|
private String savedInstanceListKey;
|
||||||
|
public InstanceListAdapter instanceListAdapter;
|
||||||
|
|
||||||
|
private ProgressBar progressBar;
|
||||||
|
private SharedPreferences sharedPreferences;
|
||||||
|
|
||||||
|
private CompositeDisposable disposables = new CompositeDisposable();
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Lifecycle
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext());
|
||||||
|
savedInstanceListKey = getString(R.string.peertube_instance_list_key);
|
||||||
|
selectedInstance = PeertubeHelper.getCurrentInstance();
|
||||||
|
updateInstanceList();
|
||||||
|
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
return inflater.inflate(R.layout.fragment_instance_list, container, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View rootView, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(rootView, savedInstanceState);
|
||||||
|
|
||||||
|
initButton(rootView);
|
||||||
|
|
||||||
|
RecyclerView listInstances = rootView.findViewById(R.id.instances);
|
||||||
|
listInstances.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||||
|
|
||||||
|
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(getItemTouchCallback());
|
||||||
|
itemTouchHelper.attachToRecyclerView(listInstances);
|
||||||
|
|
||||||
|
instanceListAdapter = new InstanceListAdapter(requireContext(), itemTouchHelper);
|
||||||
|
listInstances.setAdapter(instanceListAdapter);
|
||||||
|
|
||||||
|
progressBar = rootView.findViewById(R.id.loading_progress_bar);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
updateTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
saveChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
if (disposables != null) disposables.clear();
|
||||||
|
disposables = null;
|
||||||
|
}
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Menu
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
private final int MENU_ITEM_RESTORE_ID = 123456;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
|
||||||
|
final MenuItem restoreItem = menu.add(Menu.NONE, MENU_ITEM_RESTORE_ID, Menu.NONE, R.string.restore_defaults);
|
||||||
|
restoreItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||||
|
|
||||||
|
final int restoreIcon = ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_restore_defaults);
|
||||||
|
restoreItem.setIcon(AppCompatResources.getDrawable(requireContext(), restoreIcon));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == MENU_ITEM_RESTORE_ID) {
|
||||||
|
restoreDefaults();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Utils
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
private void updateInstanceList() {
|
||||||
|
instanceList.clear();
|
||||||
|
instanceList.addAll(PeertubeHelper.getInstanceList(requireContext()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void selectInstance(PeertubeInstance instance) {
|
||||||
|
selectedInstance = PeertubeHelper.selectInstance(instance, requireContext());
|
||||||
|
sharedPreferences.edit().putBoolean(Constants.KEY_MAIN_PAGE_CHANGE, true).apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTitle() {
|
||||||
|
if (getActivity() instanceof AppCompatActivity) {
|
||||||
|
ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
|
||||||
|
if (actionBar != null) actionBar.setTitle(R.string.peertube_instance_url_title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveChanges() {
|
||||||
|
JsonStringWriter jsonWriter = JsonWriter.string().object().array("instances");
|
||||||
|
for (PeertubeInstance instance : instanceList) {
|
||||||
|
jsonWriter.object();
|
||||||
|
jsonWriter.value("name", instance.getName());
|
||||||
|
jsonWriter.value("url", instance.getUrl());
|
||||||
|
jsonWriter.end();
|
||||||
|
}
|
||||||
|
String jsonToSave = jsonWriter.end().end().done();
|
||||||
|
sharedPreferences.edit().putString(savedInstanceListKey, jsonToSave).apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void restoreDefaults() {
|
||||||
|
new AlertDialog.Builder(requireContext(), ThemeHelper.getDialogTheme(requireContext()))
|
||||||
|
.setTitle(R.string.restore_defaults)
|
||||||
|
.setMessage(R.string.restore_defaults_confirmation)
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.setPositiveButton(R.string.yes, (dialog, which) -> {
|
||||||
|
sharedPreferences.edit().remove(savedInstanceListKey).apply();
|
||||||
|
selectInstance(PeertubeInstance.defaultInstance);
|
||||||
|
updateInstanceList();
|
||||||
|
instanceListAdapter.notifyDataSetChanged();
|
||||||
|
})
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initButton(View rootView) {
|
||||||
|
final FloatingActionButton fab = rootView.findViewById(R.id.addInstanceButton);
|
||||||
|
fab.setOnClickListener(v -> {
|
||||||
|
showAddItemDialog(requireContext());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showAddItemDialog(Context c) {
|
||||||
|
final EditText urlET = new EditText(c);
|
||||||
|
urlET.setHint(R.string.peertube_instance_add_help);
|
||||||
|
AlertDialog dialog = new AlertDialog.Builder(c)
|
||||||
|
.setTitle(R.string.peertube_instance_add_title)
|
||||||
|
.setIcon(R.drawable.place_holder_peertube)
|
||||||
|
.setView(urlET)
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.setPositiveButton(R.string.finish, (dialog1, which) -> {
|
||||||
|
String url = urlET.getText().toString();
|
||||||
|
addInstance(url);
|
||||||
|
})
|
||||||
|
.create();
|
||||||
|
dialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addInstance(String url) {
|
||||||
|
String cleanUrl = cleanUrl(url);
|
||||||
|
if(null == cleanUrl) return;
|
||||||
|
progressBar.setVisibility(View.VISIBLE);
|
||||||
|
Disposable disposable = Single.fromCallable(() -> {
|
||||||
|
PeertubeInstance instance = new PeertubeInstance(cleanUrl);
|
||||||
|
instance.fetchInstanceMetaData();
|
||||||
|
return instance;
|
||||||
|
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe((instance) -> {
|
||||||
|
progressBar.setVisibility(View.GONE);
|
||||||
|
add(instance);
|
||||||
|
}, e -> {
|
||||||
|
progressBar.setVisibility(View.GONE);
|
||||||
|
Toast.makeText(getActivity(), R.string.peertube_instance_add_fail, Toast.LENGTH_SHORT).show();
|
||||||
|
});
|
||||||
|
disposables.add(disposable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private String cleanUrl(String url){
|
||||||
|
// if protocol not present, add https
|
||||||
|
if(!url.startsWith("http")){
|
||||||
|
url = "https://" + url;
|
||||||
|
}
|
||||||
|
// remove trailing slash
|
||||||
|
url = url.replaceAll("/$", "");
|
||||||
|
// only allow https
|
||||||
|
if (!url.startsWith("https://")) {
|
||||||
|
Toast.makeText(getActivity(), R.string.peertube_instance_add_https_only, Toast.LENGTH_SHORT).show();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// only allow if not already exists
|
||||||
|
for (PeertubeInstance instance : instanceList) {
|
||||||
|
if (instance.getUrl().equals(url)) {
|
||||||
|
Toast.makeText(getActivity(), R.string.peertube_instance_add_exists, Toast.LENGTH_SHORT).show();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void add(final PeertubeInstance instance) {
|
||||||
|
instanceList.add(instance);
|
||||||
|
instanceListAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// List Handling
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
private class InstanceListAdapter extends RecyclerView.Adapter<InstanceListAdapter.TabViewHolder> {
|
||||||
|
private ItemTouchHelper itemTouchHelper;
|
||||||
|
private final LayoutInflater inflater;
|
||||||
|
private RadioButton lastChecked;
|
||||||
|
|
||||||
|
InstanceListAdapter(Context context, ItemTouchHelper itemTouchHelper) {
|
||||||
|
this.itemTouchHelper = itemTouchHelper;
|
||||||
|
this.inflater = LayoutInflater.from(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void swapItems(int fromPosition, int toPosition) {
|
||||||
|
Collections.swap(instanceList, fromPosition, toPosition);
|
||||||
|
notifyItemMoved(fromPosition, toPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public InstanceListAdapter.TabViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View view = inflater.inflate(R.layout.item_instance, parent, false);
|
||||||
|
return new InstanceListAdapter.TabViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull InstanceListAdapter.TabViewHolder holder, int position) {
|
||||||
|
holder.bind(position, holder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return instanceList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
class TabViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
private AppCompatImageView instanceIconView;
|
||||||
|
private TextView instanceNameView;
|
||||||
|
private TextView instanceUrlView;
|
||||||
|
private RadioButton instanceRB;
|
||||||
|
private ImageView handle;
|
||||||
|
|
||||||
|
TabViewHolder(View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
|
||||||
|
instanceIconView = itemView.findViewById(R.id.instanceIcon);
|
||||||
|
instanceNameView = itemView.findViewById(R.id.instanceName);
|
||||||
|
instanceUrlView = itemView.findViewById(R.id.instanceUrl);
|
||||||
|
instanceRB = itemView.findViewById(R.id.selectInstanceRB);
|
||||||
|
handle = itemView.findViewById(R.id.handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
void bind(int position, TabViewHolder holder) {
|
||||||
|
handle.setOnTouchListener(getOnTouchListener(holder));
|
||||||
|
|
||||||
|
final PeertubeInstance instance = instanceList.get(position);
|
||||||
|
instanceNameView.setText(instance.getName());
|
||||||
|
instanceUrlView.setText(instance.getUrl());
|
||||||
|
instanceRB.setOnCheckedChangeListener(null);
|
||||||
|
if (selectedInstance.getUrl().equals(instance.getUrl())) {
|
||||||
|
if (lastChecked != null && lastChecked != instanceRB) {
|
||||||
|
lastChecked.setChecked(false);
|
||||||
|
}
|
||||||
|
instanceRB.setChecked(true);
|
||||||
|
lastChecked = instanceRB;
|
||||||
|
}
|
||||||
|
instanceRB.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||||
|
if (isChecked) {
|
||||||
|
selectInstance(instance);
|
||||||
|
if (lastChecked != null && lastChecked != instanceRB) {
|
||||||
|
lastChecked.setChecked(false);
|
||||||
|
}
|
||||||
|
lastChecked = instanceRB;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
instanceIconView.setImageResource(R.drawable.place_holder_peertube);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
private View.OnTouchListener getOnTouchListener(final RecyclerView.ViewHolder item) {
|
||||||
|
return (view, motionEvent) -> {
|
||||||
|
if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
|
||||||
|
if (itemTouchHelper != null && getItemCount() > 1) {
|
||||||
|
itemTouchHelper.startDrag(item);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ItemTouchHelper.SimpleCallback getItemTouchCallback() {
|
||||||
|
return new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN,
|
||||||
|
ItemTouchHelper.START | ItemTouchHelper.END) {
|
||||||
|
@Override
|
||||||
|
public int interpolateOutOfBoundsScroll(RecyclerView recyclerView, int viewSize,
|
||||||
|
int viewSizeOutOfBounds, int totalSize,
|
||||||
|
long msSinceStartScroll) {
|
||||||
|
final int standardSpeed = super.interpolateOutOfBoundsScroll(recyclerView, viewSize,
|
||||||
|
viewSizeOutOfBounds, totalSize, msSinceStartScroll);
|
||||||
|
final int minimumAbsVelocity = Math.max(12,
|
||||||
|
Math.abs(standardSpeed));
|
||||||
|
return minimumAbsVelocity * (int) Math.signum(viewSizeOutOfBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source,
|
||||||
|
RecyclerView.ViewHolder target) {
|
||||||
|
if (source.getItemViewType() != target.getItemViewType() ||
|
||||||
|
instanceListAdapter == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int sourceIndex = source.getAdapterPosition();
|
||||||
|
final int targetIndex = target.getAdapterPosition();
|
||||||
|
instanceListAdapter.swapItems(sourceIndex, targetIndex);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLongPressDragEnabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isItemViewSwipeEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
|
||||||
|
int position = viewHolder.getAdapterPosition();
|
||||||
|
// do not allow swiping the selected instance
|
||||||
|
if(instanceList.get(position).getUrl().equals(selectedInstance.getUrl())) {
|
||||||
|
instanceListAdapter.notifyItemChanged(position);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
instanceList.remove(position);
|
||||||
|
instanceListAdapter.notifyItemRemoved(position);
|
||||||
|
|
||||||
|
if (instanceList.isEmpty()) {
|
||||||
|
instanceList.add(selectedInstance);
|
||||||
|
instanceListAdapter.notifyItemInserted(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,6 +31,12 @@ public class KioskTranslator {
|
||||||
return c.getString(R.string.top_50);
|
return c.getString(R.string.top_50);
|
||||||
case "New & hot":
|
case "New & hot":
|
||||||
return c.getString(R.string.new_and_hot);
|
return c.getString(R.string.new_and_hot);
|
||||||
|
case "Local":
|
||||||
|
return c.getString(R.string.local);
|
||||||
|
case "Recently added":
|
||||||
|
return c.getString(R.string.recently_added);
|
||||||
|
case "Most liked":
|
||||||
|
return c.getString(R.string.most_liked);
|
||||||
case "conferences":
|
case "conferences":
|
||||||
return c.getString(R.string.conferences);
|
return c.getString(R.string.conferences);
|
||||||
default:
|
default:
|
||||||
|
@ -46,6 +52,12 @@ public class KioskTranslator {
|
||||||
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_hot);
|
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_hot);
|
||||||
case "New & hot":
|
case "New & hot":
|
||||||
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_hot);
|
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_hot);
|
||||||
|
case "Local":
|
||||||
|
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_kiosk_local);
|
||||||
|
case "Recently added":
|
||||||
|
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_kiosk_recent);
|
||||||
|
case "Most liked":
|
||||||
|
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.thumbs_up);
|
||||||
case "conferences":
|
case "conferences":
|
||||||
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_hot);
|
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_hot);
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
package org.schabi.newpipe.util;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import com.grack.nanojson.JsonArray;
|
||||||
|
import com.grack.nanojson.JsonObject;
|
||||||
|
import com.grack.nanojson.JsonParser;
|
||||||
|
import com.grack.nanojson.JsonParserException;
|
||||||
|
import com.grack.nanojson.JsonStringWriter;
|
||||||
|
import com.grack.nanojson.JsonWriter;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.extractor.ServiceList;
|
||||||
|
import org.schabi.newpipe.extractor.services.peertube.PeertubeInstance;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PeertubeHelper {
|
||||||
|
|
||||||
|
public static List<PeertubeInstance> getInstanceList(Context context) {
|
||||||
|
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
String savedInstanceListKey = context.getString(R.string.peertube_instance_list_key);
|
||||||
|
final String savedJson = sharedPreferences.getString(savedInstanceListKey, null);
|
||||||
|
if (null == savedJson) {
|
||||||
|
return Collections.singletonList(getCurrentInstance());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
JsonArray array = JsonParser.object().from(savedJson).getArray("instances");
|
||||||
|
List<PeertubeInstance> result = new ArrayList<>();
|
||||||
|
for (Object o : array) {
|
||||||
|
if (o instanceof JsonObject) {
|
||||||
|
JsonObject instance = (JsonObject) o;
|
||||||
|
String name = instance.getString("name");
|
||||||
|
String url = instance.getString("url");
|
||||||
|
result.add(new PeertubeInstance(url, name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} catch (JsonParserException e) {
|
||||||
|
return Collections.singletonList(getCurrentInstance());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PeertubeInstance selectInstance(PeertubeInstance instance, Context context) {
|
||||||
|
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
String selectedInstanceKey = context.getString(R.string.peertube_selected_instance_key);
|
||||||
|
JsonStringWriter jsonWriter = JsonWriter.string().object();
|
||||||
|
jsonWriter.value("name", instance.getName());
|
||||||
|
jsonWriter.value("url", instance.getUrl());
|
||||||
|
String jsonToSave = jsonWriter.end().done();
|
||||||
|
sharedPreferences.edit().putString(selectedInstanceKey, jsonToSave).apply();
|
||||||
|
ServiceList.PeerTube.setInstance(instance);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PeertubeInstance getCurrentInstance(){
|
||||||
|
return ServiceList.PeerTube.getInstance();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +1,22 @@
|
||||||
package org.schabi.newpipe.util;
|
package org.schabi.newpipe.util;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
|
||||||
import androidx.annotation.DrawableRes;
|
import androidx.annotation.DrawableRes;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
|
|
||||||
|
import com.grack.nanojson.JsonObject;
|
||||||
|
import com.grack.nanojson.JsonParser;
|
||||||
|
import com.grack.nanojson.JsonParserException;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.extractor.ServiceList;
|
import org.schabi.newpipe.extractor.ServiceList;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
|
import org.schabi.newpipe.extractor.services.peertube.PeertubeInstance;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@ -27,13 +34,15 @@ public class ServiceHelper {
|
||||||
return R.drawable.place_holder_cloud;
|
return R.drawable.place_holder_cloud;
|
||||||
case 2:
|
case 2:
|
||||||
return R.drawable.place_holder_gadse;
|
return R.drawable.place_holder_gadse;
|
||||||
|
case 3:
|
||||||
|
return R.drawable.place_holder_peertube;
|
||||||
default:
|
default:
|
||||||
return R.drawable.place_holder_circle;
|
return R.drawable.place_holder_circle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getTranslatedFilterString(String filter, Context c) {
|
public static String getTranslatedFilterString(String filter, Context c) {
|
||||||
switch(filter) {
|
switch (filter) {
|
||||||
case "all": return c.getString(R.string.all);
|
case "all": return c.getString(R.string.all);
|
||||||
case "videos": return c.getString(R.string.videos);
|
case "videos": return c.getString(R.string.videos);
|
||||||
case "channels": return c.getString(R.string.channels);
|
case "channels": return c.getString(R.string.channels);
|
||||||
|
@ -126,9 +135,36 @@ public class ServiceHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isBeta(final StreamingService s) {
|
public static boolean isBeta(final StreamingService s) {
|
||||||
switch(s.getServiceInfo().getName()) {
|
switch (s.getServiceInfo().getName()) {
|
||||||
case "YouTube": return false;
|
case "YouTube": return false;
|
||||||
default: return true;
|
default: return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void initService(Context context, int serviceId) {
|
||||||
|
if (serviceId == ServiceList.PeerTube.getServiceId()) {
|
||||||
|
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
String json = sharedPreferences.getString(context.getString(R.string.peertube_selected_instance_key), null);
|
||||||
|
if (null == json) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObject jsonObject = null;
|
||||||
|
try {
|
||||||
|
jsonObject = JsonParser.object().from(json);
|
||||||
|
} catch (JsonParserException e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String name = jsonObject.getString("name");
|
||||||
|
String url = jsonObject.getString("url");
|
||||||
|
PeertubeInstance instance = new PeertubeInstance(url, name);
|
||||||
|
ServiceList.PeerTube.setInstance(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void initServices(Context context) {
|
||||||
|
for (StreamingService s : ServiceList.all()) {
|
||||||
|
initService(context, s.getServiceId());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
After Width: | Height: | Size: 208 B |
After Width: | Height: | Size: 206 B |
After Width: | Height: | Size: 475 B |
After Width: | Height: | Size: 460 B |
After Width: | Height: | Size: 166 B |
After Width: | Height: | Size: 166 B |
After Width: | Height: | Size: 311 B |
After Width: | Height: | Size: 312 B |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 235 B |
After Width: | Height: | Size: 226 B |
After Width: | Height: | Size: 597 B |
After Width: | Height: | Size: 588 B |
After Width: | Height: | Size: 291 B |
After Width: | Height: | Size: 284 B |
After Width: | Height: | Size: 856 B |
After Width: | Height: | Size: 835 B |
After Width: | Height: | Size: 344 B |
After Width: | Height: | Size: 345 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.1 KiB |
|
@ -47,15 +47,22 @@
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/drawer_header_service_view"
|
android:id="@+id/drawer_header_service_view"
|
||||||
android:layout_width="100dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="100dp"
|
android:layout_height="100dp"
|
||||||
android:layout_alignLeft="@id/drawer_header_np_text_view"
|
android:layout_alignLeft="@id/drawer_header_np_text_view"
|
||||||
android:layout_alignStart="@id/drawer_header_np_text_view"
|
android:layout_alignStart="@id/drawer_header_np_text_view"
|
||||||
android:layout_below="@id/drawer_header_np_text_view"
|
android:layout_below="@id/drawer_header_np_text_view"
|
||||||
|
android:layout_toLeftOf="@id/drawer_arrow"
|
||||||
|
android:layout_marginRight="5dp"
|
||||||
android:text="YouTube"
|
android:text="YouTube"
|
||||||
android:textSize="18sp"
|
android:textSize="18sp"
|
||||||
android:textColor="@color/drawer_header_font_color"
|
android:textColor="@color/drawer_header_font_color"
|
||||||
android:textStyle="italic" />
|
android:textStyle="italic"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:fadingEdge="horizontal"
|
||||||
|
android:marqueeRepeatLimit="marquee_forever"
|
||||||
|
android:scrollHorizontally="true"
|
||||||
|
android:singleLine="true" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/drawer_arrow"
|
android:id="@+id/drawer_arrow"
|
||||||
|
|
|
@ -46,15 +46,22 @@ android:focusable="true">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/drawer_header_service_view"
|
android:id="@+id/drawer_header_service_view"
|
||||||
android:layout_width="100dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="100dp"
|
android:layout_height="100dp"
|
||||||
android:text="YouTube"
|
android:text="YouTube"
|
||||||
android:layout_below="@id/drawer_header_np_text_view"
|
android:layout_below="@id/drawer_header_np_text_view"
|
||||||
android:layout_alignLeft="@id/drawer_header_np_text_view"
|
android:layout_alignLeft="@id/drawer_header_np_text_view"
|
||||||
android:layout_alignStart="@id/drawer_header_np_text_view"
|
android:layout_alignStart="@id/drawer_header_np_text_view"
|
||||||
|
android:layout_toLeftOf="@id/drawer_arrow"
|
||||||
|
android:layout_marginRight="5dp"
|
||||||
android:textSize="18sp"
|
android:textSize="18sp"
|
||||||
android:textColor="@color/drawer_header_font_color"
|
android:textColor="@color/drawer_header_font_color"
|
||||||
android:textStyle="italic"/>
|
android:textStyle="italic"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:fadingEdge="horizontal"
|
||||||
|
android:marqueeRepeatLimit="marquee_forever"
|
||||||
|
android:scrollHorizontally="true"
|
||||||
|
android:singleLine="true" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/drawer_arrow"
|
android:id="@+id/drawer_arrow"
|
||||||
|
|
|
@ -16,7 +16,8 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:elevation="0dp"
|
app:elevation="0dp"
|
||||||
android:background="?attr/android:windowBackground"
|
android:background="?attr/android:windowBackground"
|
||||||
app:headerLayout="@layout/drawer_header"/>
|
app:headerLayout="@layout/drawer_header"
|
||||||
|
android:theme="@style/NavViewTextStyle"/>
|
||||||
<!-- app:menu="@menu/drawer_items" -->
|
<!-- app:menu="@menu/drawer_items" -->
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/instanceHelpTV"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="15dp"
|
||||||
|
android:autoLink="web"
|
||||||
|
android:text="@string/peertube_instance_url_help"/>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/instances"
|
||||||
|
android:layout_below="@id/instanceHelpTV"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:listitem="@layout/item_instance" />
|
||||||
|
|
||||||
|
<!-- LOADING INDICATOR-->
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/loading_progress_bar"
|
||||||
|
style="@style/Widget.AppCompat.ProgressBar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:indeterminate="true"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/addInstanceButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:layout_alignParentBottom="true"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
app:backgroundTint="?attr/colorPrimary"
|
||||||
|
app:fabSize="auto"
|
||||||
|
app:srcCompat="?attr/ic_add" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@android:id/text1"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:maxLength="0" />
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Spinner xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/spinner"
|
||||||
|
tools:listitem="@layout/instance_spinner_item"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="end"
|
||||||
|
android:prompt="@string/choose_instance_prompt" />
|
|
@ -0,0 +1,83 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/layoutCard"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="3dp"
|
||||||
|
android:layout_marginLeft="5dp"
|
||||||
|
android:layout_marginRight="5dp"
|
||||||
|
android:layout_marginTop="3dp"
|
||||||
|
android:minHeight="?listPreferredItemHeightSmall"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:cardCornerRadius="5dp"
|
||||||
|
app:cardElevation="4dp">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center_vertical">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/instanceIcon"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:layout_marginLeft="10dp"
|
||||||
|
tools:ignore="ContentDescription,RtlHardcoded"
|
||||||
|
tools:src="@drawable/place_holder_peertube"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/instanceName"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="10dp"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:layout_toRightOf="@+id/instanceIcon"
|
||||||
|
android:layout_toLeftOf="@id/selectInstanceRB"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:textAppearance="?textAppearanceListItem"
|
||||||
|
tools:ignore="RtlHardcoded"
|
||||||
|
tools:text="Framatube"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/instanceUrl"
|
||||||
|
android:autoLink="web"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="6dp"
|
||||||
|
android:layout_marginLeft="10dp"
|
||||||
|
android:layout_toRightOf="@id/instanceIcon"
|
||||||
|
android:layout_toLeftOf="@id/selectInstanceRB"
|
||||||
|
android:layout_below="@id/instanceName"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:textAppearance="?textAppearanceListItemSecondary"
|
||||||
|
tools:ignore="RtlHardcoded"
|
||||||
|
tools:text="https://framatube.org"/>
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/selectInstanceRB"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_toLeftOf="@id/handle"
|
||||||
|
android:layout_centerVertical="true"/>
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/handle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:paddingBottom="12dp"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingRight="10dp"
|
||||||
|
android:paddingTop="12dp"
|
||||||
|
android:src="?attr/drag_handle"
|
||||||
|
tools:ignore="ContentDescription,RtlHardcoded"/>
|
||||||
|
</RelativeLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
|
@ -31,6 +31,25 @@
|
||||||
<item name="colorAccent">@color/dark_soundcloud_accent_color</item>
|
<item name="colorAccent">@color/dark_soundcloud_accent_color</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<!-- PeerTube -->
|
||||||
|
<style name="LightTheme.PeerTube" parent="LightTheme.Switchable">
|
||||||
|
<item name="colorPrimary">@color/light_peertube_primary_color</item>
|
||||||
|
<item name="colorPrimaryDark">@color/light_peertube_dark_color</item>
|
||||||
|
<item name="colorAccent">@color/light_peertube_accent_color</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="DarkTheme.PeerTube" parent="DarkTheme.Switchable">
|
||||||
|
<item name="colorPrimary">@color/dark_peertube_primary_color</item>
|
||||||
|
<item name="colorPrimaryDark">@color/dark_peertube_dark_color</item>
|
||||||
|
<item name="colorAccent">@color/dark_peertube_accent_color</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="BlackTheme.PeerTube" parent="BlackTheme.Switchable">
|
||||||
|
<item name="colorPrimary">@color/dark_peertube_primary_color</item>
|
||||||
|
<item name="colorPrimaryDark">@color/dark_peertube_dark_color</item>
|
||||||
|
<item name="colorAccent">@color/dark_peertube_accent_color</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<!-- Media.ccc -->
|
<!-- Media.ccc -->
|
||||||
<style name="LightTheme.MediaCCC" parent="LightTheme.Switchable">
|
<style name="LightTheme.MediaCCC" parent="LightTheme.Switchable">
|
||||||
<item name="colorPrimary">@color/light_media_ccc_primary_color</item>
|
<item name="colorPrimary">@color/light_media_ccc_primary_color</item>
|
||||||
|
@ -49,4 +68,5 @@
|
||||||
<item name="colorPrimaryDark">@color/dark_media_ccc_statusbar_color</item>
|
<item name="colorPrimaryDark">@color/dark_media_ccc_statusbar_color</item>
|
||||||
<item name="colorAccent">@color/dark_media_ccc_accent_color</item>
|
<item name="colorAccent">@color/dark_media_ccc_accent_color</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
|
@ -29,6 +29,8 @@
|
||||||
<attr name="bug" format="reference"/>
|
<attr name="bug" format="reference"/>
|
||||||
<attr name="settings" format="reference"/>
|
<attr name="settings" format="reference"/>
|
||||||
<attr name="ic_hot" format="reference"/>
|
<attr name="ic_hot" format="reference"/>
|
||||||
|
<attr name="ic_kiosk_local" format="reference"/>
|
||||||
|
<attr name="ic_kiosk_recent" format="reference"/>
|
||||||
<attr name="ic_channel" format="reference"/>
|
<attr name="ic_channel" format="reference"/>
|
||||||
<attr name="ic_bookmark" format="reference"/>
|
<attr name="ic_bookmark" format="reference"/>
|
||||||
<attr name="ic_playlist_add" format="reference"/>
|
<attr name="ic_playlist_add" format="reference"/>
|
||||||
|
|
|
@ -22,6 +22,17 @@
|
||||||
<color name="dark_soundcloud_accent_color">#FFFFFF</color>
|
<color name="dark_soundcloud_accent_color">#FFFFFF</color>
|
||||||
<color name="dark_soundcloud_statusbar_color">#ff9100</color>
|
<color name="dark_soundcloud_statusbar_color">#ff9100</color>
|
||||||
|
|
||||||
|
<!-- PeerTube -->
|
||||||
|
<color name="light_peertube_primary_color">#ff6f00</color>
|
||||||
|
<color name="light_peertube_dark_color">#c43e00</color>
|
||||||
|
<color name="light_peertube_accent_color">#000000</color>
|
||||||
|
<color name="light_peertube_statusbar_color">#ff833a</color>
|
||||||
|
|
||||||
|
<color name="dark_peertube_primary_color">#ff6f00</color>
|
||||||
|
<color name="dark_peertube_dark_color">#c43e00</color>
|
||||||
|
<color name="dark_peertube_accent_color">#FFFFFF</color>
|
||||||
|
<color name="dark_peertube_statusbar_color">#ff833a</color>
|
||||||
|
|
||||||
<!-- Media.CCC -->
|
<!-- Media.CCC -->
|
||||||
<color name="light_media_ccc_primary_color">#9e9e9e</color>
|
<color name="light_media_ccc_primary_color">#9e9e9e</color>
|
||||||
<color name="light_media_ccc_dark_color">#616161</color>
|
<color name="light_media_ccc_dark_color">#616161</color>
|
||||||
|
|
|
@ -145,6 +145,9 @@
|
||||||
<string name="default_language_value">en</string>
|
<string name="default_language_value">en</string>
|
||||||
<string name="default_country_value">GB</string>
|
<string name="default_country_value">GB</string>
|
||||||
<string name="content_language_key" translatable="false">content_language</string>
|
<string name="content_language_key" translatable="false">content_language</string>
|
||||||
|
<string name="peertube_instance_setup_key" translatable="false">peertube_instance_setup</string>
|
||||||
|
<string name="peertube_selected_instance_key" translatable="false">peertube_selected_instance</string>
|
||||||
|
<string name="peertube_instance_list_key" translatable="false">peertube_instance_list</string>
|
||||||
<string name="content_country_key" translatable="false">content_country</string>
|
<string name="content_country_key" translatable="false">content_country</string>
|
||||||
<string name="show_age_restricted_content" translatable="false">show_age_restricted_content</string>
|
<string name="show_age_restricted_content" translatable="false">show_age_restricted_content</string>
|
||||||
<string name="use_tor_key" translatable="false">use_tor</string>
|
<string name="use_tor_key" translatable="false">use_tor</string>
|
||||||
|
|
|
@ -109,6 +109,14 @@
|
||||||
<string name="default_content_country_title">Default content country</string>
|
<string name="default_content_country_title">Default content country</string>
|
||||||
<string name="service_title">Service</string>
|
<string name="service_title">Service</string>
|
||||||
<string name="content_language_title">Default content language</string>
|
<string name="content_language_title">Default content language</string>
|
||||||
|
<string name="peertube_instance_url_title">PeerTube instances</string>
|
||||||
|
<string name="peertube_instance_url_summary">Set your favorite peertube instances</string>
|
||||||
|
<string name="peertube_instance_url_help">Find the instances that best suit you on https://joinpeertube.org/instances#instances-list</string>
|
||||||
|
<string name="peertube_instance_add_title">Add instance</string>
|
||||||
|
<string name="peertube_instance_add_help">Enter instance url</string>
|
||||||
|
<string name="peertube_instance_add_fail">Failed to validate instance</string>
|
||||||
|
<string name="peertube_instance_add_https_only">Only https urls are supported</string>
|
||||||
|
<string name="peertube_instance_add_exists">Instance already exists</string>
|
||||||
<string name="settings_category_player_title">Player</string>
|
<string name="settings_category_player_title">Player</string>
|
||||||
<string name="settings_category_player_behavior_title">Behavior</string>
|
<string name="settings_category_player_behavior_title">Behavior</string>
|
||||||
<string name="settings_category_video_audio_title">Video & audio</string>
|
<string name="settings_category_video_audio_title">Video & audio</string>
|
||||||
|
@ -400,6 +408,9 @@
|
||||||
<string name="trending">Trending</string>
|
<string name="trending">Trending</string>
|
||||||
<string name="top_50">Top 50</string>
|
<string name="top_50">Top 50</string>
|
||||||
<string name="new_and_hot">New & hot</string>
|
<string name="new_and_hot">New & hot</string>
|
||||||
|
<string name="local">Local</string>
|
||||||
|
<string name="recently_added">Recently added</string>
|
||||||
|
<string name="most_liked">Most liked</string>
|
||||||
<string name="conferences">Conferences</string>
|
<string name="conferences">Conferences</string>
|
||||||
<string name="service_kiosk_string" translatable="false">%1$s/%2$s</string>
|
<string name="service_kiosk_string" translatable="false">%1$s/%2$s</string>
|
||||||
<!-- Play Queue -->
|
<!-- Play Queue -->
|
||||||
|
@ -576,4 +587,6 @@
|
||||||
<string name="downloads_storage_ask_summary_kitkat">You will be asked where to save each download.\nChoose SAF if you want to download to an external SD card</string>
|
<string name="downloads_storage_ask_summary_kitkat">You will be asked where to save each download.\nChoose SAF if you want to download to an external SD card</string>
|
||||||
<string name="downloads_storage_use_saf_title">Use SAF</string>
|
<string name="downloads_storage_use_saf_title">Use SAF</string>
|
||||||
<string name="downloads_storage_use_saf_summary">The Storage Access Framework allows downloads to an external SD card.\nNote: some devices are not compatible</string>
|
<string name="downloads_storage_use_saf_summary">The Storage Access Framework allows downloads to an external SD card.\nNote: some devices are not compatible</string>
|
||||||
|
<string name="choose_instance_prompt">Choose an instance</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -44,6 +44,8 @@
|
||||||
<item name="pause">@drawable/ic_pause_black_24dp</item>
|
<item name="pause">@drawable/ic_pause_black_24dp</item>
|
||||||
<item name="settings">@drawable/ic_settings_black_24dp</item>
|
<item name="settings">@drawable/ic_settings_black_24dp</item>
|
||||||
<item name="ic_hot">@drawable/ic_whatshot_black_24dp</item>
|
<item name="ic_hot">@drawable/ic_whatshot_black_24dp</item>
|
||||||
|
<item name="ic_kiosk_local">@drawable/ic_kiosk_local_black_24dp</item>
|
||||||
|
<item name="ic_kiosk_recent">@drawable/ic_kiosk_recent_black_24dp</item>
|
||||||
<item name="ic_channel">@drawable/ic_channel_black_24dp</item>
|
<item name="ic_channel">@drawable/ic_channel_black_24dp</item>
|
||||||
<item name="ic_bookmark">@drawable/ic_bookmark_black_24dp</item>
|
<item name="ic_bookmark">@drawable/ic_bookmark_black_24dp</item>
|
||||||
<item name="ic_playlist_add">@drawable/ic_playlist_add_black_24dp</item>
|
<item name="ic_playlist_add">@drawable/ic_playlist_add_black_24dp</item>
|
||||||
|
@ -108,6 +110,8 @@
|
||||||
<item name="play">@drawable/ic_play_arrow_white_24dp</item>
|
<item name="play">@drawable/ic_play_arrow_white_24dp</item>
|
||||||
<item name="settings">@drawable/ic_settings_white_24dp</item>
|
<item name="settings">@drawable/ic_settings_white_24dp</item>
|
||||||
<item name="ic_hot">@drawable/ic_whatshot_white_24dp</item>
|
<item name="ic_hot">@drawable/ic_whatshot_white_24dp</item>
|
||||||
|
<item name="ic_kiosk_local">@drawable/ic_kiosk_local_white_24dp</item>
|
||||||
|
<item name="ic_kiosk_recent">@drawable/ic_kiosk_recent_white_24dp</item>
|
||||||
<item name="ic_channel">@drawable/ic_channel_white_24dp</item>
|
<item name="ic_channel">@drawable/ic_channel_white_24dp</item>
|
||||||
<item name="ic_bookmark">@drawable/ic_bookmark_white_24dp</item>
|
<item name="ic_bookmark">@drawable/ic_bookmark_white_24dp</item>
|
||||||
<item name="ic_playlist_add">@drawable/ic_playlist_add_white_24dp</item>
|
<item name="ic_playlist_add">@drawable/ic_playlist_add_white_24dp</item>
|
||||||
|
@ -233,4 +237,8 @@
|
||||||
<item name="android:windowIsTranslucent">true</item>
|
<item name="android:windowIsTranslucent">true</item>
|
||||||
<item name="android:windowAnimationStyle">@null</item>
|
<item name="android:windowAnimationStyle">@null</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="NavViewTextStyle">
|
||||||
|
<item name="android:ellipsize">end</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -32,6 +32,25 @@
|
||||||
<item name="progress_horizontal_drawable">@drawable/progress_soundcloud_horizontal_dark</item>
|
<item name="progress_horizontal_drawable">@drawable/progress_soundcloud_horizontal_dark</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<!-- PeerTube -->
|
||||||
|
<style name="LightTheme.PeerTube" parent="LightTheme.Switchable">
|
||||||
|
<item name="colorPrimary">@color/light_peertube_primary_color</item>
|
||||||
|
<item name="colorPrimaryDark">@color/light_peertube_dark_color</item>
|
||||||
|
<item name="colorAccent">@color/light_peertube_accent_color</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="DarkTheme.PeerTube" parent="DarkTheme.Switchable">
|
||||||
|
<item name="colorPrimary">@color/dark_peertube_primary_color</item>
|
||||||
|
<item name="colorPrimaryDark">@color/dark_peertube_dark_color</item>
|
||||||
|
<item name="colorAccent">@color/dark_peertube_accent_color</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="BlackTheme.PeerTube" parent="BlackTheme.Switchable">
|
||||||
|
<item name="colorPrimary">@color/dark_peertube_primary_color</item>
|
||||||
|
<item name="colorPrimaryDark">@color/dark_peertube_dark_color</item>
|
||||||
|
<item name="colorAccent">@color/dark_peertube_accent_color</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<!-- Media.ccc -->
|
<!-- Media.ccc -->
|
||||||
<style name="LightTheme.MediaCCC" parent="LightTheme.Switchable">
|
<style name="LightTheme.MediaCCC" parent="LightTheme.Switchable">
|
||||||
<item name="colorPrimary">@color/light_media_ccc_primary_color</item>
|
<item name="colorPrimary">@color/light_media_ccc_primary_color</item>
|
||||||
|
|
|
@ -21,6 +21,13 @@
|
||||||
android:summary="%s"
|
android:summary="%s"
|
||||||
android:title="@string/default_content_country_title"/>
|
android:title="@string/default_content_country_title"/>
|
||||||
|
|
||||||
|
<PreferenceScreen
|
||||||
|
app:iconSpaceReserved="false"
|
||||||
|
android:fragment="org.schabi.newpipe.settings.PeertubeInstanceListFragment"
|
||||||
|
android:key="@string/peertube_instance_setup_key"
|
||||||
|
android:title="@string/peertube_instance_url_title"
|
||||||
|
android:summary="@string/peertube_instance_url_summary"/>
|
||||||
|
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
app:iconSpaceReserved="false"
|
app:iconSpaceReserved="false"
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
|
|