Merge remote-tracking branch 'origin/master'
|
@ -5,7 +5,7 @@ android:
|
||||||
components:
|
components:
|
||||||
# The BuildTools version used by NewPipe
|
# The BuildTools version used by NewPipe
|
||||||
- tools
|
- tools
|
||||||
- build-tools-25.0.0
|
- build-tools-25.0.2
|
||||||
|
|
||||||
# The SDK version used to compile NewPipe
|
# The SDK version used to compile NewPipe
|
||||||
- android-25
|
- android-25
|
||||||
|
|
|
@ -8,8 +8,8 @@ android {
|
||||||
applicationId "org.schabi.newpipe"
|
applicationId "org.schabi.newpipe"
|
||||||
minSdkVersion 15
|
minSdkVersion 15
|
||||||
targetSdkVersion 25
|
targetSdkVersion 25
|
||||||
versionCode 30
|
versionCode 31
|
||||||
versionName "0.9.3"
|
versionName "0.9.4"
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
|
@ -39,14 +39,16 @@ dependencies {
|
||||||
compile 'com.android.support:support-v4:25.3.1'
|
compile 'com.android.support:support-v4:25.3.1'
|
||||||
compile 'com.android.support:design:25.3.1'
|
compile 'com.android.support:design:25.3.1'
|
||||||
compile 'com.android.support:recyclerview-v7:25.3.1'
|
compile 'com.android.support:recyclerview-v7:25.3.1'
|
||||||
|
|
||||||
|
compile 'com.google.code.gson:gson:2.7'
|
||||||
compile 'org.jsoup:jsoup:1.8.3'
|
compile 'org.jsoup:jsoup:1.8.3'
|
||||||
compile 'org.mozilla:rhino:1.7.7'
|
compile 'org.mozilla:rhino:1.7.7'
|
||||||
compile 'info.guardianproject.netcipher:netcipher:1.2'
|
|
||||||
compile 'de.hdodenhof:circleimageview:2.0.0'
|
|
||||||
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
|
|
||||||
compile 'com.github.nirhart:parallaxscroll:1.0'
|
|
||||||
compile 'com.google.code.gson:gson:2.7'
|
|
||||||
compile 'com.nononsenseapps:filepicker:3.0.0'
|
|
||||||
compile 'ch.acra:acra:4.9.0'
|
compile 'ch.acra:acra:4.9.0'
|
||||||
|
compile 'info.guardianproject.netcipher:netcipher:1.2'
|
||||||
|
|
||||||
|
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
|
||||||
|
compile 'de.hdodenhof:circleimageview:2.0.0'
|
||||||
|
compile 'com.github.nirhart:parallaxscroll:1.0'
|
||||||
|
compile 'com.nononsenseapps:filepicker:3.0.0'
|
||||||
compile 'com.google.android.exoplayer:exoplayer:r2.3.1'
|
compile 'com.google.android.exoplayer:exoplayer:r2.3.1'
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.report.AcraReportSenderFactory;
|
import org.schabi.newpipe.report.AcraReportSenderFactory;
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
import org.schabi.newpipe.settings.SettingsActivity;
|
import org.schabi.newpipe.settings.SettingsActivity;
|
||||||
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
import info.guardianproject.netcipher.NetCipher;
|
import info.guardianproject.netcipher.NetCipher;
|
||||||
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
||||||
|
@ -82,6 +83,8 @@ public class App extends Application {
|
||||||
// DO NOT REMOVE THIS FUNCTION!!!
|
// DO NOT REMOVE THIS FUNCTION!!!
|
||||||
// Otherwise downloadPathPreference has invalid value.
|
// Otherwise downloadPathPreference has invalid value.
|
||||||
SettingsActivity.initSettings(this);
|
SettingsActivity.initSettings(this);
|
||||||
|
|
||||||
|
ThemeHelper.setTheme(getApplicationContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -21,19 +21,23 @@
|
||||||
package org.schabi.newpipe;
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.media.AudioManager;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.app.FragmentManager;
|
import android.support.v4.app.FragmentManager;
|
||||||
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||||
|
|
||||||
import org.schabi.newpipe.download.DownloadActivity;
|
import org.schabi.newpipe.download.DownloadActivity;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
|
import org.schabi.newpipe.fragments.MainFragment;
|
||||||
import org.schabi.newpipe.fragments.OnItemSelectedListener;
|
import org.schabi.newpipe.fragments.OnItemSelectedListener;
|
||||||
import org.schabi.newpipe.fragments.channel.ChannelFragment;
|
import org.schabi.newpipe.fragments.channel.ChannelFragment;
|
||||||
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
|
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
|
||||||
|
@ -45,7 +49,8 @@ import org.schabi.newpipe.util.PermissionHelper;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity implements OnItemSelectedListener {
|
public class MainActivity extends AppCompatActivity implements OnItemSelectedListener {
|
||||||
//private static final String TAG = "MainActivity";
|
private static final String TAG = "MainActivity";
|
||||||
|
public static final boolean DEBUG = false;
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Activity's LifeCycle
|
// Activity's LifeCycle
|
||||||
|
@ -53,18 +58,22 @@ public class MainActivity extends AppCompatActivity implements OnItemSelectedLis
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
ThemeHelper.setTheme(this, true);
|
if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
|
||||||
|
ThemeHelper.setTheme(this);
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
|
||||||
|
|
||||||
if (getSupportFragmentManager() != null && getSupportFragmentManager().getBackStackEntryCount() == 0) {
|
if (getSupportFragmentManager() != null && getSupportFragmentManager().getBackStackEntryCount() == 0) {
|
||||||
initFragments();
|
initFragments();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onNewIntent(Intent intent) {
|
protected void onNewIntent(Intent intent) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onNewIntent() called with: intent = [" + intent + "]");
|
||||||
if (intent != null) {
|
if (intent != null) {
|
||||||
// Return if launched from a launcher (e.g. Nova Launcher, Pixel Launcher ...)
|
// Return if launched from a launcher (e.g. Nova Launcher, Pixel Launcher ...)
|
||||||
// to not destroy the already created backstack
|
// to not destroy the already created backstack
|
||||||
|
@ -79,22 +88,11 @@ public class MainActivity extends AppCompatActivity implements OnItemSelectedLis
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
|
if (DEBUG) Log.d(TAG, "onBackPressed() called");
|
||||||
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder);
|
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder);
|
||||||
if (fragment instanceof VideoDetailFragment) if (((VideoDetailFragment) fragment).onActivityBackPressed()) return;
|
if (fragment instanceof VideoDetailFragment) if (((VideoDetailFragment) fragment).onActivityBackPressed()) return;
|
||||||
|
|
||||||
if (getSupportFragmentManager().getBackStackEntryCount() >= 2) {
|
super.onBackPressed();
|
||||||
getSupportFragmentManager().popBackStackImmediate();
|
|
||||||
} else {
|
|
||||||
if (fragment instanceof SearchFragment) {
|
|
||||||
SearchFragment searchFragment = (SearchFragment) fragment;
|
|
||||||
if (!searchFragment.isMainBgVisible()) {
|
|
||||||
getSupportFragmentManager().beginTransaction().remove(fragment).commitNow();
|
|
||||||
NavigationHelper.openMainActivity(this);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -103,14 +101,32 @@ public class MainActivity extends AppCompatActivity implements OnItemSelectedLis
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + "]");
|
||||||
super.onCreateOptionsMenu(menu);
|
super.onCreateOptionsMenu(menu);
|
||||||
MenuInflater inflater = getMenuInflater();
|
|
||||||
inflater.inflate(R.menu.main_menu, menu);
|
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder);
|
||||||
|
if (!(fragment instanceof VideoDetailFragment)) {
|
||||||
|
findViewById(R.id.toolbar).findViewById(R.id.toolbar_spinner).setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(fragment instanceof SearchFragment)) {
|
||||||
|
findViewById(R.id.toolbar).findViewById(R.id.toolbar_search_container).setVisibility(View.GONE);
|
||||||
|
|
||||||
|
MenuInflater inflater = getMenuInflater();
|
||||||
|
inflater.inflate(R.menu.main_menu, menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionBar actionBar = getSupportActionBar();
|
||||||
|
if (actionBar != null) {
|
||||||
|
actionBar.setDisplayShowTitleEnabled(false);
|
||||||
|
actionBar.setDisplayHomeAsUpEnabled(false);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onOptionsItemSelected() called with: item = [" + item + "]");
|
||||||
int id = item.getItemId();
|
int id = item.getItemId();
|
||||||
|
|
||||||
switch (id) {
|
switch (id) {
|
||||||
|
@ -144,9 +160,10 @@ public class MainActivity extends AppCompatActivity implements OnItemSelectedLis
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
private void initFragments() {
|
private void initFragments() {
|
||||||
|
openMainFragment();
|
||||||
if (getIntent() != null && getIntent().hasExtra(Constants.KEY_URL)) {
|
if (getIntent() != null && getIntent().hasExtra(Constants.KEY_URL)) {
|
||||||
handleIntent(getIntent());
|
handleIntent(getIntent());
|
||||||
} else openSearchFragment();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -170,6 +187,13 @@ public class MainActivity extends AppCompatActivity implements OnItemSelectedLis
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
private void handleIntent(Intent intent) {
|
private void handleIntent(Intent intent) {
|
||||||
|
if (intent.hasExtra(Constants.KEY_THEME_CHANGE) && intent.getBooleanExtra(Constants.KEY_THEME_CHANGE, false)) {
|
||||||
|
this.recreate();
|
||||||
|
Intent setI = new Intent(this, SettingsActivity.class);
|
||||||
|
startActivity(setI);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (intent.hasExtra(Constants.KEY_LINK_TYPE)) {
|
if (intent.hasExtra(Constants.KEY_LINK_TYPE)) {
|
||||||
String url = intent.getStringExtra(Constants.KEY_URL);
|
String url = intent.getStringExtra(Constants.KEY_URL);
|
||||||
int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
|
int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
|
||||||
|
@ -187,24 +211,34 @@ public class MainActivity extends AppCompatActivity implements OnItemSelectedLis
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
} else if (intent.hasExtra(Constants.KEY_OPEN_SEARCH)) {
|
||||||
|
String searchQuery = intent.getStringExtra(Constants.KEY_QUERY);
|
||||||
|
if (searchQuery == null) searchQuery = "";
|
||||||
|
int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
|
||||||
|
openSearchFragment(serviceId, searchQuery);
|
||||||
} else {
|
} else {
|
||||||
getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
|
getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
|
||||||
openSearchFragment();
|
openMainFragment();//openSearchFragment();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openSearchFragment() {
|
private void openMainFragment() {
|
||||||
ImageLoader.getInstance().clearMemoryCache();
|
ImageLoader.getInstance().clearMemoryCache();
|
||||||
getSupportFragmentManager().beginTransaction()
|
getSupportFragmentManager().beginTransaction()
|
||||||
.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out, android.R.anim.fade_in, android.R.anim.fade_out)
|
.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out, android.R.anim.fade_in, android.R.anim.fade_out)
|
||||||
.replace(R.id.fragment_holder, new SearchFragment())
|
.replace(R.id.fragment_holder, new MainFragment())
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openSearchFragment(int serviceId, String query) {
|
||||||
|
getSupportFragmentManager().beginTransaction()
|
||||||
|
.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out, android.R.anim.fade_in, android.R.anim.fade_out)
|
||||||
|
.replace(R.id.fragment_holder, SearchFragment.getInstance(serviceId, query))
|
||||||
.addToBackStack(null)
|
.addToBackStack(null)
|
||||||
.commit();
|
.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openVideoDetailFragment(int serviceId, String url, String title, boolean autoPlay) {
|
private void openVideoDetailFragment(int serviceId, String url, String title, boolean autoPlay) {
|
||||||
ImageLoader.getInstance().clearMemoryCache();
|
|
||||||
|
|
||||||
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder);
|
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder);
|
||||||
if (title == null) title = "";
|
if (title == null) title = "";
|
||||||
|
|
||||||
|
@ -226,11 +260,10 @@ public class MainActivity extends AppCompatActivity implements OnItemSelectedLis
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openChannelFragment(int serviceId, String url, String name) {
|
private void openChannelFragment(int serviceId, String url, String name) {
|
||||||
ImageLoader.getInstance().clearMemoryCache();
|
|
||||||
if (name == null) name = "";
|
if (name == null) name = "";
|
||||||
getSupportFragmentManager().beginTransaction()
|
getSupportFragmentManager().beginTransaction()
|
||||||
.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out, android.R.anim.fade_in, android.R.anim.fade_out)
|
.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out, android.R.anim.fade_in, android.R.anim.fade_out)
|
||||||
.replace(R.id.fragment_holder, ChannelFragment.newInstance(serviceId, url, name))
|
.replace(R.id.fragment_holder, ChannelFragment.getInstance(serviceId, url, name))
|
||||||
.addToBackStack(null)
|
.addToBackStack(null)
|
||||||
.commit();
|
.commit();
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import android.os.Bundle;
|
||||||
import android.support.v4.app.NavUtils;
|
import android.support.v4.app.NavUtils;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.webkit.CookieManager;
|
import android.webkit.CookieManager;
|
||||||
import android.webkit.ValueCallback;
|
import android.webkit.ValueCallback;
|
||||||
|
@ -48,10 +49,15 @@ public class ReCaptchaActivity extends AppCompatActivity {
|
||||||
// Set return to Cancel by default
|
// Set return to Cancel by default
|
||||||
setResult(RESULT_CANCELED);
|
setResult(RESULT_CANCELED);
|
||||||
|
|
||||||
|
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
|
||||||
ActionBar actionBar = getSupportActionBar();
|
ActionBar actionBar = getSupportActionBar();
|
||||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
if (actionBar != null) {
|
||||||
actionBar.setTitle(R.string.reCaptcha_title);
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
actionBar.setDisplayShowTitleEnabled(true);
|
actionBar.setTitle(R.string.reCaptcha_title);
|
||||||
|
actionBar.setDisplayShowTitleEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
WebView myWebView = (WebView) findViewById(R.id.reCaptchaWebView);
|
WebView myWebView = (WebView) findViewById(R.id.reCaptchaWebView);
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.v4.app.NavUtils;
|
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
|
@ -26,13 +25,11 @@ import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.settings.NewPipeSettings;
|
import org.schabi.newpipe.settings.NewPipeSettings;
|
||||||
import org.schabi.newpipe.settings.SettingsActivity;
|
import org.schabi.newpipe.settings.SettingsActivity;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
import us.shandian.giga.service.DownloadManagerService;
|
import us.shandian.giga.service.DownloadManagerService;
|
||||||
import us.shandian.giga.ui.fragment.AllMissionsFragment;
|
import us.shandian.giga.ui.fragment.AllMissionsFragment;
|
||||||
|
@ -64,17 +61,19 @@ public class DownloadActivity extends AppCompatActivity implements AdapterView.O
|
||||||
i.setClass(this, DownloadManagerService.class);
|
i.setClass(this, DownloadManagerService.class);
|
||||||
startService(i);
|
startService(i);
|
||||||
|
|
||||||
|
ThemeHelper.setTheme(this);
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
ThemeHelper.setTheme(this, true);
|
|
||||||
setContentView(R.layout.activity_downloader);
|
setContentView(R.layout.activity_downloader);
|
||||||
|
|
||||||
//noinspection ConstantConditions
|
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
|
||||||
// its ok if this fails, we will catch that error later, and send it as report
|
|
||||||
ActionBar actionBar = getSupportActionBar();
|
ActionBar actionBar = getSupportActionBar();
|
||||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
if (actionBar != null) {
|
||||||
actionBar.setTitle(R.string.downloads_title);
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
actionBar.setDisplayShowTitleEnabled(true);
|
actionBar.setTitle(R.string.downloads_title);
|
||||||
|
actionBar.setDisplayShowTitleEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
|
mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
|
||||||
|
@ -159,7 +158,7 @@ public class DownloadActivity extends AppCompatActivity implements AdapterView.O
|
||||||
name.setText(getIntent().getStringExtra("fileName"));
|
name.setText(getIntent().getStringExtra("fileName"));
|
||||||
|
|
||||||
toolbar.setTitle(R.string.add);
|
toolbar.setTitle(R.string.add);
|
||||||
toolbar.setNavigationIcon(R.drawable.ic_arrow_back_black_24dp);
|
toolbar.setNavigationIcon(ThemeHelper.isLightThemeSelected(this) ? R.drawable.ic_arrow_back_black_24dp : R.drawable.ic_arrow_back_white_24dp);
|
||||||
toolbar.inflateMenu(R.menu.dialog_url);
|
toolbar.inflateMenu(R.menu.dialog_url);
|
||||||
|
|
||||||
// Show the dialog
|
// Show the dialog
|
||||||
|
@ -183,7 +182,7 @@ public class DownloadActivity extends AppCompatActivity implements AdapterView.O
|
||||||
if (item.getItemId() == R.id.okay) {
|
if (item.getItemId() == R.id.okay) {
|
||||||
|
|
||||||
String location;
|
String location;
|
||||||
if(audioButton.isChecked()) {
|
if (audioButton.isChecked()) {
|
||||||
location = NewPipeSettings.getAudioDownloadPath(DownloadActivity.this);
|
location = NewPipeSettings.getAudioDownloadPath(DownloadActivity.this);
|
||||||
} else {
|
} else {
|
||||||
location = NewPipeSettings.getVideoDownloadPath(DownloadActivity.this);
|
location = NewPipeSettings.getVideoDownloadPath(DownloadActivity.this);
|
||||||
|
@ -201,7 +200,7 @@ public class DownloadActivity extends AppCompatActivity implements AdapterView.O
|
||||||
audioButton.isChecked(), threads.getProgress() + 1);
|
audioButton.isChecked(), threads.getProgress() + 1);
|
||||||
mFragment.notifyChange();
|
mFragment.notifyChange();
|
||||||
|
|
||||||
mPrefs.edit().putInt(THREADS, threads.getProgress() + 1).commit();
|
mPrefs.edit().putInt(THREADS, threads.getProgress() + 1).apply();
|
||||||
mPendingUrl = null;
|
mPendingUrl = null;
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
package org.schabi.newpipe.download;
|
package org.schabi.newpipe.download;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.ServiceConnection;
|
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
|
||||||
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.v4.app.DialogFragment;
|
import android.support.v4.app.DialogFragment;
|
||||||
|
@ -26,34 +23,32 @@ import android.widget.TextView;
|
||||||
|
|
||||||
import org.schabi.newpipe.App;
|
import org.schabi.newpipe.App;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
|
||||||
import org.schabi.newpipe.settings.NewPipeSettings;
|
import org.schabi.newpipe.settings.NewPipeSettings;
|
||||||
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import us.shandian.giga.get.DownloadManager;
|
|
||||||
import us.shandian.giga.get.DownloadMission;
|
|
||||||
import us.shandian.giga.service.DownloadManagerService;
|
import us.shandian.giga.service.DownloadManagerService;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Christian Schabesberger on 21.09.15.
|
* Created by Christian Schabesberger on 21.09.15.
|
||||||
*
|
* <p>
|
||||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
||||||
* DownloadDialog.java is part of NewPipe.
|
* DownloadDialog.java is part of NewPipe.
|
||||||
*
|
* <p>
|
||||||
* NewPipe is free software: you can redistribute it and/or modify
|
* NewPipe is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
* <p>
|
||||||
* NewPipe is distributed in the hope that it will be useful,
|
* NewPipe is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
* <p>
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
@ -71,8 +66,7 @@ public class DownloadDialog extends DialogFragment {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DownloadDialog newInstance(Bundle args)
|
public static DownloadDialog newInstance(Bundle args) {
|
||||||
{
|
|
||||||
DownloadDialog dialog = new DownloadDialog();
|
DownloadDialog dialog = new DownloadDialog();
|
||||||
dialog.setArguments(args);
|
dialog.setArguments(args);
|
||||||
dialog.setStyle(DialogFragment.STYLE_NO_TITLE, 0);
|
dialog.setStyle(DialogFragment.STYLE_NO_TITLE, 0);
|
||||||
|
@ -100,7 +94,7 @@ public class DownloadDialog extends DialogFragment {
|
||||||
final SeekBar threads = (SeekBar) view.findViewById(R.id.threads);
|
final SeekBar threads = (SeekBar) view.findViewById(R.id.threads);
|
||||||
|
|
||||||
toolbar.setTitle(R.string.download_dialog_title);
|
toolbar.setTitle(R.string.download_dialog_title);
|
||||||
toolbar.setNavigationIcon(R.drawable.ic_arrow_back_black_24dp);
|
toolbar.setNavigationIcon(ThemeHelper.isLightThemeSelected(getActivity()) ? R.drawable.ic_arrow_back_black_24dp : R.drawable.ic_arrow_back_white_24dp);
|
||||||
toolbar.inflateMenu(R.menu.dialog_url);
|
toolbar.inflateMenu(R.menu.dialog_url);
|
||||||
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
|
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -151,16 +145,16 @@ public class DownloadDialog extends DialogFragment {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void checkDownloadOptions(){
|
protected void checkDownloadOptions() {
|
||||||
View view = getView();
|
View view = getView();
|
||||||
Bundle arguments = getArguments();
|
Bundle arguments = getArguments();
|
||||||
RadioButton audioButton = (RadioButton) view.findViewById(R.id.audio_button);
|
RadioButton audioButton = (RadioButton) view.findViewById(R.id.audio_button);
|
||||||
RadioButton videoButton = (RadioButton) view.findViewById(R.id.video_button);
|
RadioButton videoButton = (RadioButton) view.findViewById(R.id.video_button);
|
||||||
|
|
||||||
if(arguments.getString(AUDIO_URL) == null) {
|
if (arguments.getString(AUDIO_URL) == null) {
|
||||||
audioButton.setVisibility(View.GONE);
|
audioButton.setVisibility(View.GONE);
|
||||||
videoButton.setChecked(true);
|
videoButton.setChecked(true);
|
||||||
} else if(arguments.getString(VIDEO_URL) == null) {
|
} else if (arguments.getString(VIDEO_URL) == null) {
|
||||||
videoButton.setVisibility(View.GONE);
|
videoButton.setVisibility(View.GONE);
|
||||||
audioButton.setChecked(true);
|
audioButton.setChecked(true);
|
||||||
}
|
}
|
||||||
|
@ -169,11 +163,11 @@ public class DownloadDialog extends DialogFragment {
|
||||||
/**
|
/**
|
||||||
* #143 #44 #42 #22: make shure that the filename does not contain illegal chars.
|
* #143 #44 #42 #22: make shure that the filename does not contain illegal chars.
|
||||||
* This should fix some of the "cannot download" problems.
|
* This should fix some of the "cannot download" problems.
|
||||||
* */
|
*/
|
||||||
private String createFileName(String fName) {
|
private String createFileName(String fName) {
|
||||||
// from http://eng-przemelek.blogspot.de/2009/07/how-to-create-valid-file-name.html
|
// from http://eng-przemelek.blogspot.de/2009/07/how-to-create-valid-file-name.html
|
||||||
|
|
||||||
List<String> forbiddenCharsPatterns = new ArrayList<> ();
|
List<String> forbiddenCharsPatterns = new ArrayList<>();
|
||||||
forbiddenCharsPatterns.add("[:]+"); // Mac OS, but it looks that also Windows XP
|
forbiddenCharsPatterns.add("[:]+"); // Mac OS, but it looks that also Windows XP
|
||||||
forbiddenCharsPatterns.add("[\\*\"/\\\\\\[\\]\\:\\;\\|\\=\\,]+"); // Windows
|
forbiddenCharsPatterns.add("[\\*\"/\\\\\\[\\]\\:\\;\\|\\=\\,]+"); // Windows
|
||||||
forbiddenCharsPatterns.add("[^\\w\\d\\.]+"); // last chance... only latin letters and digits
|
forbiddenCharsPatterns.add("[^\\w\\d\\.]+"); // last chance... only latin letters and digits
|
||||||
|
@ -186,8 +180,7 @@ public class DownloadDialog extends DialogFragment {
|
||||||
|
|
||||||
|
|
||||||
//download audio, video or both?
|
//download audio, video or both?
|
||||||
private void download()
|
private void download() {
|
||||||
{
|
|
||||||
View view = getView();
|
View view = getView();
|
||||||
Bundle arguments = getArguments();
|
Bundle arguments = getArguments();
|
||||||
final EditText name = (EditText) view.findViewById(R.id.file_name);
|
final EditText name = (EditText) view.findViewById(R.id.file_name);
|
||||||
|
@ -199,7 +192,7 @@ public class DownloadDialog extends DialogFragment {
|
||||||
|
|
||||||
boolean isAudio = audioButton.isChecked();
|
boolean isAudio = audioButton.isChecked();
|
||||||
String url, location, filename;
|
String url, location, filename;
|
||||||
if(isAudio) {
|
if (isAudio) {
|
||||||
url = arguments.getString(AUDIO_URL);
|
url = arguments.getString(AUDIO_URL);
|
||||||
location = NewPipeSettings.getAudioDownloadPath(getContext());
|
location = NewPipeSettings.getAudioDownloadPath(getContext());
|
||||||
filename = fName + arguments.getString(FILE_SUFFIX_AUDIO);
|
filename = fName + arguments.getString(FILE_SUFFIX_AUDIO);
|
||||||
|
@ -218,11 +211,11 @@ public class DownloadDialog extends DialogFragment {
|
||||||
private void download(String url, String title,
|
private void download(String url, String title,
|
||||||
String fileSuffix, File downloadDir, Context context) {
|
String fileSuffix, File downloadDir, Context context) {
|
||||||
|
|
||||||
File saveFilePath = new File(downloadDir,createFileName(title) + fileSuffix);
|
File saveFilePath = new File(downloadDir, createFileName(title) + fileSuffix);
|
||||||
|
|
||||||
long id = 0;
|
long id = 0;
|
||||||
|
|
||||||
Log.i(TAG,"Started downloading '" + url +
|
Log.i(TAG, "Started downloading '" + url +
|
||||||
"' => '" + saveFilePath + "' #" + id);
|
"' => '" + saveFilePath + "' #" + id);
|
||||||
|
|
||||||
if (App.isUsingTor()) {
|
if (App.isUsingTor()) {
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 08457de7631253a23ba57cd547fe056c00eebc21
|
Subproject commit 5907c35dfb0f23db2b6f6f864940e6535ad9abd5
|
|
@ -0,0 +1,243 @@
|
||||||
|
package org.schabi.newpipe.fragments;
|
||||||
|
|
||||||
|
import android.animation.Animator;
|
||||||
|
import android.animation.AnimatorListenerAdapter;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.AttrRes;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v4.view.ViewCompat;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.nostra13.universalimageloader.core.DisplayImageOptions;
|
||||||
|
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||||
|
import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.MainActivity;
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
public abstract class BaseFragment extends Fragment {
|
||||||
|
protected final String TAG = "BaseFragment@" + Integer.toHexString(hashCode());
|
||||||
|
protected static final boolean DEBUG = MainActivity.DEBUG;
|
||||||
|
|
||||||
|
protected AppCompatActivity activity;
|
||||||
|
protected OnItemSelectedListener onItemSelectedListener;
|
||||||
|
|
||||||
|
protected AtomicBoolean isLoading = new AtomicBoolean(false);
|
||||||
|
protected AtomicBoolean wasLoading = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
protected static final ImageLoader imageLoader = ImageLoader.getInstance();
|
||||||
|
protected static final DisplayImageOptions displayImageOptions =
|
||||||
|
new DisplayImageOptions.Builder().displayer(new FadeInBitmapDisplayer(400)).cacheInMemory(false).build();
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Views
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
protected Toolbar toolbar;
|
||||||
|
|
||||||
|
protected View errorPanel;
|
||||||
|
protected Button errorButtonRetry;
|
||||||
|
protected TextView errorTextView;
|
||||||
|
protected ProgressBar loadingProgressBar;
|
||||||
|
//protected SwipeRefreshLayout swipeRefreshLayout;
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Fragment's Lifecycle
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
if (DEBUG) Log.d(TAG, "onAttach() called with: context = [" + context + "]");
|
||||||
|
|
||||||
|
activity = (AppCompatActivity) context;
|
||||||
|
onItemSelectedListener = (OnItemSelectedListener) context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
|
||||||
|
|
||||||
|
isLoading.set(false);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View rootView, Bundle savedInstanceState) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onViewCreated() called with: rootView = [" + rootView + "], savedInstanceState = [" + savedInstanceState + "]");
|
||||||
|
initViews(rootView, savedInstanceState);
|
||||||
|
initListeners();
|
||||||
|
wasLoading.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
if (DEBUG) Log.d(TAG, "onDestroyView() called");
|
||||||
|
toolbar = null;
|
||||||
|
|
||||||
|
errorPanel = null;
|
||||||
|
errorButtonRetry = null;
|
||||||
|
errorTextView = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Init
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
protected void initViews(View rootView, Bundle savedInstanceState) {
|
||||||
|
toolbar = (Toolbar) activity.findViewById(R.id.toolbar);
|
||||||
|
|
||||||
|
loadingProgressBar = (ProgressBar) rootView.findViewById(R.id.loading_progress_bar);
|
||||||
|
//swipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swipe_refresh);
|
||||||
|
|
||||||
|
errorPanel = rootView.findViewById(R.id.error_panel);
|
||||||
|
errorButtonRetry = (Button) rootView.findViewById(R.id.error_button_retry);
|
||||||
|
errorTextView = (TextView) rootView.findViewById(R.id.error_message_view);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void initListeners() {
|
||||||
|
errorButtonRetry.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
onRetryButtonClicked();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void reloadContent();
|
||||||
|
|
||||||
|
protected void onRetryButtonClicked() {
|
||||||
|
if (DEBUG) Log.d(TAG, "onRetryButtonClicked() called");
|
||||||
|
reloadContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Utils
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
public void animateView(final View view, final boolean enterOrExit, long duration) {
|
||||||
|
animateView(view, enterOrExit, duration, 0, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void animateView(final View view, final boolean enterOrExit, long duration, final Runnable execOnEnd) {
|
||||||
|
animateView(view, enterOrExit, duration, 0, execOnEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void animateView(final View view, final boolean enterOrExit, long duration, long delay) {
|
||||||
|
animateView(view, enterOrExit, duration, delay, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animate the view
|
||||||
|
*
|
||||||
|
* @param view view that will be animated
|
||||||
|
* @param enterOrExit true to enter, false to exit
|
||||||
|
* @param duration how long the animation will take, in milliseconds
|
||||||
|
* @param delay how long the animation will take to start, in milliseconds
|
||||||
|
* @param execOnEnd runnable that will be executed when the animation ends
|
||||||
|
*/
|
||||||
|
public void animateView(final View view, final boolean enterOrExit, long duration, long delay, final Runnable execOnEnd) {
|
||||||
|
if (DEBUG) Log.d(TAG, "animateView() called with: view = [" + view + "], enterOrExit = [" + enterOrExit + "], duration = [" + duration + "], execOnEnd = [" + execOnEnd + "]");
|
||||||
|
if (view == null) return;
|
||||||
|
|
||||||
|
if (view.getVisibility() == View.VISIBLE && enterOrExit) {
|
||||||
|
view.animate().setListener(null).cancel();
|
||||||
|
view.setVisibility(View.VISIBLE);
|
||||||
|
view.setAlpha(1f);
|
||||||
|
if (execOnEnd != null) execOnEnd.run();
|
||||||
|
return;
|
||||||
|
} else if ((view.getVisibility() == View.GONE || view.getVisibility() == View.INVISIBLE) && !enterOrExit) {
|
||||||
|
view.animate().setListener(null).cancel();
|
||||||
|
view.setVisibility(View.GONE);
|
||||||
|
view.setAlpha(0f);
|
||||||
|
if (execOnEnd != null) execOnEnd.run();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
view.animate().setListener(null).cancel();
|
||||||
|
view.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
if (enterOrExit) {
|
||||||
|
view.animate().alpha(1f).setDuration(duration).setStartDelay(delay)
|
||||||
|
.setListener(new AnimatorListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
if (execOnEnd != null) execOnEnd.run();
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
} else {
|
||||||
|
view.animate().alpha(0f)
|
||||||
|
.setDuration(duration).setStartDelay(delay)
|
||||||
|
.setListener(new AnimatorListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
view.setVisibility(View.GONE);
|
||||||
|
if (execOnEnd != null) execOnEnd.run();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setErrorMessage(String message, boolean showRetryButton) {
|
||||||
|
if (errorTextView == null || activity == null) return;
|
||||||
|
|
||||||
|
errorTextView.setText(message);
|
||||||
|
if (showRetryButton) animateView(errorButtonRetry, true, 300);
|
||||||
|
else animateView(errorButtonRetry, false, 0);
|
||||||
|
|
||||||
|
animateView(errorPanel, true, 300);
|
||||||
|
isLoading.set(false);
|
||||||
|
|
||||||
|
animateView(loadingProgressBar, false, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getResourceIdFromAttr(@AttrRes int attr) {
|
||||||
|
TypedArray a = activity.getTheme().obtainStyledAttributes(new int[]{attr});
|
||||||
|
int attributeResourceId = a.getResourceId(0, 0);
|
||||||
|
a.recycle();
|
||||||
|
return attributeResourceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void showMenuTooltip(View v, String message) {
|
||||||
|
final int[] screenPos = new int[2];
|
||||||
|
final Rect displayFrame = new Rect();
|
||||||
|
v.getLocationOnScreen(screenPos);
|
||||||
|
v.getWindowVisibleDisplayFrame(displayFrame);
|
||||||
|
|
||||||
|
final Context context = v.getContext();
|
||||||
|
final int width = v.getWidth();
|
||||||
|
final int height = v.getHeight();
|
||||||
|
final int midy = screenPos[1] + height / 2;
|
||||||
|
int referenceX = screenPos[0] + width / 2;
|
||||||
|
if (ViewCompat.getLayoutDirection(v) == View.LAYOUT_DIRECTION_LTR) {
|
||||||
|
final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
|
||||||
|
referenceX = screenWidth - referenceX; // mirror
|
||||||
|
}
|
||||||
|
Toast cheatSheet = Toast.makeText(context, message, Toast.LENGTH_SHORT);
|
||||||
|
if (midy < displayFrame.height()) {
|
||||||
|
// Show along the top; follow action buttons
|
||||||
|
cheatSheet.setGravity(Gravity.TOP | Gravity.END, referenceX,
|
||||||
|
screenPos[1] + height - displayFrame.top);
|
||||||
|
} else {
|
||||||
|
// Show along the bottom center
|
||||||
|
cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height);
|
||||||
|
}
|
||||||
|
cheatSheet.show();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
package org.schabi.newpipe.fragments;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v7.app.ActionBar;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.MainActivity;
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
|
|
||||||
|
public class MainFragment extends Fragment {
|
||||||
|
private final String TAG = "MainFragment@" + Integer.toHexString(hashCode());
|
||||||
|
private static final boolean DEBUG = MainActivity.DEBUG;
|
||||||
|
|
||||||
|
private AppCompatActivity activity;
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Fragment's LifeCycle
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
if (DEBUG) Log.d(TAG, "onAttach() called with: context = [" + context + "]");
|
||||||
|
activity = ((AppCompatActivity) context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onCreateView() called with: inflater = [" + inflater + "], container = [" + container + "], savedInstanceState = [" + savedInstanceState + "]");
|
||||||
|
return inflater.inflate(R.layout.fragment_main, container, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Menu
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + "], inflater = [" + inflater + "]");
|
||||||
|
inflater.inflate(R.menu.main_fragment_menu, menu);
|
||||||
|
|
||||||
|
ActionBar supportActionBar = activity.getSupportActionBar();
|
||||||
|
if (supportActionBar != null) {
|
||||||
|
supportActionBar.setDisplayShowTitleEnabled(false);
|
||||||
|
supportActionBar.setDisplayHomeAsUpEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.action_search:
|
||||||
|
NavigationHelper.openSearch(activity, 0, "");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
package org.schabi.newpipe.fragments.channel;
|
package org.schabi.newpipe.fragments.channel;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
@ -8,9 +7,9 @@ import android.support.annotation.Nullable;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
|
@ -20,222 +19,144 @@ import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.ProgressBar;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.ImageErrorLoadingListener;
|
import org.schabi.newpipe.ImageErrorLoadingListener;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||||
import org.schabi.newpipe.fragments.OnItemSelectedListener;
|
import org.schabi.newpipe.fragments.BaseFragment;
|
||||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||||
import org.schabi.newpipe.info_list.InfoListAdapter;
|
import org.schabi.newpipe.info_list.InfoListAdapter;
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.util.Constants;
|
import org.schabi.newpipe.util.Constants;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
import org.schabi.newpipe.workers.ChannelExtractorWorker;
|
import org.schabi.newpipe.workers.ChannelExtractorWorker;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
public class ChannelFragment extends BaseFragment implements ChannelExtractorWorker.OnChannelInfoReceive {
|
||||||
|
private final String TAG = "ChannelFragment@" + Integer.toHexString(hashCode());
|
||||||
|
|
||||||
|
private static final String INFO_LIST_KEY = "info_list_key";
|
||||||
|
private static final String CHANNEL_INFO_KEY = "channel_info_key";
|
||||||
|
private static final String PAGE_NUMBER_KEY = "page_number_key";
|
||||||
|
|
||||||
/**
|
|
||||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
|
||||||
* ChannelFragment.java is part of NewPipe.
|
|
||||||
* <p>
|
|
||||||
* NewPipe is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
* <p>
|
|
||||||
* NewPipe is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
* <p>
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class ChannelFragment extends Fragment implements ChannelExtractorWorker.OnChannelInfoReceive {
|
|
||||||
private static final String TAG = "ChannelFragment";
|
|
||||||
|
|
||||||
private AppCompatActivity activity;
|
|
||||||
private OnItemSelectedListener onItemSelectedListener;
|
|
||||||
private InfoListAdapter infoListAdapter;
|
private InfoListAdapter infoListAdapter;
|
||||||
|
|
||||||
private ChannelExtractorWorker currentExtractorWorker;
|
private ChannelExtractorWorker currentChannelWorker;
|
||||||
private ChannelInfo currentChannelInfo;
|
private ChannelInfo currentChannelInfo;
|
||||||
private int serviceId = -1;
|
private int serviceId = -1;
|
||||||
private String channelName = "";
|
private String channelName = "";
|
||||||
private String channelUrl = "";
|
private String channelUrl = "";
|
||||||
|
|
||||||
private boolean isLoading = false;
|
|
||||||
private int pageNumber = 0;
|
private int pageNumber = 0;
|
||||||
private boolean hasNextPage = true;
|
private boolean hasNextPage = true;
|
||||||
|
|
||||||
private ImageLoader imageLoader = ImageLoader.getInstance();
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Views
|
// Views
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
private View rootView = null;
|
|
||||||
|
|
||||||
private RecyclerView channelVideosList;
|
private RecyclerView channelVideosList;
|
||||||
private LinearLayoutManager layoutManager;
|
|
||||||
private ProgressBar loadingProgressBar;
|
|
||||||
|
|
||||||
private View headerRootLayout;
|
private View headerRootLayout;
|
||||||
private ImageView headerChannelBanner;
|
private ImageView headerChannelBanner;
|
||||||
private ImageView headerAvatarView;
|
private ImageView headerAvatarView;
|
||||||
private TextView headerTitleView;
|
private TextView headerTitleView;
|
||||||
private TextView headerSubscriberView;
|
private TextView headerSubscribersTextView;
|
||||||
private Button headerSubscriberButton;
|
private Button headerRssButton;
|
||||||
private View headerSubscriberLayout;
|
|
||||||
|
|
||||||
/*////////////////////////////////////////////////////////////////////////*/
|
/*////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
public ChannelFragment() {
|
public ChannelFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ChannelFragment newInstance(int serviceId, String url, String name) {
|
public static Fragment getInstance(int serviceId, String channelUrl, String name) {
|
||||||
ChannelFragment instance = newInstance();
|
ChannelFragment instance = new ChannelFragment();
|
||||||
|
instance.setChannel(serviceId, channelUrl, name);
|
||||||
Bundle bundle = new Bundle();
|
|
||||||
bundle.putString(Constants.KEY_URL, url);
|
|
||||||
bundle.putString(Constants.KEY_TITLE, name);
|
|
||||||
bundle.putInt(Constants.KEY_SERVICE_ID, serviceId);
|
|
||||||
|
|
||||||
instance.setArguments(bundle);
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ChannelFragment newInstance() {
|
|
||||||
return new ChannelFragment();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Fragment's LifeCycle
|
// Fragment's LifeCycle
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAttach(Context context) {
|
|
||||||
super.onAttach(context);
|
|
||||||
activity = ((AppCompatActivity) context);
|
|
||||||
onItemSelectedListener = ((OnItemSelectedListener) context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
isLoading = false;
|
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
channelUrl = savedInstanceState.getString(Constants.KEY_URL);
|
channelUrl = savedInstanceState.getString(Constants.KEY_URL);
|
||||||
channelName = savedInstanceState.getString(Constants.KEY_TITLE);
|
channelName = savedInstanceState.getString(Constants.KEY_TITLE);
|
||||||
serviceId = savedInstanceState.getInt(Constants.KEY_SERVICE_ID, -1);
|
serviceId = savedInstanceState.getInt(Constants.KEY_SERVICE_ID, -1);
|
||||||
} else {
|
|
||||||
try {
|
pageNumber = savedInstanceState.getInt(PAGE_NUMBER_KEY, 0);
|
||||||
Bundle args = getArguments();
|
Serializable serializable = savedInstanceState.getSerializable(CHANNEL_INFO_KEY);
|
||||||
if (args != null) {
|
if (serializable instanceof ChannelInfo) currentChannelInfo = (ChannelInfo) serializable;
|
||||||
channelUrl = args.getString(Constants.KEY_URL);
|
|
||||||
channelName = args.getString(Constants.KEY_TITLE);
|
|
||||||
serviceId = args.getInt(Constants.KEY_SERVICE_ID, 0);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
ErrorActivity.reportError(getActivity(), e, null,
|
|
||||||
getActivity().findViewById(android.R.id.content),
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
|
|
||||||
NewPipe.getNameOfService(serviceId),
|
|
||||||
"", R.string.general_error));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
rootView = inflater.inflate(R.layout.fragment_channel, container, false);
|
if (DEBUG) Log.d(TAG, "onCreateView() called with: inflater = [" + inflater + "], container = [" + container + "], savedInstanceState = [" + savedInstanceState + "]");
|
||||||
return rootView;
|
return inflater.inflate(R.layout.fragment_channel, container, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||||
loadingProgressBar = (ProgressBar) view.findViewById(R.id.loading_progress_bar);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
channelVideosList = (RecyclerView) view.findViewById(R.id.channel_streams_view);
|
|
||||||
|
|
||||||
infoListAdapter = new InfoListAdapter(activity, rootView);
|
if (currentChannelInfo == null) loadPage(0);
|
||||||
layoutManager = new LinearLayoutManager(activity);
|
else handleChannelInfo(currentChannelInfo, false, false);
|
||||||
channelVideosList.setLayoutManager(layoutManager);
|
|
||||||
|
|
||||||
headerRootLayout = activity.getLayoutInflater().inflate(R.layout.channel_header, channelVideosList, false);
|
|
||||||
infoListAdapter.setHeader(headerRootLayout);
|
|
||||||
infoListAdapter.setFooter(activity.getLayoutInflater().inflate(R.layout.pignate_footer, channelVideosList, false));
|
|
||||||
channelVideosList.setAdapter(infoListAdapter);
|
|
||||||
|
|
||||||
headerChannelBanner = (ImageView) headerRootLayout.findViewById(R.id.channel_banner_image);
|
|
||||||
headerAvatarView = (ImageView) headerRootLayout.findViewById(R.id.channel_avatar_view);
|
|
||||||
headerTitleView = (TextView) headerRootLayout.findViewById(R.id.channel_title_view);
|
|
||||||
headerSubscriberView = (TextView) headerRootLayout.findViewById(R.id.channel_subscriber_view);
|
|
||||||
headerSubscriberButton = (Button) headerRootLayout.findViewById(R.id.channel_subscribe_button);
|
|
||||||
headerSubscriberLayout = headerRootLayout.findViewById(R.id.channel_subscriber_layout);
|
|
||||||
|
|
||||||
initListeners();
|
|
||||||
|
|
||||||
isLoading = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroyView() {
|
public void onDestroyView() {
|
||||||
super.onDestroyView();
|
if (DEBUG) Log.d(TAG, "onDestroyView() called");
|
||||||
headerAvatarView.setImageBitmap(null);
|
headerAvatarView.setImageBitmap(null);
|
||||||
headerChannelBanner.setImageBitmap(null);
|
headerChannelBanner.setImageBitmap(null);
|
||||||
channelVideosList.removeAllViews();
|
channelVideosList.removeAllViews();
|
||||||
|
|
||||||
rootView = null;
|
|
||||||
channelVideosList = null;
|
channelVideosList = null;
|
||||||
layoutManager = null;
|
|
||||||
loadingProgressBar = null;
|
|
||||||
headerRootLayout = null;
|
headerRootLayout = null;
|
||||||
headerChannelBanner = null;
|
headerChannelBanner = null;
|
||||||
headerAvatarView = null;
|
headerAvatarView = null;
|
||||||
headerTitleView = null;
|
headerTitleView = null;
|
||||||
headerSubscriberView = null;
|
headerSubscribersTextView = null;
|
||||||
headerSubscriberButton = null;
|
headerRssButton = null;
|
||||||
headerSubscriberLayout = null;
|
|
||||||
|
super.onDestroyView();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
|
if (DEBUG) Log.d(TAG, "onResume() called");
|
||||||
super.onResume();
|
super.onResume();
|
||||||
if (isLoading) {
|
if (wasLoading.getAndSet(false) && (currentChannelWorker == null || !currentChannelWorker.isRunning())) {
|
||||||
requestData(false);
|
loadPage(pageNumber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStop() {
|
public void onStop() {
|
||||||
|
if (DEBUG) Log.d(TAG, "onStop() called");
|
||||||
super.onStop();
|
super.onStop();
|
||||||
if (currentExtractorWorker != null) currentExtractorWorker.cancel();
|
wasLoading.set(currentChannelWorker != null && currentChannelWorker.isRunning());
|
||||||
}
|
if (currentChannelWorker != null && currentChannelWorker.isRunning()) currentChannelWorker.cancel();
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
imageLoader.clearMemoryCache();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onSaveInstanceState() called with: outState = [" + outState + "]");
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
outState.putString(Constants.KEY_URL, channelUrl);
|
outState.putString(Constants.KEY_URL, channelUrl);
|
||||||
outState.putString(Constants.KEY_TITLE, channelName);
|
outState.putString(Constants.KEY_TITLE, channelName);
|
||||||
outState.putInt(Constants.KEY_SERVICE_ID, serviceId);
|
outState.putInt(Constants.KEY_SERVICE_ID, serviceId);
|
||||||
|
|
||||||
|
outState.putSerializable(INFO_LIST_KEY, ((ArrayList<InfoItem>) infoListAdapter.getItemsList()));
|
||||||
|
outState.putSerializable(CHANNEL_INFO_KEY, currentChannelInfo);
|
||||||
|
outState.putInt(PAGE_NUMBER_KEY, pageNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -243,6 +164,7 @@ public class ChannelFragment extends Fragment implements ChannelExtractorWorker.
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + "], inflater = [" + inflater + "]");
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
inflater.inflate(R.menu.menu_channel, menu);
|
inflater.inflate(R.menu.menu_channel, menu);
|
||||||
|
|
||||||
|
@ -250,20 +172,18 @@ public class ChannelFragment extends Fragment implements ChannelExtractorWorker.
|
||||||
if (supportActionBar != null) {
|
if (supportActionBar != null) {
|
||||||
supportActionBar.setDisplayShowTitleEnabled(true);
|
supportActionBar.setDisplayShowTitleEnabled(true);
|
||||||
supportActionBar.setDisplayHomeAsUpEnabled(true);
|
supportActionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
//noinspection deprecation
|
|
||||||
supportActionBar.setNavigationMode(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onOptionsItemSelected() called with: item = [" + item + "]");
|
||||||
super.onOptionsItemSelected(item);
|
super.onOptionsItemSelected(item);
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case R.id.menu_item_openInBrowser: {
|
case R.id.menu_item_openInBrowser: {
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
intent.setAction(Intent.ACTION_VIEW);
|
intent.setAction(Intent.ACTION_VIEW);
|
||||||
intent.setData(Uri.parse(channelUrl));
|
intent.setData(Uri.parse(channelUrl));
|
||||||
|
|
||||||
startActivity(Intent.createChooser(intent, getString(R.string.choose_browser)));
|
startActivity(Intent.createChooser(intent, getString(R.string.choose_browser)));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -283,79 +203,185 @@ public class ChannelFragment extends Fragment implements ChannelExtractorWorker.
|
||||||
// Init's
|
// Init's
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
private void initListeners() {
|
@Override
|
||||||
|
protected void initViews(View rootView, Bundle savedInstanceState) {
|
||||||
|
super.initViews(rootView, savedInstanceState);
|
||||||
|
|
||||||
|
channelVideosList = (RecyclerView) rootView.findViewById(R.id.channel_streams_view);
|
||||||
|
|
||||||
|
channelVideosList.setLayoutManager(new LinearLayoutManager(activity));
|
||||||
|
if (infoListAdapter == null) {
|
||||||
|
infoListAdapter = new InfoListAdapter(activity, rootView);
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
//noinspection unchecked
|
||||||
|
ArrayList<InfoItem> serializable = (ArrayList<InfoItem>) savedInstanceState.getSerializable(INFO_LIST_KEY);
|
||||||
|
infoListAdapter.addInfoItemList(serializable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
channelVideosList.setAdapter(infoListAdapter);
|
||||||
|
headerRootLayout = activity.getLayoutInflater().inflate(R.layout.channel_header, channelVideosList, false);
|
||||||
|
infoListAdapter.setHeader(headerRootLayout);
|
||||||
|
infoListAdapter.setFooter(activity.getLayoutInflater().inflate(R.layout.pignate_footer, channelVideosList, false));
|
||||||
|
|
||||||
|
headerChannelBanner = (ImageView) headerRootLayout.findViewById(R.id.channel_banner_image);
|
||||||
|
headerAvatarView = (ImageView) headerRootLayout.findViewById(R.id.channel_avatar_view);
|
||||||
|
headerTitleView = (TextView) headerRootLayout.findViewById(R.id.channel_title_view);
|
||||||
|
headerSubscribersTextView = (TextView) headerRootLayout.findViewById(R.id.channel_subscriber_view);
|
||||||
|
headerRssButton = (Button) headerRootLayout.findViewById(R.id.channel_rss_button);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void initListeners() {
|
||||||
|
super.initListeners();
|
||||||
|
|
||||||
infoListAdapter.setOnStreamInfoItemSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() {
|
infoListAdapter.setOnStreamInfoItemSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() {
|
||||||
@Override
|
@Override
|
||||||
public void selected(int serviceId, String url, String title) {
|
public void selected(int serviceId, String url, String title) {
|
||||||
|
if (DEBUG) Log.d(TAG, "selected() called with: serviceId = [" + serviceId + "], url = [" + url + "], title = [" + title + "]");
|
||||||
NavigationHelper.openVideoDetail(onItemSelectedListener, serviceId, url, title);
|
NavigationHelper.openVideoDetail(onItemSelectedListener, serviceId, url, title);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// detect if list has ben scrolled to the bottom
|
channelVideosList.clearOnScrollListeners();
|
||||||
channelVideosList.setOnScrollListener(new RecyclerView.OnScrollListener() {
|
channelVideosList.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
||||||
int pastVisiblesItems, visibleItemCount, totalItemCount;
|
int pastVisiblesItems, visibleItemCount, totalItemCount;
|
||||||
super.onScrolled(recyclerView, dx, dy);
|
super.onScrolled(recyclerView, dx, dy);
|
||||||
//check for scroll down
|
//check for scroll down
|
||||||
if (dy > 0) {
|
if (dy > 0) {
|
||||||
|
LinearLayoutManager layoutManager = (LinearLayoutManager) channelVideosList.getLayoutManager();
|
||||||
|
|
||||||
visibleItemCount = layoutManager.getChildCount();
|
visibleItemCount = layoutManager.getChildCount();
|
||||||
totalItemCount = layoutManager.getItemCount();
|
totalItemCount = layoutManager.getItemCount();
|
||||||
pastVisiblesItems = layoutManager.findFirstVisibleItemPosition();
|
pastVisiblesItems = layoutManager.findFirstVisibleItemPosition();
|
||||||
|
|
||||||
if ((visibleItemCount + pastVisiblesItems) >= totalItemCount && !currentExtractorWorker.isRunning() && hasNextPage) {
|
if ((visibleItemCount + pastVisiblesItems) >= totalItemCount && (currentChannelWorker == null || !currentChannelWorker.isRunning()) && hasNextPage && !isLoading.get()) {
|
||||||
pageNumber++;
|
pageNumber++;
|
||||||
requestData(true);
|
loadMoreVideos();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
headerSubscriberButton.setOnClickListener(new View.OnClickListener() {
|
headerRssButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
Log.d(TAG, currentChannelInfo.feed_url);
|
if (DEBUG) Log.d(TAG, "onClick() called with: view = [" + view + "] feed url > " + currentChannelInfo.feed_url);
|
||||||
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(currentChannelInfo.feed_url));
|
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(currentChannelInfo.feed_url));
|
||||||
startActivity(i);
|
startActivity(i);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void reloadContent() {
|
||||||
|
if (DEBUG) Log.d(TAG, "reloadContent() called");
|
||||||
|
currentChannelInfo = null;
|
||||||
|
infoListAdapter.clearStreamItemList();
|
||||||
|
loadPage(0);
|
||||||
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Utils
|
// Utils
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
private String buildSubscriberString(long count) {
|
private String buildSubscriberString(long count) {
|
||||||
String out = NumberFormat.getNumberInstance().format(count);
|
String out = NumberFormat.getNumberInstance().format(count);
|
||||||
out += " " + getString(count > 1 ? R.string.subscriber_plural : R.string.subscriber);
|
out += " " + getString(count > 1 ? R.string.subscriber_plural : R.string.subscriber);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void requestData(boolean onlyVideos) {
|
private void loadPage(int page) {
|
||||||
if (currentExtractorWorker != null && currentExtractorWorker.isRunning()) currentExtractorWorker.cancel();
|
if (DEBUG) Log.d(TAG, "loadPage() called with: page = [" + page + "]");
|
||||||
|
if (currentChannelWorker != null && currentChannelWorker.isRunning()) currentChannelWorker.cancel();
|
||||||
|
isLoading.set(true);
|
||||||
|
pageNumber = page;
|
||||||
|
infoListAdapter.showFooter(false);
|
||||||
|
|
||||||
isLoading = true;
|
animateView(loadingProgressBar, true, 200);
|
||||||
if (!onlyVideos) {
|
animateView(errorPanel, false, 200);
|
||||||
//delete already displayed content
|
|
||||||
loadingProgressBar.setVisibility(View.VISIBLE);
|
|
||||||
infoListAdapter.clearSteamItemList();
|
|
||||||
pageNumber = 0;
|
|
||||||
headerSubscriberLayout.setVisibility(View.GONE);
|
|
||||||
headerTitleView.setText(channelName != null ? channelName : "");
|
|
||||||
if (activity.getSupportActionBar() != null) activity.getSupportActionBar().setTitle(channelName != null ? channelName : "");
|
|
||||||
if (SDK_INT >= 21) {
|
|
||||||
headerChannelBanner.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.channel_banner));
|
|
||||||
headerAvatarView.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.buddy));
|
|
||||||
}
|
|
||||||
infoListAdapter.showFooter(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
currentExtractorWorker = new ChannelExtractorWorker(activity, serviceId, channelUrl, pageNumber, this);
|
imageLoader.cancelDisplayTask(headerChannelBanner);
|
||||||
currentExtractorWorker.setOnlyVideos(onlyVideos);
|
imageLoader.cancelDisplayTask(headerAvatarView);
|
||||||
currentExtractorWorker.start();
|
|
||||||
|
headerRssButton.setVisibility(View.GONE);
|
||||||
|
headerSubscribersTextView.setVisibility(View.GONE);
|
||||||
|
|
||||||
|
headerTitleView.setText(channelName != null ? channelName : "");
|
||||||
|
headerChannelBanner.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.channel_banner));
|
||||||
|
headerAvatarView.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.buddy));
|
||||||
|
if (activity.getSupportActionBar() != null) activity.getSupportActionBar().setTitle(channelName != null ? channelName : "");
|
||||||
|
|
||||||
|
currentChannelWorker = new ChannelExtractorWorker(activity, serviceId, channelUrl, page, false, this);
|
||||||
|
currentChannelWorker.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addVideos(ChannelInfo info) {
|
private void loadMoreVideos() {
|
||||||
infoListAdapter.addInfoItemList(info.related_streams);
|
if (DEBUG) Log.d(TAG, "loadMoreVideos() called");
|
||||||
|
if (currentChannelWorker != null && currentChannelWorker.isRunning()) currentChannelWorker.cancel();
|
||||||
|
isLoading.set(true);
|
||||||
|
currentChannelWorker = new ChannelExtractorWorker(activity, serviceId, channelUrl, pageNumber, true, this);
|
||||||
|
currentChannelWorker.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setChannel(int serviceId, String channelUrl, String name) {
|
||||||
|
this.serviceId = serviceId;
|
||||||
|
this.channelUrl = channelUrl;
|
||||||
|
this.channelName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleChannelInfo(ChannelInfo info, boolean onlyVideos, boolean addVideos) {
|
||||||
|
currentChannelInfo = info;
|
||||||
|
|
||||||
|
animateView(errorPanel, false, 300);
|
||||||
|
animateView(channelVideosList, true, 200);
|
||||||
|
animateView(loadingProgressBar, false, 200);
|
||||||
|
|
||||||
|
if (!onlyVideos) {
|
||||||
|
headerRootLayout.setVisibility(View.VISIBLE);
|
||||||
|
//animateView(loadingProgressBar, false, 200, null);
|
||||||
|
|
||||||
|
if (!TextUtils.isEmpty(info.channel_name)) {
|
||||||
|
if (activity.getSupportActionBar() != null) activity.getSupportActionBar().setTitle(info.channel_name);
|
||||||
|
headerTitleView.setText(info.channel_name);
|
||||||
|
channelName = info.channel_name;
|
||||||
|
} else channelName = "";
|
||||||
|
|
||||||
|
if (!TextUtils.isEmpty(info.banner_url)) {
|
||||||
|
imageLoader.displayImage(info.banner_url, headerChannelBanner, displayImageOptions, new ImageErrorLoadingListener(activity, getView(), info.service_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TextUtils.isEmpty(info.avatar_url)) {
|
||||||
|
headerAvatarView.setVisibility(View.VISIBLE);
|
||||||
|
imageLoader.displayImage(info.avatar_url, headerAvatarView, displayImageOptions, new ImageErrorLoadingListener(activity, getView(), info.service_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.subscriberCount != -1) {
|
||||||
|
headerSubscribersTextView.setText(buildSubscriberString(info.subscriberCount));
|
||||||
|
headerSubscribersTextView.setVisibility(View.VISIBLE);
|
||||||
|
} else headerSubscribersTextView.setVisibility(View.GONE);
|
||||||
|
|
||||||
|
if (!TextUtils.isEmpty(info.feed_url)) headerRssButton.setVisibility(View.VISIBLE);
|
||||||
|
else headerRssButton.setVisibility(View.INVISIBLE);
|
||||||
|
|
||||||
|
infoListAdapter.showFooter(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasNextPage = info.hasNextPage;
|
||||||
|
if (!hasNextPage) infoListAdapter.showFooter(false);
|
||||||
|
|
||||||
|
//if (!listRestored) {
|
||||||
|
if (addVideos) infoListAdapter.addInfoItemList(info.related_streams);
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setErrorMessage(String message, boolean showRetryButton) {
|
||||||
|
super.setErrorMessage(message, showRetryButton);
|
||||||
|
|
||||||
|
animateView(channelVideosList, false, 200);
|
||||||
|
currentChannelInfo = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -363,59 +389,23 @@ public class ChannelFragment extends Fragment implements ChannelExtractorWorker.
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(ChannelInfo info) {
|
public void onReceive(ChannelInfo info, boolean onlyVideos) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onReceive() called with: info = [" + info + "]");
|
||||||
if (info == null || isRemoving() || !isVisible()) return;
|
if (info == null || isRemoving() || !isVisible()) return;
|
||||||
|
|
||||||
currentChannelInfo = info;
|
handleChannelInfo(info, onlyVideos, true);
|
||||||
|
isLoading.set(false);
|
||||||
if (!currentExtractorWorker.isOnlyVideos()) {
|
|
||||||
headerRootLayout.setVisibility(View.VISIBLE);
|
|
||||||
loadingProgressBar.setVisibility(View.GONE);
|
|
||||||
|
|
||||||
if (info.channel_name != null && !info.channel_name.isEmpty()) {
|
|
||||||
if (activity.getSupportActionBar() != null) activity.getSupportActionBar().setTitle(info.channel_name);
|
|
||||||
headerTitleView.setText(info.channel_name);
|
|
||||||
channelName = info.channel_name;
|
|
||||||
} else channelName = "";
|
|
||||||
|
|
||||||
if (info.banner_url != null && !info.banner_url.isEmpty()) {
|
|
||||||
imageLoader.displayImage(info.banner_url, headerChannelBanner,
|
|
||||||
new ImageErrorLoadingListener(activity, rootView, info.service_id));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info.avatar_url != null && !info.avatar_url.isEmpty()) {
|
|
||||||
headerAvatarView.setVisibility(View.VISIBLE);
|
|
||||||
imageLoader.displayImage(info.avatar_url, headerAvatarView,
|
|
||||||
new ImageErrorLoadingListener(activity, rootView, info.service_id));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info.subscriberCount != -1) {
|
|
||||||
headerSubscriberView.setText(buildSubscriberString(info.subscriberCount));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((info.feed_url != null && !info.feed_url.isEmpty()) || (info.subscriberCount != -1)) {
|
|
||||||
headerSubscriberLayout.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info.feed_url == null || info.feed_url.isEmpty()) {
|
|
||||||
headerSubscriberButton.setVisibility(View.INVISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
infoListAdapter.showFooter(true);
|
|
||||||
}
|
|
||||||
hasNextPage = info.hasNextPage;
|
|
||||||
if (!hasNextPage) infoListAdapter.showFooter(false);
|
|
||||||
addVideos(info);
|
|
||||||
isLoading = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(int messageId) {
|
public void onError(int messageId) {
|
||||||
Toast.makeText(activity, messageId, Toast.LENGTH_LONG).show();
|
if (DEBUG) Log.d(TAG, "onError() called with: messageId = [" + messageId + "]");
|
||||||
|
setErrorMessage(getString(messageId), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUnrecoverableError(Exception exception) {
|
public void onUnrecoverableError(Exception exception) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onUnrecoverableError() called with: exception = [" + exception + "]");
|
||||||
activity.finish();
|
activity.finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,15 @@ package org.schabi.newpipe.fragments.detail;
|
||||||
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.v7.app.ActionBar;
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.AdapterView;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.MediaFormat;
|
import org.schabi.newpipe.extractor.MediaFormat;
|
||||||
|
@ -19,27 +21,27 @@ import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Christian Schabesberger on 18.08.15.
|
* Created by Christian Schabesberger on 18.08.15.
|
||||||
*
|
* <p>
|
||||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
||||||
* DetailsMenuHandler.java is part of NewPipe.
|
* DetailsMenuHandler.java is part of NewPipe.
|
||||||
*
|
* <p>
|
||||||
* NewPipe is free software: you can redistribute it and/or modify
|
* NewPipe is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
* <p>
|
||||||
* NewPipe is distributed in the hope that it will be useful,
|
* NewPipe is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
* <p>
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
class ActionBarHandler {
|
class ActionBarHandler {
|
||||||
private static final String TAG = ActionBarHandler.class.toString();
|
private static final String TAG = "ActionBarHandler";
|
||||||
|
|
||||||
private AppCompatActivity activity;
|
private AppCompatActivity activity;
|
||||||
private int selectedVideoStream = -1;
|
private int selectedVideoStream = -1;
|
||||||
|
@ -52,11 +54,8 @@ class ActionBarHandler {
|
||||||
// those are edited directly. Typically VideoDetailFragment will implement those callbacks.
|
// those are edited directly. Typically VideoDetailFragment will implement those callbacks.
|
||||||
private OnActionListener onShareListener;
|
private OnActionListener onShareListener;
|
||||||
private OnActionListener onOpenInBrowserListener;
|
private OnActionListener onOpenInBrowserListener;
|
||||||
private OnActionListener onOpenInPopupListener;
|
|
||||||
private OnActionListener onDownloadListener;
|
private OnActionListener onDownloadListener;
|
||||||
private OnActionListener onPlayWithKodiListener;
|
private OnActionListener onPlayWithKodiListener;
|
||||||
private OnActionListener onPlayAudioListener;
|
|
||||||
|
|
||||||
|
|
||||||
// Triggered when a stream related action is triggered.
|
// Triggered when a stream related action is triggered.
|
||||||
public interface OnActionListener {
|
public interface OnActionListener {
|
||||||
|
@ -67,46 +66,32 @@ class ActionBarHandler {
|
||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({"deprecation", "ConstantConditions"})
|
public void setupStreamList(final List<VideoStream> videoStreams, Spinner toolbarSpinner) {
|
||||||
public void setupNavMenu(AppCompatActivity activity) {
|
if (activity == null) return;
|
||||||
this.activity = activity;
|
selectedVideoStream = 0;
|
||||||
try {
|
|
||||||
activity.getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
|
// this array will be shown in the dropdown menu for selecting the stream/resolution.
|
||||||
} catch (NullPointerException e) {
|
String[] itemArray = new String[videoStreams.size()];
|
||||||
e.printStackTrace();
|
for (int i = 0; i < videoStreams.size(); i++) {
|
||||||
|
VideoStream item = videoStreams.get(i);
|
||||||
|
itemArray[i] = MediaFormat.getNameById(item.format) + " " + item.resolution;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void setupStreamList(final List<VideoStream> videoStreams) {
|
int defaultResolutionIndex = Utils.getDefaultResolution(activity, videoStreams);
|
||||||
if (activity != null) {
|
ArrayAdapter<String> itemAdapter = new ArrayAdapter<>(activity.getBaseContext(), android.R.layout.simple_spinner_dropdown_item, itemArray);
|
||||||
selectedVideoStream = 0;
|
toolbarSpinner.setAdapter(itemAdapter);
|
||||||
|
toolbarSpinner.setSelection(defaultResolutionIndex);
|
||||||
|
toolbarSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||||
// this array will be shown in the dropdown menu for selecting the stream/resolution.
|
@Override
|
||||||
String[] itemArray = new String[videoStreams.size()];
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||||
for (int i = 0; i < videoStreams.size(); i++) {
|
selectedVideoStream = position;
|
||||||
VideoStream item = videoStreams.get(i);
|
|
||||||
itemArray[i] = MediaFormat.getNameById(item.format) + " " + item.resolution;
|
|
||||||
}
|
}
|
||||||
int defaultResolution = Utils.getDefaultResolution(activity, videoStreams);
|
|
||||||
|
|
||||||
ArrayAdapter<String> itemAdapter = new ArrayAdapter<>(activity.getBaseContext(),
|
@Override
|
||||||
android.R.layout.simple_spinner_dropdown_item, itemArray);
|
public void onNothingSelected(AdapterView<?> parent) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
ActionBar ab = activity.getSupportActionBar();
|
|
||||||
//todo: make this throwsable
|
|
||||||
assert ab != null : "Could not get actionbar";
|
|
||||||
ab.setListNavigationCallbacks(itemAdapter
|
|
||||||
, new ActionBar.OnNavigationListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onNavigationItemSelected(int itemPosition, long itemId) {
|
|
||||||
selectedVideoStream = (int) itemId;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ab.setSelectedNavigationItem(defaultResolution);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setupMenu(Menu menu, MenuInflater inflater) {
|
public void setupMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
@ -116,55 +101,36 @@ class ActionBarHandler {
|
||||||
// appcompat itemsinflater.inflate(R.menu.videoitem_detail, menu);
|
// appcompat itemsinflater.inflate(R.menu.videoitem_detail, menu);
|
||||||
|
|
||||||
defaultPreferences = PreferenceManager.getDefaultSharedPreferences(activity);
|
defaultPreferences = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||||
inflater.inflate(R.menu.videoitem_detail, menu);
|
inflater.inflate(R.menu.video_detail_menu, menu);
|
||||||
|
|
||||||
showPlayWithKodiAction(defaultPreferences
|
showPlayWithKodiAction(defaultPreferences.getBoolean(activity.getString(R.string.show_play_with_kodi_key), false));
|
||||||
.getBoolean(activity.getString(R.string.show_play_with_kodi_key), false));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onItemSelected(MenuItem item) {
|
public boolean onItemSelected(MenuItem item) {
|
||||||
int id = item.getItemId();
|
int id = item.getItemId();
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case R.id.menu_item_share: {
|
case R.id.menu_item_share: {
|
||||||
/*
|
if (onShareListener != null) {
|
||||||
Intent intent = new Intent();
|
|
||||||
intent.setAction(Intent.ACTION_SEND);
|
|
||||||
intent.putExtra(Intent.EXTRA_TEXT, websiteUrl);
|
|
||||||
intent.setType("text/plain");
|
|
||||||
activity.startActivity(Intent.createChooser(intent, activity.getString(R.string.share_dialog_title)));
|
|
||||||
*/
|
|
||||||
if(onShareListener != null) {
|
|
||||||
onShareListener.onActionSelected(selectedVideoStream);
|
onShareListener.onActionSelected(selectedVideoStream);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case R.id.menu_item_openInBrowser: {
|
case R.id.menu_item_openInBrowser: {
|
||||||
if(onOpenInBrowserListener != null) {
|
if (onOpenInBrowserListener != null) {
|
||||||
onOpenInBrowserListener.onActionSelected(selectedVideoStream);
|
onOpenInBrowserListener.onActionSelected(selectedVideoStream);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
case R.id.menu_item_download:
|
case R.id.menu_item_download:
|
||||||
if(onDownloadListener != null) {
|
if (onDownloadListener != null) {
|
||||||
onDownloadListener.onActionSelected(selectedVideoStream);
|
onDownloadListener.onActionSelected(selectedVideoStream);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
case R.id.action_play_with_kodi:
|
case R.id.action_play_with_kodi:
|
||||||
if(onPlayWithKodiListener != null) {
|
if (onPlayWithKodiListener != null) {
|
||||||
onPlayWithKodiListener.onActionSelected(selectedVideoStream);
|
onPlayWithKodiListener.onActionSelected(selectedVideoStream);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
case R.id.menu_item_play_audio:
|
|
||||||
if(onPlayAudioListener != null) {
|
|
||||||
onPlayAudioListener.onActionSelected(selectedVideoStream);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
case R.id.menu_item_popup: {
|
|
||||||
if(onOpenInPopupListener != null) {
|
|
||||||
onOpenInPopupListener.onActionSelected(selectedVideoStream);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
Log.e(TAG, "Menu Item not known");
|
Log.e(TAG, "Menu Item not known");
|
||||||
}
|
}
|
||||||
|
@ -183,10 +149,6 @@ class ActionBarHandler {
|
||||||
onOpenInBrowserListener = listener;
|
onOpenInBrowserListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOnOpenInPopupListener(OnActionListener listener) {
|
|
||||||
onOpenInPopupListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOnDownloadListener(OnActionListener listener) {
|
public void setOnDownloadListener(OnActionListener listener) {
|
||||||
onDownloadListener = listener;
|
onDownloadListener = listener;
|
||||||
}
|
}
|
||||||
|
@ -195,14 +157,6 @@ class ActionBarHandler {
|
||||||
onPlayWithKodiListener = listener;
|
onPlayWithKodiListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOnPlayAudioListener(OnActionListener listener) {
|
|
||||||
onPlayAudioListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void showAudioAction(boolean visible) {
|
|
||||||
menu.findItem(R.id.menu_item_play_audio).setVisible(visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void showDownloadAction(boolean visible) {
|
public void showDownloadAction(boolean visible) {
|
||||||
menu.findItem(R.id.menu_item_download).setVisible(visible);
|
menu.findItem(R.id.menu_item_download).setVisible(visible);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
package org.schabi.newpipe.fragments.detail;
|
package org.schabi.newpipe.fragments.detail;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
public class StackItem implements Serializable {
|
public class StackItem implements Serializable {
|
||||||
private String title, url;
|
private String title, url;
|
||||||
|
private StreamInfo info;
|
||||||
|
|
||||||
public StackItem(String url, String title) {
|
public StackItem(String url, String title) {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
|
@ -24,6 +27,14 @@ public class StackItem implements Serializable {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setInfo(StreamInfo info) {
|
||||||
|
this.info = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StreamInfo getInfo() {
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return getUrl() + " > " + getTitle();
|
return getUrl() + " > " + getTitle();
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
package org.schabi.newpipe.fragments.detail;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.MainActivity;
|
||||||
|
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
public class StreamInfoCache {
|
||||||
|
private static String TAG = "StreamInfoCache@";
|
||||||
|
private static final boolean DEBUG = MainActivity.DEBUG;
|
||||||
|
private static final StreamInfoCache instance = new StreamInfoCache();
|
||||||
|
private static final int MAX_ITEMS_ON_CACHE = 20;
|
||||||
|
|
||||||
|
private final LinkedHashMap<String, StreamInfo> myCache = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
private StreamInfoCache() {
|
||||||
|
TAG += "" + Integer.toHexString(hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StreamInfoCache getInstance() {
|
||||||
|
if (DEBUG) Log.d(TAG, "getInstance() called");
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasKey(@NonNull String url) {
|
||||||
|
if (DEBUG) Log.d(TAG, "hasKey() called with: url = [" + url + "]");
|
||||||
|
return !TextUtils.isEmpty(url) && myCache.containsKey(url) && myCache.get(url) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StreamInfo getFromKey(@NonNull String url) {
|
||||||
|
if (DEBUG) Log.d(TAG, "getFromKey() called with: url = [" + url + "]");
|
||||||
|
return myCache.get(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putInfo(@NonNull StreamInfo info) {
|
||||||
|
if (DEBUG) Log.d(TAG, "putInfo() called with: info = [" + info + "]");
|
||||||
|
putInfo(info.webpage_url, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putInfo(@NonNull String url, @NonNull StreamInfo info) {
|
||||||
|
if (DEBUG) Log.d(TAG, "putInfo() called with: url = [" + url + "], info = [" + info + "]");
|
||||||
|
myCache.put(url, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeInfo(@NonNull StreamInfo info) {
|
||||||
|
if (DEBUG) Log.d(TAG, "removeInfo() called with: info = [" + info + "]");
|
||||||
|
myCache.remove(info.webpage_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeInfo(@NonNull String url) {
|
||||||
|
if (DEBUG) Log.d(TAG, "removeInfo() called with: url = [" + url + "]");
|
||||||
|
myCache.remove(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public void clearCache() {
|
||||||
|
if (DEBUG) Log.d(TAG, "clearCache() called");
|
||||||
|
myCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeOldEntries() {
|
||||||
|
if (DEBUG) Log.d(TAG, "removeOldEntries() called , size = " + getSize());
|
||||||
|
if (getSize() > MAX_ITEMS_ON_CACHE) {
|
||||||
|
Iterator<String> iterator = myCache.keySet().iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
iterator.next();
|
||||||
|
iterator.remove();
|
||||||
|
if (DEBUG) Log.d(TAG, "getSize() = " + getSize());
|
||||||
|
if (getSize() <= MAX_ITEMS_ON_CACHE) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSize() {
|
||||||
|
return myCache.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,245 +3,189 @@ package org.schabi.newpipe.fragments.search;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
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.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.support.v7.widget.SearchView;
|
import android.text.Editable;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.text.TextWatcher;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.KeyEvent;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.animation.DecelerateInterpolator;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.AutoCompleteTextView;
|
||||||
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.ReCaptchaActivity;
|
import org.schabi.newpipe.ReCaptchaActivity;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
import org.schabi.newpipe.extractor.search.SearchEngine;
|
import org.schabi.newpipe.extractor.search.SearchEngine;
|
||||||
import org.schabi.newpipe.extractor.search.SearchResult;
|
import org.schabi.newpipe.extractor.search.SearchResult;
|
||||||
import org.schabi.newpipe.fragments.OnItemSelectedListener;
|
import org.schabi.newpipe.fragments.BaseFragment;
|
||||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||||
import org.schabi.newpipe.info_list.InfoListAdapter;
|
import org.schabi.newpipe.info_list.InfoListAdapter;
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
|
import org.schabi.newpipe.workers.SearchWorker;
|
||||||
|
import org.schabi.newpipe.workers.SuggestionWorker;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static android.app.Activity.RESULT_OK;
|
public class SearchFragment extends BaseFragment implements SuggestionWorker.OnSuggestionResult, SearchWorker.OnSearchResult {
|
||||||
import static org.schabi.newpipe.ReCaptchaActivity.RECAPTCHA_REQUEST;
|
private final String TAG = "SearchFragment@" + Integer.toHexString(hashCode());
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Christian Schabesberger on 02.08.16.
|
|
||||||
* <p>
|
|
||||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
|
||||||
* SearchFragment.java is part of NewPipe.
|
|
||||||
* <p>
|
|
||||||
* NewPipe is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
* <p>
|
|
||||||
* NewPipe is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
* <p>
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class SearchFragment extends Fragment implements SearchView.OnQueryTextListener, SearchWorker.SearchWorkerResultListener {
|
|
||||||
|
|
||||||
private static final String TAG = SearchFragment.class.toString();
|
|
||||||
|
|
||||||
// savedInstanceBundle arguments
|
// savedInstanceBundle arguments
|
||||||
private static final String QUERY = "query";
|
private static final String QUERY_KEY = "query_key";
|
||||||
private static final String STREAMING_SERVICE = "streaming_service";
|
private static final String PAGE_NUMBER_KEY = "page_number_key";
|
||||||
|
private static final String SERVICE_KEY = "service_key";
|
||||||
|
private static final String INFO_LIST_KEY = "info_list_key";
|
||||||
|
private static final String WAS_LOADING_KEY = "was_loading_key";
|
||||||
|
private static final String ERROR_KEY = "error_key";
|
||||||
|
private static final String FILTER_CHECKED_ID_KEY = "filter_checked_id_key";
|
||||||
|
|
||||||
private int streamingServiceId = -1;
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Search
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
private int filterItemCheckedId = -1;
|
||||||
|
private EnumSet<SearchEngine.Filter> filter = EnumSet.of(SearchEngine.Filter.CHANNEL, SearchEngine.Filter.STREAM);
|
||||||
|
|
||||||
|
private int serviceId = -1;
|
||||||
private String searchQuery = "";
|
private String searchQuery = "";
|
||||||
private boolean isLoading = false;
|
|
||||||
|
|
||||||
@SuppressWarnings("FieldCanBeLocal")
|
|
||||||
private SearchView searchView;
|
|
||||||
private RecyclerView recyclerView;
|
|
||||||
private ProgressBar loadingIndicator;
|
|
||||||
private int pageNumber = 0;
|
private int pageNumber = 0;
|
||||||
|
|
||||||
|
private SearchWorker curSearchWorker;
|
||||||
|
private SuggestionWorker curSuggestionWorker;
|
||||||
private SuggestionListAdapter suggestionListAdapter;
|
private SuggestionListAdapter suggestionListAdapter;
|
||||||
private InfoListAdapter infoListAdapter;
|
private InfoListAdapter infoListAdapter;
|
||||||
private LinearLayoutManager streamInfoListLayoutManager;
|
|
||||||
|
|
||||||
private EnumSet<SearchEngine.Filter> filter = EnumSet.of(SearchEngine.Filter.CHANNEL, SearchEngine.Filter.STREAM);
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
private OnItemSelectedListener onItemSelectedListener;
|
// Views
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
/**
|
private View searchToolbarContainer;
|
||||||
* Mandatory empty constructor for the fragment manager to instantiate the
|
private AutoCompleteTextView searchEditText;
|
||||||
* fragment (e.g. upon screen orientation changes).
|
private View searchClear;
|
||||||
*/
|
|
||||||
public SearchFragment() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
private RecyclerView resultRecyclerView;
|
||||||
public static SearchFragment newInstance(int streamingServiceId, String searchQuery) {
|
|
||||||
Bundle args = new Bundle();
|
/*////////////////////////////////////////////////////////////////////////*/
|
||||||
args.putInt(STREAMING_SERVICE, streamingServiceId);
|
|
||||||
args.putString(QUERY, searchQuery);
|
public static SearchFragment getInstance(int serviceId, String query) {
|
||||||
SearchFragment fragment = new SearchFragment();
|
SearchFragment searchFragment = new SearchFragment();
|
||||||
fragment.setArguments(args);
|
searchFragment.setQuery(serviceId, query);
|
||||||
return fragment;
|
return searchFragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Fragment's LifeCycle
|
// Fragment's LifeCycle
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAttach(Context context) {
|
|
||||||
super.onAttach(context);
|
|
||||||
onItemSelectedListener = ((OnItemSelectedListener) context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
searchQuery = "";
|
if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
|
||||||
isLoading = false;
|
|
||||||
if (savedInstanceState != null) {
|
|
||||||
searchQuery = savedInstanceState.getString(QUERY);
|
|
||||||
streamingServiceId = savedInstanceState.getInt(STREAMING_SERVICE);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
Bundle args = getArguments();
|
|
||||||
if (args != null) {
|
|
||||||
searchQuery = args.getString(QUERY);
|
|
||||||
streamingServiceId = args.getInt(STREAMING_SERVICE);
|
|
||||||
} else {
|
|
||||||
streamingServiceId = NewPipe.getIdOfService("Youtube");
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
ErrorActivity.reportError(getActivity(), e, null,
|
|
||||||
getActivity().findViewById(android.R.id.content),
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
|
|
||||||
NewPipe.getNameOfService(streamingServiceId),
|
|
||||||
"", R.string.general_error));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
|
if (savedInstanceState != null) {
|
||||||
SearchWorker sw = SearchWorker.getInstance();
|
searchQuery = savedInstanceState.getString(QUERY_KEY);
|
||||||
sw.setSearchWorkerResultListener(this);
|
serviceId = savedInstanceState.getInt(SERVICE_KEY, 0);
|
||||||
}
|
pageNumber = savedInstanceState.getInt(PAGE_NUMBER_KEY, 0);
|
||||||
|
wasLoading.set(savedInstanceState.getBoolean(WAS_LOADING_KEY, false));
|
||||||
@Override
|
filterItemCheckedId = savedInstanceState.getInt(FILTER_CHECKED_ID_KEY, 0);
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
|
||||||
View view = inflater.inflate(R.layout.fragment_search, container, false);
|
|
||||||
|
|
||||||
Context context = view.getContext();
|
|
||||||
loadingIndicator = (ProgressBar) view.findViewById(R.id.loading_progress_bar);
|
|
||||||
recyclerView = (RecyclerView) view.findViewById(R.id.list);
|
|
||||||
streamInfoListLayoutManager = new LinearLayoutManager(context);
|
|
||||||
recyclerView.setLayoutManager(streamInfoListLayoutManager);
|
|
||||||
|
|
||||||
infoListAdapter = new InfoListAdapter(getActivity(),
|
|
||||||
getActivity().findViewById(android.R.id.content));
|
|
||||||
infoListAdapter.setFooter(inflater.inflate(R.layout.pignate_footer, recyclerView, false));
|
|
||||||
infoListAdapter.showFooter(false);
|
|
||||||
infoListAdapter.setOnStreamInfoItemSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() {
|
|
||||||
@Override
|
|
||||||
public void selected(int serviceId, String url, String title) {
|
|
||||||
NavigationHelper.openVideoDetail(onItemSelectedListener, serviceId, url, title);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
infoListAdapter.setOnChannelInfoItemSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() {
|
|
||||||
@Override
|
|
||||||
public void selected(int serviceId, String url, String title) {
|
|
||||||
NavigationHelper.openChannel(onItemSelectedListener, serviceId, url, title);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
recyclerView.setAdapter(infoListAdapter);
|
|
||||||
recyclerView.clearOnScrollListeners();
|
|
||||||
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
|
||||||
@Override
|
|
||||||
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
|
||||||
int pastVisiblesItems, visibleItemCount, totalItemCount;
|
|
||||||
super.onScrolled(recyclerView, dx, dy);
|
|
||||||
if (dy > 0) //check for scroll down
|
|
||||||
{
|
|
||||||
visibleItemCount = streamInfoListLayoutManager.getChildCount();
|
|
||||||
totalItemCount = streamInfoListLayoutManager.getItemCount();
|
|
||||||
pastVisiblesItems = streamInfoListLayoutManager.findFirstVisibleItemPosition();
|
|
||||||
|
|
||||||
if ((visibleItemCount + pastVisiblesItems) >= totalItemCount && !isLoading) {
|
|
||||||
pageNumber++;
|
|
||||||
recyclerView.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
infoListAdapter.showFooter(true);
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
search(searchQuery, pageNumber);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
|
||||||
super.onViewCreated(view, savedInstanceState);
|
|
||||||
if (!searchQuery.isEmpty()) {
|
|
||||||
search(searchQuery);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroyView() {
|
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
super.onDestroyView();
|
if (DEBUG) Log.d(TAG, "onCreateView() called with: inflater = [" + inflater + "], container = [" + container + "], savedInstanceState = [" + savedInstanceState + "]");
|
||||||
recyclerView.removeAllViews();
|
return inflater.inflate(R.layout.fragment_search, container, false);
|
||||||
infoListAdapter.clearSteamItemList();
|
}
|
||||||
recyclerView = null;
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View rootView, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(rootView, savedInstanceState);
|
||||||
|
if (DEBUG) Log.d(TAG, "onViewCreated() called with: rootView = [" + rootView + "], savedInstanceState = [" + savedInstanceState + "]");
|
||||||
|
|
||||||
|
if (savedInstanceState != null && savedInstanceState.getBoolean(ERROR_KEY, false)) {
|
||||||
|
search(searchQuery, 0, true);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
if (isLoading && !searchQuery.isEmpty()) {
|
if (DEBUG) Log.d(TAG, "onResume() called");
|
||||||
search(searchQuery);
|
if (wasLoading.getAndSet(false) && !TextUtils.isEmpty(searchQuery)) {
|
||||||
|
if (pageNumber > 0) search(searchQuery, pageNumber);
|
||||||
|
else search(searchQuery, 0, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStop() {
|
public void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
SearchWorker.getInstance().terminate();
|
if (DEBUG) Log.d(TAG, "onStop() called");
|
||||||
|
|
||||||
|
hideSoftKeyboard(searchEditText);
|
||||||
|
|
||||||
|
wasLoading.set(curSearchWorker != null && curSearchWorker.isRunning());
|
||||||
|
if (curSearchWorker != null && curSearchWorker.isRunning()) curSearchWorker.cancel();
|
||||||
|
if (curSuggestionWorker != null && curSuggestionWorker.isRunning()) curSuggestionWorker.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
if (DEBUG) Log.d(TAG, "onDestroyView() called");
|
||||||
|
unsetSearchListeners();
|
||||||
|
|
||||||
|
resultRecyclerView.removeAllViews();
|
||||||
|
|
||||||
|
searchToolbarContainer = null;
|
||||||
|
searchEditText = null;
|
||||||
|
searchClear = null;
|
||||||
|
|
||||||
|
resultRecyclerView = null;
|
||||||
|
|
||||||
|
super.onDestroyView();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
outState.putString(QUERY, searchQuery);
|
if (DEBUG) Log.d(TAG, "onSaveInstanceState() called with: outState = [" + outState + "]");
|
||||||
outState.putInt(STREAMING_SERVICE, streamingServiceId);
|
|
||||||
|
String query = searchEditText != null && !TextUtils.isEmpty(searchEditText.getText().toString())
|
||||||
|
? searchEditText.getText().toString() : searchQuery;
|
||||||
|
outState.putString(QUERY_KEY, query);
|
||||||
|
outState.putInt(SERVICE_KEY, serviceId);
|
||||||
|
outState.putInt(PAGE_NUMBER_KEY, pageNumber);
|
||||||
|
outState.putSerializable(INFO_LIST_KEY, ((ArrayList<InfoItem>) infoListAdapter.getItemsList()));
|
||||||
|
outState.putBoolean(WAS_LOADING_KEY, curSearchWorker != null && curSearchWorker.isRunning());
|
||||||
|
|
||||||
|
if (errorPanel != null && errorPanel.getVisibility() == View.VISIBLE) outState.putBoolean(ERROR_KEY, true);
|
||||||
|
if (filterItemCheckedId != -1) outState.putInt(FILTER_CHECKED_ID_KEY, filterItemCheckedId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
switch (requestCode) {
|
switch (requestCode) {
|
||||||
case RECAPTCHA_REQUEST:
|
case ReCaptchaActivity.RECAPTCHA_REQUEST:
|
||||||
if (resultCode == RESULT_OK && searchQuery.length() != 0) {
|
if (resultCode == Activity.RESULT_OK && searchQuery.length() != 0) {
|
||||||
search(searchQuery);
|
search(searchQuery, pageNumber, true);
|
||||||
} else Log.e(TAG, "ReCaptcha failed");
|
} else Log.e(TAG, "ReCaptcha failed");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -251,6 +195,88 @@ public class SearchFragment extends Fragment implements SearchView.OnQueryTextLi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Init's
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initViews(View rootView, Bundle savedInstanceState) {
|
||||||
|
super.initViews(rootView, savedInstanceState);
|
||||||
|
resultRecyclerView = ((RecyclerView) rootView.findViewById(R.id.result_list_view));
|
||||||
|
resultRecyclerView.setLayoutManager(new LinearLayoutManager(activity));
|
||||||
|
|
||||||
|
if (infoListAdapter == null) {
|
||||||
|
infoListAdapter = new InfoListAdapter(getActivity(), getActivity().findViewById(android.R.id.content));
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
//noinspection unchecked
|
||||||
|
ArrayList<InfoItem> serializable = (ArrayList<InfoItem>) savedInstanceState.getSerializable(INFO_LIST_KEY);
|
||||||
|
infoListAdapter.addInfoItemList(serializable);
|
||||||
|
}
|
||||||
|
|
||||||
|
infoListAdapter.setFooter(activity.getLayoutInflater().inflate(R.layout.pignate_footer, resultRecyclerView, false));
|
||||||
|
infoListAdapter.showFooter(false);
|
||||||
|
infoListAdapter.setOnStreamInfoItemSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() {
|
||||||
|
@Override
|
||||||
|
public void selected(int serviceId, String url, String title) {
|
||||||
|
NavigationHelper.openVideoDetail(onItemSelectedListener, serviceId, url, title);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
infoListAdapter.setOnChannelInfoItemSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() {
|
||||||
|
@Override
|
||||||
|
public void selected(int serviceId, String url, String title) {
|
||||||
|
NavigationHelper.openChannel(onItemSelectedListener, serviceId, url, title);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
resultRecyclerView.setAdapter(infoListAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initListeners() {
|
||||||
|
super.initListeners();
|
||||||
|
resultRecyclerView.clearOnScrollListeners();
|
||||||
|
resultRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||||
|
@Override
|
||||||
|
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
||||||
|
int pastVisiblesItems, visibleItemCount, totalItemCount;
|
||||||
|
super.onScrolled(recyclerView, dx, dy);
|
||||||
|
//check for scroll down
|
||||||
|
if (dy > 0) {
|
||||||
|
LinearLayoutManager layoutManager = (LinearLayoutManager) resultRecyclerView.getLayoutManager();
|
||||||
|
visibleItemCount = resultRecyclerView.getLayoutManager().getChildCount();
|
||||||
|
totalItemCount = resultRecyclerView.getLayoutManager().getItemCount();
|
||||||
|
pastVisiblesItems = layoutManager.findFirstVisibleItemPosition();
|
||||||
|
|
||||||
|
if ((visibleItemCount + pastVisiblesItems) >= totalItemCount && !isLoading.get()) {
|
||||||
|
pageNumber++;
|
||||||
|
recyclerView.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
infoListAdapter.showFooter(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
search(searchQuery, pageNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void reloadContent() {
|
||||||
|
if (DEBUG) Log.d(TAG, "reloadContent() called");
|
||||||
|
if (!TextUtils.isEmpty(searchQuery) || (searchEditText != null && !TextUtils.isEmpty(searchEditText.getText()))) {
|
||||||
|
search(!TextUtils.isEmpty(searchQuery) ? searchQuery : searchEditText.getText().toString(), 0, true);
|
||||||
|
} else {
|
||||||
|
if (searchEditText != null) {
|
||||||
|
searchEditText.setText("");
|
||||||
|
showSoftKeyboard(searchEditText);
|
||||||
|
}
|
||||||
|
animateView(errorPanel, false, 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Menu
|
// Menu
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -258,22 +284,47 @@ public class SearchFragment extends Fragment implements SearchView.OnQueryTextLi
|
||||||
@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 = ((AppCompatActivity) getActivity()).getSupportActionBar();
|
if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + "], inflater = [" + inflater + "]");
|
||||||
if (supportActionBar != null) {
|
|
||||||
supportActionBar.setDisplayHomeAsUpEnabled(false);
|
|
||||||
supportActionBar.setDisplayShowTitleEnabled(false);
|
|
||||||
//noinspection deprecation
|
|
||||||
supportActionBar.setNavigationMode(0);
|
|
||||||
}
|
|
||||||
inflater.inflate(R.menu.search_menu, menu);
|
inflater.inflate(R.menu.search_menu, menu);
|
||||||
|
|
||||||
MenuItem searchItem = menu.findItem(R.id.action_search);
|
ActionBar supportActionBar = activity.getSupportActionBar();
|
||||||
searchView = (SearchView) searchItem.getActionView();
|
if (supportActionBar != null) {
|
||||||
setupSearchView(searchView);
|
supportActionBar.setDisplayShowTitleEnabled(false);
|
||||||
|
supportActionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
searchToolbarContainer = activity.findViewById(R.id.toolbar_search_container);
|
||||||
|
searchEditText = (AutoCompleteTextView) searchToolbarContainer.findViewById(R.id.toolbar_search_edit_text);
|
||||||
|
searchClear = searchToolbarContainer.findViewById(R.id.toolbar_search_clear);
|
||||||
|
setupSearchView();
|
||||||
|
|
||||||
|
restoreFilterChecked(menu, filterItemCheckedId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void restoreFilterChecked(Menu menu, int itemId) {
|
||||||
|
if (itemId != -1) {
|
||||||
|
MenuItem item = menu.findItem(itemId);
|
||||||
|
if (item == null) return;
|
||||||
|
|
||||||
|
item.setChecked(true);
|
||||||
|
switch (itemId) {
|
||||||
|
case R.id.menu_filter_all:
|
||||||
|
filter = EnumSet.of(SearchEngine.Filter.STREAM, SearchEngine.Filter.CHANNEL);
|
||||||
|
break;
|
||||||
|
case R.id.menu_filter_video:
|
||||||
|
filter = EnumSet.of(SearchEngine.Filter.STREAM);
|
||||||
|
break;
|
||||||
|
case R.id.menu_filter_channel:
|
||||||
|
filter = EnumSet.of(SearchEngine.Filter.CHANNEL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onOptionsItemSelected() called with: item = [" + item + "]");
|
||||||
|
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case R.id.menu_filter_all:
|
case R.id.menu_filter_all:
|
||||||
changeFilter(item, EnumSet.of(SearchEngine.Filter.STREAM, SearchEngine.Filter.CHANNEL));
|
changeFilter(item, EnumSet.of(SearchEngine.Filter.STREAM, SearchEngine.Filter.CHANNEL));
|
||||||
|
@ -289,119 +340,238 @@ public class SearchFragment extends Fragment implements SearchView.OnQueryTextLi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Search
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
private TextWatcher textWatcher;
|
||||||
|
|
||||||
|
private void setupSearchView() {
|
||||||
|
searchEditText.setText(searchQuery != null ? searchQuery : "");
|
||||||
|
searchEditText.setHint(getString(R.string.search) + "...");
|
||||||
|
////searchEditText.setCursorVisible(true);
|
||||||
|
|
||||||
|
suggestionListAdapter = new SuggestionListAdapter(activity);
|
||||||
|
searchEditText.setAdapter(suggestionListAdapter);
|
||||||
|
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(searchQuery) || TextUtils.isEmpty(searchEditText.getText())) {
|
||||||
|
searchToolbarContainer.setTranslationX(100);
|
||||||
|
searchToolbarContainer.setAlpha(0f);
|
||||||
|
searchToolbarContainer.setVisibility(View.VISIBLE);
|
||||||
|
searchToolbarContainer.animate().translationX(0).alpha(1f).setDuration(400).setInterpolator(new DecelerateInterpolator()).start();
|
||||||
|
} else {
|
||||||
|
searchToolbarContainer.setTranslationX(0);
|
||||||
|
searchToolbarContainer.setAlpha(1f);
|
||||||
|
searchToolbarContainer.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
initSearchListeners();
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(searchQuery)) showSoftKeyboard(searchEditText);
|
||||||
|
else hideSoftKeyboard(searchEditText);
|
||||||
|
|
||||||
|
if (!TextUtils.isEmpty(searchQuery) && searchQuery.length() > 2 && suggestionListAdapter != null && suggestionListAdapter.isEmpty()) {
|
||||||
|
searchSuggestions(searchQuery);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initSearchListeners() {
|
||||||
|
searchClear.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]");
|
||||||
|
if (TextUtils.isEmpty(searchEditText.getText())) {
|
||||||
|
NavigationHelper.openMainActivity(activity);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||||
|
searchEditText.setText("", false);
|
||||||
|
} else searchEditText.setText("");
|
||||||
|
suggestionListAdapter.updateAdapter(new ArrayList<String>());
|
||||||
|
showSoftKeyboard(searchEditText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
searchClear.setOnLongClickListener(new View.OnLongClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onLongClick(View v) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onLongClick() called with: v = [" + v + "]");
|
||||||
|
showMenuTooltip(v, getString(R.string.clear));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
searchEditText.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
searchEditText.showDropDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
searchEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onFocusChange(View v, boolean hasFocus) {
|
||||||
|
if (hasFocus) searchEditText.showDropDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
searchEditText.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onItemClick() called with: parent = [" + parent + "], view = [" + view + "], position = [" + position + "], id = [" + id + "]");
|
||||||
|
String s = suggestionListAdapter.getSuggestion(position);
|
||||||
|
if (DEBUG) Log.d(TAG, "onItemClick text = " + s);
|
||||||
|
submitQuery(s);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if (textWatcher != null) searchEditText.removeTextChangedListener(textWatcher);
|
||||||
|
textWatcher = new TextWatcher() {
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable s) {
|
||||||
|
String newText = searchEditText.getText().toString();
|
||||||
|
if (!TextUtils.isEmpty(newText) && newText.length() > 1) onQueryTextChange(newText);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
searchEditText.addTextChangedListener(textWatcher);
|
||||||
|
|
||||||
|
searchEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onEditorAction() called with: v = [" + v + "], actionId = [" + actionId + "], event = [" + event + "]");
|
||||||
|
if (event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER || event.getAction() == EditorInfo.IME_ACTION_SEARCH)) {
|
||||||
|
submitQuery(searchEditText.getText().toString());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unsetSearchListeners() {
|
||||||
|
searchClear.setOnClickListener(null);
|
||||||
|
searchClear.setOnLongClickListener(null);
|
||||||
|
if (textWatcher != null) searchEditText.removeTextChangedListener(textWatcher);
|
||||||
|
searchEditText.setOnClickListener(null);
|
||||||
|
searchEditText.setOnItemClickListener(null);
|
||||||
|
searchEditText.setOnFocusChangeListener(null);
|
||||||
|
searchEditText.setOnEditorActionListener(null);
|
||||||
|
|
||||||
|
textWatcher = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showSoftKeyboard(View view) {
|
||||||
|
if (DEBUG) Log.d(TAG, "showSoftKeyboard() called with: view = [" + view + "]");
|
||||||
|
if (view == null) return;
|
||||||
|
|
||||||
|
if (view.requestFocus()) {
|
||||||
|
InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hideSoftKeyboard(View view) {
|
||||||
|
if (DEBUG) Log.d(TAG, "hideSoftKeyboard() called with: view = [" + view + "]");
|
||||||
|
if (view == null) return;
|
||||||
|
|
||||||
|
InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
|
||||||
|
|
||||||
|
view.clearFocus();
|
||||||
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Utils
|
// Utils
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
private void changeFilter(MenuItem item, EnumSet<SearchEngine.Filter> filter) {
|
private void changeFilter(MenuItem item, EnumSet<SearchEngine.Filter> filter) {
|
||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
|
this.filterItemCheckedId = item.getItemId();
|
||||||
item.setChecked(true);
|
item.setChecked(true);
|
||||||
if (searchQuery != null && !searchQuery.isEmpty()) {
|
if (searchQuery != null && !searchQuery.isEmpty()) search(searchQuery, 0, true);
|
||||||
Log.e(TAG, "Fuck+ " + searchQuery);
|
|
||||||
search(searchQuery);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupSearchView(SearchView searchView) {
|
public void submitQuery(String query) {
|
||||||
suggestionListAdapter = new SuggestionListAdapter(getActivity());
|
if (DEBUG) Log.d(TAG, "submitQuery() called with: query = [" + query + "]");
|
||||||
searchView.setSuggestionsAdapter(suggestionListAdapter);
|
if (query.isEmpty()) return;
|
||||||
searchView.setOnSuggestionListener(new SearchSuggestionListener(searchView, suggestionListAdapter));
|
search(query, 0, true);
|
||||||
searchView.setOnQueryTextListener(this);
|
|
||||||
if (searchQuery != null && !searchQuery.isEmpty()) {
|
|
||||||
searchView.setQuery(searchQuery, false);
|
|
||||||
searchView.setIconifiedByDefault(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void search(String query) {
|
|
||||||
infoListAdapter.clearSteamItemList();
|
|
||||||
infoListAdapter.showFooter(false);
|
|
||||||
pageNumber = 0;
|
|
||||||
searchQuery = query;
|
searchQuery = query;
|
||||||
search(query, pageNumber);
|
|
||||||
hideBackground();
|
|
||||||
loadingIndicator.setVisibility(View.VISIBLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void search(String query, int page) {
|
public void onQueryTextChange(String newText) {
|
||||||
isLoading = true;
|
if (DEBUG) Log.d(TAG, "onQueryTextChange() called with: newText = [" + newText + "]");
|
||||||
SearchWorker sw = SearchWorker.getInstance();
|
if (!newText.isEmpty()) searchSuggestions(newText);
|
||||||
sw.search(streamingServiceId,
|
|
||||||
query,
|
|
||||||
page,
|
|
||||||
getActivity(),
|
|
||||||
filter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setDoneLoading() {
|
private void setQuery(int serviceId, String searchQuery) {
|
||||||
isLoading = false;
|
this.serviceId = serviceId;
|
||||||
loadingIndicator.setVisibility(View.GONE);
|
this.searchQuery = searchQuery;
|
||||||
infoListAdapter.showFooter(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hides the "dummy" background when no results are shown
|
|
||||||
*/
|
|
||||||
private void hideBackground() {
|
|
||||||
View view = getView();
|
|
||||||
if (view == null) return;
|
|
||||||
view.findViewById(R.id.mainBG).setVisibility(View.GONE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void searchSuggestions(String query) {
|
private void searchSuggestions(String query) {
|
||||||
SuggestionSearchRunnable suggestionSearchRunnable =
|
if (DEBUG) Log.d(TAG, "searchSuggestions() called with: query = [" + query + "]");
|
||||||
new SuggestionSearchRunnable(streamingServiceId, query, getActivity(), suggestionListAdapter);
|
if (curSuggestionWorker != null && curSuggestionWorker.isRunning()) curSuggestionWorker.cancel();
|
||||||
Thread suggestionThread = new Thread(suggestionSearchRunnable);
|
curSuggestionWorker = SuggestionWorker.startForQuery(activity, serviceId, query, this);
|
||||||
suggestionThread.start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMainBgVisible() {
|
private void search(String query, int pageNumber) {
|
||||||
return getActivity().findViewById(R.id.mainBG).getVisibility() == View.VISIBLE;
|
if (DEBUG) Log.d(TAG, "search() called with: query = [" + query + "], pageNumber = [" + pageNumber + "]");
|
||||||
|
search(query, pageNumber, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void search(String query, int pageNumber, boolean clearList) {
|
||||||
|
if (DEBUG) Log.d(TAG, "search() called with: query = [" + query + "], pageNumber = [" + pageNumber + "], clearList = [" + clearList + "]");
|
||||||
|
isLoading.set(true);
|
||||||
|
hideSoftKeyboard(searchEditText);
|
||||||
|
|
||||||
|
searchQuery = query;
|
||||||
|
this.pageNumber = pageNumber;
|
||||||
|
|
||||||
|
if (clearList) {
|
||||||
|
animateView(resultRecyclerView, false, 50);
|
||||||
|
infoListAdapter.clearStreamItemList();
|
||||||
|
infoListAdapter.showFooter(false);
|
||||||
|
animateView(loadingProgressBar, true, 200);
|
||||||
|
}
|
||||||
|
animateView(errorPanel, false, 200);
|
||||||
|
|
||||||
|
if (curSearchWorker != null && curSearchWorker.isRunning()) curSearchWorker.cancel();
|
||||||
|
curSearchWorker = SearchWorker.startForQuery(activity, serviceId, query, pageNumber, filter, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setErrorMessage(String message, boolean showRetryButton) {
|
||||||
|
super.setErrorMessage(message, showRetryButton);
|
||||||
|
|
||||||
|
animateView(resultRecyclerView, false, 400);
|
||||||
|
hideSoftKeyboard(searchEditText);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// OnQueryTextListener
|
// OnSuggestionResult
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onQueryTextSubmit(String query) {
|
public void onSuggestionResult(@NonNull List<String> suggestions) {
|
||||||
Activity a = getActivity();
|
if (DEBUG) Log.d(TAG, "onSuggestionResult() called with: suggestions = [" + suggestions + "]");
|
||||||
try {
|
suggestionListAdapter.updateAdapter(suggestions);
|
||||||
search(query);
|
|
||||||
|
|
||||||
// hide virtual keyboard
|
|
||||||
InputMethodManager inputManager =
|
|
||||||
(InputMethodManager) a.getSystemService(Context.INPUT_METHOD_SERVICE);
|
|
||||||
try {
|
|
||||||
//noinspection ConstantConditions
|
|
||||||
inputManager.hideSoftInputFromWindow(
|
|
||||||
a.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
|
|
||||||
} catch (NullPointerException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
ErrorActivity.reportError(a, e, null,
|
|
||||||
a.findViewById(android.R.id.content),
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
|
|
||||||
NewPipe.getNameOfService(streamingServiceId),
|
|
||||||
"Could not get widget with focus", R.string.general_error));
|
|
||||||
}
|
|
||||||
// clear focus
|
|
||||||
// 1. to not open up the keyboard after switching back to this
|
|
||||||
// 2. It's a workaround to a seeming bug by the Android OS it self, causing
|
|
||||||
// onQueryTextSubmit to trigger twice when focus is not cleared.
|
|
||||||
// See: http://stackoverflow.com/questions/17874951/searchview-onquerytextsubmit-runs-twice-while-i-pressed-once
|
|
||||||
a.getCurrentFocus().clearFocus();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onQueryTextChange(String newText) {
|
public void onSuggestionError(int messageId) {
|
||||||
if (!newText.isEmpty()) {
|
if (DEBUG) Log.d(TAG, "onSuggestionError() called with: messageId = [" + messageId + "]");
|
||||||
searchSuggestions(newText);
|
setErrorMessage(getString(messageId), true);
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -409,32 +579,35 @@ public class SearchFragment extends Fragment implements SearchView.OnQueryTextLi
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResult(SearchResult result) {
|
public void onSearchResult(SearchResult result) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onSearchResult() called with: result = [" + result + "]");
|
||||||
infoListAdapter.addInfoItemList(result.resultList);
|
infoListAdapter.addInfoItemList(result.resultList);
|
||||||
setDoneLoading();
|
animateView(resultRecyclerView, true, 400);
|
||||||
|
animateView(loadingProgressBar, false, 200);
|
||||||
|
isLoading.set(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNothingFound(int stringResource) {
|
public void onNothingFound(String message) {
|
||||||
//setListShown(true);
|
if (DEBUG) Log.d(TAG, "onNothingFound() called with: messageId = [" + message + "]");
|
||||||
Toast.makeText(getActivity(), getString(stringResource), Toast.LENGTH_SHORT).show();
|
setErrorMessage(message, false);
|
||||||
setDoneLoading();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(String message) {
|
public void onSearchError(int messageId) {
|
||||||
//setListShown(true);
|
if (DEBUG) Log.d(TAG, "onSearchError() called with: messageId = [" + messageId + "]");
|
||||||
Toast.makeText(getActivity(), message, Toast.LENGTH_LONG).show();
|
//Toast.makeText(getActivity(), messageId, Toast.LENGTH_LONG).show();
|
||||||
setDoneLoading();
|
setErrorMessage(getString(messageId), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReCaptchaChallenge() {
|
public void onReCaptchaChallenge() {
|
||||||
Toast.makeText(getActivity(), "ReCaptcha Challenge requested",
|
if (DEBUG) Log.d(TAG, "onReCaptchaChallenge() called");
|
||||||
Toast.LENGTH_LONG).show();
|
Toast.makeText(getActivity(), R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
|
||||||
|
setErrorMessage(getString(R.string.recaptcha_request_toast), false);
|
||||||
|
|
||||||
// Starting ReCaptcha Challenge Activity
|
// Starting ReCaptcha Challenge Activity
|
||||||
startActivityForResult(new Intent(getActivity(), ReCaptchaActivity.class), RECAPTCHA_REQUEST);
|
startActivityForResult(new Intent(getActivity(), ReCaptchaActivity.class), ReCaptchaActivity.RECAPTCHA_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,216 +0,0 @@
|
||||||
package org.schabi.newpipe.fragments.search;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
|
||||||
import org.schabi.newpipe.extractor.search.SearchEngine;
|
|
||||||
import org.schabi.newpipe.extractor.search.SearchResult;
|
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.EnumSet;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Christian Schabesberger on 02.08.16.
|
|
||||||
*
|
|
||||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
|
||||||
* SearchWorker.java is part of NewPipe.
|
|
||||||
*
|
|
||||||
* NewPipe is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* NewPipe is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
public class SearchWorker {
|
|
||||||
private static final String TAG = SearchWorker.class.toString();
|
|
||||||
|
|
||||||
public interface SearchWorkerResultListener {
|
|
||||||
void onResult(SearchResult result);
|
|
||||||
void onNothingFound(final int stringResource);
|
|
||||||
void onError(String message);
|
|
||||||
void onReCaptchaChallenge();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ResultRunnable implements Runnable {
|
|
||||||
private final SearchResult result;
|
|
||||||
private int requestId = 0;
|
|
||||||
public ResultRunnable(SearchResult result, int requestId) {
|
|
||||||
this.result = result;
|
|
||||||
this.requestId = requestId;
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if(this.requestId == SearchWorker.this.requestId) {
|
|
||||||
searchWorkerResultListener.onResult(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class SearchRunnable implements Runnable {
|
|
||||||
public static final String YOUTUBE = "Youtube";
|
|
||||||
private final String query;
|
|
||||||
private final int page;
|
|
||||||
private final EnumSet<SearchEngine.Filter> filter;
|
|
||||||
final Handler h = new Handler();
|
|
||||||
private volatile boolean runs = true;
|
|
||||||
private Activity a = null;
|
|
||||||
private int serviceId = -1;
|
|
||||||
public SearchRunnable(int serviceId,
|
|
||||||
String query,
|
|
||||||
int page,
|
|
||||||
EnumSet<SearchEngine.Filter> filter,
|
|
||||||
Activity activity,
|
|
||||||
int requestId) {
|
|
||||||
this.serviceId = serviceId;
|
|
||||||
this.query = query;
|
|
||||||
this.page = page;
|
|
||||||
this.filter = filter;
|
|
||||||
this.a = activity;
|
|
||||||
}
|
|
||||||
void terminate() {
|
|
||||||
runs = false;
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
final String serviceName = NewPipe.getNameOfService(serviceId);
|
|
||||||
SearchResult result = null;
|
|
||||||
SearchEngine engine = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
engine = NewPipe.getService(serviceId)
|
|
||||||
.getSearchEngineInstance();
|
|
||||||
} catch(ExtractionException e) {
|
|
||||||
ErrorActivity.reportError(h, a, e, null, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
|
|
||||||
Integer.toString(serviceId), query, R.string.general_error));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(a);
|
|
||||||
String searchLanguageKey = a.getString(R.string.search_language_key);
|
|
||||||
String searchLanguage = sp.getString(searchLanguageKey,
|
|
||||||
a.getString(R.string.default_language_value));
|
|
||||||
result = SearchResult
|
|
||||||
.getSearchResult(engine, query, page, searchLanguage, filter);
|
|
||||||
if(runs) {
|
|
||||||
h.post(new ResultRunnable(result, requestId));
|
|
||||||
}
|
|
||||||
|
|
||||||
// look for errors during extraction
|
|
||||||
// soft errors:
|
|
||||||
View rootView = a.findViewById(android.R.id.content);
|
|
||||||
if(result != null &&
|
|
||||||
!result.errors.isEmpty()) {
|
|
||||||
Log.e(TAG, "OCCURRED ERRORS DURING SEARCH EXTRACTION:");
|
|
||||||
for(Throwable e : result.errors) {
|
|
||||||
e.printStackTrace();
|
|
||||||
Log.e(TAG, "------");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(result.resultList.isEmpty()&& !result.errors.isEmpty()) {
|
|
||||||
// if it compleatly failes dont show snackbar, instead show error directlry
|
|
||||||
ErrorActivity.reportError(h, a, result.errors, null, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
|
|
||||||
serviceName, query, R.string.parsing_error));
|
|
||||||
} else {
|
|
||||||
// if it partly show snackbar
|
|
||||||
ErrorActivity.reportError(h, a, result.errors, null, rootView,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
|
|
||||||
serviceName, query, R.string.light_parsing_error));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// hard errors:
|
|
||||||
} catch (ReCaptchaException e) {
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
searchWorkerResultListener.onReCaptchaChallenge();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch(IOException e) {
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
searchWorkerResultListener.onNothingFound(R.string.network_error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch(final SearchEngine.NothingFoundException e) {
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
searchWorkerResultListener.onError(e.getMessage());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch(ExtractionException e) {
|
|
||||||
ErrorActivity.reportError(h, a, e, null, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
|
|
||||||
serviceName, query, R.string.parsing_error));
|
|
||||||
//postNewErrorToast(h, R.string.parsing_error);
|
|
||||||
e.printStackTrace();
|
|
||||||
|
|
||||||
} catch(Exception e) {
|
|
||||||
ErrorActivity.reportError(h, a, e, null, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
|
|
||||||
/* todo: this shoudl not be assigned static */ YOUTUBE, query, R.string.general_error));
|
|
||||||
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static SearchWorker searchWorker = null;
|
|
||||||
private SearchWorkerResultListener searchWorkerResultListener = null;
|
|
||||||
private SearchRunnable runnable = null;
|
|
||||||
private int requestId = 0; //prevents running requests that have already ben expired
|
|
||||||
|
|
||||||
public static SearchWorker getInstance() {
|
|
||||||
return searchWorker == null ? (searchWorker = new SearchWorker()) : searchWorker;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSearchWorkerResultListener(SearchWorkerResultListener listener) {
|
|
||||||
searchWorkerResultListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
private SearchWorker() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void search(int serviceId,
|
|
||||||
String query,
|
|
||||||
int page,
|
|
||||||
Activity a,
|
|
||||||
EnumSet<SearchEngine.Filter> filter) {
|
|
||||||
if(runnable != null) {
|
|
||||||
terminate();
|
|
||||||
}
|
|
||||||
runnable = new SearchRunnable(serviceId, query, page, filter, a, requestId);
|
|
||||||
Thread thread = new Thread(runnable);
|
|
||||||
thread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void terminate() {
|
|
||||||
if (runnable == null) return;
|
|
||||||
requestId++;
|
|
||||||
runnable.terminate();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -75,6 +75,11 @@ public class SuggestionListAdapter extends ResourceCursorAdapter {
|
||||||
return ((Cursor) getItem(position)).getString(INDEX_TITLE);
|
return ((Cursor) getItem(position)).getString(INDEX_TITLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence convertToString(Cursor cursor) {
|
||||||
|
return cursor.getString(INDEX_TITLE);
|
||||||
|
}
|
||||||
|
|
||||||
private class ViewHolder {
|
private class ViewHolder {
|
||||||
private final TextView suggestionTitle;
|
private final TextView suggestionTitle;
|
||||||
private ViewHolder(View view) {
|
private ViewHolder(View view) {
|
||||||
|
|
|
@ -1,105 +0,0 @@
|
||||||
package org.schabi.newpipe.fragments.search;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
|
||||||
import org.schabi.newpipe.extractor.SuggestionExtractor;
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Christian Schabesberger on 02.08.16.
|
|
||||||
*
|
|
||||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
|
||||||
* SuggestionSearchRunnable.java is part of NewPipe.
|
|
||||||
*
|
|
||||||
* NewPipe is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* NewPipe is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class SuggestionSearchRunnable implements Runnable{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runnable to update a {@link SuggestionListAdapter}
|
|
||||||
*/
|
|
||||||
private class SuggestionResultRunnable implements Runnable{
|
|
||||||
|
|
||||||
private final List<String> suggestions;
|
|
||||||
|
|
||||||
private SuggestionResultRunnable(List<String> suggestions) {
|
|
||||||
this.suggestions = suggestions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
adapter.updateAdapter(suggestions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final int serviceId;
|
|
||||||
private final String query;
|
|
||||||
private final Handler h = new Handler();
|
|
||||||
private final Activity a;
|
|
||||||
private final SuggestionListAdapter adapter;
|
|
||||||
public SuggestionSearchRunnable(int serviceId, String query,
|
|
||||||
Activity activity, SuggestionListAdapter adapter) {
|
|
||||||
this.serviceId = serviceId;
|
|
||||||
this.query = query;
|
|
||||||
this.a = activity;
|
|
||||||
this.adapter = adapter;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
SuggestionExtractor se =
|
|
||||||
NewPipe.getService(serviceId).getSuggestionExtractorInstance();
|
|
||||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(a);
|
|
||||||
String searchLanguageKey = a.getString(R.string.search_language_key);
|
|
||||||
String searchLanguage = sp.getString(searchLanguageKey,
|
|
||||||
a.getString(R.string.default_language_value));
|
|
||||||
List<String> suggestions = se.suggestionList(query, searchLanguage);
|
|
||||||
h.post(new SuggestionResultRunnable(suggestions));
|
|
||||||
} catch (ExtractionException e) {
|
|
||||||
ErrorActivity.reportError(h, a, e, null, a.findViewById(android.R.id.content),
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
|
|
||||||
NewPipe.getNameOfService(serviceId), query, R.string.parsing_error));
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (IOException e) {
|
|
||||||
postNewErrorToast(h, R.string.network_error);
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (Exception e) {
|
|
||||||
ErrorActivity.reportError(h, a, e, null, a.findViewById(android.R.id.content),
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
|
|
||||||
NewPipe.getNameOfService(serviceId), query, R.string.general_error));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void postNewErrorToast(Handler h, final int stringResource) {
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Toast.makeText(a, a.getString(stringResource),
|
|
||||||
Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,6 @@
|
||||||
package org.schabi.newpipe.info_list;
|
package org.schabi.newpipe.info_list;
|
||||||
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
|
@ -35,16 +34,17 @@ public class ChannelInfoItemHolder extends InfoItemHolder {
|
||||||
public final TextView itemSubscriberCountView;
|
public final TextView itemSubscriberCountView;
|
||||||
public final TextView itemVideoCountView;
|
public final TextView itemVideoCountView;
|
||||||
public final TextView itemChannelDescriptionView;
|
public final TextView itemChannelDescriptionView;
|
||||||
public final Button itemButton;
|
|
||||||
|
public final View itemRoot;
|
||||||
|
|
||||||
ChannelInfoItemHolder(View v) {
|
ChannelInfoItemHolder(View v) {
|
||||||
super(v);
|
super(v);
|
||||||
|
itemRoot = v.findViewById(R.id.itemRoot);
|
||||||
itemThumbnailView = (CircleImageView) v.findViewById(R.id.itemThumbnailView);
|
itemThumbnailView = (CircleImageView) v.findViewById(R.id.itemThumbnailView);
|
||||||
itemChannelTitleView = (TextView) v.findViewById(R.id.itemChannelTitleView);
|
itemChannelTitleView = (TextView) v.findViewById(R.id.itemChannelTitleView);
|
||||||
itemSubscriberCountView = (TextView) v.findViewById(R.id.itemSubscriberCountView);
|
itemSubscriberCountView = (TextView) v.findViewById(R.id.itemSubscriberCountView);
|
||||||
itemVideoCountView = (TextView) v.findViewById(R.id.itemVideoCountView);
|
itemVideoCountView = (TextView) v.findViewById(R.id.itemVideoCountView);
|
||||||
itemChannelDescriptionView = (TextView) v.findViewById(R.id.itemChannelDescriptionView);
|
itemChannelDescriptionView = (TextView) v.findViewById(R.id.itemChannelDescriptionView);
|
||||||
itemButton = (Button) v.findViewById(R.id.item_button);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.schabi.newpipe.info_list;
|
package org.schabi.newpipe.info_list;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -18,20 +19,20 @@ import org.schabi.newpipe.extractor.stream_info.StreamInfoItem;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Christian Schabesberger on 26.09.16.
|
* Created by Christian Schabesberger on 26.09.16.
|
||||||
*
|
* <p>
|
||||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||||
* InfoItemBuilder.java is part of NewPipe.
|
* InfoItemBuilder.java is part of NewPipe.
|
||||||
*
|
* <p>
|
||||||
* NewPipe is free software: you can redistribute it and/or modify
|
* NewPipe is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
* <p>
|
||||||
* NewPipe is distributed in the hope that it will be useful,
|
* NewPipe is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
* <p>
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
@ -48,11 +49,13 @@ public class InfoItemBuilder {
|
||||||
private final String billion;
|
private final String billion;
|
||||||
|
|
||||||
private static final String TAG = InfoItemBuilder.class.toString();
|
private static final String TAG = InfoItemBuilder.class.toString();
|
||||||
|
|
||||||
public interface OnInfoItemSelectedListener {
|
public interface OnInfoItemSelectedListener {
|
||||||
void selected(int serviceId, String url, String title);
|
void selected(int serviceId, String url, String title);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Context mContext = null;
|
private Context mContext = null;
|
||||||
|
private LayoutInflater inflater;
|
||||||
private View rootView = null;
|
private View rootView = null;
|
||||||
private ImageLoader imageLoader = ImageLoader.getInstance();
|
private ImageLoader imageLoader = ImageLoader.getInstance();
|
||||||
private DisplayImageOptions displayImageOptions =
|
private DisplayImageOptions displayImageOptions =
|
||||||
|
@ -70,6 +73,7 @@ public class InfoItemBuilder {
|
||||||
thousand = context.getString(R.string.short_thousand);
|
thousand = context.getString(R.string.short_thousand);
|
||||||
million = context.getString(R.string.short_million);
|
million = context.getString(R.string.short_million);
|
||||||
billion = context.getString(R.string.short_billion);
|
billion = context.getString(R.string.short_billion);
|
||||||
|
inflater = LayoutInflater.from(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOnStreamInfoItemSelectedListener(
|
public void setOnStreamInfoItemSelectedListener(
|
||||||
|
@ -83,9 +87,9 @@ public class InfoItemBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void buildByHolder(InfoItemHolder holder, final InfoItem i) {
|
public void buildByHolder(InfoItemHolder holder, final InfoItem i) {
|
||||||
if(i.infoType() != holder.infoType())
|
if (i.infoType() != holder.infoType())
|
||||||
return;
|
return;
|
||||||
switch(i.infoType()) {
|
switch (i.infoType()) {
|
||||||
case STREAM:
|
case STREAM:
|
||||||
buildStreamInfoItem((StreamInfoItemHolder) holder, (StreamInfoItem) i);
|
buildStreamInfoItem((StreamInfoItemHolder) holder, (StreamInfoItem) i);
|
||||||
break;
|
break;
|
||||||
|
@ -103,15 +107,15 @@ public class InfoItemBuilder {
|
||||||
public View buildView(ViewGroup parent, final InfoItem info) {
|
public View buildView(ViewGroup parent, final InfoItem info) {
|
||||||
View itemView = null;
|
View itemView = null;
|
||||||
InfoItemHolder holder = null;
|
InfoItemHolder holder = null;
|
||||||
switch(info.infoType()) {
|
switch (info.infoType()) {
|
||||||
case STREAM:
|
case STREAM:
|
||||||
itemView = LayoutInflater.from(parent.getContext())
|
//long start = System.nanoTime();
|
||||||
.inflate(R.layout.stream_item, parent, false);
|
itemView = inflater.inflate(R.layout.stream_item, parent, false);
|
||||||
|
//Log.d(TAG, "time to inflate: " + ((System.nanoTime() - start) / 1000000L) + "ms");
|
||||||
holder = new StreamInfoItemHolder(itemView);
|
holder = new StreamInfoItemHolder(itemView);
|
||||||
break;
|
break;
|
||||||
case CHANNEL:
|
case CHANNEL:
|
||||||
itemView = LayoutInflater.from(parent.getContext())
|
itemView = inflater.inflate(R.layout.channel_item, parent, false);
|
||||||
.inflate(R.layout.channel_item, parent, false);
|
|
||||||
holder = new ChannelInfoItemHolder(itemView);
|
holder = new ChannelInfoItemHolder(itemView);
|
||||||
break;
|
break;
|
||||||
case PLAYLIST:
|
case PLAYLIST:
|
||||||
|
@ -124,43 +128,39 @@ public class InfoItemBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildStreamInfoItem(StreamInfoItemHolder holder, final StreamInfoItem info) {
|
private void buildStreamInfoItem(StreamInfoItemHolder holder, final StreamInfoItem info) {
|
||||||
if(info.infoType() != InfoItem.InfoType.STREAM) {
|
if (info.infoType() != InfoItem.InfoType.STREAM) {
|
||||||
Log.e("InfoItemBuilder", "Info type not yet supported");
|
Log.e("InfoItemBuilder", "Info type not yet supported");
|
||||||
}
|
}
|
||||||
// fill holder with information
|
// fill holder with information
|
||||||
holder.itemVideoTitleView.setText(info.title);
|
if (!TextUtils.isEmpty(info.title)) holder.itemVideoTitleView.setText(info.title);
|
||||||
if(info.uploader != null && !info.uploader.isEmpty()) {
|
|
||||||
holder.itemUploaderView.setText(info.uploader);
|
if (!TextUtils.isEmpty(info.uploader)) holder.itemUploaderView.setText(info.uploader);
|
||||||
} else {
|
else holder.itemUploaderView.setVisibility(View.INVISIBLE);
|
||||||
holder.itemUploaderView.setVisibility(View.INVISIBLE);
|
|
||||||
}
|
if (info.duration > 0) {
|
||||||
if(info.duration > 0) {
|
|
||||||
holder.itemDurationView.setText(getDurationString(info.duration));
|
holder.itemDurationView.setText(getDurationString(info.duration));
|
||||||
} else {
|
} else {
|
||||||
if(info.stream_type == AbstractStreamInfo.StreamType.LIVE_STREAM) {
|
if (info.stream_type == AbstractStreamInfo.StreamType.LIVE_STREAM) {
|
||||||
holder.itemDurationView.setText(R.string.duration_live);
|
holder.itemDurationView.setText(R.string.duration_live);
|
||||||
} else {
|
} else {
|
||||||
holder.itemDurationView.setVisibility(View.GONE);
|
holder.itemDurationView.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(info.view_count >= 0) {
|
if (info.view_count >= 0) {
|
||||||
holder.itemViewCountView.setText(shortViewCount(info.view_count));
|
holder.itemViewCountView.setText(shortViewCount(info.view_count));
|
||||||
} else {
|
} else {
|
||||||
holder.itemViewCountView.setVisibility(View.GONE);
|
holder.itemViewCountView.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
if(info.upload_date != null && !info.upload_date.isEmpty()) {
|
if (!TextUtils.isEmpty(info.upload_date)) holder.itemUploadDateView.setText(info.upload_date + " • ");
|
||||||
holder.itemUploadDateView.setText(info.upload_date + " • ");
|
|
||||||
}
|
|
||||||
|
|
||||||
holder.itemThumbnailView.setImageResource(R.drawable.dummy_thumbnail);
|
holder.itemThumbnailView.setImageResource(R.drawable.dummy_thumbnail);
|
||||||
if(info.thumbnail_url != null && !info.thumbnail_url.isEmpty()) {
|
if (!TextUtils.isEmpty(info.thumbnail_url)) {
|
||||||
imageLoader.displayImage(info.thumbnail_url,
|
imageLoader.displayImage(info.thumbnail_url,
|
||||||
holder.itemThumbnailView,
|
holder.itemThumbnailView, displayImageOptions,
|
||||||
displayImageOptions,
|
|
||||||
new ImageErrorLoadingListener(mContext, rootView, info.service_id));
|
new ImageErrorLoadingListener(mContext, rootView, info.service_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.itemButton.setOnClickListener(new View.OnClickListener() {
|
holder.itemRoot.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
onStreamInfoItemSelectedListener.selected(info.service_id, info.webpage_url, info.getTitle());
|
onStreamInfoItemSelectedListener.selected(info.service_id, info.webpage_url, info.getTitle());
|
||||||
|
@ -169,20 +169,20 @@ public class InfoItemBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildChannelInfoItem(ChannelInfoItemHolder holder, final ChannelInfoItem info) {
|
private void buildChannelInfoItem(ChannelInfoItemHolder holder, final ChannelInfoItem info) {
|
||||||
holder.itemChannelTitleView.setText(info.getTitle());
|
if (!TextUtils.isEmpty(info.getTitle())) holder.itemChannelTitleView.setText(info.getTitle());
|
||||||
holder.itemSubscriberCountView.setText(shortSubscriber(info.subscriberCount) + " • ");
|
holder.itemSubscriberCountView.setText(shortSubscriber(info.subscriberCount) + " • ");
|
||||||
holder.itemVideoCountView.setText(info.videoAmount + " " + videosS);
|
holder.itemVideoCountView.setText(info.videoAmount + " " + videosS);
|
||||||
holder.itemChannelDescriptionView.setText(info.description);
|
if (!TextUtils.isEmpty(info.description)) holder.itemChannelDescriptionView.setText(info.description);
|
||||||
|
|
||||||
holder.itemThumbnailView.setImageResource(R.drawable.buddy_channel_item);
|
holder.itemThumbnailView.setImageResource(R.drawable.buddy_channel_item);
|
||||||
if(info.thumbnailUrl != null && !info.thumbnailUrl.isEmpty()) {
|
if (!TextUtils.isEmpty(info.thumbnailUrl)) {
|
||||||
imageLoader.displayImage(info.thumbnailUrl,
|
imageLoader.displayImage(info.thumbnailUrl,
|
||||||
holder.itemThumbnailView,
|
holder.itemThumbnailView,
|
||||||
displayImageOptions,
|
displayImageOptions,
|
||||||
new ImageErrorLoadingListener(mContext, rootView, info.serviceId));
|
new ImageErrorLoadingListener(mContext, rootView, info.serviceId));
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.itemButton.setOnClickListener(new View.OnClickListener() {
|
holder.itemRoot.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
onChannelInfoItemSelectedListener.selected(info.serviceId, info.getLink(), info.channelName);
|
onChannelInfoItemSelectedListener.selected(info.serviceId, info.getLink(), info.channelName);
|
||||||
|
@ -191,15 +191,15 @@ public class InfoItemBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String shortViewCount(Long viewCount){
|
public String shortViewCount(Long viewCount) {
|
||||||
if(viewCount >= 1000000000){
|
if (viewCount >= 1000000000) {
|
||||||
return Long.toString(viewCount/1000000000)+ billion + " " + viewsS;
|
return Long.toString(viewCount / 1000000000) + billion + " " + viewsS;
|
||||||
}else if(viewCount>=1000000){
|
} else if (viewCount >= 1000000) {
|
||||||
return Long.toString(viewCount/1000000)+ million + " " + viewsS;
|
return Long.toString(viewCount / 1000000) + million + " " + viewsS;
|
||||||
}else if(viewCount>=1000){
|
} else if (viewCount >= 1000) {
|
||||||
return Long.toString(viewCount/1000)+ thousand + " " + viewsS;
|
return Long.toString(viewCount / 1000) + thousand + " " + viewsS;
|
||||||
}else {
|
} else {
|
||||||
return Long.toString(viewCount)+ " " + viewsS;
|
return Long.toString(viewCount) + " " + viewsS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,13 +227,13 @@ public class InfoItemBuilder {
|
||||||
int seconds = duration % 60;
|
int seconds = duration % 60;
|
||||||
|
|
||||||
//handle days
|
//handle days
|
||||||
if(days > 0) {
|
if (days > 0) {
|
||||||
output = Integer.toString(days) + ":";
|
output = Integer.toString(days) + ":";
|
||||||
}
|
}
|
||||||
// handle hours
|
// handle hours
|
||||||
if(hours > 0 || !output.isEmpty()) {
|
if (hours > 0 || !output.isEmpty()) {
|
||||||
if(hours > 0) {
|
if (hours > 0) {
|
||||||
if(hours >= 10 || output.isEmpty()) {
|
if (hours >= 10 || output.isEmpty()) {
|
||||||
output += Integer.toString(hours);
|
output += Integer.toString(hours);
|
||||||
} else {
|
} else {
|
||||||
output += "0" + Integer.toString(hours);
|
output += "0" + Integer.toString(hours);
|
||||||
|
@ -244,9 +244,9 @@ public class InfoItemBuilder {
|
||||||
output += ":";
|
output += ":";
|
||||||
}
|
}
|
||||||
//handle minutes
|
//handle minutes
|
||||||
if(minutes > 0 || !output.isEmpty()) {
|
if (minutes > 0 || !output.isEmpty()) {
|
||||||
if(minutes > 0) {
|
if (minutes > 0) {
|
||||||
if(minutes >= 10 || output.isEmpty()) {
|
if (minutes >= 10 || output.isEmpty()) {
|
||||||
output += Integer.toString(minutes);
|
output += Integer.toString(minutes);
|
||||||
} else {
|
} else {
|
||||||
output += "0" + Integer.toString(minutes);
|
output += "0" + Integer.toString(minutes);
|
||||||
|
@ -258,11 +258,11 @@ public class InfoItemBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
//handle seconds
|
//handle seconds
|
||||||
if(output.isEmpty()) {
|
if (output.isEmpty()) {
|
||||||
output += "0:";
|
output += "0:";
|
||||||
}
|
}
|
||||||
|
|
||||||
if(seconds >= 10) {
|
if (seconds >= 10) {
|
||||||
output += Integer.toString(seconds);
|
output += Integer.toString(seconds);
|
||||||
} else {
|
} else {
|
||||||
output += "0" + Integer.toString(seconds);
|
output += "0" + Integer.toString(seconds);
|
||||||
|
|
|
@ -10,8 +10,8 @@ import android.view.ViewGroup;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.InfoItem;
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Christian Schabesberger on 01.08.16.
|
* Created by Christian Schabesberger on 01.08.16.
|
||||||
|
@ -57,7 +57,7 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
||||||
|
|
||||||
public InfoListAdapter(Activity a, View rootView) {
|
public InfoListAdapter(Activity a, View rootView) {
|
||||||
infoItemBuilder = new InfoItemBuilder(a, rootView);
|
infoItemBuilder = new InfoItemBuilder(a, rootView);
|
||||||
infoItemList = new Vector<>();
|
infoItemList = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOnStreamInfoItemSelectedListener
|
public void setOnStreamInfoItemSelectedListener
|
||||||
|
@ -77,7 +77,7 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearSteamItemList() {
|
public void clearStreamItemList() {
|
||||||
infoItemList.clear();
|
infoItemList.clear();
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
@ -92,12 +92,16 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<InfoItem> getItemsList() {
|
||||||
|
return infoItemList;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount() {
|
public int getItemCount() {
|
||||||
int cound = infoItemList.size();
|
int count = infoItemList.size();
|
||||||
if(header != null) cound++;
|
if(header != null) count++;
|
||||||
if(footer != null && showFooter) cound++;
|
if(footer != null && showFooter) count++;
|
||||||
return cound;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't ask why we have to do that this way... it's android accept it -.-
|
// don't ask why we have to do that this way... it's android accept it -.-
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
package org.schabi.newpipe.info_list;
|
package org.schabi.newpipe.info_list;
|
||||||
|
|
||||||
import android.icu.text.IDNA;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
@ -12,20 +9,20 @@ import org.schabi.newpipe.extractor.InfoItem;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Christian Schabesberger on 01.08.16.
|
* Created by Christian Schabesberger on 01.08.16.
|
||||||
*
|
* <p>
|
||||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||||
* StreamInfoItemHolder.java is part of NewPipe.
|
* StreamInfoItemHolder.java is part of NewPipe.
|
||||||
*
|
* <p>
|
||||||
* NewPipe is free software: you can redistribute it and/or modify
|
* NewPipe is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
* <p>
|
||||||
* NewPipe is distributed in the hope that it will be useful,
|
* NewPipe is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
* <p>
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
@ -38,17 +35,17 @@ public class StreamInfoItemHolder extends InfoItemHolder {
|
||||||
itemDurationView,
|
itemDurationView,
|
||||||
itemUploadDateView,
|
itemUploadDateView,
|
||||||
itemViewCountView;
|
itemViewCountView;
|
||||||
public final Button itemButton;
|
public final View itemRoot;
|
||||||
|
|
||||||
public StreamInfoItemHolder(View v) {
|
public StreamInfoItemHolder(View v) {
|
||||||
super(v);
|
super(v);
|
||||||
|
itemRoot = v.findViewById(R.id.itemRoot);
|
||||||
itemThumbnailView = (ImageView) v.findViewById(R.id.itemThumbnailView);
|
itemThumbnailView = (ImageView) v.findViewById(R.id.itemThumbnailView);
|
||||||
itemVideoTitleView = (TextView) v.findViewById(R.id.itemVideoTitleView);
|
itemVideoTitleView = (TextView) v.findViewById(R.id.itemVideoTitleView);
|
||||||
itemUploaderView = (TextView) v.findViewById(R.id.itemUploaderView);
|
itemUploaderView = (TextView) v.findViewById(R.id.itemUploaderView);
|
||||||
itemDurationView = (TextView) v.findViewById(R.id.itemDurationView);
|
itemDurationView = (TextView) v.findViewById(R.id.itemDurationView);
|
||||||
itemUploadDateView = (TextView) v.findViewById(R.id.itemUploadDateView);
|
itemUploadDateView = (TextView) v.findViewById(R.id.itemUploadDateView);
|
||||||
itemViewCountView = (TextView) v.findViewById(R.id.itemViewCountView);
|
itemViewCountView = (TextView) v.findViewById(R.id.itemViewCountView);
|
||||||
itemButton = (Button) v.findViewById(R.id.item_button);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -78,7 +78,7 @@ public class BackgroundPlayer extends Service {
|
||||||
powerManager = ((PowerManager) getSystemService(POWER_SERVICE));
|
powerManager = ((PowerManager) getSystemService(POWER_SERVICE));
|
||||||
wifiManager = ((WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE));
|
wifiManager = ((WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE));
|
||||||
|
|
||||||
ThemeHelper.setTheme(this, false);
|
ThemeHelper.setTheme(this);
|
||||||
basePlayerImpl = new BasePlayerImpl(this);
|
basePlayerImpl = new BasePlayerImpl(this);
|
||||||
basePlayerImpl.setup();
|
basePlayerImpl.setup();
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ public class MainVideoPlayer extends Activity {
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
|
if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
|
||||||
ThemeHelper.setTheme(this, false);
|
ThemeHelper.setTheme(this);
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) getWindow().setStatusBarColor(Color.BLACK);
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) getWindow().setStatusBarColor(Color.BLACK);
|
||||||
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
||||||
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
|
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
@ -67,7 +67,7 @@ public class MainVideoPlayer extends Activity {
|
||||||
}
|
}
|
||||||
|
|
||||||
showSystemUi();
|
showSystemUi();
|
||||||
setContentView(R.layout.activity_exo_player);
|
setContentView(R.layout.activity_main_player);
|
||||||
playerImpl = new VideoPlayerImpl();
|
playerImpl = new VideoPlayerImpl();
|
||||||
playerImpl.setup(findViewById(android.R.id.content));
|
playerImpl.setup(findViewById(android.R.id.content));
|
||||||
playerImpl.handleIntent(getIntent());
|
playerImpl.handleIntent(getIntent());
|
||||||
|
@ -474,7 +474,7 @@ public class MainVideoPlayer extends Activity {
|
||||||
@Override
|
@Override
|
||||||
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
|
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
|
||||||
//noinspection PointlessBooleanExpression
|
//noinspection PointlessBooleanExpression
|
||||||
if (DEBUG && true) Log.d(TAG, "MainVideoPlayer.onScroll = " +
|
if (DEBUG && false) Log.d(TAG, "MainVideoPlayer.onScroll = " +
|
||||||
", 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 + "]");
|
||||||
|
@ -531,12 +531,14 @@ public class MainVideoPlayer extends Activity {
|
||||||
if (playerImpl.getBrightnessTextView().getVisibility() == View.VISIBLE) playerImpl.animateView(playerImpl.getBrightnessTextView(), false, 200, 200);
|
if (playerImpl.getBrightnessTextView().getVisibility() == View.VISIBLE) playerImpl.animateView(playerImpl.getBrightnessTextView(), false, 200, 200);
|
||||||
|
|
||||||
if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == BasePlayer.STATE_PLAYING) {
|
if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == BasePlayer.STATE_PLAYING) {
|
||||||
playerImpl.animateView(playerImpl.getControlsRoot(), false, 300, VideoPlayer.DEFAULT_CONTROLS_HIDE_TIME);
|
playerImpl.animateView(playerImpl.getControlsRoot(), false, 300, VideoPlayer.DEFAULT_CONTROLS_HIDE_TIME, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onTouch(View v, MotionEvent event) {
|
public boolean onTouch(View v, MotionEvent event) {
|
||||||
|
//noinspection PointlessBooleanExpression
|
||||||
|
if (DEBUG && false) Log.d(TAG, "onTouch() called with: v = [" + v + "], event = [" + event + "]");
|
||||||
gestureDetector.onTouchEvent(event);
|
gestureDetector.onTouchEvent(event);
|
||||||
if (event.getAction() == MotionEvent.ACTION_UP && isMoving) {
|
if (event.getAction() == MotionEvent.ACTION_UP && isMoving) {
|
||||||
isMoving = false;
|
isMoving = false;
|
||||||
|
|
|
@ -89,7 +89,7 @@ public class PopupVideoPlayer extends Service {
|
||||||
notificationManager = ((NotificationManager) getSystemService(NOTIFICATION_SERVICE));
|
notificationManager = ((NotificationManager) getSystemService(NOTIFICATION_SERVICE));
|
||||||
|
|
||||||
playerImpl = new VideoPlayerImpl();
|
playerImpl = new VideoPlayerImpl();
|
||||||
ThemeHelper.setTheme(this, false);
|
ThemeHelper.setTheme(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -124,7 +124,6 @@ public class PopupVideoPlayer extends Service {
|
||||||
playerImpl.destroy();
|
playerImpl.destroy();
|
||||||
if (playerImpl.getRootView() != null) windowManager.removeView(playerImpl.getRootView());
|
if (playerImpl.getRootView() != null) windowManager.removeView(playerImpl.getRootView());
|
||||||
}
|
}
|
||||||
if (imageLoader != null) imageLoader.clearMemoryCache();
|
|
||||||
if (notificationManager != null) notificationManager.cancel(NOTIFICATION_ID);
|
if (notificationManager != null) notificationManager.cancel(NOTIFICATION_ID);
|
||||||
if (currentExtractorWorker != null) {
|
if (currentExtractorWorker != null) {
|
||||||
currentExtractorWorker.cancel();
|
currentExtractorWorker.cancel();
|
||||||
|
|
|
@ -125,7 +125,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
||||||
this.aspectRatioFrameLayout = (AspectRatioFrameLayout) rootView.findViewById(R.id.aspectRatioLayout);
|
this.aspectRatioFrameLayout = (AspectRatioFrameLayout) rootView.findViewById(R.id.aspectRatioLayout);
|
||||||
this.surfaceView = (SurfaceView) rootView.findViewById(R.id.surfaceView);
|
this.surfaceView = (SurfaceView) rootView.findViewById(R.id.surfaceView);
|
||||||
this.surfaceForeground = rootView.findViewById(R.id.surfaceForeground);
|
this.surfaceForeground = rootView.findViewById(R.id.surfaceForeground);
|
||||||
this.loadingPanel = rootView.findViewById(R.id.loadingPanel);
|
this.loadingPanel = rootView.findViewById(R.id.loading_panel);
|
||||||
this.endScreen = (ImageView) rootView.findViewById(R.id.endScreen);
|
this.endScreen = (ImageView) rootView.findViewById(R.id.endScreen);
|
||||||
this.controlAnimationView = (ImageView) rootView.findViewById(R.id.controlAnimationView);
|
this.controlAnimationView = (ImageView) rootView.findViewById(R.id.controlAnimationView);
|
||||||
this.controlsRoot = rootView.findViewById(R.id.playbackControlRoot);
|
this.controlsRoot = rootView.findViewById(R.id.playbackControlRoot);
|
||||||
|
|
|
@ -15,6 +15,7 @@ import android.support.design.widget.Snackbar;
|
||||||
import android.support.v4.app.NavUtils;
|
import android.support.v4.app.NavUtils;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
|
@ -205,19 +206,19 @@ public class ErrorActivity extends AppCompatActivity {
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
ThemeHelper.setTheme(this, true);
|
ThemeHelper.setTheme(this);
|
||||||
setContentView(R.layout.activity_error);
|
setContentView(R.layout.activity_error);
|
||||||
|
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
|
|
||||||
try {
|
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||||
ActionBar actionBar = getSupportActionBar();
|
setSupportActionBar(toolbar);
|
||||||
|
|
||||||
|
ActionBar actionBar = getSupportActionBar();
|
||||||
|
if (actionBar != null) {
|
||||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
actionBar.setTitle(R.string.error_report_title);
|
actionBar.setTitle(R.string.error_report_title);
|
||||||
actionBar.setDisplayShowTitleEnabled(true);
|
actionBar.setDisplayShowTitleEnabled(true);
|
||||||
} catch (Throwable e) {
|
|
||||||
Log.e(TAG, "Error turing exception handling");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reportButton = (Button) findViewById(R.id.errorReportButton);
|
reportButton = (Button) findViewById(R.id.errorReportButton);
|
||||||
|
|
|
@ -1,18 +1,11 @@
|
||||||
package org.schabi.newpipe.settings;
|
package org.schabi.newpipe.settings;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceActivity;
|
|
||||||
import android.support.annotation.LayoutRes;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.AppCompatDelegate;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.view.MenuInflater;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
@ -38,9 +31,7 @@ import org.schabi.newpipe.util.ThemeHelper;
|
||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class SettingsActivity extends PreferenceActivity {
|
public class SettingsActivity extends AppCompatActivity {
|
||||||
SettingsFragment f = new SettingsFragment();
|
|
||||||
private AppCompatDelegate mDelegate = null;
|
|
||||||
|
|
||||||
public static void initSettings(Context context) {
|
public static void initSettings(Context context) {
|
||||||
NewPipeSettings.initSettings(context);
|
NewPipeSettings.initSettings(context);
|
||||||
|
@ -48,104 +39,25 @@ public class SettingsActivity extends PreferenceActivity {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceBundle) {
|
protected void onCreate(Bundle savedInstanceBundle) {
|
||||||
ThemeHelper.setTheme(this, true);
|
ThemeHelper.setTheme(this);
|
||||||
getDelegate().installViewFactory();
|
|
||||||
getDelegate().onCreate(savedInstanceBundle);
|
|
||||||
super.onCreate(savedInstanceBundle);
|
super.onCreate(savedInstanceBundle);
|
||||||
|
setContentView(R.layout.settings_layout);
|
||||||
|
|
||||||
|
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
|
||||||
ActionBar actionBar = getSupportActionBar();
|
ActionBar actionBar = getSupportActionBar();
|
||||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
if (actionBar != null) {
|
||||||
actionBar.setTitle(R.string.settings_title);
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
actionBar.setDisplayShowTitleEnabled(true);
|
actionBar.setTitle(R.string.settings_title);
|
||||||
|
actionBar.setDisplayShowTitleEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
getFragmentManager().beginTransaction()
|
getFragmentManager().beginTransaction()
|
||||||
.replace(android.R.id.content, f)
|
.replace(R.id.fragment_holder, new SettingsFragment())
|
||||||
.commit();
|
.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
|
||||||
f.onActivityResult(requestCode, resultCode, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostCreate(Bundle savedInstanceState) {
|
|
||||||
super.onPostCreate(savedInstanceState);
|
|
||||||
getDelegate().onPostCreate(savedInstanceState);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ActionBar getSupportActionBar() {
|
|
||||||
return getDelegate().getSupportActionBar();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public MenuInflater getMenuInflater() {
|
|
||||||
return getDelegate().getMenuInflater();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setContentView(@LayoutRes int layoutResID) {
|
|
||||||
getDelegate().setContentView(layoutResID);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setContentView(View view) {
|
|
||||||
getDelegate().setContentView(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setContentView(View view, ViewGroup.LayoutParams params) {
|
|
||||||
getDelegate().setContentView(view, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addContentView(View view, ViewGroup.LayoutParams params) {
|
|
||||||
getDelegate().addContentView(view, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostResume() {
|
|
||||||
super.onPostResume();
|
|
||||||
getDelegate().onPostResume();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onTitleChanged(CharSequence title, int color) {
|
|
||||||
super.onTitleChanged(title, color);
|
|
||||||
getDelegate().setTitle(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onConfigurationChanged(Configuration newConfig) {
|
|
||||||
super.onConfigurationChanged(newConfig);
|
|
||||||
getDelegate().onConfigurationChanged(newConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStop() {
|
|
||||||
super.onStop();
|
|
||||||
getDelegate().onStop();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
getDelegate().onDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void invalidateOptionsMenu() {
|
|
||||||
getDelegate().invalidateOptionsMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
private AppCompatDelegate getDelegate() {
|
|
||||||
if (mDelegate == null) {
|
|
||||||
mDelegate = AppCompatDelegate.create(this, null);
|
|
||||||
}
|
|
||||||
return mDelegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
int id = item.getItemId();
|
int id = item.getItemId();
|
||||||
|
|
|
@ -1,205 +1,84 @@
|
||||||
package org.schabi.newpipe.settings;
|
package org.schabi.newpipe.settings;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.ClipData;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.ListPreference;
|
|
||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
import android.preference.PreferenceFragment;
|
import android.preference.PreferenceFragment;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.preference.PreferenceScreen;
|
import android.preference.PreferenceScreen;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import com.nononsenseapps.filepicker.FilePickerActivity;
|
import com.nononsenseapps.filepicker.FilePickerActivity;
|
||||||
|
|
||||||
import org.schabi.newpipe.App;
|
import org.schabi.newpipe.App;
|
||||||
import org.schabi.newpipe.MainActivity;
|
import org.schabi.newpipe.MainActivity;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.util.Constants;
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
||||||
|
|
||||||
/**
|
public class SettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
* Created by david on 15/06/16.
|
private static final int REQUEST_INSTALL_ORBOT = 0x1234;
|
||||||
*
|
private static final int REQUEST_DOWNLOAD_PATH = 0x1235;
|
||||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
private static final int REQUEST_DOWNLOAD_AUDIO_PATH = 0x1236;
|
||||||
* SettingsFragment.java is part of NewPipe.
|
|
||||||
*
|
|
||||||
* NewPipe is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* NewPipe is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class SettingsFragment extends PreferenceFragment
|
private String DOWNLOAD_PATH_PREFERENCE;
|
||||||
implements SharedPreferences.OnSharedPreferenceChangeListener
|
private String DOWNLOAD_PATH_AUDIO_PREFERENCE;
|
||||||
{
|
private String USE_TOR_KEY;
|
||||||
public static final int REQUEST_INSTALL_ORBOT = 0x1234;
|
private String THEME;
|
||||||
SharedPreferences.OnSharedPreferenceChangeListener prefListener;
|
|
||||||
// get keys
|
private String currentTheme;
|
||||||
String DEFAULT_RESOLUTION_PREFERENCE;
|
|
||||||
String DEFAULT_POPUP_RESOLUTION_PREFERENCE;
|
|
||||||
String PREFERRED_VIDEO_FORMAT_PREFERENCE;
|
|
||||||
String DEFAULT_AUDIO_FORMAT_PREFERENCE;
|
|
||||||
String SEARCH_LANGUAGE_PREFERENCE;
|
|
||||||
String DOWNLOAD_PATH_PREFERENCE;
|
|
||||||
String DOWNLOAD_PATH_AUDIO_PREFERENCE;
|
|
||||||
String USE_TOR_KEY;
|
|
||||||
String THEME;
|
|
||||||
private ListPreference defaultResolutionPreference;
|
|
||||||
private ListPreference defaultPopupResolutionPreference;
|
|
||||||
private ListPreference preferredVideoFormatPreference;
|
|
||||||
private ListPreference defaultAudioFormatPreference;
|
|
||||||
private ListPreference searchLanguagePreference;
|
|
||||||
private Preference downloadPathPreference;
|
|
||||||
private Preference downloadPathAudioPreference;
|
|
||||||
private Preference themePreference;
|
|
||||||
private SharedPreferences defaultPreferences;
|
private SharedPreferences defaultPreferences;
|
||||||
|
|
||||||
|
private Activity activity;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(final Bundle savedInstanceState) {
|
public void onCreate(final Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
activity = getActivity();
|
||||||
addPreferencesFromResource(R.xml.settings);
|
addPreferencesFromResource(R.xml.settings);
|
||||||
|
|
||||||
final Activity activity = getActivity();
|
|
||||||
|
|
||||||
defaultPreferences = PreferenceManager.getDefaultSharedPreferences(activity);
|
defaultPreferences = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||||
|
initKeys();
|
||||||
|
updatePreferencesSummary();
|
||||||
|
|
||||||
// get keys
|
currentTheme = defaultPreferences.getString(THEME, getString(R.string.default_theme_value));
|
||||||
DEFAULT_RESOLUTION_PREFERENCE = getString(R.string.default_resolution_key);
|
}
|
||||||
DEFAULT_POPUP_RESOLUTION_PREFERENCE = getString(R.string.default_popup_resolution_key);
|
|
||||||
PREFERRED_VIDEO_FORMAT_PREFERENCE = getString(R.string.preferred_video_format_key);
|
@Override
|
||||||
DEFAULT_AUDIO_FORMAT_PREFERENCE = getString(R.string.default_audio_format_key);
|
public void onResume() {
|
||||||
SEARCH_LANGUAGE_PREFERENCE = getString(R.string.search_language_key);
|
super.onResume();
|
||||||
|
defaultPreferences.registerOnSharedPreferenceChangeListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
defaultPreferences.unregisterOnSharedPreferenceChangeListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initKeys() {
|
||||||
DOWNLOAD_PATH_PREFERENCE = getString(R.string.download_path_key);
|
DOWNLOAD_PATH_PREFERENCE = getString(R.string.download_path_key);
|
||||||
DOWNLOAD_PATH_AUDIO_PREFERENCE = getString(R.string.download_path_audio_key);
|
DOWNLOAD_PATH_AUDIO_PREFERENCE = getString(R.string.download_path_audio_key);
|
||||||
THEME = getString(R.string.theme_key);
|
THEME = getString(R.string.theme_key);
|
||||||
USE_TOR_KEY = getString(R.string.use_tor_key);
|
USE_TOR_KEY = getString(R.string.use_tor_key);
|
||||||
|
|
||||||
// get pref objects
|
|
||||||
defaultResolutionPreference =
|
|
||||||
(ListPreference) findPreference(DEFAULT_RESOLUTION_PREFERENCE);
|
|
||||||
defaultPopupResolutionPreference =
|
|
||||||
(ListPreference) findPreference(DEFAULT_POPUP_RESOLUTION_PREFERENCE);
|
|
||||||
preferredVideoFormatPreference =
|
|
||||||
(ListPreference) findPreference(PREFERRED_VIDEO_FORMAT_PREFERENCE);
|
|
||||||
defaultAudioFormatPreference =
|
|
||||||
(ListPreference) findPreference(DEFAULT_AUDIO_FORMAT_PREFERENCE);
|
|
||||||
searchLanguagePreference =
|
|
||||||
(ListPreference) findPreference(SEARCH_LANGUAGE_PREFERENCE);
|
|
||||||
downloadPathPreference = findPreference(DOWNLOAD_PATH_PREFERENCE);
|
|
||||||
downloadPathAudioPreference = findPreference(DOWNLOAD_PATH_AUDIO_PREFERENCE);
|
|
||||||
themePreference = findPreference(THEME);
|
|
||||||
|
|
||||||
final String currentTheme = defaultPreferences.getString(THEME, "Light");
|
|
||||||
|
|
||||||
// TODO: Clean this, as the class is already implementing the class
|
|
||||||
// and those double equals...
|
|
||||||
|
|
||||||
prefListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
|
|
||||||
@Override
|
|
||||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
|
|
||||||
String key) {
|
|
||||||
Activity a = getActivity();
|
|
||||||
if(a == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (key == USE_TOR_KEY)
|
|
||||||
{
|
|
||||||
if (defaultPreferences.getBoolean(USE_TOR_KEY, false)) {
|
|
||||||
if (OrbotHelper.isOrbotInstalled(a)) {
|
|
||||||
App.configureTor(true);
|
|
||||||
OrbotHelper.requestStartTor(a);
|
|
||||||
} else {
|
|
||||||
Intent intent = OrbotHelper.getOrbotInstallIntent(a);
|
|
||||||
a.startActivityForResult(intent, REQUEST_INSTALL_ORBOT);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
App.configureTor(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (key == DOWNLOAD_PATH_PREFERENCE)
|
|
||||||
{
|
|
||||||
String downloadPath = sharedPreferences
|
|
||||||
.getString(DOWNLOAD_PATH_PREFERENCE,
|
|
||||||
getString(R.string.download_path_summary));
|
|
||||||
downloadPathPreference
|
|
||||||
.setSummary(downloadPath);
|
|
||||||
}
|
|
||||||
else if (key == DOWNLOAD_PATH_AUDIO_PREFERENCE)
|
|
||||||
{
|
|
||||||
String downloadPath = sharedPreferences
|
|
||||||
.getString(DOWNLOAD_PATH_AUDIO_PREFERENCE,
|
|
||||||
getString(R.string.download_path_audio_summary));
|
|
||||||
downloadPathAudioPreference
|
|
||||||
.setSummary(downloadPath);
|
|
||||||
}
|
|
||||||
else if (key == THEME)
|
|
||||||
{
|
|
||||||
String selectedTheme = sharedPreferences.getString(THEME, "Light");
|
|
||||||
themePreference.setSummary(selectedTheme);
|
|
||||||
|
|
||||||
if(!selectedTheme.equals(currentTheme)) { // If it's not the current theme
|
|
||||||
new AlertDialog.Builder(activity)
|
|
||||||
.setTitle(R.string.restart_title)
|
|
||||||
.setMessage(R.string.msg_restart)
|
|
||||||
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
Intent intentToMain = new Intent(activity, MainActivity.class);
|
|
||||||
intentToMain.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
|
||||||
activity.startActivity(intentToMain);
|
|
||||||
|
|
||||||
activity.finish();
|
|
||||||
Runtime.getRuntime().exit(0);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.setNegativeButton(R.string.later, null)
|
|
||||||
.create().show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateSummary();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
defaultPreferences.registerOnSharedPreferenceChangeListener(prefListener);
|
|
||||||
|
|
||||||
updateSummary();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
|
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
|
||||||
|
Log.d("TAG", "onPreferenceTreeClick() called with: preferenceScreen = [" + preferenceScreen + "], preference = [" + preference + "]");
|
||||||
if(preference.getKey().equals(downloadPathPreference.getKey()) ||
|
if (preference.getKey().equals(DOWNLOAD_PATH_PREFERENCE) || preference.getKey().equals(DOWNLOAD_PATH_AUDIO_PREFERENCE)) {
|
||||||
preference.getKey().equals(downloadPathAudioPreference.getKey()))
|
Intent i = new Intent(activity, FilePickerActivity.class)
|
||||||
{
|
.putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false)
|
||||||
Activity activity = getActivity();
|
.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, true)
|
||||||
Intent i = new Intent(activity, FilePickerActivity.class);
|
.putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_DIR);
|
||||||
|
if (preference.getKey().equals(DOWNLOAD_PATH_PREFERENCE)) {
|
||||||
i.putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false);
|
startActivityForResult(i, REQUEST_DOWNLOAD_PATH);
|
||||||
i.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, true);
|
} else if (preference.getKey().equals(DOWNLOAD_PATH_AUDIO_PREFERENCE)) {
|
||||||
i.putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_DIR);
|
startActivityForResult(i, REQUEST_DOWNLOAD_AUDIO_PATH);
|
||||||
if(preference.getKey().equals(downloadPathPreference.getKey()))
|
|
||||||
{
|
|
||||||
activity.startActivityForResult(i, R.string.download_path_key);
|
|
||||||
}
|
|
||||||
else if (preference.getKey().equals(downloadPathAudioPreference.getKey()))
|
|
||||||
{
|
|
||||||
activity.startActivityForResult(i, R.string.download_path_audio_key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.onPreferenceTreeClick(preferenceScreen, preference);
|
return super.onPreferenceTreeClick(preferenceScreen, preference);
|
||||||
|
@ -208,90 +87,56 @@ public class SettingsFragment extends PreferenceFragment
|
||||||
@Override
|
@Override
|
||||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
Activity a = getActivity();
|
Log.d("TAG", "onActivityResult() called with: requestCode = [" + requestCode + "], resultCode = [" + resultCode + "], data = [" + data + "]");
|
||||||
|
|
||||||
if ((requestCode == R.string.download_path_audio_key
|
if ((requestCode == REQUEST_DOWNLOAD_PATH || requestCode == REQUEST_DOWNLOAD_AUDIO_PATH) && resultCode == Activity.RESULT_OK) {
|
||||||
|| requestCode == R.string.download_path_key)
|
String key = getString(requestCode == REQUEST_DOWNLOAD_PATH ? R.string.download_path_key : R.string.download_path_audio_key);
|
||||||
&& resultCode == Activity.RESULT_OK) {
|
|
||||||
|
|
||||||
Uri uri = null;
|
|
||||||
if (data.getBooleanExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false)) {
|
|
||||||
// For JellyBean and above
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
|
||||||
ClipData clip = data.getClipData();
|
|
||||||
|
|
||||||
if (clip != null) {
|
|
||||||
for (int i = 0; i < clip.getItemCount(); i++) {
|
|
||||||
uri = clip.getItemAt(i).getUri();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// For Ice Cream Sandwich
|
|
||||||
} else {
|
|
||||||
ArrayList<String> paths = data.getStringArrayListExtra
|
|
||||||
(FilePickerActivity.EXTRA_PATHS);
|
|
||||||
|
|
||||||
if (paths != null) {
|
|
||||||
for (String path: paths) {
|
|
||||||
uri = Uri.parse(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
uri = data.getData();
|
|
||||||
}
|
|
||||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(a);
|
|
||||||
|
|
||||||
//requestCode is equal to R.string.download_path_key or
|
|
||||||
//R.string.download_path_audio_key
|
|
||||||
String key = getString(requestCode);
|
|
||||||
String path = data.getData().toString().substring(7);
|
String path = data.getData().toString().substring(7);
|
||||||
prefs.edit()
|
defaultPreferences.edit().putString(key, path).apply();
|
||||||
.putString(key, path)
|
updatePreferencesSummary();
|
||||||
.apply();
|
} else if (requestCode == REQUEST_INSTALL_ORBOT) {
|
||||||
|
|
||||||
}
|
|
||||||
else if(requestCode == REQUEST_INSTALL_ORBOT)
|
|
||||||
{
|
|
||||||
// try to start tor regardless of resultCode since clicking back after
|
// try to start tor regardless of resultCode since clicking back after
|
||||||
// installing the app does not necessarily return RESULT_OK
|
// installing the app does not necessarily return RESULT_OK
|
||||||
App.configureTor(requestCode == REQUEST_INSTALL_ORBOT
|
App.configureTor(OrbotHelper.requestStartTor(activity));
|
||||||
&& OrbotHelper.requestStartTor(a));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSummary();
|
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is used to show the status of some preference in the description
|
/**
|
||||||
private void updateSummary() {
|
* Update ONLY the summary of some preferences that don't fire in the onSharedPreferenceChanged or CAN'T be update via xml (%s)
|
||||||
defaultResolutionPreference.setSummary(
|
*
|
||||||
defaultPreferences.getString(DEFAULT_RESOLUTION_PREFERENCE,
|
* For example, the download_path use the startActivityForResult, firing the onStop of this fragment,
|
||||||
getString(R.string.default_resolution_value)));
|
* unregistering the listener (unregisterOnSharedPreferenceChangeListener)
|
||||||
defaultPopupResolutionPreference.setSummary(
|
*/
|
||||||
defaultPreferences.getString(DEFAULT_POPUP_RESOLUTION_PREFERENCE,
|
private void updatePreferencesSummary() {
|
||||||
getString(R.string.default_popup_resolution_value)));
|
findPreference(DOWNLOAD_PATH_PREFERENCE).setSummary(defaultPreferences.getString(DOWNLOAD_PATH_PREFERENCE, getString(R.string.download_path_summary)));
|
||||||
preferredVideoFormatPreference.setSummary(
|
findPreference(DOWNLOAD_PATH_AUDIO_PREFERENCE).setSummary(defaultPreferences.getString(DOWNLOAD_PATH_AUDIO_PREFERENCE, getString(R.string.download_path_audio_summary)));
|
||||||
defaultPreferences.getString(PREFERRED_VIDEO_FORMAT_PREFERENCE,
|
|
||||||
getString(R.string.preferred_video_format_default)));
|
|
||||||
defaultAudioFormatPreference.setSummary(
|
|
||||||
defaultPreferences.getString(DEFAULT_AUDIO_FORMAT_PREFERENCE,
|
|
||||||
getString(R.string.default_audio_format_value)));
|
|
||||||
searchLanguagePreference.setSummary(
|
|
||||||
defaultPreferences.getString(SEARCH_LANGUAGE_PREFERENCE,
|
|
||||||
getString(R.string.default_language_value)));
|
|
||||||
downloadPathPreference.setSummary(
|
|
||||||
defaultPreferences.getString(DOWNLOAD_PATH_PREFERENCE,
|
|
||||||
getString(R.string.download_path_summary)));
|
|
||||||
downloadPathAudioPreference.setSummary(
|
|
||||||
defaultPreferences.getString(DOWNLOAD_PATH_AUDIO_PREFERENCE,
|
|
||||||
getString(R.string.download_path_audio_summary)));
|
|
||||||
themePreference.setSummary(
|
|
||||||
defaultPreferences.getString(THEME,
|
|
||||||
getString(R.string.light_theme_title)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||||
|
Log.d("TAG", "onSharedPreferenceChanged() called with: sharedPreferences = [" + sharedPreferences + "], key = [" + key + "]");
|
||||||
|
String summary = null;
|
||||||
|
|
||||||
|
if (key.equals(USE_TOR_KEY)) {
|
||||||
|
if (defaultPreferences.getBoolean(USE_TOR_KEY, false)) {
|
||||||
|
if (OrbotHelper.isOrbotInstalled(activity)) {
|
||||||
|
App.configureTor(true);
|
||||||
|
OrbotHelper.requestStartTor(activity);
|
||||||
|
} else {
|
||||||
|
Intent intent = OrbotHelper.getOrbotInstallIntent(activity);
|
||||||
|
startActivityForResult(intent, REQUEST_INSTALL_ORBOT);
|
||||||
|
}
|
||||||
|
} else App.configureTor(false);
|
||||||
|
return;
|
||||||
|
} else if (key.equals(THEME)) {
|
||||||
|
summary = sharedPreferences.getString(THEME, getString(R.string.default_theme_value));
|
||||||
|
if (!summary.equals(currentTheme)) { // If it's not the current theme
|
||||||
|
Intent intentToMain = new Intent(activity, MainActivity.class);
|
||||||
|
intentToMain.putExtra(Constants.KEY_THEME_CHANGE, true);
|
||||||
|
startActivity(intentToMain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TextUtils.isEmpty(summary)) findPreference(key).setSummary(summary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,4 +5,8 @@ public class Constants {
|
||||||
public static final String KEY_URL = "key_url";
|
public static final String KEY_URL = "key_url";
|
||||||
public static final String KEY_TITLE = "key_title";
|
public static final String KEY_TITLE = "key_title";
|
||||||
public static final String KEY_LINK_TYPE = "key_link_type";
|
public static final String KEY_LINK_TYPE = "key_link_type";
|
||||||
|
public static final String KEY_OPEN_SEARCH = "key_open_search";
|
||||||
|
public static final String KEY_QUERY = "key_query";
|
||||||
|
|
||||||
|
public static final String KEY_THEME_CHANGE = "key_theme_change";
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,10 @@ import org.schabi.newpipe.player.VideoPlayer;
|
||||||
@SuppressWarnings({"unused", "WeakerAccess"})
|
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||||
public class NavigationHelper {
|
public class NavigationHelper {
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Players
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
public static Intent getOpenVideoPlayerIntent(Context context, Class targetClazz, StreamInfo info, int selectedStreamIndex) {
|
public static Intent getOpenVideoPlayerIntent(Context context, Class targetClazz, StreamInfo info, int selectedStreamIndex) {
|
||||||
Intent mIntent = new Intent(context, targetClazz)
|
Intent mIntent = new Intent(context, targetClazz)
|
||||||
.putExtra(BasePlayer.VIDEO_TITLE, info.title)
|
.putExtra(BasePlayer.VIDEO_TITLE, info.title)
|
||||||
|
@ -61,7 +65,6 @@ public class NavigationHelper {
|
||||||
return mIntent;
|
return mIntent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Through Interface (faster)
|
// Through Interface (faster)
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -115,6 +118,14 @@ public class NavigationHelper {
|
||||||
context.startActivity(mIntent);
|
context.startActivity(mIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void openSearch(Context context, int serviceId, String query) {
|
||||||
|
Intent mIntent = new Intent(context, MainActivity.class);
|
||||||
|
mIntent.putExtra(Constants.KEY_SERVICE_ID, serviceId);
|
||||||
|
mIntent.putExtra(Constants.KEY_QUERY, query);
|
||||||
|
mIntent.putExtra(Constants.KEY_OPEN_SEARCH, true);
|
||||||
|
context.startActivity(mIntent);
|
||||||
|
}
|
||||||
|
|
||||||
private static Intent getOpenIntent(Context context, String url, int serviceId, StreamingService.LinkType type) {
|
private static Intent getOpenIntent(Context context, String url, int serviceId, StreamingService.LinkType type) {
|
||||||
Intent mIntent = new Intent(context, MainActivity.class);
|
Intent mIntent = new Intent(context, MainActivity.class);
|
||||||
mIntent.putExtra(Constants.KEY_SERVICE_ID, serviceId);
|
mIntent.putExtra(Constants.KEY_SERVICE_ID, serviceId);
|
||||||
|
|
|
@ -10,25 +10,33 @@ public class ThemeHelper {
|
||||||
/**
|
/**
|
||||||
* Apply the selected theme (on NewPipe settings) in the context
|
* Apply the selected theme (on NewPipe settings) in the context
|
||||||
*
|
*
|
||||||
* @param context context that the theme will be applied
|
* @param context context that the theme will be applied
|
||||||
* @param useActionbarTheme whether to use an action bar theme or not
|
|
||||||
*/
|
*/
|
||||||
public static void setTheme(Context context, boolean useActionbarTheme) {
|
public static void setTheme(Context context) {
|
||||||
|
|
||||||
String themeKey = context.getString(R.string.theme_key);
|
String themeKey = context.getString(R.string.theme_key);
|
||||||
String darkTheme = context.getResources().getString(R.string.dark_theme_title);
|
String darkTheme = context.getResources().getString(R.string.dark_theme_title);
|
||||||
String blackTheme = context.getResources().getString(R.string.black_theme_title);
|
String blackTheme = context.getResources().getString(R.string.black_theme_title);
|
||||||
|
|
||||||
String sp = PreferenceManager.getDefaultSharedPreferences(context)
|
String sp = PreferenceManager.getDefaultSharedPreferences(context).getString(themeKey, context.getResources().getString(R.string.light_theme_title));
|
||||||
.getString(themeKey, context.getResources().getString(R.string.light_theme_title));
|
|
||||||
|
|
||||||
if (useActionbarTheme) {
|
if (sp.equals(darkTheme)) context.setTheme(R.style.DarkTheme);
|
||||||
if (sp.equals(darkTheme)) context.setTheme(R.style.DarkTheme);
|
else if (sp.equals(blackTheme)) context.setTheme(R.style.BlackTheme);
|
||||||
else if (sp.equals(blackTheme)) context.setTheme(R.style.BlackTheme);
|
else context.setTheme(R.style.AppTheme);
|
||||||
else context.setTheme(R.style.AppTheme);
|
}
|
||||||
} else {
|
|
||||||
if (sp.equals(darkTheme)) context.setTheme(R.style.DarkTheme_NoActionBar);
|
/**
|
||||||
else if (sp.equals(blackTheme)) context.setTheme(R.style.BlackTheme_NoActionBar);
|
* Return true if the selected theme (on NewPipe settings) is the Light theme
|
||||||
else context.setTheme(R.style.AppTheme_NoActionBar);
|
*
|
||||||
}
|
* @param context context to get the preference
|
||||||
|
*/
|
||||||
|
public static boolean isLightThemeSelected(Context context) {
|
||||||
|
String themeKey = context.getString(R.string.theme_key);
|
||||||
|
String darkTheme = context.getResources().getString(R.string.dark_theme_title);
|
||||||
|
String blackTheme = context.getResources().getString(R.string.black_theme_title);
|
||||||
|
|
||||||
|
String sp = PreferenceManager.getDefaultSharedPreferences(context).getString(themeKey, context.getResources().getString(R.string.light_theme_title));
|
||||||
|
|
||||||
|
return !(sp.equals(darkTheme) || sp.equals(blackTheme));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
package org.schabi.newpipe.workers;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Handler;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
|
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
||||||
|
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common properties of Workers
|
||||||
|
*
|
||||||
|
* @author mauriciocolli
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
public abstract class AbstractWorker extends Thread {
|
||||||
|
|
||||||
|
private final AtomicBoolean isRunning = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
private final int serviceId;
|
||||||
|
private Context context;
|
||||||
|
private Handler handler;
|
||||||
|
private StreamingService service;
|
||||||
|
|
||||||
|
public AbstractWorker(Context context, int serviceId) {
|
||||||
|
this.context = context;
|
||||||
|
this.serviceId = serviceId;
|
||||||
|
this.handler = new Handler(context.getMainLooper());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
isRunning.set(true);
|
||||||
|
service = NewPipe.getService(serviceId);
|
||||||
|
doWork(serviceId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Handle the exception only if thread is not interrupted
|
||||||
|
e.printStackTrace();
|
||||||
|
if (!isInterrupted() && !(e instanceof InterruptedIOException) && !(e.getCause() instanceof InterruptedIOException)) {
|
||||||
|
handleException(e, serviceId);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
isRunning.set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Here is the place that the heavy work is realized
|
||||||
|
*
|
||||||
|
* @param serviceId serviceId that was passed when created this object
|
||||||
|
*
|
||||||
|
* @throws Exception these exceptions are handled by the {@link #handleException(Exception, int)}
|
||||||
|
*/
|
||||||
|
protected abstract void doWork(int serviceId) throws Exception;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method that handle the exception thrown by the {@link #doWork(int)}.
|
||||||
|
*
|
||||||
|
* @param exception {@link Exception} that was thrown by {@link #doWork(int)}
|
||||||
|
*/
|
||||||
|
protected abstract void handleException(Exception exception, int serviceId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the extraction is not completed yet
|
||||||
|
*
|
||||||
|
* @return the value of the AtomicBoolean {@link #isRunning}
|
||||||
|
*/
|
||||||
|
public boolean isRunning() {
|
||||||
|
return isRunning.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel this ExtractorWorker, calling {@link #onDestroy()} and interrupting this thread.
|
||||||
|
* <p>
|
||||||
|
* <b>Note:</b> Any I/O that is active in the moment that this method is called will be canceled and a Exception will be thrown, because of the {@link #interrupt()}.<br>
|
||||||
|
* This is useful when you don't want the resulting {@link StreamInfo} anymore, but don't want to waste bandwidth, otherwise it'd run till it receives the StreamInfo.
|
||||||
|
*/
|
||||||
|
public void cancel() {
|
||||||
|
onDestroy();
|
||||||
|
this.interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method that discards everything that doesn't need anymore.<br>
|
||||||
|
* Subclasses can override this method to destroy their garbage.
|
||||||
|
*/
|
||||||
|
protected void onDestroy() {
|
||||||
|
this.isRunning.set(false);
|
||||||
|
this.context = null;
|
||||||
|
this.handler = null;
|
||||||
|
this.service = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Handler getHandler() {
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StreamingService getService() {
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getServiceId() {
|
||||||
|
return serviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getServiceName() {
|
||||||
|
return service == null ? "none" : service.getServiceInfo().name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Context getContext() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,7 +31,7 @@ public class ChannelExtractorWorker extends ExtractorWorker {
|
||||||
* Interface which will be called for result and errors
|
* Interface which will be called for result and errors
|
||||||
*/
|
*/
|
||||||
public interface OnChannelInfoReceive {
|
public interface OnChannelInfoReceive {
|
||||||
void onReceive(ChannelInfo info);
|
void onReceive(ChannelInfo info, boolean onlyVideos);
|
||||||
void onError(int messageId);
|
void onError(int messageId);
|
||||||
/**
|
/**
|
||||||
* Called when an unrecoverable error has occurred.
|
* Called when an unrecoverable error has occurred.
|
||||||
|
@ -44,12 +44,15 @@ public class ChannelExtractorWorker extends ExtractorWorker {
|
||||||
* @param context context for error reporting purposes
|
* @param context context for error reporting purposes
|
||||||
* @param serviceId id of the request service
|
* @param serviceId id of the request service
|
||||||
* @param channelUrl channelUrl of the service (e.g. https://www.youtube.com/channel/UC_aEa8K-EOJ3D6gOs7HcyNg)
|
* @param channelUrl channelUrl of the service (e.g. https://www.youtube.com/channel/UC_aEa8K-EOJ3D6gOs7HcyNg)
|
||||||
|
* @param pageNumber which page to extract
|
||||||
|
* @param onlyVideos flag that will be send by {@link OnChannelInfoReceive#onReceive(ChannelInfo, boolean)}
|
||||||
* @param callback listener that will be called-back when events occur (check {@link ChannelExtractorWorker.OnChannelInfoReceive})
|
* @param callback listener that will be called-back when events occur (check {@link ChannelExtractorWorker.OnChannelInfoReceive})
|
||||||
*/
|
*/
|
||||||
public ChannelExtractorWorker(Context context, int serviceId, String channelUrl, int pageNumber, OnChannelInfoReceive callback) {
|
public ChannelExtractorWorker(Context context, int serviceId, String channelUrl, int pageNumber, boolean onlyVideos, OnChannelInfoReceive callback) {
|
||||||
super(context, channelUrl, serviceId);
|
super(context, channelUrl, serviceId);
|
||||||
this.pageNumber = pageNumber;
|
this.pageNumber = pageNumber;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
|
this.onlyVideos = onlyVideos;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -71,7 +74,7 @@ public class ChannelExtractorWorker extends ExtractorWorker {
|
||||||
public void run() {
|
public void run() {
|
||||||
if (isInterrupted() || callback == null) return;
|
if (isInterrupted() || callback == null) return;
|
||||||
|
|
||||||
callback.onReceive(channelInfo);
|
callback.onReceive(channelInfo, onlyVideos);
|
||||||
onDestroy();
|
onDestroy();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -107,13 +110,5 @@ public class ChannelExtractorWorker extends ExtractorWorker {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isOnlyVideos() {
|
|
||||||
return onlyVideos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOnlyVideos(boolean onlyVideos) {
|
|
||||||
this.onlyVideos = onlyVideos;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,18 +2,12 @@ package org.schabi.newpipe.workers;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Handler;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
|
||||||
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
|
|
||||||
import java.io.InterruptedIOException;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common properties of ExtractorWorkers
|
* Common properties of ExtractorWorkers
|
||||||
|
@ -21,38 +15,18 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
* @author mauriciocolli
|
* @author mauriciocolli
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
public abstract class ExtractorWorker extends Thread {
|
public abstract class ExtractorWorker extends AbstractWorker {
|
||||||
|
|
||||||
private final AtomicBoolean isRunning = new AtomicBoolean(false);
|
|
||||||
|
|
||||||
private final String url;
|
private final String url;
|
||||||
private final int serviceId;
|
|
||||||
private Context context;
|
|
||||||
private Handler handler;
|
|
||||||
private StreamingService service;
|
|
||||||
|
|
||||||
public ExtractorWorker(Context context, String url, int serviceId) {
|
public ExtractorWorker(Context context, String url, int serviceId) {
|
||||||
this.context = context;
|
super(context, serviceId);
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.serviceId = serviceId;
|
|
||||||
this.handler = new Handler(context.getMainLooper());
|
|
||||||
if (url.length() >= 40) setName("Thread-" + url.substring(url.length() - 11, url.length()));
|
if (url.length() >= 40) setName("Thread-" + url.substring(url.length() - 11, url.length()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
protected void doWork(int serviceId) throws Exception {
|
||||||
try {
|
doWork(serviceId, url);
|
||||||
isRunning.set(true);
|
|
||||||
service = NewPipe.getService(serviceId);
|
|
||||||
doWork(serviceId, url);
|
|
||||||
} catch (Exception e) {
|
|
||||||
// Handle the exception only if thread is not interrupted
|
|
||||||
if (!isInterrupted() && !(e instanceof InterruptedIOException) && !(e.getCause() instanceof InterruptedIOException)) {
|
|
||||||
handleException(e, serviceId, url);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
isRunning.set(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -65,6 +39,10 @@ public abstract class ExtractorWorker extends Thread {
|
||||||
*/
|
*/
|
||||||
protected abstract void doWork(int serviceId, String url) throws Exception;
|
protected abstract void doWork(int serviceId, String url) throws Exception;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void handleException(Exception exception, int serviceId) {
|
||||||
|
handleException(exception, serviceId, url);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method that handle the exception thrown by the {@link #doWork(int, String)}.
|
* Method that handle the exception thrown by the {@link #doWork(int, String)}.
|
||||||
|
@ -99,63 +77,12 @@ public abstract class ExtractorWorker extends Thread {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getContext() instanceof Activity) {
|
if (getContext() instanceof Activity) {
|
||||||
View rootView = getContext() != null ? ((Activity) getContext()).findViewById(android.R.id.content) : null;
|
View rootView = getContext() instanceof Activity ? ((Activity) getContext()).findViewById(android.R.id.content) : null;
|
||||||
ErrorActivity.reportError(getHandler(), getContext(), errorsList, null, rootView, ErrorActivity.ErrorInfo.make(errorUserAction, getServiceName(), url, 0 /* no message for the user */));
|
ErrorActivity.reportError(getHandler(), getContext(), errorsList, null, rootView, ErrorActivity.ErrorInfo.make(errorUserAction, getServiceName(), url, 0 /* no message for the user */));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if the extraction is not completed yet
|
|
||||||
*
|
|
||||||
* @return the value of the AtomicBoolean {@link #isRunning}
|
|
||||||
*/
|
|
||||||
public boolean isRunning() {
|
|
||||||
return isRunning.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancel this ExtractorWorker, calling {@link #onDestroy()} and interrupting this thread.
|
|
||||||
* <p>
|
|
||||||
* <b>Note:</b> Any I/O that is active in the moment that this method is called will be canceled and a Exception will be thrown, because of the {@link #interrupt()}.<br>
|
|
||||||
* This is useful when you don't want the resulting {@link StreamInfo} anymore, but don't want to waste bandwidth, otherwise it'd run till it receives the StreamInfo.
|
|
||||||
*/
|
|
||||||
public void cancel() {
|
|
||||||
onDestroy();
|
|
||||||
this.interrupt();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method that discards everything that doesn't need anymore.<br>
|
|
||||||
* Subclasses can override this method to destroy their garbage.
|
|
||||||
*/
|
|
||||||
protected void onDestroy() {
|
|
||||||
this.isRunning.set(false);
|
|
||||||
this.context = null;
|
|
||||||
this.handler = null;
|
|
||||||
this.service = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Handler getHandler() {
|
|
||||||
return handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUrl() {
|
public String getUrl() {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
public StreamingService getService() {
|
|
||||||
return service;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getServiceId() {
|
|
||||||
return serviceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getServiceName() {
|
|
||||||
return service == null ? "none" : service.getServiceInfo().name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Context getContext() {
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
package org.schabi.newpipe.workers;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
||||||
|
import org.schabi.newpipe.extractor.search.SearchEngine;
|
||||||
|
import org.schabi.newpipe.extractor.search.SearchResult;
|
||||||
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return list of results based on a query
|
||||||
|
*
|
||||||
|
* @author mauriciocolli
|
||||||
|
*/
|
||||||
|
public class SearchWorker extends AbstractWorker {
|
||||||
|
|
||||||
|
private EnumSet<SearchEngine.Filter> filter;
|
||||||
|
private String query;
|
||||||
|
private int page;
|
||||||
|
private OnSearchResult callback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface which will be called for result and errors
|
||||||
|
*/
|
||||||
|
public interface OnSearchResult {
|
||||||
|
void onSearchResult(SearchResult result);
|
||||||
|
void onNothingFound(String message);
|
||||||
|
void onSearchError(int messageId);
|
||||||
|
void onReCaptchaChallenge();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SearchWorker(Context context, int serviceId, String query, int page, EnumSet<SearchEngine.Filter> filter, OnSearchResult callback) {
|
||||||
|
super(context, serviceId);
|
||||||
|
this.callback = callback;
|
||||||
|
this.query = query;
|
||||||
|
this.page = page;
|
||||||
|
this.filter = filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SearchWorker startForQuery(Context context, int serviceId, @NonNull String query, int page, EnumSet<SearchEngine.Filter> filter, OnSearchResult callback) {
|
||||||
|
SearchWorker worker = new SearchWorker(context, serviceId, query, page, filter, callback);
|
||||||
|
worker.start();
|
||||||
|
return worker;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
this.callback = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doWork(int serviceId) throws Exception {
|
||||||
|
SearchEngine searchEngine = getService().getSearchEngineInstance();
|
||||||
|
|
||||||
|
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||||
|
String searchLanguageKey = getContext().getString(R.string.search_language_key);
|
||||||
|
String searchLanguage = sharedPreferences.getString(searchLanguageKey, getContext().getString(R.string.default_language_value));
|
||||||
|
|
||||||
|
final SearchResult searchResult = SearchResult.getSearchResult(searchEngine, query, page, searchLanguage, filter);
|
||||||
|
if (callback != null && searchResult != null && !isInterrupted()) getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (isInterrupted() || callback == null) return;
|
||||||
|
|
||||||
|
callback.onSearchResult(searchResult);
|
||||||
|
onDestroy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void handleException(final Exception exception, int serviceId) {
|
||||||
|
if (callback == null || getHandler() == null || isInterrupted()) return;
|
||||||
|
|
||||||
|
if (exception instanceof ReCaptchaException) {
|
||||||
|
getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onReCaptchaChallenge();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (exception instanceof IOException) {
|
||||||
|
getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onSearchError(R.string.network_error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (exception instanceof SearchEngine.NothingFoundException) {
|
||||||
|
getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onNothingFound(exception.getMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (exception instanceof ExtractionException) {
|
||||||
|
View rootView = getContext() instanceof Activity ? ((Activity) getContext()).findViewById(android.R.id.content) : null;
|
||||||
|
ErrorActivity.reportError(getHandler(), getContext(), exception, null, rootView, ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED, getServiceName(), query, R.string.parsing_error));
|
||||||
|
getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onSearchError(R.string.parsing_error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
View rootView = getContext() instanceof Activity ? ((Activity) getContext()).findViewById(android.R.id.content) : null;
|
||||||
|
ErrorActivity.reportError(getHandler(), getContext(), exception, null, rootView, ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED, getServiceName(), query, R.string.general_error));
|
||||||
|
getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onSearchError(R.string.general_error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
package org.schabi.newpipe.workers;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.extractor.SuggestionExtractor;
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Worker that get suggestions based on the query
|
||||||
|
*
|
||||||
|
* @author mauriciocolli
|
||||||
|
*/
|
||||||
|
public class SuggestionWorker extends AbstractWorker {
|
||||||
|
|
||||||
|
private String query;
|
||||||
|
private OnSuggestionResult callback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface which will be called for result and errors
|
||||||
|
*/
|
||||||
|
public interface OnSuggestionResult {
|
||||||
|
void onSuggestionResult(@NonNull List<String> suggestions);
|
||||||
|
void onSuggestionError(int messageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SuggestionWorker(Context context, int serviceId, String query, OnSuggestionResult callback) {
|
||||||
|
super(context, serviceId);
|
||||||
|
this.callback = callback;
|
||||||
|
this.query = query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SuggestionWorker startForQuery(Context context, int serviceId, @NonNull String query, OnSuggestionResult callback) {
|
||||||
|
SuggestionWorker worker = new SuggestionWorker(context, serviceId, query, callback);
|
||||||
|
worker.start();
|
||||||
|
return worker;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
this.callback = null;
|
||||||
|
this.query = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doWork(int serviceId) throws Exception {
|
||||||
|
SuggestionExtractor suggestionExtractor = getService().getSuggestionExtractorInstance();
|
||||||
|
|
||||||
|
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||||
|
String searchLanguageKey = getContext().getString(R.string.search_language_key);
|
||||||
|
String searchLanguage = sharedPreferences.getString(searchLanguageKey, getContext().getString(R.string.default_language_value));
|
||||||
|
|
||||||
|
final List<String> suggestions = suggestionExtractor.suggestionList(query, searchLanguage);
|
||||||
|
|
||||||
|
if (callback != null && suggestions != null && !isInterrupted()) getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (isInterrupted() || callback == null) return;
|
||||||
|
|
||||||
|
callback.onSuggestionResult(suggestions);
|
||||||
|
onDestroy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void handleException(final Exception exception, int serviceId) {
|
||||||
|
if (callback == null || getHandler() == null || isInterrupted()) return;
|
||||||
|
|
||||||
|
if (exception instanceof ExtractionException) {
|
||||||
|
View rootView = getContext() instanceof Activity ? ((Activity) getContext()).findViewById(android.R.id.content) : null;
|
||||||
|
ErrorActivity.reportError(getHandler(), getContext(), exception, null, rootView, ErrorActivity.ErrorInfo.make(ErrorActivity.GET_SUGGESTIONS, getServiceName(), query, R.string.parsing_error));
|
||||||
|
getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onSuggestionError(R.string.parsing_error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (exception instanceof IOException) {
|
||||||
|
getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onSuggestionError(R.string.network_error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
View rootView = getContext() instanceof Activity ? ((Activity) getContext()).findViewById(android.R.id.content) : null;
|
||||||
|
ErrorActivity.reportError(getHandler(), getContext(), exception, null, rootView, ErrorActivity.ErrorInfo.make(ErrorActivity.GET_SUGGESTIONS, getServiceName(), query, R.string.general_error));
|
||||||
|
getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onSuggestionError(R.string.general_error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 207 B |
After Width: | Height: | Size: 221 B |
After Width: | Height: | Size: 149 B |
After Width: | Height: | Size: 156 B |
After Width: | Height: | Size: 151 B |
After Width: | Height: | Size: 159 B |
After Width: | Height: | Size: 107 B |
After Width: | Height: | Size: 111 B |
After Width: | Height: | Size: 189 B |
After Width: | Height: | Size: 193 B |
After Width: | Height: | Size: 390 B |
After Width: | Height: | Size: 396 B |
After Width: | Height: | Size: 164 B |
After Width: | Height: | Size: 175 B |
After Width: | Height: | Size: 126 B |
After Width: | Height: | Size: 129 B |
After Width: | Height: | Size: 125 B |
After Width: | Height: | Size: 129 B |
After Width: | Height: | Size: 89 B |
After Width: | Height: | Size: 90 B |
After Width: | Height: | Size: 127 B |
After Width: | Height: | Size: 131 B |
After Width: | Height: | Size: 249 B |
After Width: | Height: | Size: 247 B |
Before Width: | Height: | Size: 320 B |
After Width: | Height: | Size: 235 B |
Before Width: | Height: | Size: 257 B After Width: | Height: | Size: 257 B |
After Width: | Height: | Size: 171 B |
After Width: | Height: | Size: 179 B |
After Width: | Height: | Size: 168 B |
After Width: | Height: | Size: 182 B |
After Width: | Height: | Size: 103 B |
After Width: | Height: | Size: 103 B |
After Width: | Height: | Size: 185 B |
After Width: | Height: | Size: 187 B |
After Width: | Height: | Size: 464 B |
After Width: | Height: | Size: 465 B |
After Width: | Height: | Size: 309 B |
Before Width: | Height: | Size: 524 B |
Before Width: | Height: | Size: 347 B After Width: | Height: | Size: 347 B |
After Width: | Height: | Size: 213 B |
After Width: | Height: | Size: 230 B |
After Width: | Height: | Size: 215 B |
After Width: | Height: | Size: 237 B |
After Width: | Height: | Size: 112 B |
After Width: | Height: | Size: 107 B |
After Width: | Height: | Size: 248 B |
After Width: | Height: | Size: 254 B |
After Width: | Height: | Size: 684 B |
After Width: | Height: | Size: 728 B |
After Width: | Height: | Size: 377 B |
Before Width: | Height: | Size: 707 B |
Before Width: | Height: | Size: 436 B After Width: | Height: | Size: 436 B |
After Width: | Height: | Size: 261 B |
After Width: | Height: | Size: 284 B |
After Width: | Height: | Size: 256 B |
After Width: | Height: | Size: 287 B |
After Width: | Height: | Size: 123 B |
After Width: | Height: | Size: 106 B |
After Width: | Height: | Size: 315 B |
After Width: | Height: | Size: 323 B |
After Width: | Height: | Size: 868 B |