Merge branch 'tablet_ui' of https://github.com/nv95/NewPipe into tablet_ui

This commit is contained in:
Vasily 2018-08-29 09:00:15 +03:00
commit 8aef24be1e
82 changed files with 1941 additions and 867 deletions

View File

@ -49,12 +49,13 @@ ext {
icepickLibVersion = '3.2.0' icepickLibVersion = '3.2.0'
stethoLibVersion = '1.5.0' stethoLibVersion = '1.5.0'
} }
dependencies { dependencies {
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2') { androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2') {
exclude module: 'support-annotations' exclude module: 'support-annotations'
} }
implementation 'com.github.TeamNewPipe:NewPipeExtractor:1eff8c5708' implementation 'com.github.TeamNewPipe:NewPipeExtractor:fef71aeccc37'
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:2.8.9' testImplementation 'org.mockito:mockito-core:2.8.9'
@ -93,6 +94,9 @@ dependencies {
debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakCanaryLibVersion" debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakCanaryLibVersion"
releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$leakCanaryLibVersion" releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$leakCanaryLibVersion"
implementation "com.squareup.okhttp3:okhttp:$okHttpLibVersion" implementation "com.squareup.okhttp3:okhttp:$okHttpLibVersion"
debugImplementation "com.facebook.stetho:stetho-okhttp3:$stethoLibVersion" debugImplementation "com.facebook.stetho:stetho-okhttp3:$stethoLibVersion"
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
implementation 'com.android.support:cardview-v7:27.1.1'
} }

View File

@ -76,10 +76,6 @@
android:name=".about.AboutActivity" android:name=".about.AboutActivity"
android:label="@string/title_activity_about"/> android:label="@string/title_activity_about"/>
<activity
android:name=".history.HistoryActivity"
android:label="@string/title_activity_history"/>
<service android:name=".local.subscription.services.SubscriptionsImportService"/> <service android:name=".local.subscription.services.SubscriptionsImportService"/>
<service android:name=".local.subscription.services.SubscriptionsExportService"/> <service android:name=".local.subscription.services.SubscriptionsExportService"/>
@ -122,6 +118,7 @@
<activity <activity
android:name=".ReCaptchaActivity" android:name=".ReCaptchaActivity"
android:label="@string/reCaptchaActivity"/> android:label="@string/reCaptchaActivity"/>
<activity android:name=".download.ExtSDDownloadFailedActivity" />
<provider <provider
android:name="android.support.v4.content.FileProvider" android:name="android.support.v4.content.FileProvider"

View File

@ -4,6 +4,7 @@ import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
@ -11,7 +12,10 @@ import android.view.View;
import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoader;
import com.squareup.leakcanary.RefWatcher; import com.squareup.leakcanary.RefWatcher;
import org.schabi.newpipe.report.UserAction;
import icepick.Icepick; import icepick.Icepick;
import icepick.State;
public abstract class BaseFragment extends Fragment { public abstract class BaseFragment extends Fragment {
protected final String TAG = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()); protected final String TAG = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode());
@ -20,6 +24,15 @@ public abstract class BaseFragment extends Fragment {
protected AppCompatActivity activity; protected AppCompatActivity activity;
public static final ImageLoader imageLoader = ImageLoader.getInstance(); public static final ImageLoader imageLoader = ImageLoader.getInstance();
//These values are used for controlling framgents when they are part of the frontpage
@State
protected boolean useAsFrontPage = false;
protected boolean mIsVisibleToUser = false;
public void useAsFrontPage(boolean value) {
useAsFrontPage = value;
}
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Fragment's Lifecycle // Fragment's Lifecycle
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@ -72,6 +85,12 @@ public abstract class BaseFragment extends Fragment {
if (refWatcher != null) refWatcher.watch(this); if (refWatcher != null) refWatcher.watch(this);
} }
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
mIsVisibleToUser = isVisibleToUser;
}
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Init // Init
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@ -88,8 +107,15 @@ public abstract class BaseFragment extends Fragment {
public void setTitle(String title) { public void setTitle(String title) {
if (DEBUG) Log.d(TAG, "setTitle() called with: title = [" + title + "]"); if (DEBUG) Log.d(TAG, "setTitle() called with: title = [" + title + "]");
if (activity != null && activity.getSupportActionBar() != null) { if((!useAsFrontPage || mIsVisibleToUser)
&& (activity != null && activity.getSupportActionBar() != null)) {
activity.getSupportActionBar().setTitle(title); activity.getSupportActionBar().setTitle(title);
} }
} }
protected FragmentManager getFM() {
return getParentFragment() == null
? getFragmentManager()
: getParentFragment().getFragmentManager();
}
} }

View File

@ -24,6 +24,7 @@ import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
@ -43,18 +44,22 @@ 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.WindowManager;
import android.widget.Button; import android.widget.Button;
import android.widget.ImageButton; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
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;
import org.schabi.newpipe.fragments.list.search.SearchFragment; import org.schabi.newpipe.fragments.list.search.SearchFragment;
import org.schabi.newpipe.report.ErrorActivity; 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.NavigationHelper; import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PermissionHelper; import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.ServiceHelper; import org.schabi.newpipe.util.ServiceHelper;
@ -72,6 +77,19 @@ public class MainActivity extends AppCompatActivity {
private NavigationView drawerItems = null; private NavigationView drawerItems = null;
private TextView headerServiceView = null; private TextView headerServiceView = null;
private boolean servicesShown = false;
private ImageView serviceArrow;
private static final int ITEM_ID_SUBSCRIPTIONS = - 1;
private static final int ITEM_ID_FEED = - 2;
private static final int ITEM_ID_BOOKMARKS = - 3;
private static final int ITEM_ID_DOWNLOADS = - 4;
private static final int ITEM_ID_HISTORY = - 5;
private static final int ITEM_ID_SETTINGS = 0;
private static final int ITEM_ID_ABOUT = 1;
private static final int ORDER = 0;
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Activity's LifeCycle // Activity's LifeCycle
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@ -85,42 +103,66 @@ public class MainActivity extends AppCompatActivity {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window w = getWindow();
w.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
if (getSupportFragmentManager() != null && getSupportFragmentManager().getBackStackEntryCount() == 0) { if (getSupportFragmentManager() != null && getSupportFragmentManager().getBackStackEntryCount() == 0) {
initFragments(); initFragments();
} }
setSupportActionBar(findViewById(R.id.toolbar)); setSupportActionBar(findViewById(R.id.toolbar));
setupDrawer(); try {
setupDrawer();
} catch (Exception e) {
ErrorActivity.reportUiError(this, e);
}
} }
private void setupDrawer() { private void setupDrawer() throws Exception {
final Toolbar toolbar = findViewById(R.id.toolbar); final Toolbar toolbar = findViewById(R.id.toolbar);
drawer = findViewById(R.id.drawer_layout); drawer = findViewById(R.id.drawer_layout);
drawerItems = findViewById(R.id.navigation); drawerItems = findViewById(R.id.navigation);
for(StreamingService s : NewPipe.getServices()) { //Tabs
final String title = s.getServiceInfo().getName() + int currentServiceId = ServiceHelper.getSelectedServiceId(this);
(ServiceHelper.isBeta(s) ? " (beta)" : ""); StreamingService service = NewPipe.getService(currentServiceId);
final MenuItem item = drawerItems.getMenu()
.add(R.id.menu_services_group, s.getServiceId(), 0, title); int kioskId = 0;
item.setIcon(ServiceHelper.getIcon(s.getServiceId()));
for (final String ks : service.getKioskList().getAvailableKiosks()) {
drawerItems.getMenu()
.add(R.id.menu_tabs_group, kioskId, 0, KioskTranslator.getTranslatedKioskName(ks, this))
.setIcon(KioskTranslator.getKioskIcons(ks, this));
kioskId ++;
} }
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(true); drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_SUBSCRIPTIONS, ORDER, R.string.tab_subscriptions)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_channel));
drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_FEED, ORDER, R.string.fragment_whats_new)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.rss));
drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_BOOKMARKS, ORDER, R.string.tab_bookmarks)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_bookmark));
drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_DOWNLOADS, ORDER, R.string.downloads)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.download));
drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_HISTORY, ORDER, R.string.action_history)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.history));
toggle = new ActionBarDrawerToggle(this, drawer, toolbar, //Settings and About
R.string.drawer_open, R.string.drawer_close) { drawerItems.getMenu()
@Override .add(R.id.menu_options_about_group, ITEM_ID_SETTINGS, ORDER, R.string.settings)
public void onDrawerClosed(View view) { super.onDrawerClosed(view); } .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.settings));
drawerItems.getMenu()
.add(R.id.menu_options_about_group, ITEM_ID_ABOUT, ORDER, R.string.tab_about)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.info));
@Override toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.drawer_open, R.string.drawer_close);
public void onDrawerOpened(View drawerView) { super.onDrawerOpened(drawerView); }
@Override
public void onDrawerSlide(View drawerView, float slideOffset) {
super.onDrawerSlide(drawerView, 0);
}
};
toggle.syncState(); toggle.syncState();
drawer.addDrawerListener(toggle); drawer.addDrawerListener(toggle);
drawer.addDrawerListener(new DrawerLayout.SimpleDrawerListener() { drawer.addDrawerListener(new DrawerLayout.SimpleDrawerListener() {
@ -133,51 +175,179 @@ public class MainActivity extends AppCompatActivity {
@Override @Override
public void onDrawerClosed(View drawerView) { public void onDrawerClosed(View drawerView) {
if(servicesShown) {
toggleServices();
}
if (lastService != ServiceHelper.getSelectedServiceId(MainActivity.this)) { if (lastService != ServiceHelper.getSelectedServiceId(MainActivity.this)) {
new Handler(Looper.getMainLooper()).post(MainActivity.this::recreate); new Handler(Looper.getMainLooper()).post(MainActivity.this::recreate);
} }
} }
}); });
drawerItems.setNavigationItemSelectedListener(this::changeService); drawerItems.setNavigationItemSelectedListener(this::drawerItemSelected);
setupDrawerFooter();
setupDrawerHeader(); setupDrawerHeader();
} }
private boolean drawerItemSelected(MenuItem item) {
switch (item.getGroupId()) {
case R.id.menu_services_group:
changeService(item);
break;
case R.id.menu_tabs_group:
try {
tabSelected(item);
} catch (Exception e) {
ErrorActivity.reportUiError(this, e);
}
break;
case R.id.menu_options_about_group:
optionsAboutSelected(item);
break;
default:
return false;
}
private boolean changeService(MenuItem item) {
if (item.getGroupId() != R.id.menu_services_group)
return false;
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(false);
ServiceHelper.setSelectedServiceId(this, item.getItemId());
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(true);
drawer.closeDrawers(); drawer.closeDrawers();
return true; return true;
} }
private void setupDrawerFooter() { private void changeService(MenuItem item) {
ImageButton settings = findViewById(R.id.drawer_settings); drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(false);
ImageButton downloads = findViewById(R.id.drawer_downloads); ServiceHelper.setSelectedServiceId(this, item.getItemId());
ImageButton history = findViewById(R.id.drawer_history); drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(true);
}
settings.setOnClickListener(view -> NavigationHelper.openSettings(this)); private void tabSelected(MenuItem item) throws ExtractionException {
downloads.setOnClickListener(view ->NavigationHelper.openDownloads(this)); switch(item.getItemId()) {
history.setOnClickListener(view -> case ITEM_ID_SUBSCRIPTIONS:
NavigationHelper.openStatisticFragment(getSupportFragmentManager())); NavigationHelper.openSubscriptionFragment(getSupportFragmentManager());
break;
case ITEM_ID_FEED:
NavigationHelper.openWhatsNewFragment(getSupportFragmentManager());
break;
case ITEM_ID_BOOKMARKS:
NavigationHelper.openBookmarksFragment(getSupportFragmentManager());
break;
case ITEM_ID_DOWNLOADS:
NavigationHelper.openDownloads(this);
break;
case ITEM_ID_HISTORY:
NavigationHelper.openStatisticFragment(getSupportFragmentManager());
break;
default:
int currentServiceId = ServiceHelper.getSelectedServiceId(this);
StreamingService service = NewPipe.getService(currentServiceId);
String serviceName = "";
int kioskId = 0;
for (final String ks : service.getKioskList().getAvailableKiosks()) {
if(kioskId == item.getItemId()) {
serviceName = ks;
}
kioskId ++;
}
NavigationHelper.openKioskFragment(getSupportFragmentManager(), currentServiceId, serviceName);
break;
}
}
private void optionsAboutSelected(MenuItem item) {
switch(item.getItemId()) {
case ITEM_ID_SETTINGS:
NavigationHelper.openSettings(this);
break;
case ITEM_ID_ABOUT:
NavigationHelper.openAbout(this);
break;
}
} }
private void setupDrawerHeader() { private void setupDrawerHeader() {
headerServiceView = findViewById(R.id.drawer_header_service_view); NavigationView navigationView = findViewById(R.id.navigation);
Button action = findViewById(R.id.drawer_header_action_button); View hView = navigationView.getHeaderView(0);
serviceArrow = hView.findViewById(R.id.drawer_arrow);
headerServiceView = hView.findViewById(R.id.drawer_header_service_view);
Button action = hView.findViewById(R.id.drawer_header_action_button);
action.setOnClickListener(view -> { action.setOnClickListener(view -> {
Intent intent = new Intent(Intent.ACTION_VIEW); toggleServices();
intent.setData(Uri.parse("https://newpipe.schabi.org/blog/"));
startActivity(intent);
drawer.closeDrawers();
}); });
} }
private void toggleServices() {
servicesShown = !servicesShown;
drawerItems.getMenu().removeGroup(R.id.menu_services_group);
drawerItems.getMenu().removeGroup(R.id.menu_tabs_group);
drawerItems.getMenu().removeGroup(R.id.menu_options_about_group);
if(servicesShown) {
showServices();
} else {
try {
showTabs();
} catch (Exception e) {
ErrorActivity.reportUiError(this, e);
}
}
}
private void showServices() {
serviceArrow.setImageResource(R.drawable.ic_arrow_up_white);
for(StreamingService s : NewPipe.getServices()) {
final String title = s.getServiceInfo().getName() +
(ServiceHelper.isBeta(s) ? " (beta)" : "");
drawerItems.getMenu()
.add(R.id.menu_services_group, s.getServiceId(), ORDER, title)
.setIcon(ServiceHelper.getIcon(s.getServiceId()));
}
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(true);
}
private void showTabs() throws ExtractionException {
serviceArrow.setImageResource(R.drawable.ic_arrow_down_white);
//Tabs
int currentServiceId = ServiceHelper.getSelectedServiceId(this);
StreamingService service = NewPipe.getService(currentServiceId);
int kioskId = 0;
for (final String ks : service.getKioskList().getAvailableKiosks()) {
drawerItems.getMenu()
.add(R.id.menu_tabs_group, kioskId, ORDER, KioskTranslator.getTranslatedKioskName(ks, this))
.setIcon(KioskTranslator.getKioskIcons(ks, this));
kioskId ++;
}
drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_SUBSCRIPTIONS, ORDER, R.string.tab_subscriptions)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_channel));
drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_FEED, ORDER, R.string.fragment_whats_new)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.rss));
drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_BOOKMARKS, ORDER, R.string.tab_bookmarks)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_bookmark));
drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_DOWNLOADS, ORDER, R.string.downloads)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.download));
drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_HISTORY, ORDER, R.string.action_history)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.history));
//Settings and About
drawerItems.getMenu()
.add(R.id.menu_options_about_group, ITEM_ID_SETTINGS, ORDER, R.string.settings)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.settings));
drawerItems.getMenu()
.add(R.id.menu_options_about_group, ITEM_ID_ABOUT, ORDER, R.string.tab_about)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.info));
}
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
@ -341,16 +511,13 @@ public class MainActivity extends AppCompatActivity {
onHomeButtonPressed(); onHomeButtonPressed();
return true; return true;
case R.id.action_show_downloads: case R.id.action_show_downloads:
return NavigationHelper.openDownloads(this); return NavigationHelper.openDownloads(this);
case R.id.action_history: case R.id.action_history:
NavigationHelper.openStatisticFragment(getSupportFragmentManager()); NavigationHelper.openStatisticFragment(getSupportFragmentManager());
return true; return true;
case R.id.action_about:
NavigationHelper.openAbout(this);
return true;
case R.id.action_settings: case R.id.action_settings:
NavigationHelper.openSettings(this); NavigationHelper.openSettings(this);
return true; return true;
default: default:
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }

View File

@ -0,0 +1,38 @@
package org.schabi.newpipe.download;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import org.schabi.newpipe.R;
import org.schabi.newpipe.settings.NewPipeSettings;
import org.schabi.newpipe.util.ServiceHelper;
import org.schabi.newpipe.util.ThemeHelper;
public class ExtSDDownloadFailedActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeHelper.setTheme(this, ServiceHelper.getSelectedServiceId(this));
}
@Override
protected void onStart() {
super.onStart();
new AlertDialog.Builder(this)
.setTitle(R.string.download_to_sdcard_error_title)
.setMessage(R.string.download_to_sdcard_error_message)
.setPositiveButton(R.string.yes, (DialogInterface dialogInterface, int i) -> {
NewPipeSettings.resetDownloadFolders(this);
finish();
})
.setNegativeButton(R.string.cancel, (DialogInterface dialogInterface, int i) -> {
dialogInterface.dismiss();
finish();
})
.create()
.show();
}
}

View File

@ -51,9 +51,6 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
protected Button errorButtonRetry; protected Button errorButtonRetry;
protected TextView errorTextView; protected TextView errorTextView;
@State
protected boolean useAsFrontPage = false;
@Override @Override
public void onViewCreated(View rootView, Bundle savedInstanceState) { public void onViewCreated(View rootView, Bundle savedInstanceState) {
super.onViewCreated(rootView, savedInstanceState); super.onViewCreated(rootView, savedInstanceState);
@ -66,9 +63,6 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
wasLoading.set(isLoading.get()); wasLoading.set(isLoading.get());
} }
public void useAsFrontPage(boolean value) {
useAsFrontPage = value;
}
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Init // Init
@ -93,12 +87,7 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
RxView.clicks(errorButtonRetry) RxView.clicks(errorButtonRetry)
.debounce(300, TimeUnit.MILLISECONDS) .debounce(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Object>() { .subscribe(o -> onRetryButtonClicked());
@Override
public void accept(Object o) throws Exception {
onRetryButtonClicked();
}
});
} }
protected void onRetryButtonClicked() { protected void onRetryButtonClicked() {

View File

@ -14,24 +14,16 @@ public class BlankFragment extends BaseFragment {
@Nullable @Nullable
@Override @Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
if(activity != null && activity.getSupportActionBar() != null) { setTitle("NewPipe");
activity.getSupportActionBar()
.setTitle("NewPipe");
}
return inflater.inflate(R.layout.fragment_blank, container, false); return inflater.inflate(R.layout.fragment_blank, container, false);
} }
@Override @Override
public void setUserVisibleHint(boolean isVisibleToUser) { public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser); super.setUserVisibleHint(isVisibleToUser);
if(isVisibleToUser) { setTitle("NewPipe");
if(activity != null && activity.getSupportActionBar() != null) { // leave this inline. Will make it harder for copy cats.
activity.getSupportActionBar() // If you are a Copy cat FUCK YOU.
.setTitle("NewPipe"); // I WILL FIND YOU, AND I WILL ...
}
// leave this inline. Will make it harder for copy cats.
// If you are a Copy cat FUCK YOU.
// I WILL FIND YOU, AND I WILL ...
}
} }
} }

View File

@ -1,5 +1,6 @@
package org.schabi.newpipe.fragments; package org.schabi.newpipe.fragments;
import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
@ -17,20 +18,16 @@ 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.SubMenu;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import org.schabi.newpipe.BaseFragment; import org.schabi.newpipe.BaseFragment;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.kiosk.KioskList;
import org.schabi.newpipe.fragments.list.channel.ChannelFragment; import org.schabi.newpipe.fragments.list.channel.ChannelFragment;
import org.schabi.newpipe.local.feed.FeedFragment;
import org.schabi.newpipe.fragments.list.kiosk.KioskFragment; import org.schabi.newpipe.fragments.list.kiosk.KioskFragment;
import org.schabi.newpipe.local.bookmark.BookmarkFragment; import org.schabi.newpipe.local.bookmark.BookmarkFragment;
import org.schabi.newpipe.local.feed.FeedFragment;
import org.schabi.newpipe.local.history.StatisticsPlaylistFragment;
import org.schabi.newpipe.local.subscription.SubscriptionFragment; import org.schabi.newpipe.local.subscription.SubscriptionFragment;
import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.report.UserAction;
@ -39,20 +36,29 @@ import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.ServiceHelper; import org.schabi.newpipe.util.ServiceHelper;
import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.util.ThemeHelper;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class MainFragment extends BaseFragment implements TabLayout.OnTabSelectedListener { public class MainFragment extends BaseFragment implements TabLayout.OnTabSelectedListener {
public int currentServiceId = -1; public int currentServiceId = -1;
private ViewPager viewPager; private ViewPager viewPager;
private List<String> tabs = new ArrayList<>();
static PagerAdapter adapter;
TabLayout tabLayout;
private SharedPreferences prefs;
private Bundle savedInstanceStateBundle;
/*////////////////////////////////////////////////////////////////////////// private static final String TAB_NUMBER_BLANK = "0";
// Constants private static final String TAB_NUMBER_KIOSK = "1";
//////////////////////////////////////////////////////////////////////////*/ private static final String TAB_NUMBER_SUBSCIRPTIONS = "2";
private static final String TAB_NUMBER_FEED = "3";
private static final String TAB_NUMBER_BOOKMARKS = "4";
private static final String TAB_NUMBER_HISTORY = "5";
private static final String TAB_NUMBER_CHANNEL = "6";
private static final int FALLBACK_SERVICE_ID = ServiceList.YouTube.getServiceId(); SharedPreferences.OnSharedPreferenceChangeListener listener;
private static final String FALLBACK_CHANNEL_URL = "https://www.youtube.com/channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ";
private static final String FALLBACK_CHANNEL_NAME = "Music";
private static final String FALLBACK_KIOSK_ID = "Trending";
private static final int KIOSK_MENU_OFFSET = 2000;
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Fragment's LifeCycle // Fragment's LifeCycle
@ -60,13 +66,23 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
savedInstanceStateBundle = savedInstanceState;
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setHasOptionsMenu(true); setHasOptionsMenu(true);
listener = (prefs, key) -> {
if(key.equals("saveUsedTabs")) {
mainPageChanged();
}
};
} }
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
currentServiceId = ServiceHelper.getSelectedServiceId(activity); currentServiceId = ServiceHelper.getSelectedServiceId(activity);
prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
prefs.registerOnSharedPreferenceChangeListener(listener);
return inflater.inflate(R.layout.fragment_main, container, false); return inflater.inflate(R.layout.fragment_main, container, false);
} }
@ -74,30 +90,116 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
protected void initViews(View rootView, Bundle savedInstanceState) { protected void initViews(View rootView, Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState); super.initViews(rootView, savedInstanceState);
TabLayout tabLayout = rootView.findViewById(R.id.main_tab_layout); tabLayout = rootView.findViewById(R.id.main_tab_layout);
viewPager = rootView.findViewById(R.id.pager); viewPager = rootView.findViewById(R.id.pager);
/* Nested fragment, use child fragment here to maintain backstack in view pager. */ /* Nested fragment, use child fragment here to maintain backstack in view pager. */
PagerAdapter adapter = new PagerAdapter(getChildFragmentManager()); adapter = new PagerAdapter(getChildFragmentManager());
viewPager.setAdapter(adapter); viewPager.setAdapter(adapter);
viewPager.setOffscreenPageLimit(adapter.getCount());
tabLayout.setupWithViewPager(viewPager); tabLayout.setupWithViewPager(viewPager);
int channelIcon = ThemeHelper.resolveResourceIdFromAttr(activity, R.attr.ic_channel); mainPageChanged();
int whatsHotIcon = ThemeHelper.resolveResourceIdFromAttr(activity, R.attr.ic_hot); }
int bookmarkIcon = ThemeHelper.resolveResourceIdFromAttr(activity, R.attr.ic_bookmark);
public void mainPageChanged() {
getTabOrder();
adapter.notifyDataSetChanged();
viewPager.setOffscreenPageLimit(adapter.getCount());
setIcons();
setFirstTitle();
}
private void setFirstTitle() {
if((tabs.size() > 0)
&& activity != null) {
String tabInformation = tabs.get(0);
if (tabInformation.startsWith(TAB_NUMBER_KIOSK + "\t")) {
String kiosk[] = tabInformation.split("\t");
if (kiosk.length == 3) {
setTitle(kiosk[1]);
}
} else if (tabInformation.startsWith(TAB_NUMBER_CHANNEL + "\t")) {
String channelInfo[] = tabInformation.split("\t");
if(channelInfo.length==4) {
setTitle(channelInfo[2]);
}
} else {
switch (tabInformation) {
case TAB_NUMBER_BLANK:
setTitle(getString(R.string.app_name));
break;
case TAB_NUMBER_SUBSCIRPTIONS:
setTitle(getString(R.string.tab_subscriptions));
break;
case TAB_NUMBER_FEED:
setTitle(getString(R.string.fragment_whats_new));
break;
case TAB_NUMBER_BOOKMARKS:
setTitle(getString(R.string.tab_bookmarks));
break;
case TAB_NUMBER_HISTORY:
setTitle(getString(R.string.title_activity_history));
break;
}
}
if (isSubscriptionsPageOnlySelected()) {
tabLayout.getTabAt(0).setIcon(channelIcon);
tabLayout.getTabAt(1).setIcon(bookmarkIcon);
} else {
tabLayout.getTabAt(0).setIcon(whatsHotIcon);
tabLayout.getTabAt(1).setIcon(channelIcon);
tabLayout.getTabAt(2).setIcon(bookmarkIcon);
} }
} }
private void setIcons() {
for (int i = 0; i < tabs.size(); i++) {
String tabInformation = tabs.get(i);
TabLayout.Tab tabToSet = tabLayout.getTabAt(i);
Context c = getContext();
if (tabToSet != null && c != null) {
if (tabInformation.startsWith(TAB_NUMBER_KIOSK + "\t")) {
String kiosk[] = tabInformation.split("\t");
if (kiosk.length == 3) {
tabToSet.setIcon(KioskTranslator.getKioskIcons(kiosk[1], getContext()));
}
} else if (tabInformation.startsWith(TAB_NUMBER_CHANNEL + "\t")) {
tabToSet.setIcon(ThemeHelper.resolveResourceIdFromAttr(getContext(), R.attr.ic_channel));
} else {
switch (tabInformation) {
case TAB_NUMBER_BLANK:
tabToSet.setIcon(ThemeHelper.resolveResourceIdFromAttr(getContext(), R.attr.ic_hot));
break;
case TAB_NUMBER_SUBSCIRPTIONS:
tabToSet.setIcon(ThemeHelper.resolveResourceIdFromAttr(getContext(), R.attr.ic_channel));
break;
case TAB_NUMBER_FEED:
tabToSet.setIcon(ThemeHelper.resolveResourceIdFromAttr(getContext(), R.attr.rss));
break;
case TAB_NUMBER_BOOKMARKS:
tabToSet.setIcon(ThemeHelper.resolveResourceIdFromAttr(getContext(), R.attr.ic_bookmark));
break;
case TAB_NUMBER_HISTORY:
tabToSet.setIcon(ThemeHelper.resolveResourceIdFromAttr(getContext(), R.attr.history));
break;
}
}
}
}
}
private void getTabOrder() {
tabs.clear();
String save = prefs.getString("saveUsedTabs", "1\tTrending\t0\n2\n4\n");
String tabsArray[] = save.trim().split("\n");
Collections.addAll(tabs, tabsArray);
}
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Menu // Menu
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@ -107,16 +209,6 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
super.onCreateOptionsMenu(menu, inflater); super.onCreateOptionsMenu(menu, inflater);
if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + "], inflater = [" + inflater + "]"); if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + "], inflater = [" + inflater + "]");
inflater.inflate(R.menu.main_fragment_menu, menu); inflater.inflate(R.menu.main_fragment_menu, menu);
SubMenu kioskMenu = menu.addSubMenu(Menu.NONE, Menu.NONE, 200, getString(R.string.kiosk));
try {
createKioskMenu(kioskMenu, inflater);
} catch (Exception e) {
ErrorActivity.reportError(activity, e,
activity.getClass(),
null,
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
"none", "", R.string.app_ui_crash));
}
ActionBar supportActionBar = activity.getSupportActionBar(); ActionBar supportActionBar = activity.getSupportActionBar();
if (supportActionBar != null) { if (supportActionBar != null) {
@ -165,115 +257,77 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
@Override @Override
public Fragment getItem(int position) { public Fragment getItem(int position) {
switch (position) { String tabInformation = tabs.get(position);
case 0:
return isSubscriptionsPageOnlySelected() ? new SubscriptionFragment() : getMainPageFragment(); if(tabInformation.startsWith(TAB_NUMBER_KIOSK + "\t")) {
case 1: String kiosk[] = tabInformation.split("\t");
if(PreferenceManager.getDefaultSharedPreferences(getActivity()) if(kiosk.length==3) {
.getString(getString(R.string.main_page_content_key), getString(R.string.blank_page_key)) KioskFragment fragment = null;
.equals(getString(R.string.subscription_page_key))) { try {
return new BookmarkFragment(); fragment = KioskFragment.getInstance(Integer.parseInt(kiosk[2]), kiosk[1]);
} else { fragment.useAsFrontPage(true);
return new SubscriptionFragment(); return fragment;
} catch (Exception e) {
ErrorActivity.reportError(activity, e,
activity.getClass(),
null,
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
"none", "", R.string.app_ui_crash));
} }
case 2: }
return new BookmarkFragment(); } else if(tabInformation.startsWith(TAB_NUMBER_CHANNEL + "\t")) {
default: String channelInfo[] = tabInformation.split("\t");
if(channelInfo.length==4) {
ChannelFragment fragment = ChannelFragment.getInstance(Integer.parseInt(channelInfo[3]), channelInfo[1], channelInfo[2]);
fragment.useAsFrontPage(true);
return fragment;
} else {
return new BlankFragment(); return new BlankFragment();
}
} else {
switch (tabInformation) {
case TAB_NUMBER_BLANK:
return new BlankFragment();
case TAB_NUMBER_SUBSCIRPTIONS:
SubscriptionFragment sFragment = new SubscriptionFragment();
sFragment.useAsFrontPage(true);
return sFragment;
case TAB_NUMBER_FEED:
FeedFragment fFragment = new FeedFragment();
fFragment.useAsFrontPage(true);
return fFragment;
case TAB_NUMBER_BOOKMARKS:
BookmarkFragment bFragment = new BookmarkFragment();
bFragment.useAsFrontPage(true);
return bFragment;
case TAB_NUMBER_HISTORY:
StatisticsPlaylistFragment cFragment = new StatisticsPlaylistFragment();
cFragment.useAsFrontPage(true);
return cFragment;
}
}
return new BlankFragment();
} }
}
@Override @Override
public CharSequence getPageTitle(int position) { public int getItemPosition(Object object) {
//return getString(this.tabTitles[position]); // Causes adapter to reload all Fragments when
return ""; // notifyDataSetChanged is called
return POSITION_NONE;
} }
@Override @Override
public int getCount() { public int getCount() {
return isSubscriptionsPageOnlySelected() ? 2 : 3; return tabs.size();
} }
}
/*////////////////////////////////////////////////////////////////////////// @Override
// Main page content public void destroyItem(ViewGroup container, int position, Object object) {
//////////////////////////////////////////////////////////////////////////*/ getFragmentManager()
.beginTransaction()
private boolean isSubscriptionsPageOnlySelected() { .remove((Fragment)object)
return PreferenceManager.getDefaultSharedPreferences(activity) .commitNowAllowingStateLoss();
.getString(getString(R.string.main_page_content_key), getString(R.string.blank_page_key))
.equals(getString(R.string.subscription_page_key));
}
private Fragment getMainPageFragment() {
if (getActivity() == null) return new BlankFragment();
try {
SharedPreferences preferences =
PreferenceManager.getDefaultSharedPreferences(getActivity());
final String setMainPage = preferences.getString(getString(R.string.main_page_content_key),
getString(R.string.main_page_selectd_kiosk_id));
if (setMainPage.equals(getString(R.string.blank_page_key))) {
return new BlankFragment();
} else if (setMainPage.equals(getString(R.string.kiosk_page_key))) {
int serviceId = preferences.getInt(getString(R.string.main_page_selected_service),
FALLBACK_SERVICE_ID);
String kioskId = preferences.getString(getString(R.string.main_page_selectd_kiosk_id),
FALLBACK_KIOSK_ID);
KioskFragment fragment = KioskFragment.getInstance(serviceId, kioskId);
fragment.useAsFrontPage(true);
return fragment;
} else if (setMainPage.equals(getString(R.string.feed_page_key))) {
FeedFragment fragment = new FeedFragment();
fragment.useAsFrontPage(true);
return fragment;
} else if (setMainPage.equals(getString(R.string.channel_page_key))) {
int serviceId = preferences.getInt(getString(R.string.main_page_selected_service),
FALLBACK_SERVICE_ID);
String url = preferences.getString(getString(R.string.main_page_selected_channel_url),
FALLBACK_CHANNEL_URL);
String name = preferences.getString(getString(R.string.main_page_selected_channel_name),
FALLBACK_CHANNEL_NAME);
ChannelFragment fragment = ChannelFragment.getInstance(serviceId,
url,
name);
fragment.useAsFrontPage(true);
return fragment;
} else {
return new BlankFragment();
}
} catch (Exception e) {
ErrorActivity.reportError(activity, e,
activity.getClass(),
null,
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
"none", "", R.string.app_ui_crash));
return new BlankFragment();
}
}
/*//////////////////////////////////////////////////////////////////////////
// Select Kiosk
//////////////////////////////////////////////////////////////////////////*/
private void createKioskMenu(Menu menu, MenuInflater menuInflater)
throws Exception {
StreamingService service = NewPipe.getService(currentServiceId);
KioskList kl = service.getKioskList();
int i = 0;
for (final String ks : kl.getAvailableKiosks()) {
menu.add(0, KIOSK_MENU_OFFSET + i, Menu.NONE,
KioskTranslator.getTranslatedKioskName(ks, getContext()))
.setOnMenuItemClickListener(menuItem -> {
try {
NavigationHelper.openKioskFragment(getFragmentManager(), currentServiceId, ks);
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
return true;
});
i++;
} }
} }
} }

View File

@ -1247,10 +1247,10 @@ public class VideoDetailFragment
spinnerToolbar.setVisibility(View.GONE); spinnerToolbar.setVisibility(View.GONE);
break; break;
default: default:
if(info.getAudioStreams().isEmpty()) detailControlsBackground.setVisibility(View.GONE);
if (!info.getVideoStreams().isEmpty() if (!info.getVideoStreams().isEmpty()
|| !info.getVideoOnlyStreams().isEmpty()) break; || !info.getVideoOnlyStreams().isEmpty()) break;
detailControlsBackground.setVisibility(View.GONE);
detailControlsPopup.setVisibility(View.GONE); detailControlsPopup.setVisibility(View.GONE);
spinnerToolbar.setVisibility(View.GONE); spinnerToolbar.setVisibility(View.GONE);
thumbnailPlayButton.setImageResource(R.drawable.ic_headset_white_24dp); thumbnailPlayButton.setImageResource(R.drawable.ic_headset_white_24dp);

View File

@ -195,9 +195,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
public void selected(ChannelInfoItem selectedItem) { public void selected(ChannelInfoItem selectedItem) {
try { try {
onItemSelected(selectedItem); onItemSelected(selectedItem);
NavigationHelper.openChannelFragment(useAsFrontPage ? NavigationHelper.openChannelFragment(getFM(),
getParentFragment().getFragmentManager()
: getFragmentManager(),
selectedItem.getServiceId(), selectedItem.getServiceId(),
selectedItem.getUrl(), selectedItem.getUrl(),
selectedItem.getName()); selectedItem.getName());
@ -212,10 +210,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
public void selected(PlaylistInfoItem selectedItem) { public void selected(PlaylistInfoItem selectedItem) {
try { try {
onItemSelected(selectedItem); onItemSelected(selectedItem);
NavigationHelper.openPlaylistFragment( NavigationHelper.openPlaylistFragment(getFM(),
useAsFrontPage
? getParentFragment().getFragmentManager()
: getFragmentManager(),
selectedItem.getServiceId(), selectedItem.getServiceId(),
selectedItem.getUrl(), selectedItem.getUrl(),
selectedItem.getName()); selectedItem.getName());
@ -236,9 +231,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
private void onStreamSelected(StreamInfoItem selectedItem) { private void onStreamSelected(StreamInfoItem selectedItem) {
onItemSelected(selectedItem); onItemSelected(selectedItem);
NavigationHelper.openVideoDetailFragment(useAsFrontPage NavigationHelper.openVideoDetailFragment(getFM(),
? getParentFragment().getFragmentManager()
: getFragmentManager(),
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName()); selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
} }

View File

@ -91,6 +91,8 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
private MenuItem menuRssButton; private MenuItem menuRssButton;
private boolean mIsVisibleToUser = false;
public static ChannelFragment getInstance(int serviceId, String url, String name) { public static ChannelFragment getInstance(int serviceId, String url, String name) {
ChannelFragment instance = new ChannelFragment(); ChannelFragment instance = new ChannelFragment();
instance.setInitialData(serviceId, url, name); instance.setInitialData(serviceId, url, name);
@ -104,6 +106,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);
mIsVisibleToUser = isVisibleToUser;
if(activity != null if(activity != null
&& useAsFrontPage && useAsFrontPage
&& isVisibleToUser) { && isVisibleToUser) {
@ -166,38 +169,35 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
context.getResources().getString(R.string.share) context.getResources().getString(R.string.share)
}; };
final DialogInterface.OnClickListener actions = new DialogInterface.OnClickListener() { final DialogInterface.OnClickListener actions = (DialogInterface dialogInterface, int i) -> {
@Override final int index = Math.max(infoListAdapter.getItemsList().indexOf(item), 0);
public void onClick(DialogInterface dialogInterface, int i) { switch (i) {
final int index = Math.max(infoListAdapter.getItemsList().indexOf(item), 0); case 0:
switch (i) { NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
case 0: break;
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item)); case 1:
break; NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item));
case 1: break;
NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item)); case 2:
break; NavigationHelper.playOnMainPlayer(context, getPlayQueue(index));
case 2: break;
NavigationHelper.playOnMainPlayer(context, getPlayQueue(index)); case 3:
break; NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index));
case 3: break;
NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index)); case 4:
break; NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index));
case 4: break;
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index)); case 5:
break; if (getFragmentManager() != null) {
case 5: PlaylistAppendDialog.fromStreamInfoItems(Collections.singletonList(item))
if (getFragmentManager() != null) { .show(getFragmentManager(), TAG);
PlaylistAppendDialog.fromStreamInfoItems(Collections.singletonList(item)) }
.show(getFragmentManager(), TAG); break;
} case 6:
break; shareUrl(item.getName(), item.getUrl());
case 6: break;
shareUrl(item.getName(), item.getUrl()); default:
break; break;
default:
break;
}
} }
}; };
@ -255,12 +255,12 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
private static final int BUTTON_DEBOUNCE_INTERVAL = 100; private static final int BUTTON_DEBOUNCE_INTERVAL = 100;
private void monitorSubscription(final ChannelInfo info) { private void monitorSubscription(final ChannelInfo info) {
final Consumer<Throwable> onError = new Consumer<Throwable>() { final Consumer<Throwable> onError = (Throwable throwable) -> {
@Override
public void accept(Throwable throwable) throws Exception {
animateView(headerSubscribeButton, false, 100); animateView(headerSubscribeButton, false, 100);
showSnackBarError(throwable, UserAction.SUBSCRIPTION, NewPipe.getNameOfService(currentInfo.getServiceId()), "Get subscription status", 0); showSnackBarError(throwable, UserAction.SUBSCRIPTION,
} NewPipe.getNameOfService(currentInfo.getServiceId()),
"Get subscription status",
0);
}; };
final Observable<List<SubscriptionEntity>> observable = subscriptionService.subscriptionTable() final Observable<List<SubscriptionEntity>> observable = subscriptionService.subscriptionTable()
@ -276,50 +276,38 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
// so only update the UI for the latest emission ("sync" the subscribe button's state) // so only update the UI for the latest emission ("sync" the subscribe button's state)
.debounce(100, TimeUnit.MILLISECONDS) .debounce(100, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<List<SubscriptionEntity>>() { .subscribe((List<SubscriptionEntity> subscriptionEntities) ->
@Override updateSubscribeButton(!subscriptionEntities.isEmpty())
public void accept(List<SubscriptionEntity> subscriptionEntities) throws Exception { , onError));
updateSubscribeButton(!subscriptionEntities.isEmpty());
}
}, onError));
} }
private Function<Object, Object> mapOnSubscribe(final SubscriptionEntity subscription) { private Function<Object, Object> mapOnSubscribe(final SubscriptionEntity subscription) {
return new Function<Object, Object>() { return (@NonNull Object o) -> {
@Override subscriptionService.subscriptionTable().insert(subscription);
public Object apply(@NonNull Object o) throws Exception { return o;
subscriptionService.subscriptionTable().insert(subscription);
return o;
}
}; };
} }
private Function<Object, Object> mapOnUnsubscribe(final SubscriptionEntity subscription) { private Function<Object, Object> mapOnUnsubscribe(final SubscriptionEntity subscription) {
return new Function<Object, Object>() { return (@NonNull Object o) -> {
@Override subscriptionService.subscriptionTable().delete(subscription);
public Object apply(@NonNull Object o) throws Exception { return o;
subscriptionService.subscriptionTable().delete(subscription);
return o;
}
}; };
} }
private void updateSubscription(final ChannelInfo info) { private void updateSubscription(final ChannelInfo info) {
if (DEBUG) Log.d(TAG, "updateSubscription() called with: info = [" + info + "]"); if (DEBUG) Log.d(TAG, "updateSubscription() called with: info = [" + info + "]");
final Action onComplete = new Action() { final Action onComplete = () -> {
@Override
public void run() throws Exception {
if (DEBUG) Log.d(TAG, "Updated subscription: " + info.getUrl()); if (DEBUG) Log.d(TAG, "Updated subscription: " + info.getUrl());
}
}; };
final Consumer<Throwable> onError = new Consumer<Throwable>() { final Consumer<Throwable> onError = (@NonNull Throwable throwable) ->
@Override onUnrecoverableError(throwable,
public void accept(@NonNull Throwable throwable) throws Exception { UserAction.SUBSCRIPTION,
onUnrecoverableError(throwable, UserAction.SUBSCRIPTION, NewPipe.getNameOfService(info.getServiceId()), "Updating Subscription for " + info.getUrl(), R.string.subscription_update_failed); NewPipe.getNameOfService(info.getServiceId()),
} "Updating Subscription for " + info.getUrl(),
}; R.string.subscription_update_failed);
disposables.add(subscriptionService.updateChannelInfo(info) disposables.add(subscriptionService.updateChannelInfo(info)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
@ -328,19 +316,16 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
} }
private Disposable monitorSubscribeButton(final Button subscribeButton, final Function<Object, Object> action) { private Disposable monitorSubscribeButton(final Button subscribeButton, final Function<Object, Object> action) {
final Consumer<Object> onNext = new Consumer<Object>() { final Consumer<Object> onNext = (@NonNull Object o) -> {
@Override
public void accept(@NonNull Object o) throws Exception {
if (DEBUG) Log.d(TAG, "Changed subscription status to this channel!"); if (DEBUG) Log.d(TAG, "Changed subscription status to this channel!");
}
}; };
final Consumer<Throwable> onError = new Consumer<Throwable>() { final Consumer<Throwable> onError = (@NonNull Throwable throwable) ->
@Override onUnrecoverableError(throwable,
public void accept(@NonNull Throwable throwable) throws Exception { UserAction.SUBSCRIPTION,
onUnrecoverableError(throwable, UserAction.SUBSCRIPTION, NewPipe.getNameOfService(currentInfo.getServiceId()), "Subscription Change", R.string.subscription_change_failed); NewPipe.getNameOfService(currentInfo.getServiceId()),
} "Subscription Change",
}; R.string.subscription_change_failed);
/* Emit clicks from main thread unto io thread */ /* Emit clicks from main thread unto io thread */
return RxView.clicks(subscribeButton) return RxView.clicks(subscribeButton)
@ -352,25 +337,25 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
} }
private Consumer<List<SubscriptionEntity>> getSubscribeUpdateMonitor(final ChannelInfo info) { private Consumer<List<SubscriptionEntity>> getSubscribeUpdateMonitor(final ChannelInfo info) {
return new Consumer<List<SubscriptionEntity>>() { return (List<SubscriptionEntity> subscriptionEntities) -> {
@Override if (DEBUG)
public void accept(List<SubscriptionEntity> subscriptionEntities) throws Exception { Log.d(TAG, "subscriptionService.subscriptionTable.doOnNext() called with: subscriptionEntities = [" + subscriptionEntities + "]");
if (DEBUG) if (subscribeButtonMonitor != null) subscribeButtonMonitor.dispose();
Log.d(TAG, "subscriptionService.subscriptionTable.doOnNext() called with: subscriptionEntities = [" + subscriptionEntities + "]");
if (subscribeButtonMonitor != null) subscribeButtonMonitor.dispose();
if (subscriptionEntities.isEmpty()) { if (subscriptionEntities.isEmpty()) {
if (DEBUG) Log.d(TAG, "No subscription to this channel!"); if (DEBUG) Log.d(TAG, "No subscription to this channel!");
SubscriptionEntity channel = new SubscriptionEntity(); SubscriptionEntity channel = new SubscriptionEntity();
channel.setServiceId(info.getServiceId()); channel.setServiceId(info.getServiceId());
channel.setUrl(info.getUrl()); channel.setUrl(info.getUrl());
channel.setData(info.getName(), info.getAvatarUrl(), info.getDescription(), info.getSubscriberCount()); channel.setData(info.getName(),
subscribeButtonMonitor = monitorSubscribeButton(headerSubscribeButton, mapOnSubscribe(channel)); info.getAvatarUrl(),
} else { info.getDescription(),
if (DEBUG) Log.d(TAG, "Found subscription to this channel!"); info.getSubscriberCount());
final SubscriptionEntity subscription = subscriptionEntities.get(0); subscribeButtonMonitor = monitorSubscribeButton(headerSubscribeButton, mapOnSubscribe(channel));
subscribeButtonMonitor = monitorSubscribeButton(headerSubscribeButton, mapOnUnsubscribe(subscription)); } else {
} if (DEBUG) Log.d(TAG, "Found subscription to this channel!");
final SubscriptionEntity subscription = subscriptionEntities.get(0);
subscribeButtonMonitor = monitorSubscribeButton(headerSubscribeButton, mapOnUnsubscribe(subscription));
} }
}; };
} }
@ -488,8 +473,11 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
super.handleNextItems(result); super.handleNextItems(result);
if (!result.getErrors().isEmpty()) { if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(), UserAction.REQUESTED_CHANNEL, NewPipe.getNameOfService(serviceId), showSnackBarError(result.getErrors(),
"Get next page of: " + url, R.string.general_error); UserAction.REQUESTED_CHANNEL,
NewPipe.getNameOfService(serviceId),
"Get next page of: " + url,
R.string.general_error);
} }
} }
@ -517,6 +505,6 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
@Override @Override
public void setTitle(String title) { public void setTitle(String title) {
super.setTitle(title); super.setTitle(title);
headerTitleView.setText(title); if (!useAsFrontPage) headerTitleView.setText(title);
} }
} }

View File

@ -57,6 +57,7 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
protected String kioskId = ""; protected String kioskId = "";
protected String kioskTranslatedName; protected String kioskTranslatedName;
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Views // Views
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@ -167,7 +168,9 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
super.handleResult(result); super.handleResult(result);
name = kioskTranslatedName; name = kioskTranslatedName;
setTitle(kioskTranslatedName); if(!useAsFrontPage) {
setTitle(kioskTranslatedName);
}
if (!result.getErrors().isEmpty()) { if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(), showSnackBarError(result.getErrors(),

View File

@ -365,7 +365,7 @@ public class SearchFragment
int itemId = 0; int itemId = 0;
boolean isFirstItem = true; boolean isFirstItem = true;
final Context c = getContext(); final Context c = getContext();
for(String filter : service.getSearchQIHFactory().getAvailableContentFilter()) { for(String filter : service.getSearchQHFactory().getAvailableContentFilter()) {
menuItemToFilterName.put(itemId, filter); menuItemToFilterName.put(itemId, filter);
MenuItem item = menu.add(1, MenuItem item = menu.add(1,
itemId++, itemId++,
@ -575,8 +575,7 @@ public class SearchFragment
.onNext(searchEditText.getText().toString()), .onNext(searchEditText.getText().toString()),
throwable -> showSnackBarError(throwable, throwable -> showSnackBarError(throwable,
UserAction.DELETE_FROM_HISTORY, "none", UserAction.DELETE_FROM_HISTORY, "none",
"Deleting item failed", R.string.general_error) "Deleting item failed", R.string.general_error));
);
disposables.add(onDelete); disposables.add(onDelete);
}) })
.show(); .show();
@ -837,7 +836,10 @@ public class SearchFragment
@Override @Override
public void handleResult(@NonNull SearchInfo result) { public void handleResult(@NonNull SearchInfo result) {
if (!result.getErrors().isEmpty()) { final List<Throwable> exceptions = result.getErrors();
if (!exceptions.isEmpty()
&& !(exceptions.size() == 1
&& exceptions.get(0) instanceof SearchExtractor.NothingFoundException)){
showSnackBarError(result.getErrors(), UserAction.SEARCHED, showSnackBarError(result.getErrors(), UserAction.SEARCHED,
NewPipe.getNameOfService(serviceId), searchString, 0); NewPipe.getNameOfService(serviceId), searchString, 0);
} }
@ -864,6 +866,7 @@ public class SearchFragment
showListFooter(false); showListFooter(false);
currentPageUrl = result.getNextPageUrl(); currentPageUrl = result.getNextPageUrl();
infoListAdapter.addInfoItemList(result.getItems()); infoListAdapter.addInfoItemList(result.getItems());
nextPageUrl = result.getNextPageUrl();
if (!result.getErrors().isEmpty()) { if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(), UserAction.SEARCHED, showSnackBarError(result.getErrors(), UserAction.SEARCHED,

View File

@ -6,7 +6,6 @@ import android.os.Parcelable;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -20,11 +19,9 @@ import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.playlist.PlaylistLocalItem; import org.schabi.newpipe.database.playlist.PlaylistLocalItem;
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity; import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.local.BaseLocalListFragment; import org.schabi.newpipe.local.BaseLocalListFragment;
import org.schabi.newpipe.local.playlist.LocalPlaylistManager; import org.schabi.newpipe.local.playlist.LocalPlaylistManager;
import org.schabi.newpipe.local.playlist.RemotePlaylistManager; import org.schabi.newpipe.local.playlist.RemotePlaylistManager;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.OnClickGesture; import org.schabi.newpipe.util.OnClickGesture;
@ -69,11 +66,10 @@ public final class BookmarkFragment
public View onCreateView(@NonNull LayoutInflater inflater, public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
if (activity != null && activity.getSupportActionBar() != null) {
activity.getSupportActionBar().setDisplayShowTitleEnabled(true);
activity.setTitle(R.string.tab_subscriptions);
}
if(!useAsFrontPage) {
setTitle(activity.getString(R.string.tab_bookmarks));
}
return inflater.inflate(R.layout.fragment_bookmarks, container, false); return inflater.inflate(R.layout.fragment_bookmarks, container, false);
} }
@ -102,26 +98,20 @@ public final class BookmarkFragment
itemListAdapter.setSelectedListener(new OnClickGesture<LocalItem>() { itemListAdapter.setSelectedListener(new OnClickGesture<LocalItem>() {
@Override @Override
public void selected(LocalItem selectedItem) { public void selected(LocalItem selectedItem) {
try { final FragmentManager fragmentManager = getFM();
// Requires the parent fragment to find holder for fragment replacement
if (getParentFragment() == null) return;
final FragmentManager fragmentManager = getParentFragment().getFragmentManager();
if (selectedItem instanceof PlaylistMetadataEntry) { if (selectedItem instanceof PlaylistMetadataEntry) {
final PlaylistMetadataEntry entry = ((PlaylistMetadataEntry) selectedItem); final PlaylistMetadataEntry entry = ((PlaylistMetadataEntry) selectedItem);
NavigationHelper.openLocalPlaylistFragment(fragmentManager, entry.uid, NavigationHelper.openLocalPlaylistFragment(fragmentManager, entry.uid,
entry.name); entry.name);
} else if (selectedItem instanceof PlaylistRemoteEntity) { } else if (selectedItem instanceof PlaylistRemoteEntity) {
final PlaylistRemoteEntity entry = ((PlaylistRemoteEntity) selectedItem); final PlaylistRemoteEntity entry = ((PlaylistRemoteEntity) selectedItem);
NavigationHelper.openPlaylistFragment( NavigationHelper.openPlaylistFragment(
fragmentManager, fragmentManager,
entry.getServiceId(), entry.getServiceId(),
entry.getUrl(), entry.getUrl(),
entry.getName()); entry.getName());
}
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
} }
} }

View File

@ -71,6 +71,10 @@ public class FeedFragment extends BaseListFragment<List<SubscriptionEntity>, Voi
@Override @Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
if(!useAsFrontPage) {
setTitle(activity.getString(R.string.fragment_whats_new));
}
return inflater.inflate(R.layout.fragment_feed, container, false); return inflater.inflate(R.layout.fragment_feed, container, false);
} }
@ -105,20 +109,19 @@ public class FeedFragment extends BaseListFragment<List<SubscriptionEntity>, Voi
super.onDestroyView(); super.onDestroyView();
} }
/*@Override @Override
protected RecyclerView.LayoutManager getListLayoutManager() { public void setUserVisibleHint(boolean isVisibleToUser) {
boolean isPortrait = getResources().getDisplayMetrics().heightPixels > getResources().getDisplayMetrics().widthPixels; super.setUserVisibleHint(isVisibleToUser);
return new GridLayoutManager(activity, isPortrait ? 1 : 2); if (activity != null && isVisibleToUser) {
}*/ setTitle(activity.getString(R.string.fragment_whats_new));
}
}
@Override @Override
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 (supportActionBar != null) {
supportActionBar.setTitle(R.string.fragment_whats_new);
}
if(useAsFrontPage) { if(useAsFrontPage) {
supportActionBar.setDisplayShowTitleEnabled(true); supportActionBar.setDisplayShowTitleEnabled(true);
@ -176,19 +179,9 @@ public class FeedFragment extends BaseListFragment<List<SubscriptionEntity>, Voi
showLoading(); showLoading();
showListFooter(true); showListFooter(true);
subscriptionObserver = subscriptionService.getSubscription() subscriptionObserver = subscriptionService.getSubscription()
.onErrorReturnItem(Collections.<SubscriptionEntity>emptyList()) .onErrorReturnItem(Collections.emptyList())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<List<SubscriptionEntity>>() { .subscribe(this::handleResult, this::onError);
@Override
public void accept(List<SubscriptionEntity> subscriptionEntities) throws Exception {
handleResult(subscriptionEntities);
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
onError(throwable);
}
});
} }
@Override @Override
@ -239,13 +232,12 @@ public class FeedFragment extends BaseListFragment<List<SubscriptionEntity>, Voi
if (!itemsLoaded.contains(subscriptionEntity.getServiceId() + subscriptionEntity.getUrl())) { if (!itemsLoaded.contains(subscriptionEntity.getServiceId() + subscriptionEntity.getUrl())) {
subscriptionService.getChannelInfo(subscriptionEntity) subscriptionService.getChannelInfo(subscriptionEntity)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.onErrorComplete(new Predicate<Throwable>() { .onErrorComplete(
@Override (@io.reactivex.annotations.NonNull Throwable throwable) ->
public boolean test(@io.reactivex.annotations.NonNull Throwable throwable) throws Exception { FeedFragment.super.onError(throwable))
return FeedFragment.super.onError(throwable); .subscribe(
} getChannelInfoObserver(subscriptionEntity.getServiceId(),
}) subscriptionEntity.getUrl()));
.subscribe(getChannelInfoObserver(subscriptionEntity.getServiceId(), subscriptionEntity.getUrl()));
} else { } else {
requestFeed(1); requestFeed(1);
} }
@ -316,7 +308,10 @@ public class FeedFragment extends BaseListFragment<List<SubscriptionEntity>, Voi
@Override @Override
public void onError(Throwable exception) { public void onError(Throwable exception) {
showSnackBarError(exception, UserAction.SUBSCRIPTION, NewPipe.getNameOfService(serviceId), url, 0); showSnackBarError(exception,
UserAction.SUBSCRIPTION,
NewPipe.getNameOfService(serviceId),
url, 0);
requestFeed(1); requestFeed(1);
onDone(); onDone();
} }
@ -361,12 +356,7 @@ public class FeedFragment extends BaseListFragment<List<SubscriptionEntity>, Voi
delayHandler.removeCallbacksAndMessages(null); delayHandler.removeCallbacksAndMessages(null);
// Add a little of a delay when requesting more items because the cache is so fast, // Add a little of a delay when requesting more items because the cache is so fast,
// that the view seems stuck to the user when he scroll to the bottom // that the view seems stuck to the user when he scroll to the bottom
delayHandler.postDelayed(new Runnable() { delayHandler.postDelayed(() -> requestFeed(FEED_LOAD_COUNT), 300);
@Override
public void run() {
requestFeed(FEED_LOAD_COUNT);
}
}, 300);
} }
@Override @Override
@ -423,7 +413,9 @@ public class FeedFragment extends BaseListFragment<List<SubscriptionEntity>, Voi
int heightPixels = getResources().getDisplayMetrics().heightPixels; int heightPixels = getResources().getDisplayMetrics().heightPixels;
int itemHeightPixels = activity.getResources().getDimensionPixelSize(R.dimen.video_item_search_height); int itemHeightPixels = activity.getResources().getDimensionPixelSize(R.dimen.video_item_search_height);
int items = itemHeightPixels > 0 ? heightPixels / itemHeightPixels + OFF_SCREEN_ITEMS_COUNT : MIN_ITEMS_INITIAL_LOAD; int items = itemHeightPixels > 0
? heightPixels / itemHeightPixels + OFF_SCREEN_ITEMS_COUNT
: MIN_ITEMS_INITIAL_LOAD;
return Math.max(MIN_ITEMS_INITIAL_LOAD, items); return Math.max(MIN_ITEMS_INITIAL_LOAD, items);
} }
@ -441,8 +433,14 @@ public class FeedFragment extends BaseListFragment<List<SubscriptionEntity>, Voi
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; int errorId = exception instanceof ExtractionException
onUnrecoverableError(exception, UserAction.SOMETHING_ELSE, "none", "Requesting feed", errorId); ? R.string.parsing_error
: R.string.general_error;
onUnrecoverableError(exception,
UserAction.SOMETHING_ELSE,
"none",
"Requesting feed",
errorId);
return true; return true;
} }
} }

View File

@ -21,6 +21,7 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.stream.StreamStatisticsEntry; import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.fragments.list.BaseListFragment;
import org.schabi.newpipe.local.BaseLocalListFragment; import org.schabi.newpipe.local.BaseLocalListFragment;
import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.info_list.InfoItemDialog;
import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueue;
@ -73,7 +74,7 @@ public class StatisticsPlaylistFragment
return results; return results;
case MOST_PLAYED: case MOST_PLAYED:
Collections.sort(results, (left, right) -> Collections.sort(results, (left, right) ->
((Long) right.watchCount).compareTo(left.watchCount)); Long.compare(right.watchCount, left.watchCount));
return results; return results;
default: return null; default: return null;
} }
@ -96,6 +97,14 @@ public class StatisticsPlaylistFragment
return inflater.inflate(R.layout.fragment_playlist, container, false); return inflater.inflate(R.layout.fragment_playlist, container, false);
} }
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (activity != null && isVisibleToUser) {
setTitle(activity.getString(R.string.title_activity_history));
}
}
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Fragment LifeCycle - Views // Fragment LifeCycle - Views
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@ -103,7 +112,9 @@ public class StatisticsPlaylistFragment
@Override @Override
protected void initViews(View rootView, Bundle savedInstanceState) { protected void initViews(View rootView, Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState); super.initViews(rootView, savedInstanceState);
setTitle(getString(R.string.title_last_played)); if(!useAsFrontPage) {
setTitle(getString(R.string.title_last_played));
}
} }
@Override @Override
@ -129,8 +140,10 @@ public class StatisticsPlaylistFragment
public void selected(LocalItem selectedItem) { public void selected(LocalItem selectedItem) {
if (selectedItem instanceof StreamStatisticsEntry) { if (selectedItem instanceof StreamStatisticsEntry) {
final StreamStatisticsEntry item = (StreamStatisticsEntry) selectedItem; final StreamStatisticsEntry item = (StreamStatisticsEntry) selectedItem;
NavigationHelper.openVideoDetailFragment(getFragmentManager(), NavigationHelper.openVideoDetailFragment(getFM(),
item.serviceId, item.url, item.title); item.serviceId,
item.url,
item.title);
} }
} }
@ -341,7 +354,7 @@ public class StatisticsPlaylistFragment
final Disposable onDelete = recordManager.deleteStreamHistory(entry.streamId) final Disposable onDelete = recordManager.deleteStreamHistory(entry.streamId)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe( .subscribe(
howManyDelted -> { howManyDeleted -> {
if(getView() != null) { if(getView() != null) {
Snackbar.make(getView(), R.string.one_item_deleted, Snackbar.make(getView(), R.string.one_item_deleted,
Snackbar.LENGTH_SHORT).show(); Snackbar.LENGTH_SHORT).show();

View File

@ -13,6 +13,7 @@ import android.os.Parcelable;
import android.support.annotation.DrawableRes; import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.app.FragmentManager;
import android.support.v4.content.LocalBroadcastManager; import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
@ -209,7 +210,8 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
} }
private void setupImportFromItems(final ViewGroup listHolder) { private void setupImportFromItems(final ViewGroup listHolder) {
final View previousBackupItem = addItemView(getString(R.string.previous_export), ThemeHelper.resolveResourceIdFromAttr(getContext(), R.attr.ic_backup), listHolder); final View previousBackupItem = addItemView(getString(R.string.previous_export),
ThemeHelper.resolveResourceIdFromAttr(getContext(), R.attr.ic_backup), listHolder);
previousBackupItem.setOnClickListener(item -> onImportPreviousSelected()); previousBackupItem.setOnClickListener(item -> onImportPreviousSelected());
final int iconColor = ThemeHelper.isLightThemeSelected(getContext()) ? Color.BLACK : Color.WHITE; final int iconColor = ThemeHelper.isLightThemeSelected(getContext()) ? Color.BLACK : Color.WHITE;
@ -241,8 +243,8 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
} }
private void onImportFromServiceSelected(int serviceId) { private void onImportFromServiceSelected(int serviceId) {
if (getParentFragment() == null) return; FragmentManager fragmentManager = getFM();
NavigationHelper.openSubscriptionsImportFragment(getParentFragment().getFragmentManager(), serviceId); NavigationHelper.openSubscriptionsImportFragment(fragmentManager, serviceId);
} }
private void onImportPreviousSelected() { private void onImportPreviousSelected() {
@ -320,21 +322,19 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
infoListAdapter.setOnChannelSelectedListener(new OnClickGesture<ChannelInfoItem>() { infoListAdapter.setOnChannelSelectedListener(new OnClickGesture<ChannelInfoItem>() {
@Override @Override
public void selected(ChannelInfoItem selectedItem) { public void selected(ChannelInfoItem selectedItem) {
try { final FragmentManager fragmentManager = getFM();
// Requires the parent fragment to find holder for fragment replacement NavigationHelper.openChannelFragment(fragmentManager,
NavigationHelper.openChannelFragment(getParentFragment().getFragmentManager(), selectedItem.getServiceId(),
selectedItem.getServiceId(), selectedItem.getUrl(),
selectedItem.getUrl(), selectedItem.getName());
selectedItem.getName());
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
} }
}); });
//noinspection ConstantConditions //noinspection ConstantConditions
whatsNewItemListHeader.setOnClickListener(v -> whatsNewItemListHeader.setOnClickListener(v -> {
NavigationHelper.openWhatsNewFragment(getParentFragment().getFragmentManager())); FragmentManager fragmentManager = getFM();
NavigationHelper.openWhatsNewFragment(fragmentManager);
});
importExportListHeader.setOnClickListener(v -> importExportOptions.switchState()); importExportListHeader.setOnClickListener(v -> importExportOptions.switchState());
} }
@ -405,10 +405,13 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
private List<InfoItem> getSubscriptionItems(List<SubscriptionEntity> subscriptions) { private List<InfoItem> getSubscriptionItems(List<SubscriptionEntity> subscriptions) {
List<InfoItem> items = new ArrayList<>(); List<InfoItem> items = new ArrayList<>();
for (final SubscriptionEntity subscription : subscriptions) items.add(subscription.toChannelInfoItem()); for (final SubscriptionEntity subscription : subscriptions) {
items.add(subscription.toChannelInfoItem());
}
Collections.sort(items, Collections.sort(items,
(InfoItem o1, InfoItem o2) -> o1.getName().compareToIgnoreCase(o2.getName())); (InfoItem o1, InfoItem o2) ->
o1.getName().compareToIgnoreCase(o2.getName()));
return items; return items;
} }
@ -437,7 +440,11 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
resetFragment(); resetFragment();
if (super.onError(exception)) return true; if (super.onError(exception)) return true;
onUnrecoverableError(exception, UserAction.SOMETHING_ELSE, "none", "Subscriptions", R.string.general_error); onUnrecoverableError(exception,
UserAction.SOMETHING_ELSE,
"none",
"Subscriptions",
R.string.general_error);
return true; return true;
} }
} }

View File

@ -26,6 +26,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.os.Build;
import android.os.IBinder; import android.os.IBinder;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
@ -343,6 +344,7 @@ public final class BackgroundPlayer extends Service {
if (!shouldUpdateOnProgress) return; if (!shouldUpdateOnProgress) return;
resetNotification(); resetNotification();
if(Build.VERSION.SDK_INT >= 26 /*Oreo*/) updateNotificationThumbnail();
if (bigNotRemoteView != null) { if (bigNotRemoteView != null) {
bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, duration, currentProgress, false); bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, duration, currentProgress, false);
bigNotRemoteView.setTextViewText(R.id.notificationTime, getTimeString(currentProgress) + " / " + getTimeString(duration)); bigNotRemoteView.setTextViewText(R.id.notificationTime, getTimeString(currentProgress) + " / " + getTimeString(duration));

View File

@ -51,6 +51,7 @@ import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.FailReason; import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.Downloader;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfo;
@ -69,6 +70,7 @@ import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.PlayQueueAdapter; import org.schabi.newpipe.player.playqueue.PlayQueueAdapter;
import org.schabi.newpipe.player.playqueue.PlayQueueItem; import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.player.resolver.MediaSourceTag; import org.schabi.newpipe.player.resolver.MediaSourceTag;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.util.ImageDisplayConstants; import org.schabi.newpipe.util.ImageDisplayConstants;
import org.schabi.newpipe.util.SerializedCache; import org.schabi.newpipe.util.SerializedCache;
@ -86,6 +88,7 @@ import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_INTERNAL
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_PERIOD_TRANSITION; import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_PERIOD_TRANSITION;
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK; import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK;
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT; import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT;
import static org.schabi.newpipe.report.UserAction.PLAY_STREAM;
/** /**
* Base for the players, joining the common properties * Base for the players, joining the common properties
@ -96,7 +99,7 @@ import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK_ADJ
public abstract class BasePlayer implements public abstract class BasePlayer implements
Player.EventListener, PlaybackListener, ImageLoadingListener { Player.EventListener, PlaybackListener, ImageLoadingListener {
public static final boolean DEBUG = true; public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");
@NonNull public static final String TAG = "BasePlayer"; @NonNull public static final String TAG = "BasePlayer";
@NonNull final protected Context context; @NonNull final protected Context context;
@ -363,7 +366,10 @@ public abstract class BasePlayer implements
try { try {
context.unregisterReceiver(broadcastReceiver); context.unregisterReceiver(broadcastReceiver);
} catch (final IllegalArgumentException unregisteredException) { } catch (final IllegalArgumentException unregisteredException) {
Log.e(TAG, "Broadcast receiver already unregistered.", unregisteredException); ErrorActivity.reportError(context, unregisteredException, null, null,
ErrorActivity.ErrorInfo.make(PLAY_STREAM,
"none",
"play stream", R.string.general_error));
} }
} }
@ -1001,6 +1007,8 @@ public abstract class BasePlayer implements
try { try {
metadata = (MediaSourceTag) simpleExoPlayer.getCurrentTag(); metadata = (MediaSourceTag) simpleExoPlayer.getCurrentTag();
} catch (IndexOutOfBoundsException | ClassCastException error) { } catch (IndexOutOfBoundsException | ClassCastException error) {
if(DEBUG) Log.d(TAG, "Could not update metadata: " + error.getMessage());
if(DEBUG) error.printStackTrace();
return; return;
} }
@ -1087,6 +1095,9 @@ public abstract class BasePlayer implements
return simpleExoPlayer.isCurrentWindowDynamic(); return simpleExoPlayer.isCurrentWindowDynamic();
} catch (@NonNull IndexOutOfBoundsException ignored) { } catch (@NonNull IndexOutOfBoundsException ignored) {
// Why would this even happen =( // Why would this even happen =(
// But lets log it anyway. Save is save
if(DEBUG) Log.d(TAG, "Could not update metadata: " + ignored.getMessage());
if(DEBUG) ignored.printStackTrace();
return false; return false;
} }
} }

View File

@ -36,6 +36,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat; import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.support.v7.content.res.AppCompatResources;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper; import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
@ -46,7 +47,9 @@ import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.PopupMenu; import android.widget.PopupMenu;
import android.widget.ProgressBar;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.SeekBar; import android.widget.SeekBar;
import android.widget.TextView; import android.widget.TextView;
@ -82,6 +85,7 @@ import java.util.UUID;
import static org.schabi.newpipe.player.BasePlayer.STATE_PLAYING; import static org.schabi.newpipe.player.BasePlayer.STATE_PLAYING;
import static org.schabi.newpipe.player.VideoPlayer.DEFAULT_CONTROLS_DURATION; import static org.schabi.newpipe.player.VideoPlayer.DEFAULT_CONTROLS_DURATION;
import static org.schabi.newpipe.player.VideoPlayer.DEFAULT_CONTROLS_HIDE_TIME; import static org.schabi.newpipe.player.VideoPlayer.DEFAULT_CONTROLS_HIDE_TIME;
import static org.schabi.newpipe.util.AnimationUtils.Type.SCALE_AND_ALPHA;
import static org.schabi.newpipe.util.AnimationUtils.Type.SLIDE_AND_ALPHA; import static org.schabi.newpipe.util.AnimationUtils.Type.SLIDE_AND_ALPHA;
import static org.schabi.newpipe.util.AnimationUtils.animateRotation; import static org.schabi.newpipe.util.AnimationUtils.animateRotation;
import static org.schabi.newpipe.util.AnimationUtils.animateView; import static org.schabi.newpipe.util.AnimationUtils.animateView;
@ -365,10 +369,16 @@ public final class MainVideoPlayer extends AppCompatActivity
@SuppressWarnings({"unused", "WeakerAccess"}) @SuppressWarnings({"unused", "WeakerAccess"})
private class VideoPlayerImpl extends VideoPlayer { private class VideoPlayerImpl extends VideoPlayer {
private final float MAX_GESTURE_LENGTH = 0.75f;
private TextView titleTextView; private TextView titleTextView;
private TextView channelTextView; private TextView channelTextView;
private TextView volumeTextView; private RelativeLayout volumeRelativeLayout;
private TextView brightnessTextView; private ProgressBar volumeProgressBar;
private ImageView volumeImageView;
private RelativeLayout brightnessRelativeLayout;
private ProgressBar brightnessProgressBar;
private ImageView brightnessImageView;
private ImageButton queueButton; private ImageButton queueButton;
private ImageButton repeatButton; private ImageButton repeatButton;
private ImageButton shuffleButton; private ImageButton shuffleButton;
@ -392,6 +402,8 @@ public final class MainVideoPlayer extends AppCompatActivity
private RelativeLayout windowRootLayout; private RelativeLayout windowRootLayout;
private View secondaryControls; private View secondaryControls;
private int maxGestureLength;
VideoPlayerImpl(final Context context) { VideoPlayerImpl(final Context context) {
super("VideoPlayerImpl" + MainVideoPlayer.TAG, context); super("VideoPlayerImpl" + MainVideoPlayer.TAG, context);
} }
@ -401,8 +413,12 @@ public final class MainVideoPlayer extends AppCompatActivity
super.initViews(rootView); super.initViews(rootView);
this.titleTextView = rootView.findViewById(R.id.titleTextView); this.titleTextView = rootView.findViewById(R.id.titleTextView);
this.channelTextView = rootView.findViewById(R.id.channelTextView); this.channelTextView = rootView.findViewById(R.id.channelTextView);
this.volumeTextView = rootView.findViewById(R.id.volumeTextView); this.volumeRelativeLayout = rootView.findViewById(R.id.volumeRelativeLayout);
this.brightnessTextView = rootView.findViewById(R.id.brightnessTextView); this.volumeProgressBar = rootView.findViewById(R.id.volumeProgressBar);
this.volumeImageView = rootView.findViewById(R.id.volumeImageView);
this.brightnessRelativeLayout = rootView.findViewById(R.id.brightnessRelativeLayout);
this.brightnessProgressBar = rootView.findViewById(R.id.brightnessProgressBar);
this.brightnessImageView = rootView.findViewById(R.id.brightnessImageView);
this.queueButton = rootView.findViewById(R.id.queueButton); this.queueButton = rootView.findViewById(R.id.queueButton);
this.repeatButton = rootView.findViewById(R.id.repeatButton); this.repeatButton = rootView.findViewById(R.id.repeatButton);
this.shuffleButton = rootView.findViewById(R.id.shuffleButton); this.shuffleButton = rootView.findViewById(R.id.shuffleButton);
@ -461,6 +477,20 @@ public final class MainVideoPlayer extends AppCompatActivity
toggleOrientationButton.setOnClickListener(this); toggleOrientationButton.setOnClickListener(this);
switchBackgroundButton.setOnClickListener(this); switchBackgroundButton.setOnClickListener(this);
switchPopupButton.setOnClickListener(this); switchPopupButton.setOnClickListener(this);
getRootView().addOnLayoutChangeListener((view, l, t, r, b, ol, ot, or, ob) -> {
if (l != ol || t != ot || r != or || b != ob) {
// Use smaller value to be consistent between screen orientations
// (and to make usage easier)
int width = r - l, height = b - t;
maxGestureLength = (int) (Math.min(width, height) * MAX_GESTURE_LENGTH);
if (DEBUG) Log.d(TAG, "maxGestureLength = " + maxGestureLength);
volumeProgressBar.setMax(maxGestureLength);
brightnessProgressBar.setMax(maxGestureLength);
}
});
} }
public void minimize() { public void minimize() {
@ -872,12 +902,28 @@ public final class MainVideoPlayer extends AppCompatActivity
return channelTextView; return channelTextView;
} }
public TextView getVolumeTextView() { public RelativeLayout getVolumeRelativeLayout() {
return volumeTextView; return volumeRelativeLayout;
} }
public TextView getBrightnessTextView() { public ProgressBar getVolumeProgressBar() {
return brightnessTextView; return volumeProgressBar;
}
public ImageView getVolumeImageView() {
return volumeImageView;
}
public RelativeLayout getBrightnessRelativeLayout() {
return brightnessRelativeLayout;
}
public ProgressBar getBrightnessProgressBar() {
return brightnessProgressBar;
}
public ImageView getBrightnessImageView() {
return brightnessImageView;
} }
public ImageButton getRepeatButton() { public ImageButton getRepeatButton() {
@ -887,6 +933,10 @@ public final class MainVideoPlayer extends AppCompatActivity
public ImageButton getPlayPauseButton() { public ImageButton getPlayPauseButton() {
return playPauseButton; return playPauseButton;
} }
public int getMaxGestureLength() {
return maxGestureLength;
}
} }
private class MySimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener { private class MySimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener {
@ -930,23 +980,10 @@ public final class MainVideoPlayer extends AppCompatActivity
private final boolean isPlayerGestureEnabled = PlayerHelper.isPlayerGestureEnabled(getApplicationContext()); private final boolean isPlayerGestureEnabled = PlayerHelper.isPlayerGestureEnabled(getApplicationContext());
private final float stepsBrightness = 15, stepBrightness = (1f / stepsBrightness), minBrightness = .01f; private final int maxVolume = playerImpl.getAudioReactor().getMaxVolume();
private float currentBrightness = getWindow().getAttributes().screenBrightness > 0
? getWindow().getAttributes().screenBrightness
: 0.5f;
private int currentVolume, maxVolume = playerImpl.getAudioReactor().getMaxVolume();
private final float stepsVolume = 15, stepVolume = (float) Math.ceil(maxVolume / stepsVolume), minVolume = 0;
private final String brightnessUnicode = new String(Character.toChars(0x2600));
private final String volumeUnicode = new String(Character.toChars(0x1F508));
private final int MOVEMENT_THRESHOLD = 40; private final int MOVEMENT_THRESHOLD = 40;
private final int eventsThreshold = 8;
private boolean triggered = false;
private int eventsNum;
// TODO: Improve video gesture controls
@Override @Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (!isPlayerGestureEnabled) return false; if (!isPlayerGestureEnabled) return false;
@ -956,63 +993,77 @@ public final class MainVideoPlayer extends AppCompatActivity
", e1.getRaw = [" + e1.getRawX() + ", " + e1.getRawY() + "]" + ", e1.getRaw = [" + e1.getRawX() + ", " + e1.getRawY() + "]" +
", e2.getRaw = [" + e2.getRawX() + ", " + e2.getRawY() + "]" + ", e2.getRaw = [" + e2.getRawX() + ", " + e2.getRawY() + "]" +
", distanceXy = [" + distanceX + ", " + distanceY + "]"); ", distanceXy = [" + distanceX + ", " + distanceY + "]");
float abs = Math.abs(e2.getY() - e1.getY());
if (!triggered) { if (!isMoving && (
triggered = abs > MOVEMENT_THRESHOLD; Math.abs(e2.getY() - e1.getY()) <= MOVEMENT_THRESHOLD
|| Math.abs(distanceX) > Math.abs(distanceY)
) || playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED)
return false; return false;
}
if (eventsNum++ % eventsThreshold != 0 || playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) return false;
isMoving = true; isMoving = true;
// boolean up = !((e2.getY() - e1.getY()) > 0) && distanceY > 0; // Android's origin point is on top
boolean up = distanceY > 0;
if (e1.getX() > playerImpl.getRootView().getWidth() / 2) { if (e1.getX() > playerImpl.getRootView().getWidth() / 2) {
double floor = Math.floor(up ? stepVolume : -stepVolume); playerImpl.getVolumeProgressBar().incrementProgressBy((int) distanceY);
currentVolume = (int) (playerImpl.getAudioReactor().getVolume() + floor); float currentProgressPercent =
if (currentVolume >= maxVolume) currentVolume = maxVolume; (float) playerImpl.getVolumeProgressBar().getProgress() / playerImpl.getMaxGestureLength();
if (currentVolume <= minVolume) currentVolume = (int) minVolume; int currentVolume = (int) (maxVolume * currentProgressPercent);
playerImpl.getAudioReactor().setVolume(currentVolume); playerImpl.getAudioReactor().setVolume(currentVolume);
currentVolume = playerImpl.getAudioReactor().getVolume();
if (DEBUG) Log.d(TAG, "onScroll().volumeControl, currentVolume = " + currentVolume); if (DEBUG) Log.d(TAG, "onScroll().volumeControl, currentVolume = " + currentVolume);
final String volumeText = volumeUnicode + " " + Math.round((((float) currentVolume) / maxVolume) * 100) + "%";
playerImpl.getVolumeTextView().setText(volumeText);
if (playerImpl.getVolumeTextView().getVisibility() != View.VISIBLE) animateView(playerImpl.getVolumeTextView(), true, 200); final int resId =
if (playerImpl.getBrightnessTextView().getVisibility() == View.VISIBLE) playerImpl.getBrightnessTextView().setVisibility(View.GONE); currentProgressPercent <= 0 ? R.drawable.ic_volume_off_white_72dp
: currentProgressPercent < 0.25 ? R.drawable.ic_volume_mute_white_72dp
: currentProgressPercent < 0.75 ? R.drawable.ic_volume_down_white_72dp
: R.drawable.ic_volume_up_white_72dp;
playerImpl.getVolumeImageView().setImageDrawable(
AppCompatResources.getDrawable(getApplicationContext(), resId)
);
if (playerImpl.getVolumeRelativeLayout().getVisibility() != View.VISIBLE) {
animateView(playerImpl.getVolumeRelativeLayout(), SCALE_AND_ALPHA, true, 200);
}
if (playerImpl.getBrightnessRelativeLayout().getVisibility() == View.VISIBLE) {
playerImpl.getBrightnessRelativeLayout().setVisibility(View.GONE);
}
} else { } else {
WindowManager.LayoutParams lp = getWindow().getAttributes(); playerImpl.getBrightnessProgressBar().incrementProgressBy((int) distanceY);
currentBrightness += up ? stepBrightness : -stepBrightness; float currentProgressPercent =
if (currentBrightness >= 1f) currentBrightness = 1f; (float) playerImpl.getBrightnessProgressBar().getProgress() / playerImpl.getMaxGestureLength();
if (currentBrightness <= minBrightness) currentBrightness = minBrightness; WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
layoutParams.screenBrightness = currentProgressPercent;
getWindow().setAttributes(layoutParams);
lp.screenBrightness = currentBrightness; if (DEBUG) Log.d(TAG, "onScroll().brightnessControl, currentBrightness = " + currentProgressPercent);
getWindow().setAttributes(lp);
if (DEBUG) Log.d(TAG, "onScroll().brightnessControl, currentBrightness = " + currentBrightness);
int brightnessNormalized = Math.round(currentBrightness * 100);
final String brightnessText = brightnessUnicode + " " + (brightnessNormalized == 1 ? 0 : brightnessNormalized) + "%"; final int resId =
playerImpl.getBrightnessTextView().setText(brightnessText); currentProgressPercent < 0.25 ? R.drawable.ic_brightness_low_white_72dp
: currentProgressPercent < 0.75 ? R.drawable.ic_brightness_medium_white_72dp
: R.drawable.ic_brightness_high_white_72dp;
if (playerImpl.getBrightnessTextView().getVisibility() != View.VISIBLE) animateView(playerImpl.getBrightnessTextView(), true, 200); playerImpl.getBrightnessImageView().setImageDrawable(
if (playerImpl.getVolumeTextView().getVisibility() == View.VISIBLE) playerImpl.getVolumeTextView().setVisibility(View.GONE); AppCompatResources.getDrawable(getApplicationContext(), resId)
);
if (playerImpl.getBrightnessRelativeLayout().getVisibility() != View.VISIBLE) {
animateView(playerImpl.getBrightnessRelativeLayout(), SCALE_AND_ALPHA, true, 200);
}
if (playerImpl.getVolumeRelativeLayout().getVisibility() == View.VISIBLE) {
playerImpl.getVolumeRelativeLayout().setVisibility(View.GONE);
}
} }
return true; return true;
} }
private void onScrollEnd() { private void onScrollEnd() {
if (DEBUG) Log.d(TAG, "onScrollEnd() called"); if (DEBUG) Log.d(TAG, "onScrollEnd() called");
triggered = false;
eventsNum = 0; if (playerImpl.getVolumeRelativeLayout().getVisibility() == View.VISIBLE) {
/* if (playerImpl.getVolumeTextView().getVisibility() == View.VISIBLE) playerImpl.getVolumeTextView().setVisibility(View.GONE); animateView(playerImpl.getVolumeRelativeLayout(), SCALE_AND_ALPHA, false, 200, 200);
if (playerImpl.getBrightnessTextView().getVisibility() == View.VISIBLE) playerImpl.getBrightnessTextView().setVisibility(View.GONE);*/
if (playerImpl.getVolumeTextView().getVisibility() == View.VISIBLE) {
animateView(playerImpl.getVolumeTextView(), false, 200, 200);
} }
if (playerImpl.getBrightnessTextView().getVisibility() == View.VISIBLE) { if (playerImpl.getBrightnessRelativeLayout().getVisibility() == View.VISIBLE) {
animateView(playerImpl.getBrightnessTextView(), false, 200, 200); animateView(playerImpl.getBrightnessRelativeLayout(), SCALE_AND_ALPHA, false, 200, 200);
} }
if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == STATE_PLAYING) { if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == STATE_PLAYING) {

View File

@ -19,6 +19,8 @@
package org.schabi.newpipe.player; package org.schabi.newpipe.player;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.app.PendingIntent; import android.app.PendingIntent;
@ -34,6 +36,7 @@ import android.os.Build;
import android.os.IBinder; import android.os.IBinder;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Log; import android.util.Log;
@ -41,7 +44,9 @@ import android.view.GestureDetector;
import android.view.Gravity; import android.view.Gravity;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager; import android.view.WindowManager;
import android.view.animation.AnticipateInterpolator;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.PopupMenu; import android.widget.PopupMenu;
@ -104,10 +109,13 @@ public final class PopupVideoPlayer extends Service {
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
private WindowManager windowManager; private WindowManager windowManager;
private WindowManager.LayoutParams windowLayoutParams; private WindowManager.LayoutParams popupLayoutParams;
private GestureDetector gestureDetector; private GestureDetector popupGestureDetector;
private View closeOverlayView;
private FloatingActionButton closeOverlayButton;
private WindowManager.LayoutParams closeOverlayLayoutParams;
private int shutdownFlingVelocity;
private int tossFlingVelocity; private int tossFlingVelocity;
private float screenWidth, screenHeight; private float screenWidth, screenHeight;
@ -122,6 +130,7 @@ public final class PopupVideoPlayer extends Service {
private VideoPlayerImpl playerImpl; private VideoPlayerImpl playerImpl;
private LockManager lockManager; private LockManager lockManager;
private boolean isPopupClosing = false;
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Service-Activity Binder // Service-Activity Binder
@ -150,7 +159,10 @@ public final class PopupVideoPlayer extends Service {
public int onStartCommand(final Intent intent, int flags, int startId) { public int onStartCommand(final Intent intent, int flags, int startId) {
if (DEBUG) if (DEBUG)
Log.d(TAG, "onStartCommand() called with: intent = [" + intent + "], flags = [" + flags + "], startId = [" + startId + "]"); Log.d(TAG, "onStartCommand() called with: intent = [" + intent + "], flags = [" + flags + "], startId = [" + startId + "]");
if (playerImpl.getPlayer() == null) initPopup(); if (playerImpl.getPlayer() == null) {
initPopup();
initPopupCloseOverlay();
}
if (!playerImpl.isPlaying()) playerImpl.getPlayer().setPlayWhenReady(true); if (!playerImpl.isPlaying()) playerImpl.getPlayer().setPlayWhenReady(true);
playerImpl.handleIntent(intent); playerImpl.handleIntent(intent);
@ -160,15 +172,16 @@ public final class PopupVideoPlayer extends Service {
@Override @Override
public void onConfigurationChanged(Configuration newConfig) { public void onConfigurationChanged(Configuration newConfig) {
if (DEBUG) Log.d(TAG, "onConfigurationChanged() called with: newConfig = [" + newConfig + "]");
updateScreenSize(); updateScreenSize();
updatePopupSize(windowLayoutParams.width, -1); updatePopupSize(popupLayoutParams.width, -1);
checkPositionBounds(); checkPopupPositionBounds();
} }
@Override @Override
public void onDestroy() { public void onDestroy() {
if (DEBUG) Log.d(TAG, "onDestroy() called"); if (DEBUG) Log.d(TAG, "onDestroy() called");
onClose(); closePopup();
} }
@Override @Override
@ -186,7 +199,6 @@ public final class PopupVideoPlayer extends Service {
View rootView = View.inflate(this, R.layout.player_popup, null); View rootView = View.inflate(this, R.layout.player_popup, null);
playerImpl.setup(rootView); playerImpl.setup(rootView);
shutdownFlingVelocity = PlayerHelper.getShutdownFlingVelocity(this);
tossFlingVelocity = PlayerHelper.getTossFlingVelocity(this); tossFlingVelocity = PlayerHelper.getTossFlingVelocity(this);
updateScreenSize(); updateScreenSize();
@ -200,27 +212,52 @@ public final class PopupVideoPlayer extends Service {
WindowManager.LayoutParams.TYPE_PHONE : WindowManager.LayoutParams.TYPE_PHONE :
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
windowLayoutParams = new WindowManager.LayoutParams( popupLayoutParams = new WindowManager.LayoutParams(
(int) popupWidth, (int) getMinimumVideoHeight(popupWidth), (int) popupWidth, (int) getMinimumVideoHeight(popupWidth),
layoutParamType, layoutParamType,
IDLE_WINDOW_FLAGS, IDLE_WINDOW_FLAGS,
PixelFormat.TRANSLUCENT); PixelFormat.TRANSLUCENT);
windowLayoutParams.gravity = Gravity.LEFT | Gravity.TOP; popupLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; popupLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
int centerX = (int) (screenWidth / 2f - popupWidth / 2f); int centerX = (int) (screenWidth / 2f - popupWidth / 2f);
int centerY = (int) (screenHeight / 2f - popupHeight / 2f); int centerY = (int) (screenHeight / 2f - popupHeight / 2f);
windowLayoutParams.x = popupRememberSizeAndPos ? sharedPreferences.getInt(POPUP_SAVED_X, centerX) : centerX; popupLayoutParams.x = popupRememberSizeAndPos ? sharedPreferences.getInt(POPUP_SAVED_X, centerX) : centerX;
windowLayoutParams.y = popupRememberSizeAndPos ? sharedPreferences.getInt(POPUP_SAVED_Y, centerY) : centerY; popupLayoutParams.y = popupRememberSizeAndPos ? sharedPreferences.getInt(POPUP_SAVED_Y, centerY) : centerY;
checkPositionBounds(); checkPopupPositionBounds();
MySimpleOnGestureListener listener = new MySimpleOnGestureListener(); PopupWindowGestureListener listener = new PopupWindowGestureListener();
gestureDetector = new GestureDetector(this, listener); popupGestureDetector = new GestureDetector(this, listener);
rootView.setOnTouchListener(listener); rootView.setOnTouchListener(listener);
playerImpl.getLoadingPanel().setMinimumWidth(windowLayoutParams.width);
playerImpl.getLoadingPanel().setMinimumHeight(windowLayoutParams.height); playerImpl.getLoadingPanel().setMinimumWidth(popupLayoutParams.width);
windowManager.addView(rootView, windowLayoutParams); playerImpl.getLoadingPanel().setMinimumHeight(popupLayoutParams.height);
windowManager.addView(rootView, popupLayoutParams);
}
@SuppressLint("RtlHardcoded")
private void initPopupCloseOverlay() {
if (DEBUG) Log.d(TAG, "initPopupCloseOverlay() called");
closeOverlayView = View.inflate(this, R.layout.player_popup_close_overlay, null);
closeOverlayButton = closeOverlayView.findViewById(R.id.closeButton);
final int layoutParamType = Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O ?
WindowManager.LayoutParams.TYPE_PHONE :
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
final int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
closeOverlayLayoutParams = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
layoutParamType,
flags,
PixelFormat.TRANSLUCENT);
closeOverlayLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
closeOverlayLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
closeOverlayButton.setVisibility(View.GONE);
windowManager.addView(closeOverlayView, closeOverlayLayoutParams);
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
@ -280,44 +317,105 @@ public final class PopupVideoPlayer extends Service {
// Misc // Misc
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
public void onClose() { public void closePopup() {
if (DEBUG) Log.d(TAG, "onClose() called"); if (DEBUG) Log.d(TAG, "closePopup() called, isPopupClosing = " + isPopupClosing);
if (isPopupClosing) return;
isPopupClosing = true;
if (playerImpl != null) { if (playerImpl != null) {
if (playerImpl.getRootView() != null) { if (playerImpl.getRootView() != null) {
windowManager.removeView(playerImpl.getRootView()); windowManager.removeView(playerImpl.getRootView());
playerImpl.setRootView(null);
} }
playerImpl.setRootView(null);
playerImpl.stopActivityBinding(); playerImpl.stopActivityBinding();
playerImpl.destroy(); playerImpl.destroy();
playerImpl = null;
} }
mBinder = null;
if (lockManager != null) lockManager.releaseWifiAndCpu(); if (lockManager != null) lockManager.releaseWifiAndCpu();
if (notificationManager != null) notificationManager.cancel(NOTIFICATION_ID); if (notificationManager != null) notificationManager.cancel(NOTIFICATION_ID);
mBinder = null;
playerImpl = null;
stopForeground(true); animateOverlayAndFinishService();
stopSelf(); }
private void animateOverlayAndFinishService() {
final int targetTranslationY = (int) (closeOverlayButton.getRootView().getHeight() - closeOverlayButton.getY());
closeOverlayButton.animate().setListener(null).cancel();
closeOverlayButton.animate()
.setInterpolator(new AnticipateInterpolator())
.translationY(targetTranslationY)
.setDuration(400)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationCancel(Animator animation) {
end();
}
@Override
public void onAnimationEnd(Animator animation) {
end();
}
private void end() {
windowManager.removeView(closeOverlayView);
stopForeground(true);
stopSelf();
}
}).start();
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Utils // Utils
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
private void checkPositionBounds() { /**
if (windowLayoutParams.x > screenWidth - windowLayoutParams.width) * @see #checkPopupPositionBounds(float, float)
windowLayoutParams.x = (int) (screenWidth - windowLayoutParams.width); */
if (windowLayoutParams.x < 0) windowLayoutParams.x = 0; @SuppressWarnings("UnusedReturnValue")
if (windowLayoutParams.y > screenHeight - windowLayoutParams.height) private boolean checkPopupPositionBounds() {
windowLayoutParams.y = (int) (screenHeight - windowLayoutParams.height); return checkPopupPositionBounds(screenWidth, screenHeight);
if (windowLayoutParams.y < 0) windowLayoutParams.y = 0; }
/**
* Check if {@link #popupLayoutParams}' position is within a arbitrary boundary that goes from (0,0) to (boundaryWidth,boundaryHeight).
* <p>
* If it's out of these boundaries, {@link #popupLayoutParams}' position is changed and {@code true} is returned
* to represent this change.
*
* @return if the popup was out of bounds and have been moved back to it
*/
private boolean checkPopupPositionBounds(final float boundaryWidth, final float boundaryHeight) {
if (DEBUG) {
Log.d(TAG, "checkPopupPositionBounds() called with: boundaryWidth = [" + boundaryWidth + "], boundaryHeight = [" + boundaryHeight + "]");
}
if (popupLayoutParams.x < 0) {
popupLayoutParams.x = 0;
return true;
} else if (popupLayoutParams.x > boundaryWidth - popupLayoutParams.width) {
popupLayoutParams.x = (int) (boundaryWidth - popupLayoutParams.width);
return true;
}
if (popupLayoutParams.y < 0) {
popupLayoutParams.y = 0;
return true;
} else if (popupLayoutParams.y > boundaryHeight - popupLayoutParams.height) {
popupLayoutParams.y = (int) (boundaryHeight - popupLayoutParams.height);
return true;
}
return false;
} }
private void savePositionAndSize() { private void savePositionAndSize() {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(PopupVideoPlayer.this); SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(PopupVideoPlayer.this);
sharedPreferences.edit().putInt(POPUP_SAVED_X, windowLayoutParams.x).apply(); sharedPreferences.edit().putInt(POPUP_SAVED_X, popupLayoutParams.x).apply();
sharedPreferences.edit().putInt(POPUP_SAVED_Y, windowLayoutParams.y).apply(); sharedPreferences.edit().putInt(POPUP_SAVED_Y, popupLayoutParams.y).apply();
sharedPreferences.edit().putFloat(POPUP_SAVED_WIDTH, windowLayoutParams.width).apply(); sharedPreferences.edit().putFloat(POPUP_SAVED_WIDTH, popupLayoutParams.width).apply();
} }
private float getMinimumVideoHeight(float width) { private float getMinimumVideoHeight(float width) {
@ -352,13 +450,13 @@ public final class PopupVideoPlayer extends Service {
if (height == -1) height = (int) getMinimumVideoHeight(width); if (height == -1) height = (int) getMinimumVideoHeight(width);
else height = (int) (height > maximumHeight ? maximumHeight : height < minimumHeight ? minimumHeight : height); else height = (int) (height > maximumHeight ? maximumHeight : height < minimumHeight ? minimumHeight : height);
windowLayoutParams.width = width; popupLayoutParams.width = width;
windowLayoutParams.height = height; popupLayoutParams.height = height;
popupWidth = width; popupWidth = width;
popupHeight = height; popupHeight = height;
if (DEBUG) Log.d(TAG, "updatePopupSize() updated values: width = [" + width + "], height = [" + height + "]"); if (DEBUG) Log.d(TAG, "updatePopupSize() updated values: width = [" + width + "], height = [" + height + "]");
windowManager.updateViewLayout(playerImpl.getRootView(), windowLayoutParams); windowManager.updateViewLayout(playerImpl.getRootView(), popupLayoutParams);
} }
protected void setRepeatModeRemote(final RemoteViews remoteViews, final int repeatMode) { protected void setRepeatModeRemote(final RemoteViews remoteViews, final int repeatMode) {
@ -380,10 +478,10 @@ public final class PopupVideoPlayer extends Service {
} }
private void updateWindowFlags(final int flags) { private void updateWindowFlags(final int flags) {
if (windowLayoutParams == null || windowManager == null || playerImpl == null) return; if (popupLayoutParams == null || windowManager == null || playerImpl == null) return;
windowLayoutParams.flags = flags; popupLayoutParams.flags = flags;
windowManager.updateViewLayout(playerImpl.getRootView(), windowLayoutParams); windowManager.updateViewLayout(playerImpl.getRootView(), popupLayoutParams);
} }
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@ -393,6 +491,7 @@ public final class PopupVideoPlayer extends Service {
private ImageView videoPlayPause; private ImageView videoPlayPause;
private View extraOptionsView; private View extraOptionsView;
private View closingOverlayView;
@Override @Override
public void handleIntent(Intent intent) { public void handleIntent(Intent intent) {
@ -413,12 +512,18 @@ public final class PopupVideoPlayer extends Service {
fullScreenButton = rootView.findViewById(R.id.fullScreenButton); fullScreenButton = rootView.findViewById(R.id.fullScreenButton);
fullScreenButton.setOnClickListener(v -> onFullScreenButtonClicked()); fullScreenButton.setOnClickListener(v -> onFullScreenButtonClicked());
videoPlayPause = rootView.findViewById(R.id.videoPlayPause); videoPlayPause = rootView.findViewById(R.id.videoPlayPause);
videoPlayPause.setOnClickListener(this::onPlayPauseButtonPressed);
extraOptionsView = rootView.findViewById(R.id.extraOptionsView); extraOptionsView = rootView.findViewById(R.id.extraOptionsView);
closingOverlayView = rootView.findViewById(R.id.closingOverlay);
rootView.addOnLayoutChangeListener(this); rootView.addOnLayoutChangeListener(this);
} }
@Override
public void initListeners() {
super.initListeners();
videoPlayPause.setOnClickListener(v -> onPlayPause());
}
@Override @Override
protected void setupSubtitleView(@NonNull SubtitleView view, protected void setupSubtitleView(@NonNull SubtitleView view,
final float captionScale, final float captionScale,
@ -429,10 +534,6 @@ public final class PopupVideoPlayer extends Service {
view.setStyle(captionStyle); view.setStyle(captionStyle);
} }
private void onPlayPauseButtonPressed(View ib) {
onPlayPause();
}
@Override @Override
public void onLayoutChange(final View view, int left, int top, int right, int bottom, public void onLayoutChange(final View view, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) { int oldLeft, int oldTop, int oldRight, int oldBottom) {
@ -476,7 +577,7 @@ public final class PopupVideoPlayer extends Service {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
} }
context.startActivity(intent); context.startActivity(intent);
onClose(); closePopup();
} }
@Override @Override
@ -634,7 +735,7 @@ public final class PopupVideoPlayer extends Service {
@Override @Override
public void onPlaybackShutdown() { public void onPlaybackShutdown() {
super.onPlaybackShutdown(); super.onPlaybackShutdown();
onClose(); closePopup();
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
@ -660,7 +761,7 @@ public final class PopupVideoPlayer extends Service {
if (DEBUG) Log.d(TAG, "onBroadcastReceived() called with: intent = [" + intent + "]"); if (DEBUG) Log.d(TAG, "onBroadcastReceived() called with: intent = [" + intent + "]");
switch (intent.getAction()) { switch (intent.getAction()) {
case ACTION_CLOSE: case ACTION_CLOSE:
onClose(); closePopup();
break; break;
case ACTION_PLAY_PAUSE: case ACTION_PLAY_PAUSE:
onPlayPause(); onPlayPause();
@ -791,12 +892,15 @@ public final class PopupVideoPlayer extends Service {
public TextView getResizingIndicator() { public TextView getResizingIndicator() {
return resizingIndicator; return resizingIndicator;
} }
public View getClosingOverlayView() {
return closingOverlayView;
}
} }
private class MySimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener { private class PopupWindowGestureListener extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener {
private int initialPopupX, initialPopupY; private int initialPopupX, initialPopupY;
private boolean isMoving; private boolean isMoving;
private boolean isResizing; private boolean isResizing;
@Override @Override
@ -832,10 +936,15 @@ public final class PopupVideoPlayer extends Service {
@Override @Override
public boolean onDown(MotionEvent e) { public boolean onDown(MotionEvent e) {
if (DEBUG) Log.d(TAG, "onDown() called with: e = [" + e + "]"); if (DEBUG) Log.d(TAG, "onDown() called with: e = [" + e + "]");
initialPopupX = windowLayoutParams.x;
initialPopupY = windowLayoutParams.y; // Fix popup position when the user touch it, it may have the wrong one
popupWidth = windowLayoutParams.width; // because the soft input is visible (the draggable area is currently resized).
popupHeight = windowLayoutParams.height; checkPopupPositionBounds(closeOverlayView.getWidth(), closeOverlayView.getHeight());
initialPopupX = popupLayoutParams.x;
initialPopupY = popupLayoutParams.y;
popupWidth = popupLayoutParams.width;
popupHeight = popupLayoutParams.height;
return super.onDown(e); return super.onDown(e);
} }
@ -843,20 +952,22 @@ public final class PopupVideoPlayer extends Service {
public void onLongPress(MotionEvent e) { public void onLongPress(MotionEvent e) {
if (DEBUG) Log.d(TAG, "onLongPress() called with: e = [" + e + "]"); if (DEBUG) Log.d(TAG, "onLongPress() called with: e = [" + e + "]");
updateScreenSize(); updateScreenSize();
checkPositionBounds(); checkPopupPositionBounds();
updatePopupSize((int) screenWidth, -1); updatePopupSize((int) screenWidth, -1);
} }
@Override @Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { public boolean onScroll(MotionEvent initialEvent, MotionEvent movingEvent, float distanceX, float distanceY) {
if (isResizing || playerImpl == null) return super.onScroll(e1, e2, distanceX, distanceY); if (isResizing || playerImpl == null) return super.onScroll(initialEvent, movingEvent, distanceX, distanceY);
if (!isMoving) {
animateView(closeOverlayButton, true, 200);
}
if (playerImpl.getCurrentState() != BasePlayer.STATE_BUFFERING
&& (!isMoving || playerImpl.getControlsRoot().getAlpha() != 1f)) playerImpl.showControls(0);
isMoving = true; isMoving = true;
float diffX = (int) (e2.getRawX() - e1.getRawX()), posX = (int) (initialPopupX + diffX); float diffX = (int) (movingEvent.getRawX() - initialEvent.getRawX()), posX = (int) (initialPopupX + diffX);
float diffY = (int) (e2.getRawY() - e1.getRawY()), posY = (int) (initialPopupY + diffY); float diffY = (int) (movingEvent.getRawY() - initialEvent.getRawY()), posY = (int) (initialPopupY + diffY);
if (posX > (screenWidth - popupWidth)) posX = (int) (screenWidth - popupWidth); if (posX > (screenWidth - popupWidth)) posX = (int) (screenWidth - popupWidth);
else if (posX < 0) posX = 0; else if (posX < 0) posX = 0;
@ -864,26 +975,49 @@ public final class PopupVideoPlayer extends Service {
if (posY > (screenHeight - popupHeight)) posY = (int) (screenHeight - popupHeight); if (posY > (screenHeight - popupHeight)) posY = (int) (screenHeight - popupHeight);
else if (posY < 0) posY = 0; else if (posY < 0) posY = 0;
windowLayoutParams.x = (int) posX; popupLayoutParams.x = (int) posX;
windowLayoutParams.y = (int) posY; popupLayoutParams.y = (int) posY;
final View closingOverlayView = playerImpl.getClosingOverlayView();
if (isInsideClosingRadius(movingEvent)) {
if (closingOverlayView.getVisibility() == View.GONE) {
animateView(closingOverlayView, true, 250);
}
} else {
if (closingOverlayView.getVisibility() == View.VISIBLE) {
animateView(closingOverlayView, false, 0);
}
}
//noinspection PointlessBooleanExpression //noinspection PointlessBooleanExpression
if (DEBUG && false) Log.d(TAG, "PopupVideoPlayer.onScroll = " + if (DEBUG && false) {
", e1.getRaw = [" + e1.getRawX() + ", " + e1.getRawY() + "]" + Log.d(TAG, "PopupVideoPlayer.onScroll = " +
", e2.getRaw = [" + e2.getRawX() + ", " + e2.getRawY() + "]" + ", e1.getRaw = [" + initialEvent.getRawX() + ", " + initialEvent.getRawY() + "]" + ", e1.getX,Y = [" + initialEvent.getX() + ", " + initialEvent.getY() + "]" +
", distanceXy = [" + distanceX + ", " + distanceY + "]" + ", e2.getRaw = [" + movingEvent.getRawX() + ", " + movingEvent.getRawY() + "]" + ", e2.getX,Y = [" + movingEvent.getX() + ", " + movingEvent.getY() + "]" +
", posXy = [" + posX + ", " + posY + "]" + ", distanceX,Y = [" + distanceX + ", " + distanceY + "]" +
", popupWh = [" + popupWidth + " x " + popupHeight + "]"); ", posX,Y = [" + posX + ", " + posY + "]" +
windowManager.updateViewLayout(playerImpl.getRootView(), windowLayoutParams); ", popupW,H = [" + popupWidth + " x " + popupHeight + "]");
}
windowManager.updateViewLayout(playerImpl.getRootView(), popupLayoutParams);
return true; return true;
} }
private void onScrollEnd() { private void onScrollEnd(MotionEvent event) {
if (DEBUG) Log.d(TAG, "onScrollEnd() called"); if (DEBUG) Log.d(TAG, "onScrollEnd() called");
if (playerImpl == null) return; if (playerImpl == null) return;
if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == STATE_PLAYING) { if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == STATE_PLAYING) {
playerImpl.hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME); playerImpl.hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME);
} }
if (isInsideClosingRadius(event)) {
closePopup();
} else {
animateView(playerImpl.getClosingOverlayView(), false, 0);
if (!isPopupClosing) {
animateView(closeOverlayButton, false, 200);
}
}
} }
@Override @Override
@ -893,14 +1027,11 @@ public final class PopupVideoPlayer extends Service {
final float absVelocityX = Math.abs(velocityX); final float absVelocityX = Math.abs(velocityX);
final float absVelocityY = Math.abs(velocityY); final float absVelocityY = Math.abs(velocityY);
if (absVelocityX > shutdownFlingVelocity) { if (Math.max(absVelocityX, absVelocityY) > tossFlingVelocity) {
onClose(); if (absVelocityX > tossFlingVelocity) popupLayoutParams.x = (int) velocityX;
return true; if (absVelocityY > tossFlingVelocity) popupLayoutParams.y = (int) velocityY;
} else if (Math.max(absVelocityX, absVelocityY) > tossFlingVelocity) { checkPopupPositionBounds();
if (absVelocityX > tossFlingVelocity) windowLayoutParams.x = (int) velocityX; windowManager.updateViewLayout(playerImpl.getRootView(), popupLayoutParams);
if (absVelocityY > tossFlingVelocity) windowLayoutParams.y = (int) velocityY;
checkPositionBounds();
windowManager.updateViewLayout(playerImpl.getRootView(), windowLayoutParams);
return true; return true;
} }
return false; return false;
@ -908,7 +1039,7 @@ public final class PopupVideoPlayer extends Service {
@Override @Override
public boolean onTouch(View v, MotionEvent event) { public boolean onTouch(View v, MotionEvent event) {
gestureDetector.onTouchEvent(event); popupGestureDetector.onTouchEvent(event);
if (playerImpl == null) return false; if (playerImpl == null) return false;
if (event.getPointerCount() == 2 && !isResizing) { if (event.getPointerCount() == 2 && !isResizing) {
if (DEBUG) Log.d(TAG, "onTouch() 2 finger pointer detected, enabling resizing."); if (DEBUG) Log.d(TAG, "onTouch() 2 finger pointer detected, enabling resizing.");
@ -931,7 +1062,7 @@ public final class PopupVideoPlayer extends Service {
Log.d(TAG, "onTouch() ACTION_UP > v = [" + v + "], e1.getRaw = [" + event.getRawX() + ", " + event.getRawY() + "]"); Log.d(TAG, "onTouch() ACTION_UP > v = [" + v + "], e1.getRaw = [" + event.getRawX() + ", " + event.getRawY() + "]");
if (isMoving) { if (isMoving) {
isMoving = false; isMoving = false;
onScrollEnd(); onScrollEnd(event);
} }
if (isResizing) { if (isResizing) {
@ -939,7 +1070,10 @@ public final class PopupVideoPlayer extends Service {
animateView(playerImpl.getResizingIndicator(), false, 100, 0); animateView(playerImpl.getResizingIndicator(), false, 100, 0);
playerImpl.changeState(playerImpl.getCurrentState()); playerImpl.changeState(playerImpl.getCurrentState());
} }
savePositionAndSize();
if (!isPopupClosing) {
savePositionAndSize();
}
} }
v.performClick(); v.performClick();
@ -955,13 +1089,13 @@ public final class PopupVideoPlayer extends Service {
final float diff = Math.abs(firstPointerX - secondPointerX); final float diff = Math.abs(firstPointerX - secondPointerX);
if (firstPointerX > secondPointerX) { if (firstPointerX > secondPointerX) {
// second pointer is the anchor (the leftmost pointer) // second pointer is the anchor (the leftmost pointer)
windowLayoutParams.x = (int) (event.getRawX() - diff); popupLayoutParams.x = (int) (event.getRawX() - diff);
} else { } else {
// first pointer is the anchor // first pointer is the anchor
windowLayoutParams.x = (int) event.getRawX(); popupLayoutParams.x = (int) event.getRawX();
} }
checkPositionBounds(); checkPopupPositionBounds();
updateScreenSize(); updateScreenSize();
final int width = (int) Math.min(screenWidth, diff); final int width = (int) Math.min(screenWidth, diff);
@ -969,5 +1103,29 @@ public final class PopupVideoPlayer extends Service {
return true; return true;
} }
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
private int distanceFromCloseButton(MotionEvent popupMotionEvent) {
final int closeOverlayButtonX = closeOverlayButton.getLeft() + closeOverlayButton.getWidth() / 2;
final int closeOverlayButtonY = closeOverlayButton.getTop() + closeOverlayButton.getHeight() / 2;
float fingerX = popupLayoutParams.x + popupMotionEvent.getX();
float fingerY = popupLayoutParams.y + popupMotionEvent.getY();
return (int) Math.sqrt(Math.pow(closeOverlayButtonX - fingerX, 2) + Math.pow(closeOverlayButtonY - fingerY, 2));
}
private float getClosingRadius() {
final int buttonRadius = closeOverlayButton.getWidth() / 2;
// 20% wider than the button itself
return buttonRadius * 1.2f;
}
private boolean isInsideClosingRadius(MotionEvent popupMotionEvent) {
return distanceFromCloseButton(popupMotionEvent) <= getClosingRadius();
}
} }
} }

View File

@ -16,6 +16,7 @@ import android.util.Log;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.PopupMenu; import android.widget.PopupMenu;
@ -562,6 +563,12 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
if (player != null) { if (player != null) {
progressLiveSync.setClickable(!player.isLiveEdge()); progressLiveSync.setClickable(!player.isLiveEdge());
} }
// this will make shure progressCurrentTime has the same width as progressEndTime
final ViewGroup.LayoutParams endTimeParams = progressEndTime.getLayoutParams();
final ViewGroup.LayoutParams currentTimeParams = progressCurrentTime.getLayoutParams();
currentTimeParams.width = progressEndTime.getWidth();
progressCurrentTime.setLayoutParams(currentTimeParams);
} }
@Override @Override

View File

@ -251,10 +251,6 @@ public class PlayerHelper {
return true; return true;
} }
public static int getShutdownFlingVelocity(@NonNull final Context context) {
return 6000;
}
public static int getTossFlingVelocity(@NonNull final Context context) { public static int getTossFlingVelocity(@NonNull final Context context) {
return 2500; return 2500;
} }

View File

@ -6,6 +6,7 @@ import android.util.Log;
import org.reactivestreams.Subscriber; import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription; import org.reactivestreams.Subscription;
import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.player.playqueue.events.AppendEvent; import org.schabi.newpipe.player.playqueue.events.AppendEvent;
import org.schabi.newpipe.player.playqueue.events.ErrorEvent; import org.schabi.newpipe.player.playqueue.events.ErrorEvent;
import org.schabi.newpipe.player.playqueue.events.InitEvent; import org.schabi.newpipe.player.playqueue.events.InitEvent;
@ -41,7 +42,7 @@ import io.reactivex.subjects.BehaviorSubject;
public abstract class PlayQueue implements Serializable { public abstract class PlayQueue implements Serializable {
private final String TAG = "PlayQueue@" + Integer.toHexString(hashCode()); private final String TAG = "PlayQueue@" + Integer.toHexString(hashCode());
public static final boolean DEBUG = true; public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");
private ArrayList<PlayQueueItem> backup; private ArrayList<PlayQueueItem> backup;
private ArrayList<PlayQueueItem> streams; private ArrayList<PlayQueueItem> streams;

View File

@ -15,7 +15,8 @@ public enum UserAction {
REQUESTED_CHANNEL("requested channel"), REQUESTED_CHANNEL("requested channel"),
REQUESTED_PLAYLIST("requested playlist"), REQUESTED_PLAYLIST("requested playlist"),
REQUESTED_KIOSK("requested kiosk"), REQUESTED_KIOSK("requested kiosk"),
DELETE_FROM_HISTORY("delete from history"); DELETE_FROM_HISTORY("delete from history"),
PLAY_STREAM("Play stream");
private final String message; private final String message;

View File

@ -0,0 +1,41 @@
package org.schabi.newpipe.settings;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
import android.widget.TextView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
public class AddTabsDialog {
private final AlertDialog dialog;
public AddTabsDialog(@NonNull final Context context,
@NonNull final String title,
@NonNull final String[] commands,
@NonNull final DialogInterface.OnClickListener actions) {
final View bannerView = View.inflate(context, R.layout.dialog_title, null);
bannerView.setSelected(true);
TextView titleView = bannerView.findViewById(R.id.itemTitleView);
titleView.setText(title);
TextView detailsView = bannerView.findViewById(R.id.itemAdditionalDetails);
detailsView.setVisibility(View.GONE);
dialog = new AlertDialog.Builder(context)
.setCustomTitle(bannerView)
.setItems(commands, actions)
.create();
}
public void show() {
dialog.show();
}
}

View File

@ -0,0 +1,291 @@
package org.schabi.newpipe.settings;
import android.app.Dialog;
import android.content.SharedPreferences;
import android.content.res.ColorStateList;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.CardView;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.util.ThemeHelper;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ChoseTabsFragment extends Fragment {
public ChoseTabsFragment.SelectedTabsAdapter selectedTabsAdapter;
RecyclerView selectedTabsView;
List<String> selectedTabs = new ArrayList<>();
private String saveString;
public String[] availableTabs = new String[7];
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
((AppCompatActivity)getContext()).getSupportActionBar().setTitle(R.string.main_page_content);
return inflater.inflate(R.layout.fragment_chose_tabs, container, false);
}
@Override
public void onViewCreated(@NonNull View rootView, @Nullable Bundle savedInstanceState) {
super.onViewCreated(rootView, savedInstanceState);
tabNames();
initUsedTabs();
initButton(rootView);
selectedTabsView = rootView.findViewById(R.id.usedTabs);
selectedTabsView.setLayoutManager(new LinearLayoutManager(getContext()));
selectedTabsAdapter = new ChoseTabsFragment.SelectedTabsAdapter();
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(getItemTouchCallback());
itemTouchHelper.attachToRecyclerView(selectedTabsView);
selectedTabsAdapter.setOnItemSelectedListener(itemTouchHelper);
selectedTabsView.setAdapter(selectedTabsAdapter);
}
private void saveChanges() {
StringBuilder save = new StringBuilder();
if(selectedTabs.size()==0) {
save = new StringBuilder("0");
} else {
for(String s: selectedTabs) {
save.append(s);
save.append("\n");
}
}
saveString = save.toString();
}
@Override
public void onPause() {
saveChanges();
SharedPreferences sharedPreferences = android.preference.PreferenceManager.getDefaultSharedPreferences(getContext());
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("saveUsedTabs", saveString);
editor.commit();
super.onPause();
}
private void initUsedTabs() {
String save = android.preference.PreferenceManager.getDefaultSharedPreferences(getContext()).getString("saveUsedTabs", "1\tTrending\t0\n2\n4\n");
String tabs[] = save.trim().split("\n");
selectedTabs.addAll(Arrays.asList(tabs));
}
private void tabNames() {
availableTabs[0] = getString(R.string.blank_page_summary);
availableTabs[1] = getString(R.string.kiosk_page_summary);
availableTabs[2] = getString(R.string.subscription_page_summary);
availableTabs[3] = getString(R.string.feed_page_summary);
availableTabs[4] = getString(R.string.tab_bookmarks);
availableTabs[5] = getString(R.string.title_activity_history);
availableTabs[6] = getString(R.string.channel_page_summary);
}
private void initButton(View rootView) {
FloatingActionButton fab = rootView.findViewById(R.id.floatingActionButton);
fab.setImageResource(ThemeHelper.getIconByAttr(R.attr.ic_add, getContext()));
fab.setOnClickListener(v -> {
Dialog.OnClickListener onClickListener = (dialog, which) -> addTab(which);
new AddTabsDialog(getContext(),
getString(R.string.tab_chose),
availableTabs,
onClickListener)
.show();
});
TypedValue typedValue = new TypedValue();
getActivity().getTheme().resolveAttribute(R.attr.colorPrimary, typedValue, true);
int color = typedValue.data;
fab.setBackgroundTintList(ColorStateList.valueOf(color));
}
private void addTab(int position) {
if(position==6) {
SelectChannelFragment selectChannelFragment = new SelectChannelFragment();
selectChannelFragment.setOnSelectedLisener((String url, String name, int service) -> {
selectedTabs.add(position+"\t"+url+"\t"+name+"\t"+service);
selectedTabsAdapter.notifyDataSetChanged();
saveChanges();
});
selectChannelFragment.show(getFragmentManager(), "select_channel");
} else if(position==1) {
SelectKioskFragment selectKioskFragment = new SelectKioskFragment();
selectKioskFragment.setOnSelectedLisener((String kioskId, int service_id) -> {
selectedTabs.add(position+"\t"+kioskId+"\t"+service_id);
selectedTabsAdapter.notifyDataSetChanged();
saveChanges();
});
selectKioskFragment.show(getFragmentManager(), "select_kiosk");
} else {
selectedTabs.add(String.valueOf(position));
selectedTabsAdapter.notifyDataSetChanged();
saveChanges();
}
}
public class SelectedTabsAdapter extends RecyclerView.Adapter<ChoseTabsFragment.SelectedTabsAdapter.TabViewHolder>{
private ItemTouchHelper itemTouchHelper;
public void setOnItemSelectedListener(ItemTouchHelper mItemTouchHelper) {
itemTouchHelper = mItemTouchHelper;
}
public void swapItems(int fromPosition, int toPosition) {
String temp = selectedTabs.get(fromPosition);
selectedTabs.set(fromPosition, selectedTabs.get(toPosition));
selectedTabs.set(toPosition, temp);
notifyItemMoved(fromPosition, toPosition);
saveChanges();
}
@Override
public ChoseTabsFragment.SelectedTabsAdapter.TabViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(getContext());
View view = inflater.inflate(R.layout.viewholder_chose_tabs, parent, false);
return new ChoseTabsFragment.SelectedTabsAdapter.TabViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ChoseTabsFragment.SelectedTabsAdapter.TabViewHolder holder, int position) {
holder.bind(position, holder);
}
@Override
public int getItemCount() {
return selectedTabs.size();
}
class TabViewHolder extends RecyclerView.ViewHolder {
TextView text;
View view;
CardView cardView;
ImageView handle;
public TabViewHolder(View itemView) {
super(itemView);
text = itemView.findViewById(R.id.tabName);
cardView = itemView.findViewById(R.id.layoutCard);
handle = itemView.findViewById(R.id.handle);
view = itemView;
}
void bind(int position, TabViewHolder holder) {
handle.setImageResource(ThemeHelper.getIconByAttr(R.attr.drag_handle, getContext()));
handle.setOnTouchListener(getOnTouchListener(holder));
view.setOnLongClickListener(getOnLongClickListener(holder));
if(selectedTabs.get(position).startsWith("6\t")) {
String channelInfo[] = selectedTabs.get(position).split("\t");
String channelName = "";
if (channelInfo.length == 4) channelName = channelInfo[2];
String textToSet = availableTabs[6] + ": " + channelName;
text.setText(textToSet);
} else if(selectedTabs.get(position).startsWith("1\t")) {
String kioskInfo[] = selectedTabs.get(position).split("\t");
String kioskName = "";
if (kioskInfo.length == 3) kioskName = kioskInfo[1];
String textToSet = availableTabs[1] + ": " + kioskName;
text.setText(textToSet);
} else {
text.setText(availableTabs[Integer.parseInt(selectedTabs.get(position))]);
}
}
private View.OnTouchListener getOnTouchListener(final RecyclerView.ViewHolder item) {
return (view, motionEvent) -> {
view.performClick();
if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
if(itemTouchHelper != null) itemTouchHelper.startDrag(item);
}
return false;
};
}
private View.OnLongClickListener getOnLongClickListener(TabViewHolder holder) {
return (view) -> {
if(itemTouchHelper != null) itemTouchHelper.startSwipe(holder);
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() ||
selectedTabsAdapter == null) {
return false;
}
final int sourceIndex = source.getAdapterPosition();
final int targetIndex = target.getAdapterPosition();
selectedTabsAdapter.swapItems(sourceIndex, targetIndex);
return true;
}
@Override
public boolean isLongPressDragEnabled() {
return false;
}
@Override
public boolean isItemViewSwipeEnabled() {
return false;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
int position = viewHolder.getAdapterPosition();
selectedTabs.remove(position);
selectedTabsAdapter.notifyItemRemoved(position);
saveChanges();
}
};
}
}

View File

@ -9,6 +9,7 @@ import android.os.Bundle;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.preference.ListPreference; import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.util.Log; import android.util.Log;
@ -20,15 +21,12 @@ import com.nostra13.universalimageloader.core.ImageLoader;
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.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.FilePickerActivityHelper; import org.schabi.newpipe.util.FilePickerActivityHelper;
import org.schabi.newpipe.util.KioskTranslator; import org.schabi.newpipe.util.KioskTranslator;
import org.schabi.newpipe.util.ZipHelper; import org.schabi.newpipe.util.ZipHelper;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@ -42,11 +40,8 @@ import java.util.Date;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.zip.ZipFile; import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import static android.content.Context.MODE_PRIVATE;
public class ContentSettingsFragment extends BasePreferenceFragment { public class ContentSettingsFragment extends BasePreferenceFragment {
private static final int REQUEST_IMPORT_PATH = 8945; private static final int REQUEST_IMPORT_PATH = 8945;
@ -98,68 +93,6 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
addPreferencesFromResource(R.xml.content_settings); addPreferencesFromResource(R.xml.content_settings);
final ListPreference mainPageContentPref = (ListPreference) findPreference(getString(R.string.main_page_content_key));
mainPageContentPref.setOnPreferenceChangeListener((Preference preference, Object newValueO) -> {
final String newValue = newValueO.toString();
final String mainPrefOldValue =
defaultPreferences.getString(getString(R.string.main_page_content_key), "blank_page");
final String mainPrefOldSummary = getMainPagePrefSummery(mainPrefOldValue, mainPageContentPref);
if(newValue.equals(getString(R.string.kiosk_page_key))) {
SelectKioskFragment selectKioskFragment = new SelectKioskFragment();
selectKioskFragment.setOnSelectedLisener((String kioskId, int service_id) -> {
defaultPreferences.edit()
.putInt(getString(R.string.main_page_selected_service), service_id).apply();
defaultPreferences.edit()
.putString(getString(R.string.main_page_selectd_kiosk_id), kioskId).apply();
String serviceName = "";
try {
serviceName = NewPipe.getService(service_id).getServiceInfo().getName();
} catch (ExtractionException e) {
onError(e);
}
String kioskName = KioskTranslator.getTranslatedKioskName(kioskId,
getContext());
String summary =
String.format(getString(R.string.service_kiosk_string),
serviceName,
kioskName);
mainPageContentPref.setSummary(summary);
});
selectKioskFragment.setOnCancelListener(() -> {
mainPageContentPref.setSummary(mainPrefOldSummary);
mainPageContentPref.setValue(mainPrefOldValue);
});
selectKioskFragment.show(getFragmentManager(), "select_kiosk");
} else if(newValue.equals(getString(R.string.channel_page_key))) {
SelectChannelFragment selectChannelFragment = new SelectChannelFragment();
selectChannelFragment.setOnSelectedLisener((String url, String name, int service) -> {
defaultPreferences.edit()
.putInt(getString(R.string.main_page_selected_service), service).apply();
defaultPreferences.edit()
.putString(getString(R.string.main_page_selected_channel_url), url).apply();
defaultPreferences.edit()
.putString(getString(R.string.main_page_selected_channel_name), name).apply();
mainPageContentPref.setSummary(name);
});
selectChannelFragment.setOnCancelListener(() -> {
mainPageContentPref.setSummary(mainPrefOldSummary);
mainPageContentPref.setValue(mainPrefOldValue);
});
selectChannelFragment.show(getFragmentManager(), "select_channel");
} else {
mainPageContentPref.setSummary(getMainPageSummeryByKey(newValue));
}
defaultPreferences.edit().putBoolean(Constants.KEY_MAIN_PAGE_CHANGE, true).apply();
return true;
});
Preference importDataPreference = findPreference(getString(R.string.import_data)); Preference importDataPreference = findPreference(getString(R.string.import_data));
importDataPreference.setOnPreferenceClickListener((Preference p) -> { importDataPreference.setOnPreferenceClickListener((Preference p) -> {
Intent i = new Intent(getActivity(), FilePickerActivityHelper.class) Intent i = new Intent(getActivity(), FilePickerActivityHelper.class)
@ -349,66 +282,6 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
} }
} }
@Override
public void onResume() {
super.onResume();
final String mainPageContentKey = getString(R.string.main_page_content_key);
final Preference mainPagePref = findPreference(getString(R.string.main_page_content_key));
final String bpk = getString(R.string.blank_page_key);
if(defaultPreferences.getString(mainPageContentKey, bpk)
.equals(getString(R.string.channel_page_key))) {
mainPagePref.setSummary(defaultPreferences.getString(getString(R.string.main_page_selected_channel_name), "error"));
} else if(defaultPreferences.getString(mainPageContentKey, bpk)
.equals(getString(R.string.kiosk_page_key))) {
try {
StreamingService service = NewPipe.getService(
defaultPreferences.getInt(
getString(R.string.main_page_selected_service), 0));
String kioskName = KioskTranslator.getTranslatedKioskName(
defaultPreferences.getString(
getString(R.string.main_page_selectd_kiosk_id), "Trending"),
getContext());
String summary =
String.format(getString(R.string.service_kiosk_string),
service.getServiceInfo().getName(),
kioskName);
mainPagePref.setSummary(summary);
} catch (Exception e) {
onError(e);
}
}
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
private String getMainPagePrefSummery(final String mainPrefOldValue, final ListPreference mainPageContentPref) {
if(mainPrefOldValue.equals(getString(R.string.channel_page_key))) {
return defaultPreferences.getString(getString(R.string.main_page_selected_channel_name), "error");
} else {
return mainPageContentPref.getSummary().toString();
}
}
private int getMainPageSummeryByKey(final String key) {
if(key.equals(getString(R.string.blank_page_key))) {
return R.string.blank_page_summary;
} else if(key.equals(getString(R.string.kiosk_page_key))) {
return R.string.kiosk_page_summary;
} else if(key.equals(getString(R.string.feed_page_key))) {
return R.string.feed_page_summary;
} else if(key.equals(getString(R.string.subscription_page_key))) {
return R.string.subscription_page_summary;
} else if(key.equals(getString(R.string.channel_page_key))) {
return R.string.channel_page_summary;
}
return R.string.blank_page_summary;
}
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Error // Error
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/

View File

@ -71,7 +71,7 @@ public class NewPipeSettings {
} }
public static File getVideoDownloadFolder(Context context) { public static File getVideoDownloadFolder(Context context) {
return getFolder(context, R.string.download_path_key, Environment.DIRECTORY_MOVIES); return getDir(context, R.string.download_path_key, Environment.DIRECTORY_MOVIES);
} }
public static String getVideoDownloadPath(Context context) { public static String getVideoDownloadPath(Context context) {
@ -81,7 +81,7 @@ public class NewPipeSettings {
} }
public static File getAudioDownloadFolder(Context context) { public static File getAudioDownloadFolder(Context context) {
return getFolder(context, R.string.download_path_audio_key, Environment.DIRECTORY_MUSIC); return getDir(context, R.string.download_path_audio_key, Environment.DIRECTORY_MUSIC);
} }
public static String getAudioDownloadPath(Context context) { public static String getAudioDownloadPath(Context context) {
@ -90,21 +90,37 @@ public class NewPipeSettings {
return prefs.getString(key, Environment.DIRECTORY_MUSIC); return prefs.getString(key, Environment.DIRECTORY_MUSIC);
} }
private static File getFolder(Context context, int keyID, String defaultDirectoryName) { private static File getDir(Context context, int keyID, String defaultDirectoryName) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
final String key = context.getString(keyID); final String key = context.getString(keyID);
String downloadPath = prefs.getString(key, null); String downloadPath = prefs.getString(key, null);
if ((downloadPath != null) && (!downloadPath.isEmpty())) return new File(downloadPath.trim()); if ((downloadPath != null) && (!downloadPath.isEmpty())) return new File(downloadPath.trim());
final File folder = getFolder(defaultDirectoryName); final File dir = getDir(defaultDirectoryName);
SharedPreferences.Editor spEditor = prefs.edit(); SharedPreferences.Editor spEditor = prefs.edit();
spEditor.putString(key, new File(folder, "NewPipe").getAbsolutePath()); spEditor.putString(key, getNewPipeChildFolderPathForDir(dir));
spEditor.apply(); spEditor.apply();
return folder; return dir;
} }
@NonNull @NonNull
private static File getFolder(String defaultDirectoryName) { private static File getDir(String defaultDirectoryName) {
return new File(Environment.getExternalStorageDirectory(), defaultDirectoryName); return new File(Environment.getExternalStorageDirectory(), defaultDirectoryName);
} }
public static void resetDownloadFolders(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
resetDownloadFolder(prefs, context.getString(R.string.download_path_audio_key), Environment.DIRECTORY_MUSIC);
resetDownloadFolder(prefs, context.getString(R.string.download_path_key), Environment.DIRECTORY_MOVIES);
}
private static void resetDownloadFolder(SharedPreferences prefs, String key, String defaultDirectoryName) {
SharedPreferences.Editor spEditor = prefs.edit();
spEditor.putString(key, getNewPipeChildFolderPathForDir(getDir(defaultDirectoryName)));
spEditor.apply();
}
private static String getNewPipeChildFolderPathForDir(File dir) {
return new File(dir, "NewPipe").getAbsolutePath();
}
} }

View File

@ -73,7 +73,7 @@ public final class ExtractorHelper {
return Single.fromCallable(() -> return Single.fromCallable(() ->
SearchInfo.getInfo(NewPipe.getService(serviceId), SearchInfo.getInfo(NewPipe.getService(serviceId),
NewPipe.getService(serviceId) NewPipe.getService(serviceId)
.getSearchQIHFactory() .getSearchQHFactory()
.fromQuery(searchString, contentFilter, sortFilter), .fromQuery(searchString, contentFilter, sortFilter),
contentCountry)); contentCountry));
} }
@ -88,7 +88,7 @@ public final class ExtractorHelper {
return Single.fromCallable(() -> return Single.fromCallable(() ->
SearchInfo.getMoreItems(NewPipe.getService(serviceId), SearchInfo.getMoreItems(NewPipe.getService(serviceId),
NewPipe.getService(serviceId) NewPipe.getService(serviceId)
.getSearchQIHFactory() .getSearchQHFactory()
.fromQuery(searchString, contentFilter, sortFilter), .fromQuery(searchString, contentFilter, sortFilter),
contentCountry, contentCountry,
pageUrl)); pageUrl));

View File

@ -24,7 +24,7 @@ import org.schabi.newpipe.R;
public class KioskTranslator { public class KioskTranslator {
public static String getTranslatedKioskName(String kioskId, Context c) { public static String getTranslatedKioskName(String kioskId, Context c) {
switch(kioskId) { switch (kioskId) {
case "Trending": case "Trending":
return c.getString(R.string.trending); return c.getString(R.string.trending);
case "Top 50": case "Top 50":
@ -35,4 +35,17 @@ public class KioskTranslator {
return kioskId; return kioskId;
} }
} }
public static int getKioskIcons(String kioskId, Context c) {
switch(kioskId) {
case "Trending":
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_hot);
case "Top 50":
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_hot);
case "New & hot":
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_hot);
default:
return 0;
}
}
} }

View File

@ -31,17 +31,17 @@ import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.Stream; import org.schabi.newpipe.extractor.stream.Stream;
import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler;
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;
import org.schabi.newpipe.fragments.list.channel.ChannelFragment; import org.schabi.newpipe.fragments.list.channel.ChannelFragment;
import org.schabi.newpipe.local.bookmark.BookmarkFragment;
import org.schabi.newpipe.local.feed.FeedFragment; import org.schabi.newpipe.local.feed.FeedFragment;
import org.schabi.newpipe.fragments.list.kiosk.KioskFragment; import org.schabi.newpipe.fragments.list.kiosk.KioskFragment;
import org.schabi.newpipe.fragments.list.playlist.PlaylistFragment; import org.schabi.newpipe.fragments.list.playlist.PlaylistFragment;
import org.schabi.newpipe.fragments.list.search.SearchFragment; import org.schabi.newpipe.fragments.list.search.SearchFragment;
import org.schabi.newpipe.local.history.StatisticsPlaylistFragment; import org.schabi.newpipe.local.history.StatisticsPlaylistFragment;
import org.schabi.newpipe.local.playlist.LocalPlaylistFragment; import org.schabi.newpipe.local.playlist.LocalPlaylistFragment;
import org.schabi.newpipe.local.subscription.SubscriptionFragment;
import org.schabi.newpipe.local.subscription.SubscriptionsImportFragment; import org.schabi.newpipe.local.subscription.SubscriptionsImportFragment;
import org.schabi.newpipe.player.BackgroundPlayer; import org.schabi.newpipe.player.BackgroundPlayer;
import org.schabi.newpipe.player.BackgroundPlayerActivity; import org.schabi.newpipe.player.BackgroundPlayerActivity;
@ -349,6 +349,20 @@ public class NavigationHelper {
.commit(); .commit();
} }
public static void openBookmarksFragment(FragmentManager fragmentManager) {
defaultTransaction(fragmentManager)
.replace(R.id.fragment_holder, new BookmarkFragment())
.addToBackStack(null)
.commit();
}
public static void openSubscriptionFragment(FragmentManager fragmentManager) {
defaultTransaction(fragmentManager)
.replace(R.id.fragment_holder, new SubscriptionFragment())
.addToBackStack(null)
.commit();
}
public static void openKioskFragment(FragmentManager fragmentManager, int serviceId, String kioskId) throws ExtractionException { public static void openKioskFragment(FragmentManager fragmentManager, int serviceId, String kioskId) throws ExtractionException {
defaultTransaction(fragmentManager) defaultTransaction(fragmentManager)
.replace(R.id.fragment_holder, KioskFragment.getInstance(serviceId, kioskId)) .replace(R.id.fragment_holder, KioskFragment.getInstance(serviceId, kioskId))

View File

@ -1,10 +1,17 @@
package us.shandian.giga.get; package us.shandian.giga.get;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import org.schabi.newpipe.download.ExtSDDownloadFailedActivity;
import java.io.File; import java.io.File;
import java.io.FilenameFilter; import java.io.FilenameFilter;
import java.io.IOException;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
@ -23,7 +30,9 @@ public class DownloadManagerImpl implements DownloadManager {
private static final String TAG = DownloadManagerImpl.class.getSimpleName(); private static final String TAG = DownloadManagerImpl.class.getSimpleName();
private final DownloadDataSource mDownloadDataSource; private final DownloadDataSource mDownloadDataSource;
private final ArrayList<DownloadMission> mMissions = new ArrayList<DownloadMission>(); private final ArrayList<DownloadMission> mMissions = new ArrayList<>();
@NonNull
private final Context context;
/** /**
* Create a new instance * Create a new instance
@ -33,6 +42,13 @@ public class DownloadManagerImpl implements DownloadManager {
*/ */
public DownloadManagerImpl(Collection<String> searchLocations, DownloadDataSource downloadDataSource) { public DownloadManagerImpl(Collection<String> searchLocations, DownloadDataSource downloadDataSource) {
mDownloadDataSource = downloadDataSource; mDownloadDataSource = downloadDataSource;
this.context = null;
loadMissions(searchLocations);
}
public DownloadManagerImpl(Collection<String> searchLocations, DownloadDataSource downloadDataSource, Context context) {
mDownloadDataSource = downloadDataSource;
this.context = context;
loadMissions(searchLocations); loadMissions(searchLocations);
} }
@ -277,10 +293,12 @@ public class DownloadManagerImpl implements DownloadManager {
} }
private class Initializer extends Thread { private class Initializer extends Thread {
private DownloadMission mission; private final DownloadMission mission;
private final Handler handler;
public Initializer(DownloadMission mission) { public Initializer(DownloadMission mission) {
this.mission = mission; this.mission = mission;
this.handler = new Handler();
} }
@Override @Override
@ -335,6 +353,13 @@ public class DownloadManagerImpl implements DownloadManager {
af.close(); af.close();
mission.start(); mission.start();
} catch (IOException ie) {
if(context == null) throw new RuntimeException(ie);
if(ie.getMessage().contains("Permission denied")) {
handler.post(() ->
context.startActivity(new Intent(context, ExtSDDownloadFailedActivity.class)));
} else throw new RuntimeException(ie);
} catch (Exception e) { } catch (Exception e) {
// TODO Notify // TODO Notify
throw new RuntimeException(e); throw new RuntimeException(e);

View File

@ -81,7 +81,7 @@ public class DownloadManagerService extends Service {
ArrayList<String> paths = new ArrayList<>(2); ArrayList<String> paths = new ArrayList<>(2);
paths.add(NewPipeSettings.getVideoDownloadPath(this)); paths.add(NewPipeSettings.getVideoDownloadPath(this));
paths.add(NewPipeSettings.getAudioDownloadPath(this)); paths.add(NewPipeSettings.getAudioDownloadPath(this));
mManager = new DownloadManagerImpl(paths, mDataSource); mManager = new DownloadManagerImpl(paths, mDataSource, this);
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "mManager == null"); Log.d(TAG, "mManager == null");
Log.d(TAG, "Download directory: " + paths); Log.d(TAG, "Download directory: " + paths);

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 611 B

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#64000000" />
</shape>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="72dp"
android:height="72dp"
android:tint="#FFFFFF"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M20,8.69L20,4h-4.69L12,0.69 8.69,4L4,4v4.69L0.69,12 4,15.31L4,20h4.69L12,23.31 15.31,20L20,20v-4.69L23.31,12 20,8.69zM12,18c-3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6 6,2.69 6,6 -2.69,6 -6,6zM12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4z" />
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="72dp"
android:height="72dp"
android:tint="#FFFFFF"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M20,15.31L23.31,12 20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69zM12,18c-3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6 6,2.69 6,6 -2.69,6 -6,6z" />
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="72dp"
android:height="72dp"
android:tint="#FFFFFF"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M20,15.31L23.31,12 20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69zM12,18V6c3.31,0 6,2.69 6,6s-2.69,6 -6,6z" />
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="72dp"
android:height="72dp"
android:tint="#FFFFFF"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M18.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM5,9v6h4l5,5V4L9,9H5z" />
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="72dp"
android:height="72dp"
android:tint="#FFFFFF"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M7,9v6h4l5,5V4l-5,5H7z" />
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="72dp"
android:height="72dp"
android:tint="#FFFFFF"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v2.21l2.45,2.45c0.03,-0.2 0.05,-0.41 0.05,-0.63zM19,12c0,0.94 -0.2,1.82 -0.54,2.64l1.51,1.51C20.63,14.91 21,13.5 21,12c0,-4.28 -2.99,-7.86 -7,-8.77v2.06c2.89,0.86 5,3.54 5,6.71zM4.27,3L3,4.27 7.73,9L3,9v6h4l5,5v-6.73l4.25,4.25c-0.67,0.52 -1.42,0.93 -2.25,1.18v2.06c1.38,-0.31 2.63,-0.95 3.69,-1.81L19.73,21 21,19.73l-9,-9L4.27,3zM12,4L9.91,6.09 12,8.18L12,4z" />
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="72dp"
android:height="72dp"
android:tint="#FFFFFF"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z" />
</vector>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="-90"
android:toDegrees="-90">
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:innerRadiusRatio="2.25"
android:shape="ring"
android:thicknessRatio="17.75"
android:useLevel="true">
<solid android:color="@android:color/white" />
</shape>
</rotate>

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="150dp"
android:clickable="true"
android:focusable="true">
<Button
android:id="@+id/drawer_header_action_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/selectableItemBackground" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorPrimary"
android:scaleType="centerCrop"
android:src="@drawable/background_header" />
<ImageView
android:id="@+id/drawer_header_np_nude_view"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginLeft="30dp"
android:layout_marginStart="30dp"
android:layout_marginTop="30dp"
android:src="@drawable/np_logo_nude_shadow" />
<TextView
android:id="@+id/drawer_header_np_text_view"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_alignBottom="@id/drawer_header_np_nude_view"
android:layout_alignTop="@id/drawer_header_np_nude_view"
android:layout_toEndOf="@id/drawer_header_np_nude_view"
android:layout_toRightOf="@id/drawer_header_np_nude_view"
android:gravity="center"
android:text="@string/app_name"
android:textSize="30dp"
android:textStyle="bold|italic" />
<TextView
android:id="@+id/drawer_header_service_view"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_alignLeft="@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:text="YouTube"
android:textSize="18dp"
android:textStyle="italic" />
<ImageView
android:id="@+id/drawer_arrow"
android:layout_width="30dp"
android:layout_height="35dp"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_gravity="bottom"
android:layout_marginBottom="0dp"
android:paddingBottom="20dp"
android:paddingEnd="20dp"
android:paddingRight="20dp"
android:src="@drawable/ic_arrow_down_white" />
</RelativeLayout>

View File

@ -4,7 +4,8 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout" android:id="@+id/drawer_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:fitsSystemWindows="true">
<FrameLayout <FrameLayout
@ -23,4 +24,4 @@
<include layout="@layout/drawer_layout"/> <include layout="@layout/drawer_layout"/>
</android.support.v4.widget.DrawerLayout> </android.support.v4.widget.DrawerLayout>

View File

@ -471,10 +471,11 @@
android:id="@+id/controlAnimationView" android:id="@+id/controlAnimationView"
android:layout_width="100dp" android:layout_width="100dp"
android:layout_height="100dp" android:layout_height="100dp"
android:src="@drawable/ic_action_av_fast_rewind" android:background="@drawable/background_oval_black_transparent"
android:visibility="gone" android:visibility="gone"
tools:ignore="ContentDescription" tools:ignore="ContentDescription"
tools:visibility="visible"/> tools:src="@drawable/ic_action_av_fast_rewind"
tools:visibility="visible" />
</LinearLayout> </LinearLayout>
@ -503,43 +504,57 @@
android:layout_toRightOf="@+id/loading_panel" android:layout_toRightOf="@+id/loading_panel"
tools:ignore="RtlHardcoded"> tools:ignore="RtlHardcoded">
<TextView <RelativeLayout
android:id="@+id/volumeTextView" android:id="@+id/volumeRelativeLayout"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:layout_marginLeft="20dp" android:background="@drawable/background_oval_black_transparent"
android:background="#64000000"
android:paddingBottom="10dp"
android:paddingLeft="30dp"
android:paddingRight="30dp"
android:paddingTop="10dp"
android:textColor="@android:color/white"
android:textSize="35sp"
android:visibility="gone" android:visibility="gone"
tools:ignore="RtlHardcoded" tools:visibility="visible">
tools:text="Volume 0"
tools:visibility="visible" />
<TextView <ProgressBar
android:id="@+id/brightnessTextView" android:id="@+id/volumeProgressBar"
style="?android:progressBarStyleHorizontal"
android:layout_width="128dp"
android:layout_height="128dp"
android:indeterminate="false"
android:progressDrawable="@drawable/progress_circular_white" />
<ImageView
android:id="@+id/volumeImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
tools:ignore="ContentDescription"
tools:src="@drawable/ic_volume_up_white_72dp" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/brightnessRelativeLayout"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:layout_marginRight="20dp" android:background="@drawable/background_oval_black_transparent"
android:background="#64000000"
android:paddingBottom="10dp"
android:paddingLeft="30dp"
android:paddingRight="30dp"
android:paddingTop="10dp"
android:textColor="@android:color/white"
android:textSize="35sp"
android:visibility="gone" android:visibility="gone"
tools:ignore="RtlHardcoded" tools:visibility="visible">
tools:text="Brightness 0"
tools:visibility="visible" /> <ProgressBar
android:id="@+id/brightnessProgressBar"
style="?android:progressBarStyleHorizontal"
android:layout_width="128dp"
android:layout_height="128dp"
android:indeterminate="false"
android:progressDrawable="@drawable/progress_circular_white" />
<ImageView
android:id="@+id/brightnessImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
tools:ignore="ContentDescription"
tools:src="@drawable/ic_brightness_high_white_72dp" />
</RelativeLayout>
<TextView <TextView
android:id="@+id/currentDisplaySeek" android:id="@+id/currentDisplaySeek"

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="150dp"
android:clickable="true"
android:focusable="true">
<Button
android:id="@+id/drawer_header_action_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/selectableItemBackground"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorPrimary"
android:src="@drawable/background_header"
android:scaleType="centerCrop"/>
<ImageView
android:id="@+id/drawer_header_np_nude_view"
android:layout_marginLeft="30dp"
android:layout_marginStart="30dp"
android:layout_marginTop="20dp"
android:layout_width="70dp"
android:layout_height="70dp"
android:src="@drawable/np_logo_nude_shadow"/>
<TextView
android:id="@+id/drawer_header_np_text_view"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:text="@string/app_name"
android:layout_toRightOf="@id/drawer_header_np_nude_view"
android:layout_toEndOf="@id/drawer_header_np_nude_view"
android:layout_alignTop="@id/drawer_header_np_nude_view"
android:layout_alignBottom="@id/drawer_header_np_nude_view"
android:gravity="center"
android:textSize="30dp"
android:textStyle="bold|italic"/>
<TextView
android:id="@+id/drawer_header_service_view"
android:layout_width="100dp"
android:layout_height="100dp"
android:text="YouTube"
android:layout_below="@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:textSize="18dp"
android:textStyle="italic"/>
<ImageView
android:id="@+id/drawer_arrow"
android:layout_width="30dp"
android:layout_height="35dp"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_gravity="bottom"
android:layout_marginBottom="0dp"
android:paddingBottom="20dp"
android:paddingRight="20dp"
android:src="@drawable/ic_arrow_down_white"
android:paddingEnd="20dp" />
</RelativeLayout>

View File

@ -1,81 +1,22 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="@+id/navigation_layout" <android.support.design.widget.NavigationView android:id="@+id/navigation_layout"
android:orientation="vertical" android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_gravity="start" android:layout_gravity="start"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="?attr/android:windowBackground"
android:clickable="true" android:clickable="true"
android:focusable="true"> android:focusable="true"
>
<RelativeLayout
android:id="@+id/drawer_header"
android:layout_width="0dp"
android:layout_height="150dp"
android:layout_alignLeft="@id/navigation"
android:layout_alignRight="@id/navigation"
android:layout_alignStart="@id/navigation"
android:layout_alignEnd="@id/navigation"
android:clickable="true"
android:focusable="true">
<Button
android:id="@+id/drawer_header_action_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/selectableItemBackground"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorPrimary"
android:src="@drawable/background_header"
android:scaleType="centerCrop"/>
<ImageView
android:id="@+id/drawer_header_np_nude_view"
android:layout_marginLeft="30dp"
android:layout_marginStart="30dp"
android:layout_marginTop="20dp"
android:layout_width="70dp"
android:layout_height="70dp"
android:src="@drawable/np_logo_nude_shadow"/>
<TextView
android:id="@+id/drawer_header_np_text_view"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:text="@string/app_name"
android:layout_toRightOf="@id/drawer_header_np_nude_view"
android:layout_toEndOf="@id/drawer_header_np_nude_view"
android:layout_alignTop="@id/drawer_header_np_nude_view"
android:layout_alignBottom="@id/drawer_header_np_nude_view"
android:gravity="center"
android:textSize="30dp"
android:textStyle="bold|italic"/>
<TextView
android:id="@+id/drawer_header_service_view"
android:layout_width="100dp"
android:layout_height="100dp"
android:text="YouTube"
android:layout_below="@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:textSize="18dp"
android:textStyle="italic"/>
</RelativeLayout>
<android.support.design.widget.NavigationView <android.support.design.widget.NavigationView
android:id="@+id/navigation" android:id="@+id/navigation"
android:layout_below="@id/drawer_header"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:elevation="0dp"/> app:elevation="0dp"
android:background="?attr/android:windowBackground"
app:headerLayout="@layout/drawer_header"/>
<!-- app:menu="@menu/drawer_items" --> <!-- app:menu="@menu/drawer_items" -->
<LinearLayout <LinearLayout
@ -89,29 +30,6 @@
android:layout_alignEnd="@id/navigation" android:layout_alignEnd="@id/navigation"
android:layout_alignParentBottom="true"> android:layout_alignParentBottom="true">
<ImageButton
android:id="@+id/drawer_settings"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="?attr/settings"/>
<ImageButton
android:id="@+id/drawer_downloads"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="?attr/download" />
<ImageButton
android:id="@+id/drawer_history"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="?attr/history"/>
</LinearLayout> </LinearLayout>
</RelativeLayout> </android.support.design.widget.NavigationView>

View File

@ -0,0 +1,32 @@
<?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"
android:id="@+id/relLay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/usedTabs"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="0dp"
android:paddingBottom="0dp"
android:paddingTop="0dp" >
</android.support.v7.widget.RecyclerView>
<android.support.design.widget.FloatingActionButton
android:id="@+id/floatingActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_marginBottom="16dp"
android:layout_marginEnd="16dp"
android:clickable="true"
android:layout_alignParentRight="true"
android:layout_marginRight="16dp"
android:focusable="true" />
</RelativeLayout>

View File

@ -9,7 +9,6 @@
tools:layout_height="84dp" tools:layout_height="84dp"
tools:layout_width="@dimen/popup_minimum_width"> tools:layout_width="@dimen/popup_minimum_width">
<com.google.android.exoplayer2.ui.AspectRatioFrameLayout <com.google.android.exoplayer2.ui.AspectRatioFrameLayout
android:id="@+id/aspectRatioLayout" android:id="@+id/aspectRatioLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -290,4 +289,11 @@
android:visibility="gone" android:visibility="gone"
tools:ignore="RtlHardcoded" tools:ignore="RtlHardcoded"
tools:visibility="gone"/> tools:visibility="gone"/>
<View
android:id="@+id/closingOverlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#AAFF0000"
android:visibility="gone"/>
</FrameLayout> </FrameLayout>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.FloatingActionButton
android:id="@+id/closeButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginBottom="24dp"
android:src="@drawable/ic_close_white_24dp"
app:backgroundTint="@color/light_youtube_primary_color"
app:borderWidth="0dp"
app:fabSize="normal"/>
</FrameLayout>

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
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:orientation="horizontal"
android:paddingBottom="4dp"
android:paddingTop="4dp"
app:cardCornerRadius="5dp"
app:cardElevation="4dp">
<ImageView
android:id="@+id/handle"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center_vertical|end"
android:layout_marginRight="10dp" />
<TextView
android:id="@+id/tabName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:paddingBottom="9dp"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:paddingStart="3dp"
android:paddingTop="9dp"
android:textAppearance="?android:attr/textAppearanceListItem"
android:textSize="16sp" />
</android.support.v7.widget.CardView>

View File

@ -2,13 +2,11 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android">
<group <group
android:id="@+id/menu_services_group"> android:id="@+id/menu_services_group">
<item </group>
android:id="@+id/menu_service_youtube" <group
android:icon="@drawable/place_holder_youtube" android:id="@+id/menu_tabs_group">
android:title="@string/youtube"/> </group>
<item <group
android:id="@+id/menu_service_soundcloud" android:id="@+id/menu_options_about_group">
android:icon="@drawable/place_holder_circle"
android:title="@string/soundcloud"/>
</group> </group>
</menu> </menu>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item <item
android:id="@+id/action_show_downloads" android:id="@+id/action_show_downloads"
@ -19,9 +19,4 @@
android:orderInCategory="990" android:orderInCategory="990"
android:title="@string/settings" android:title="@string/settings"
app:showAsAction="never"/> app:showAsAction="never"/>
<item
android:id="@+id/action_about"
android:orderInCategory="1000"
android:title="@string/action_about"/>
</menu> </menu>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- YouTube -->
<style name="LightTheme.YouTube" parent="LightTheme.Switchable">
<item name="colorPrimaryDark">@color/light_youtube_statusbar_color</item>
</style>
<style name="DarkTheme.YouTube" parent="DarkTheme.Switchable">
<item name="colorPrimaryDark">@color/dark_youtube_statusbar_color</item>
</style>
<style name="BlackTheme.YouTube" parent="BlackTheme.Switchable">
<item name="colorPrimaryDark">@color/dark_youtube_statusbar_color</item>
</style>
<!-- SoundCloud -->
<style name="LightTheme.SoundCloud" parent="LightTheme.Switchable">
<item name="colorPrimary">@color/light_soundcloud_primary_color</item>
<item name="colorPrimaryDark">@color/light_soundcloud_statusbar_color</item>
<item name="colorAccent">@color/light_soundcloud_accent_color</item>
</style>
<style name="DarkTheme.SoundCloud" parent="DarkTheme.Switchable">
<item name="colorPrimary">@color/dark_soundcloud_primary_color</item>
<item name="colorPrimaryDark">@color/dark_soundcloud_statusbar_color</item>
<item name="colorAccent">@color/dark_soundcloud_accent_color</item>
</style>
<style name="BlackTheme.SoundCloud" parent="BlackTheme.Switchable">
<item name="colorPrimary">@color/dark_soundcloud_primary_color</item>
<item name="colorPrimaryDark">@color/dark_soundcloud_statusbar_color</item>
<item name="colorAccent">@color/dark_soundcloud_accent_color</item>
</style>
</resources>

View File

@ -35,6 +35,7 @@
<attr name="ic_import_export" format="reference"/> <attr name="ic_import_export" format="reference"/>
<attr name="ic_save" format="reference"/> <attr name="ic_save" format="reference"/>
<attr name="ic_backup" format="reference"/> <attr name="ic_backup" format="reference"/>
<attr name="ic_add" format="reference"/>
<!-- Can't refer to colors directly into drawable's xml--> <!-- Can't refer to colors directly into drawable's xml-->
<attr name="toolbar_shadow_drawable" format="reference"/> <attr name="toolbar_shadow_drawable" format="reference"/>

View File

@ -29,13 +29,13 @@
<color name="dark_queue_background_color">#af000000</color> <color name="dark_queue_background_color">#af000000</color>
<!-- Black Theme --> <!-- Black Theme -->
<color name="black_background_color">#000</color> <color name="black_background_color">#000000</color>
<color name="black_settings_accent_color">@color/dark_settings_accent_color</color> <color name="black_settings_accent_color">@color/dark_settings_accent_color</color>
<color name="black_separator_color">#1effffff</color> <color name="black_separator_color">#1effffff</color>
<color name="black_contrast_background_color">#23454545</color> <color name="black_contrast_background_color">#23454545</color>
<!-- Miscellaneous --> <!-- Miscellaneous -->
<color name="duration_background_color">#AA000000</color> <color name="duration_background_color">#aa000000</color>
<color name="live_duration_background_color">#c8E53935</color> <color name="live_duration_background_color">#c8E53935</color>
<color name="playlist_stream_count_background_color">#e6000000</color> <color name="playlist_stream_count_background_color">#e6000000</color>
<color name="duration_text_color">#EEFFFFFF</color> <color name="duration_text_color">#EEFFFFFF</color>
@ -70,5 +70,6 @@
<color name="gray">#616161</color> <color name="gray">#616161</color>
<color name="black">#000</color> <color name="black">#000</color>
<color name="gray_transparent">#be757575</color>
</resources> </resources>

View File

@ -2,20 +2,24 @@
<resources> <resources>
<!-- YouTube --> <!-- YouTube -->
<color name="light_youtube_primary_color">#e53935</color> <color name="light_youtube_primary_color">#e53935</color>
<color name="light_youtube_dark_color">#d32f2f</color> <color name="light_youtube_dark_color">#992722</color>
<color name="light_youtube_accent_color">#000000</color> <color name="light_youtube_accent_color">#000000</color>
<color name="light_youtube_statusbar_color">#ff4336</color>
<color name="dark_youtube_primary_color">#CD322E</color> <color name="dark_youtube_primary_color">#CD322E</color>
<color name="dark_youtube_dark_color">#BC211D</color> <color name="dark_youtube_dark_color">#992722</color>
<color name="dark_youtube_accent_color">#FFFFFF</color> <color name="dark_youtube_accent_color">#FFFFFF</color>
<color name="dark_youtube_statusbar_color">#ff4336</color>
<!-- SoundCloud --> <!-- SoundCloud -->
<color name="light_soundcloud_primary_color">#f57c00</color> <color name="light_soundcloud_primary_color">#f57c00</color>
<color name="light_soundcloud_dark_color">#ef6c00</color> <color name="light_soundcloud_dark_color">#995700</color>
<color name="light_soundcloud_accent_color">#000000</color> <color name="light_soundcloud_accent_color">#000000</color>
<color name="light_soundcloud_statusbar_color">#ff9100</color>
<color name="dark_soundcloud_primary_color">#f57c00</color> <color name="dark_soundcloud_primary_color">#f57c00</color>
<color name="dark_soundcloud_dark_color">#ef6c00</color> <color name="dark_soundcloud_dark_color">#995700</color>
<color name="dark_soundcloud_accent_color">#FFFFFF</color> <color name="dark_soundcloud_accent_color">#FFFFFF</color>
<color name="dark_soundcloud_statusbar_color">#ff9100</color>
</resources> </resources>

View File

@ -35,6 +35,8 @@
<string name="tab_main">Main</string> <string name="tab_main">Main</string>
<string name="tab_subscriptions">Subscriptions</string> <string name="tab_subscriptions">Subscriptions</string>
<string name="tab_bookmarks">Bookmarks</string> <string name="tab_bookmarks">Bookmarks</string>
<string name="tab_new">New Tab</string>
<string name="tab_chose">Chose Tab</string>
<string name="fragment_whats_new">What\'s New</string> <string name="fragment_whats_new">What\'s New</string>
@ -168,6 +170,8 @@
<string name="search_history_deleted">Search history deleted.</string> <string name="search_history_deleted">Search history deleted.</string>
<!-- error strings --> <!-- error strings -->
<string name="general_error">Error</string> <string name="general_error">Error</string>
<string name="download_to_sdcard_error_title">External storage not available.</string>
<string name="download_to_sdcard_error_message">Download to external SD Card is not possible yet. Should the download place be reset?</string>
<string name="network_error">Network error</string> <string name="network_error">Network error</string>
<string name="could_not_load_thumbnails">Could not load all thumbnails</string> <string name="could_not_load_thumbnails">Could not load all thumbnails</string>
<string name="youtube_signature_decryption_error">Could not decrypt video URL signature</string> <string name="youtube_signature_decryption_error">Could not decrypt video URL signature</string>
@ -357,6 +361,9 @@
<!-- Content --> <!-- Content -->
<string name="main_page_content">Content of main page</string> <string name="main_page_content">Content of main page</string>
<string name="main_page_content_summary">What tabs are shown on the main page</string>
<string name="selection">Selection</string>
<string name="chosenTabs">Your tabs</string>
<string name="blank_page_summary">Blank Page</string> <string name="blank_page_summary">Blank Page</string>
<string name="kiosk_page_summary">Kiosk Page</string> <string name="kiosk_page_summary">Kiosk Page</string>
<string name="subscription_page_summary">Subscription Page</string> <string name="subscription_page_summary">Subscription Page</string>

View File

@ -51,6 +51,7 @@
<item name="ic_import_export">@drawable/ic_import_export_black_24dp</item> <item name="ic_import_export">@drawable/ic_import_export_black_24dp</item>
<item name="ic_save">@drawable/ic_save_black_24dp</item> <item name="ic_save">@drawable/ic_save_black_24dp</item>
<item name="ic_backup">@drawable/ic_backup_black_24dp</item> <item name="ic_backup">@drawable/ic_backup_black_24dp</item>
<item name="ic_add">@drawable/ic_add_black_24dp</item>
<item name="separator_color">@color/light_separator_color</item> <item name="separator_color">@color/light_separator_color</item>
<item name="contrast_background_color">@color/light_contrast_background_color</item> <item name="contrast_background_color">@color/light_contrast_background_color</item>
@ -108,6 +109,7 @@
<item name="ic_import_export">@drawable/ic_import_export_white_24dp</item> <item name="ic_import_export">@drawable/ic_import_export_white_24dp</item>
<item name="ic_save">@drawable/ic_save_white_24dp</item> <item name="ic_save">@drawable/ic_save_white_24dp</item>
<item name="ic_backup">@drawable/ic_backup_white_24dp</item> <item name="ic_backup">@drawable/ic_backup_white_24dp</item>
<item name="ic_add">@drawable/ic_add_white_24dp</item>
<item name="separator_color">@color/dark_separator_color</item> <item name="separator_color">@color/dark_separator_color</item>
<item name="contrast_background_color">@color/dark_contrast_background_color</item> <item name="contrast_background_color">@color/dark_contrast_background_color</item>

View File

@ -1,12 +1,14 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<!-- YouTube --> <!-- YouTube -->
<style name="LightTheme.YouTube" parent="LightTheme.Switchable"/> <style name="LightTheme.YouTube" parent="LightTheme.Switchable">
</style>
<style name="DarkTheme.YouTube" parent="DarkTheme.Switchable"/> <style name="DarkTheme.YouTube" parent="DarkTheme.Switchable">
</style>
<style name="BlackTheme.YouTube" parent="BlackTheme.Switchable"/>
<style name="BlackTheme.YouTube" parent="BlackTheme.Switchable">
</style>
<!-- SoundCloud --> <!-- SoundCloud -->
<style name="LightTheme.SoundCloud" parent="LightTheme.Switchable"> <style name="LightTheme.SoundCloud" parent="LightTheme.Switchable">
<item name="colorPrimary">@color/light_soundcloud_primary_color</item> <item name="colorPrimary">@color/light_soundcloud_primary_color</item>

View File

@ -34,4 +34,11 @@
android:key="@string/caption_settings_key" android:key="@string/caption_settings_key"
android:title="@string/caption_setting_title" android:title="@string/caption_setting_title"
android:summary="@string/caption_setting_description"/> android:summary="@string/caption_setting_description"/>
<PreferenceScreen
android:fragment="org.schabi.newpipe.settings.ChoseTabsFragment"
android:summary="@string/main_page_content_summary"
android:key="@string/main_page_content_key"
android:title="@string/main_page_content"/>
</PreferenceScreen> </PreferenceScreen>

View File

@ -43,14 +43,6 @@
android:title="@string/download_thumbnail_title" android:title="@string/download_thumbnail_title"
android:summary="@string/download_thumbnail_summary"/> android:summary="@string/download_thumbnail_summary"/>
<ListPreference
android:defaultValue="@string/kiosk_page_key"
android:entries="@array/main_page_content_names"
android:entryValues="@array/main_page_content_pages"
android:key="@string/main_page_content_key"
android:title="@string/main_page_content"
android:summary="%s"/>
<Preference <Preference
android:summary="@string/import_data_summary" android:summary="@string/import_data_summary"
android:key="@string/import_data" android:key="@string/import_data"

View File

@ -6,7 +6,7 @@ buildscript {
google() google()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.1.3' classpath 'com.android.tools.build:gradle:3.1.4'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files