mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-31 23:32:59 +00:00 
			
		
		
		
	fix merge with add_cahnnels
This commit is contained in:
		| @@ -31,6 +31,7 @@ android { | |||||||
| } | } | ||||||
|  |  | ||||||
| dependencies { | dependencies { | ||||||
|  |     testCompile 'junit:junit:4.12' | ||||||
|     compile 'com.android.support:appcompat-v7:24.1.1' |     compile 'com.android.support:appcompat-v7:24.1.1' | ||||||
|     compile 'com.android.support:support-v4:24.1.1' |     compile 'com.android.support:support-v4:24.1.1' | ||||||
|     compile 'com.android.support:design:24.1.1' |     compile 'com.android.support:design:24.1.1' | ||||||
|   | |||||||
| @@ -79,6 +79,10 @@ public class YoutubeStreamExtractorDefaultTest extends AndroidTestCase { | |||||||
|         assertTrue(extractor.getUploadDate().length() > 0); |         assertTrue(extractor.getUploadDate().length() > 0); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public void testGetChannelUrl() throws ParsingException { | ||||||
|  |         assertTrue(extractor.getChannelUrl().length() > 0); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public void testGetThumbnailUrl() throws ParsingException { |     public void testGetThumbnailUrl() throws ParsingException { | ||||||
|         assertTrue(extractor.getThumbnailUrl(), |         assertTrue(extractor.getThumbnailUrl(), | ||||||
|                 extractor.getThumbnailUrl().contains(HTTPS)); |                 extractor.getThumbnailUrl().contains(HTTPS)); | ||||||
|   | |||||||
| @@ -16,10 +16,10 @@ | |||||||
|         android:logo="@mipmap/ic_launcher" |         android:logo="@mipmap/ic_launcher" | ||||||
|         android:theme="@style/AppTheme" |         android:theme="@style/AppTheme" | ||||||
|         tools:ignore="AllowBackup"> |         tools:ignore="AllowBackup"> | ||||||
|  |  | ||||||
|         <activity |         <activity | ||||||
|             android:name=".VideoItemListActivity" |             android:name=".MainActivity" | ||||||
|             android:label="@string/app_name" |             android:label="@string/app_name"> | ||||||
|             android:theme="@style/SplashScreenTheme"> |  | ||||||
|             <intent-filter> |             <intent-filter> | ||||||
|                 <action android:name="android.intent.action.MAIN" /> |                 <action android:name="android.intent.action.MAIN" /> | ||||||
|  |  | ||||||
| @@ -27,12 +27,12 @@ | |||||||
|             </intent-filter> |             </intent-filter> | ||||||
|         </activity> |         </activity> | ||||||
|         <activity |         <activity | ||||||
|             android:name=".VideoItemDetailActivity" |             android:name=".detail.VideoItemDetailActivity" | ||||||
|             android:label="@string/title_videoitem_detail" |             android:label="@string/title_videoitem_detail" | ||||||
|             android:theme="@style/AppTheme"> |             android:theme="@style/AppTheme"> | ||||||
|             <meta-data |             <meta-data | ||||||
|                 android:name="android.support.PARENT_ACTIVITY" |                 android:name="android.support.PARENT_ACTIVITY" | ||||||
|                 android:value=".VideoItemListActivity" /> |                 android:value=".MainActivity" /> | ||||||
|  |  | ||||||
|             <intent-filter> |             <intent-filter> | ||||||
|                 <action android:name="android.intent.action.VIEW" /> |                 <action android:name="android.intent.action.VIEW" /> | ||||||
| @@ -80,12 +80,14 @@ | |||||||
|             android:name=".player.PlayVideoActivity" |             android:name=".player.PlayVideoActivity" | ||||||
|             android:configChanges="orientation|keyboardHidden|screenSize" |             android:configChanges="orientation|keyboardHidden|screenSize" | ||||||
|             android:theme="@style/VideoPlayerTheme" |             android:theme="@style/VideoPlayerTheme" | ||||||
|             tools:ignore="UnusedAttribute"/> |             tools:ignore="UnusedAttribute" /> | ||||||
|  |  | ||||||
|         <service |         <service | ||||||
|             android:name=".player.BackgroundPlayer" |             android:name=".player.BackgroundPlayer" | ||||||
|             android:exported="false" |             android:exported="false" | ||||||
|             android:label="@string/background_player_name"/> |             android:label="@string/background_player_name" /> | ||||||
| 		<activity |  | ||||||
|  |         <activity | ||||||
|             android:name=".player.ExoPlayerActivity" |             android:name=".player.ExoPlayerActivity" | ||||||
|             android:configChanges="keyboard|keyboardHidden|orientation|screenSize" |             android:configChanges="keyboard|keyboardHidden|orientation|screenSize" | ||||||
|             android:label="@string/app_name" |             android:label="@string/app_name" | ||||||
| @@ -103,12 +105,14 @@ | |||||||
|                 <data android:scheme="file" /> |                 <data android:scheme="file" /> | ||||||
|             </intent-filter> |             </intent-filter> | ||||||
|         </activity> |         </activity> | ||||||
|  |  | ||||||
|         <service |         <service | ||||||
|             android:name=".player.BackgroundPlayer" |             android:name=".player.BackgroundPlayer" | ||||||
|             android:label="@string/background_player_name" |             android:exported="false" | ||||||
|             android:exported="false" /> |             android:label="@string/background_player_name" /> | ||||||
|  |  | ||||||
|         <activity |         <activity | ||||||
|             android:name=".SettingsActivity" |             android:name=".settings.SettingsActivity" | ||||||
|             android:label="@string/settings_activity_title" /> |             android:label="@string/settings_activity_title" /> | ||||||
|         <activity |         <activity | ||||||
|             android:name=".PanicResponderActivity" |             android:name=".PanicResponderActivity" | ||||||
| @@ -125,18 +129,16 @@ | |||||||
|             android:name=".ExitActivity" |             android:name=".ExitActivity" | ||||||
|             android:label="@string/general_error" |             android:label="@string/general_error" | ||||||
|             android:theme="@android:style/Theme.NoDisplay" /> |             android:theme="@android:style/Theme.NoDisplay" /> | ||||||
|         <activity android:name=".ErrorActivity"/> |         <activity android:name=".ErrorActivity" /> | ||||||
|  |  | ||||||
|         <!-- giga get related --> |         <!-- giga get related --> | ||||||
|         <activity |         <activity | ||||||
|             android:name=".download.MainActivity" |             android:name=".download.MainActivity" | ||||||
|             android:label="@string/app_name" |             android:label="@string/app_name" | ||||||
|             android:theme="@style/AppTheme" |             android:launchMode="singleTask" | ||||||
|             android:launchMode="singleTask"> |             android:theme="@style/AppTheme" /> | ||||||
|         </activity> |  | ||||||
|  |  | ||||||
|         <service |         <service android:name="us.shandian.giga.service.DownloadManagerService" /> | ||||||
|             android:name="us.shandian.giga.service.DownloadManagerService"/> |  | ||||||
|  |  | ||||||
|         <activity |         <activity | ||||||
|             android:name="com.nononsenseapps.filepicker.FilePickerActivity" |             android:name="com.nononsenseapps.filepicker.FilePickerActivity" | ||||||
| @@ -145,5 +147,11 @@ | |||||||
|             android:launchMode="singleTop"> |             android:launchMode="singleTop"> | ||||||
|         </activity> |         </activity> | ||||||
|  |  | ||||||
|  |         <activity | ||||||
|  |             android:name=".ChannelActivity" | ||||||
|  |             android:label="@string/title_activity_channel" | ||||||
|  |             android:theme="@style/AppTheme.NoActionBar" /> | ||||||
|  |  | ||||||
|     </application> |     </application> | ||||||
|  |  | ||||||
| </manifest> | </manifest> | ||||||
| @@ -43,7 +43,7 @@ public class ActivityCommunicator { | |||||||
|     public volatile Bitmap backgroundPlayerThumbnail; |     public volatile Bitmap backgroundPlayerThumbnail; | ||||||
|  |  | ||||||
|     // Sent from any activity to ErrorActivity. |     // Sent from any activity to ErrorActivity. | ||||||
|     public volatile List<Exception> errorList; |     public volatile List<Throwable> errorList; | ||||||
|     public volatile Class returnActivity; |     public volatile Class returnActivity; | ||||||
|     public volatile ErrorActivity.ErrorInfo errorInfo; |     public volatile ErrorActivity.ErrorInfo errorInfo; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -2,12 +2,12 @@ package org.schabi.newpipe; | |||||||
|  |  | ||||||
| import android.app.Application; | import android.app.Application; | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.content.SharedPreferences; |  | ||||||
| import android.preference.PreferenceManager; |  | ||||||
|  |  | ||||||
| import com.nostra13.universalimageloader.core.ImageLoader; | import com.nostra13.universalimageloader.core.ImageLoader; | ||||||
| import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; | import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; | ||||||
|  |  | ||||||
|  | import org.schabi.newpipe.settings.SettingsActivity; | ||||||
|  |  | ||||||
| import info.guardianproject.netcipher.NetCipher; | import info.guardianproject.netcipher.NetCipher; | ||||||
| import info.guardianproject.netcipher.proxy.OrbotHelper; | import info.guardianproject.netcipher.proxy.OrbotHelper; | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										248
									
								
								app/src/main/java/org/schabi/newpipe/ChannelActivity.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										248
									
								
								app/src/main/java/org/schabi/newpipe/ChannelActivity.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,248 @@ | |||||||
|  | package org.schabi.newpipe; | ||||||
|  |  | ||||||
|  | import android.content.Intent; | ||||||
|  | import android.net.Uri; | ||||||
|  | import android.os.Bundle; | ||||||
|  | import android.os.Handler; | ||||||
|  | import android.support.design.widget.CollapsingToolbarLayout; | ||||||
|  | import android.support.design.widget.FloatingActionButton; | ||||||
|  | import android.support.v7.app.AppCompatActivity; | ||||||
|  | import android.support.v7.widget.LinearLayoutManager; | ||||||
|  | import android.support.v7.widget.RecyclerView; | ||||||
|  | import android.support.v7.widget.Toolbar; | ||||||
|  | import android.util.Log; | ||||||
|  | import android.view.View; | ||||||
|  | import android.widget.ImageView; | ||||||
|  | import android.widget.ProgressBar; | ||||||
|  | import android.widget.Toast; | ||||||
|  |  | ||||||
|  | import com.nostra13.universalimageloader.core.ImageLoader; | ||||||
|  |  | ||||||
|  | import org.schabi.newpipe.detail.VideoItemDetailActivity; | ||||||
|  | import org.schabi.newpipe.detail.VideoItemDetailFragment; | ||||||
|  | import org.schabi.newpipe.extractor.ChannelExtractor; | ||||||
|  | import org.schabi.newpipe.extractor.ChannelInfo; | ||||||
|  | import org.schabi.newpipe.extractor.ExtractionException; | ||||||
|  | import org.schabi.newpipe.extractor.ParsingException; | ||||||
|  | import org.schabi.newpipe.extractor.ServiceList; | ||||||
|  | import org.schabi.newpipe.extractor.StreamingService; | ||||||
|  | import org.schabi.newpipe.info_list.InfoListAdapter; | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
|  |  | ||||||
|  | public class ChannelActivity extends AppCompatActivity { | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     private static final String TAG = ChannelActivity.class.toString(); | ||||||
|  |     private View rootView = null; | ||||||
|  |  | ||||||
|  |     // intent const | ||||||
|  |     public static final String CHANNEL_URL = "channel_url"; | ||||||
|  |     public static final String SERVICE_ID = "service_id"; | ||||||
|  |  | ||||||
|  |     private int serviceId = -1; | ||||||
|  |     private String channelUrl = ""; | ||||||
|  |     private int pageNumber = 0; | ||||||
|  |     private boolean hasNextPage = true; | ||||||
|  |     private boolean isLoading = false; | ||||||
|  |  | ||||||
|  |     private ImageLoader imageLoader = ImageLoader.getInstance(); | ||||||
|  |     private InfoListAdapter infoListAdapter = null; | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected void onCreate(Bundle savedInstanceState) { | ||||||
|  |         super.onCreate(savedInstanceState); | ||||||
|  |         setContentView(R.layout.activity_channel); | ||||||
|  |         Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); | ||||||
|  |         rootView = findViewById(R.id.rootView); | ||||||
|  |         setSupportActionBar(toolbar); | ||||||
|  |         Intent i = getIntent(); | ||||||
|  |         channelUrl = i.getStringExtra(CHANNEL_URL); | ||||||
|  |         serviceId = i.getIntExtra(SERVICE_ID, -1); | ||||||
|  |  | ||||||
|  |         infoListAdapter = new InfoListAdapter(this, rootView); | ||||||
|  |         RecyclerView recyclerView = (RecyclerView) findViewById(R.id.channel_streams_view); | ||||||
|  |         final LinearLayoutManager layoutManager = new LinearLayoutManager(this); | ||||||
|  |         recyclerView.setLayoutManager(layoutManager); | ||||||
|  |         recyclerView.setAdapter(infoListAdapter); | ||||||
|  |         infoListAdapter.setOnItemSelectedListener(new InfoListAdapter.OnItemSelectedListener() { | ||||||
|  |             @Override | ||||||
|  |             public void selected(String url) { | ||||||
|  |                 Intent detailIntent = new Intent(ChannelActivity.this, VideoItemDetailActivity.class); | ||||||
|  |                 detailIntent.putExtra(VideoItemDetailFragment.VIDEO_URL, url); | ||||||
|  |                 detailIntent.putExtra( | ||||||
|  |                         VideoItemDetailFragment.STREAMING_SERVICE, serviceId); | ||||||
|  |                 startActivity(detailIntent); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         // detect if list has ben scrolled to the bottom | ||||||
|  |         recyclerView.setOnScrollListener(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 = layoutManager.getChildCount(); | ||||||
|  |                     totalItemCount = layoutManager.getItemCount(); | ||||||
|  |                     pastVisiblesItems = layoutManager.findFirstVisibleItemPosition(); | ||||||
|  |  | ||||||
|  |                     if ( (visibleItemCount + pastVisiblesItems) >= totalItemCount | ||||||
|  |                             && !isLoading | ||||||
|  |                             && hasNextPage) | ||||||
|  |                     { | ||||||
|  |                         pageNumber++; | ||||||
|  |                         requestData(true); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         requestData(false); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     private void updateUi(final ChannelInfo info) { | ||||||
|  |         CollapsingToolbarLayout ctl = (CollapsingToolbarLayout) findViewById(R.id.channel_toolbar_layout); | ||||||
|  |         ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar); | ||||||
|  |         ImageView channelBanner = (ImageView) findViewById(R.id.channel_banner_image); | ||||||
|  |         FloatingActionButton feedButton = (FloatingActionButton) findViewById(R.id.channel_rss_fab); | ||||||
|  |         ImageView avatarView = (ImageView) findViewById(R.id.channel_avatar_view); | ||||||
|  |         ImageView haloView = (ImageView) findViewById(R.id.channel_avatar_halo); | ||||||
|  |  | ||||||
|  |         progressBar.setVisibility(View.GONE); | ||||||
|  |  | ||||||
|  |         if(info.channel_name != null && !info.channel_name.isEmpty()) { | ||||||
|  |             ctl.setTitle(info.channel_name); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if(info.banner_url != null && !info.banner_url.isEmpty()) { | ||||||
|  |             imageLoader.displayImage(info.banner_url, channelBanner, | ||||||
|  |                     new ImageErrorLoadingListener(this, rootView ,info.service_id)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if(info.avatar_url != null && !info.avatar_url.isEmpty()) { | ||||||
|  |             avatarView.setVisibility(View.VISIBLE); | ||||||
|  |             haloView.setVisibility(View.VISIBLE); | ||||||
|  |             imageLoader.displayImage(info.avatar_url, avatarView, | ||||||
|  |                     new ImageErrorLoadingListener(this, rootView ,info.service_id)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if(info.feed_url != null && !info.feed_url.isEmpty()) { | ||||||
|  |             feedButton.setOnClickListener(new View.OnClickListener() { | ||||||
|  |                 @Override | ||||||
|  |                 public void onClick(View view) { | ||||||
|  |                     Log.d(TAG, info.feed_url); | ||||||
|  |                     Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(info.feed_url)); | ||||||
|  |                     startActivity(i); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |         } else { | ||||||
|  |             feedButton.setVisibility(View.GONE); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void addVideos(final ChannelInfo info) { | ||||||
|  |         infoListAdapter.addStreamItemList(info.related_streams); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void postNewErrorToast(Handler h, final int stringResource) { | ||||||
|  |         h.post(new Runnable() { | ||||||
|  |             @Override | ||||||
|  |             public void run() { | ||||||
|  |                 Toast.makeText(ChannelActivity.this, | ||||||
|  |                         stringResource, Toast.LENGTH_LONG).show(); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void requestData(final boolean onlyVideos) { | ||||||
|  |         // start processing | ||||||
|  |         isLoading = true; | ||||||
|  |         Thread channelExtractorThread = new Thread(new Runnable() { | ||||||
|  |             Handler h = new Handler(); | ||||||
|  |  | ||||||
|  |             @Override | ||||||
|  |             public void run() { | ||||||
|  |                 StreamingService service = null; | ||||||
|  |                 try { | ||||||
|  |                     service = ServiceList.getService(serviceId); | ||||||
|  |                     ChannelExtractor extractor = service.getChannelExtractorInstance( | ||||||
|  |                             channelUrl, pageNumber, new Downloader()); | ||||||
|  |  | ||||||
|  |                     final ChannelInfo info = ChannelInfo.getInfo(extractor, new Downloader()); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |                     h.post(new Runnable() { | ||||||
|  |                         @Override | ||||||
|  |                         public void run() { | ||||||
|  |                             isLoading = false; | ||||||
|  |                             if(!onlyVideos) { | ||||||
|  |                                 updateUi(info); | ||||||
|  |                             } | ||||||
|  |                             hasNextPage = info.hasNextPage; | ||||||
|  |                             addVideos(info); | ||||||
|  |                         } | ||||||
|  |                     }); | ||||||
|  |  | ||||||
|  |                     // look for non critical errors during extraction | ||||||
|  |                     if(info != null && | ||||||
|  |                             !info.errors.isEmpty()) { | ||||||
|  |                         Log.e(TAG, "OCCURRED ERRORS DURING EXTRACTION:"); | ||||||
|  |                         for (Throwable e : info.errors) { | ||||||
|  |                             e.printStackTrace(); | ||||||
|  |                             Log.e(TAG, "------"); | ||||||
|  |                         } | ||||||
|  |  | ||||||
|  |                         View rootView = findViewById(android.R.id.content); | ||||||
|  |                         ErrorActivity.reportError(h, ChannelActivity.this, | ||||||
|  |                                 info.errors, null, rootView, | ||||||
|  |                                 ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, | ||||||
|  |                                         service.getServiceInfo().name, channelUrl, 0 /* no message for the user */)); | ||||||
|  |                     } | ||||||
|  |                 } catch(IOException ioe) { | ||||||
|  |                     postNewErrorToast(h, R.string.network_error); | ||||||
|  |                     ioe.printStackTrace(); | ||||||
|  |                 } catch(ParsingException pe) { | ||||||
|  |                     ErrorActivity.reportError(h, ChannelActivity.this, pe, VideoItemDetailFragment.class, null, | ||||||
|  |                             ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, | ||||||
|  |                                     service.getServiceInfo().name, channelUrl, R.string.parsing_error)); | ||||||
|  |                     h.post(new Runnable() { | ||||||
|  |                         @Override | ||||||
|  |                         public void run() { | ||||||
|  |                             ChannelActivity.this.finish(); | ||||||
|  |                         } | ||||||
|  |                     }); | ||||||
|  |                     pe.printStackTrace(); | ||||||
|  |                 } catch(ExtractionException ex) { | ||||||
|  |                     ErrorActivity.reportError(h, ChannelActivity.this, ex, VideoItemDetailFragment.class, null, | ||||||
|  |                             ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, | ||||||
|  |                                     service.getServiceInfo().name, channelUrl, R.string.parsing_error)); | ||||||
|  |                     h.post(new Runnable() { | ||||||
|  |                         @Override | ||||||
|  |                         public void run() { | ||||||
|  |                             ChannelActivity.this.finish(); | ||||||
|  |                         } | ||||||
|  |                     }); | ||||||
|  |                     ex.printStackTrace(); | ||||||
|  |                 } catch(Exception e) { | ||||||
|  |                     ErrorActivity.reportError(h, ChannelActivity.this, e, VideoItemDetailFragment.class, null, | ||||||
|  |                             ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, | ||||||
|  |                                     service.getServiceInfo().name, channelUrl, R.string.general_error)); | ||||||
|  |                     h.post(new Runnable() { | ||||||
|  |                         @Override | ||||||
|  |                         public void run() { | ||||||
|  |                             ChannelActivity.this.finish(); | ||||||
|  |                         } | ||||||
|  |                     }); | ||||||
|  |                     e.printStackTrace(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         channelExtractorThread.start(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -5,10 +5,12 @@ import java.io.IOException; | |||||||
| import java.io.InputStreamReader; | import java.io.InputStreamReader; | ||||||
| import java.net.URL; | import java.net.URL; | ||||||
| import java.net.UnknownHostException; | import java.net.UnknownHostException; | ||||||
|  | import java.util.HashMap; | ||||||
|  | import java.util.Iterator; | ||||||
|  | import java.util.Map; | ||||||
|  |  | ||||||
| import javax.net.ssl.HttpsURLConnection; | import javax.net.ssl.HttpsURLConnection; | ||||||
|  |  | ||||||
| import info.guardianproject.netcipher.NetCipher; |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Created by Christian Schabesberger on 28.01.16. |  * Created by Christian Schabesberger on 28.01.16. | ||||||
| @@ -40,10 +42,26 @@ public class Downloader implements org.schabi.newpipe.extractor.Downloader { | |||||||
|      * @param language the language (usually a 2-character code) to set as the preferred language |      * @param language the language (usually a 2-character code) to set as the preferred language | ||||||
|      * @return the contents of the specified text file*/ |      * @return the contents of the specified text file*/ | ||||||
|     public String download(String siteUrl, String language) throws IOException { |     public String download(String siteUrl, String language) throws IOException { | ||||||
|  |         Map<String, String> requestProperties = new HashMap<>(); | ||||||
|  |         requestProperties.put("Accept-Language", language); | ||||||
|  |         return download(siteUrl, requestProperties); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /**Download the text file at the supplied URL as in download(String), | ||||||
|  |      * but set the HTTP header field "Accept-Language" to the supplied string. | ||||||
|  |      * @param siteUrl the URL of the text file to return the contents of | ||||||
|  |      * @param customProperties set request header properties | ||||||
|  |      * @return the contents of the specified text file | ||||||
|  |      * @throws IOException*/ | ||||||
|  |     public String download(String siteUrl, Map<String, String> customProperties) throws IOException { | ||||||
|         URL url = new URL(siteUrl); |         URL url = new URL(siteUrl); | ||||||
|         HttpsURLConnection con = (HttpsURLConnection) url.openConnection(); |         HttpsURLConnection con = (HttpsURLConnection) url.openConnection(); | ||||||
|         //HttpsURLConnection con = NetCipher.getHttpsURLConnection(url); |         Iterator it = customProperties.entrySet().iterator(); | ||||||
|         con.setRequestProperty("Accept-Language", language); |         while(it.hasNext()) { | ||||||
|  |             Map.Entry pair = (Map.Entry)it.next(); | ||||||
|  |             con.setRequestProperty((String)pair.getKey(), (String)pair.getValue()); | ||||||
|  |         } | ||||||
|         return dl(con); |         return dl(con); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -79,16 +79,19 @@ public class ErrorActivity extends AppCompatActivity { | |||||||
|     public static final int GET_SUGGESTIONS = 2; |     public static final int GET_SUGGESTIONS = 2; | ||||||
|     public static final int SOMETHING_ELSE = 3; |     public static final int SOMETHING_ELSE = 3; | ||||||
|     public static final int USER_REPORT = 4; |     public static final int USER_REPORT = 4; | ||||||
|  |     public static final int LOAD_IMAGE = 5; | ||||||
|     public static final String SEARCHED_STRING = "searched"; |     public static final String SEARCHED_STRING = "searched"; | ||||||
|     public static final String REQUESTED_STREAM_STRING = "requested stream"; |     public static final String REQUESTED_STREAM_STRING = "requested stream"; | ||||||
|     public static final String GET_SUGGESTIONS_STRING = "get suggestions"; |     public static final String GET_SUGGESTIONS_STRING = "get suggestions"; | ||||||
|     public static final String SOMETHING_ELSE_STRING = "something"; |     public static final String SOMETHING_ELSE_STRING = "something"; | ||||||
|     public static final String USER_REPORT_STRING = "user report"; |     public static final String USER_REPORT_STRING = "user report"; | ||||||
|  |     public static final String LOAD_IMAGE_STRING = "load image"; | ||||||
|  |  | ||||||
|  |  | ||||||
|     public static final String ERROR_EMAIL_ADDRESS = "crashreport@newpipe.schabi.org"; |     public static final String ERROR_EMAIL_ADDRESS = "crashreport@newpipe.schabi.org"; | ||||||
|     public static final String ERROR_EMAIL_SUBJECT = "Exception in NewPipe " + BuildConfig.VERSION_NAME; |     public static final String ERROR_EMAIL_SUBJECT = "Exception in NewPipe " + BuildConfig.VERSION_NAME; | ||||||
|  |  | ||||||
|     private List<Exception> errorList; |     private List<Throwable> errorList; | ||||||
|     private ErrorInfo errorInfo; |     private ErrorInfo errorInfo; | ||||||
|     private Class returnActivity; |     private Class returnActivity; | ||||||
|     private String currentTimeStamp; |     private String currentTimeStamp; | ||||||
| @@ -102,7 +105,7 @@ public class ErrorActivity extends AppCompatActivity { | |||||||
|     private TextView infoView; |     private TextView infoView; | ||||||
|     private TextView errorMessageView; |     private TextView errorMessageView; | ||||||
|  |  | ||||||
|     public static void reportError(final Context context, final List<Exception> el, |     public static void reportError(final Context context, final List<Throwable> el, | ||||||
|                                    final Class returnAcitivty, View rootView, final ErrorInfo errorInfo) { |                                    final Class returnAcitivty, View rootView, final ErrorInfo errorInfo) { | ||||||
|  |  | ||||||
|         if (rootView != null) { |         if (rootView != null) { | ||||||
| @@ -129,9 +132,9 @@ public class ErrorActivity extends AppCompatActivity { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static void reportError(final Context context, final Exception e, |     public static void reportError(final Context context, final Throwable e, | ||||||
|                                    final Class returnAcitivty, View rootView, final ErrorInfo errorInfo) { |                                    final Class returnAcitivty, View rootView, final ErrorInfo errorInfo) { | ||||||
|         List<Exception> el = null; |         List<Throwable> el = null; | ||||||
|         if(e != null) { |         if(e != null) { | ||||||
|             el = new Vector<>(); |             el = new Vector<>(); | ||||||
|             el.add(e); |             el.add(e); | ||||||
| @@ -140,10 +143,10 @@ public class ErrorActivity extends AppCompatActivity { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     // async call |     // async call | ||||||
|     public static void reportError(Handler handler, final Context context, final Exception e, |     public static void reportError(Handler handler, final Context context, final Throwable e, | ||||||
|                                    final Class returnAcitivty, final View rootView, final ErrorInfo errorInfo) { |                                    final Class returnAcitivty, final View rootView, final ErrorInfo errorInfo) { | ||||||
|  |  | ||||||
|         List<Exception> el = null; |         List<Throwable> el = null; | ||||||
|         if(e != null) { |         if(e != null) { | ||||||
|             el = new Vector<>(); |             el = new Vector<>(); | ||||||
|             el.add(e); |             el.add(e); | ||||||
| @@ -152,7 +155,7 @@ public class ErrorActivity extends AppCompatActivity { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     // async call |     // async call | ||||||
|     public static void reportError(Handler handler, final Context context, final List<Exception> el, |     public static void reportError(Handler handler, final Context context, final List<Throwable> el, | ||||||
|                                    final Class returnAcitivty, final View rootView, final ErrorInfo errorInfo) { |                                    final Class returnAcitivty, final View rootView, final ErrorInfo errorInfo) { | ||||||
|         handler.post(new Runnable() { |         handler.post(new Runnable() { | ||||||
|             @Override |             @Override | ||||||
| @@ -171,7 +174,7 @@ public class ErrorActivity extends AppCompatActivity { | |||||||
|             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 (Exception e) { |         } catch (Throwable e) { | ||||||
|             Log.e(TAG, "Error turing exception handling"); |             Log.e(TAG, "Error turing exception handling"); | ||||||
|             e.printStackTrace(); |             e.printStackTrace(); | ||||||
|         } |         } | ||||||
| @@ -252,10 +255,10 @@ public class ErrorActivity extends AppCompatActivity { | |||||||
|         return sw.getBuffer().toString(); |         return sw.getBuffer().toString(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private String formErrorText(List<Exception> el) { |     private String formErrorText(List<Throwable> el) { | ||||||
|         String text = ""; |         String text = ""; | ||||||
|         if(el != null) { |         if(el != null) { | ||||||
|             for (Exception e : el) { |             for (Throwable e : el) { | ||||||
|                 text += "-------------------------------------\n" |                 text += "-------------------------------------\n" | ||||||
|                         + getStackTrace(e); |                         + getStackTrace(e); | ||||||
|             } |             } | ||||||
| @@ -273,7 +276,7 @@ public class ErrorActivity extends AppCompatActivity { | |||||||
|                     returnActivity.isAssignableFrom(Activity.class)) { |                     returnActivity.isAssignableFrom(Activity.class)) { | ||||||
|                 intent = new Intent(this, returnActivity); |                 intent = new Intent(this, returnActivity); | ||||||
|             } else { |             } else { | ||||||
|                 intent = new Intent(this, VideoItemListActivity.class); |                 intent = new Intent(this, MainActivity.class); | ||||||
|             } |             } | ||||||
|             intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); |             intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); | ||||||
|             NavUtils.navigateUpTo(this, intent); |             NavUtils.navigateUpTo(this, intent); | ||||||
| @@ -313,7 +316,7 @@ public class ErrorActivity extends AppCompatActivity { | |||||||
|  |  | ||||||
|             JSONArray exceptionArray = new JSONArray(); |             JSONArray exceptionArray = new JSONArray(); | ||||||
|             if(errorList != null) { |             if(errorList != null) { | ||||||
|                 for (Exception e : errorList) { |                 for (Throwable e : errorList) { | ||||||
|                     exceptionArray.put(getStackTrace(e)); |                     exceptionArray.put(getStackTrace(e)); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| @@ -322,7 +325,7 @@ public class ErrorActivity extends AppCompatActivity { | |||||||
|             errorObject.put("user_comment", userCommentBox.getText().toString()); |             errorObject.put("user_comment", userCommentBox.getText().toString()); | ||||||
|  |  | ||||||
|             return errorObject.toString(3); |             return errorObject.toString(3); | ||||||
|         } catch (Exception e) { |         } catch (Throwable e) { | ||||||
|             Log.e(TAG, "Error while erroring: Could not build json"); |             Log.e(TAG, "Error while erroring: Could not build json"); | ||||||
|             e.printStackTrace(); |             e.printStackTrace(); | ||||||
|         } |         } | ||||||
| @@ -342,6 +345,8 @@ public class ErrorActivity extends AppCompatActivity { | |||||||
|                 return SOMETHING_ELSE_STRING; |                 return SOMETHING_ELSE_STRING; | ||||||
|             case USER_REPORT: |             case USER_REPORT: | ||||||
|                 return USER_REPORT_STRING; |                 return USER_REPORT_STRING; | ||||||
|  |             case LOAD_IMAGE: | ||||||
|  |                 return LOAD_IMAGE_STRING; | ||||||
|             default: |             default: | ||||||
|                 return "Your description is in another castle."; |                 return "Your description is in another castle."; | ||||||
|         } |         } | ||||||
| @@ -390,7 +395,7 @@ public class ErrorActivity extends AppCompatActivity { | |||||||
|  |  | ||||||
|                 ipRange = Parser.matchGroup1("([0-9]*\\.[0-9]*\\.)[0-9]*\\.[0-9]*", ip) |                 ipRange = Parser.matchGroup1("([0-9]*\\.[0-9]*\\.)[0-9]*\\.[0-9]*", ip) | ||||||
|                         + "0.0"; |                         + "0.0"; | ||||||
|             } catch(Exception e) { |             } catch(Throwable e) { | ||||||
|                 Log.d(TAG, "Error while error: could not get iprange"); |                 Log.d(TAG, "Error while error: could not get iprange"); | ||||||
|                 e.printStackTrace(); |                 e.printStackTrace(); | ||||||
|             } finally { |             } finally { | ||||||
|   | |||||||
| @@ -0,0 +1,65 @@ | |||||||
|  | package org.schabi.newpipe; | ||||||
|  |  | ||||||
|  | import android.app.Activity; | ||||||
|  | import android.graphics.Bitmap; | ||||||
|  | import android.view.View; | ||||||
|  |  | ||||||
|  | import com.nostra13.universalimageloader.core.assist.FailReason; | ||||||
|  | import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; | ||||||
|  |  | ||||||
|  | import org.schabi.newpipe.ErrorActivity; | ||||||
|  | import org.schabi.newpipe.R; | ||||||
|  | import org.schabi.newpipe.extractor.ServiceList; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Created by Christian Schabesberger on 01.08.16. | ||||||
|  |  * | ||||||
|  |  * Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org> | ||||||
|  |  * StreamInfoItemViewCreator.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 ImageErrorLoadingListener implements ImageLoadingListener { | ||||||
|  |  | ||||||
|  |     private int serviceId = -1; | ||||||
|  |     private Activity activity = null; | ||||||
|  |     private View rootView = null; | ||||||
|  |  | ||||||
|  |     public ImageErrorLoadingListener(Activity activity, View rootView, int serviceId) { | ||||||
|  |         this.activity = activity; | ||||||
|  |         this.serviceId= serviceId; | ||||||
|  |         this.rootView = rootView; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onLoadingStarted(String imageUri, View view) {} | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onLoadingFailed(String imageUri, View view, FailReason failReason) { | ||||||
|  |         ErrorActivity.reportError(activity, | ||||||
|  |                 failReason.getCause(), null, rootView, | ||||||
|  |                 ErrorActivity.ErrorInfo.make(ErrorActivity.LOAD_IMAGE, | ||||||
|  |                         ServiceList.getNameOfService(serviceId), imageUri, | ||||||
|  |                         R.string.could_not_load_image)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onLoadingCancelled(String imageUri, View view) {} | ||||||
|  | } | ||||||
							
								
								
									
										69
									
								
								app/src/main/java/org/schabi/newpipe/MainActivity.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								app/src/main/java/org/schabi/newpipe/MainActivity.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | |||||||
|  | package org.schabi.newpipe; | ||||||
|  |  | ||||||
|  | import android.content.Intent; | ||||||
|  | import android.media.AudioManager; | ||||||
|  | import android.support.v4.app.Fragment; | ||||||
|  | import android.support.v4.app.NavUtils; | ||||||
|  | import android.support.v7.app.AppCompatActivity; | ||||||
|  | import android.os.Bundle; | ||||||
|  | import android.view.Menu; | ||||||
|  | import android.view.MenuInflater; | ||||||
|  | import android.view.MenuItem; | ||||||
|  |  | ||||||
|  | import org.schabi.newpipe.settings.SettingsActivity; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Created by Christian Schabesberger on 02.08.16. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | public class MainActivity extends AppCompatActivity { | ||||||
|  |  | ||||||
|  |     private Fragment mainFragment = null; | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected void onCreate(Bundle savedInstanceState) { | ||||||
|  |         super.onCreate(savedInstanceState); | ||||||
|  |         setContentView(R.layout.activity_main); | ||||||
|  |  | ||||||
|  |         setVolumeControlStream(AudioManager.STREAM_MUSIC); | ||||||
|  |         mainFragment = getSupportFragmentManager() | ||||||
|  |                 .findFragmentById(R.id.search_fragment); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public boolean onCreateOptionsMenu(Menu menu) { | ||||||
|  |         super.onCreateOptionsMenu(menu); | ||||||
|  |         MenuInflater inflater = getMenuInflater(); | ||||||
|  |  | ||||||
|  |         inflater.inflate(R.menu.main_menu, menu); | ||||||
|  |  | ||||||
|  |         mainFragment.onCreateOptionsMenu(menu, inflater); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean onOptionsItemSelected(MenuItem item) { | ||||||
|  |         int id = item.getItemId(); | ||||||
|  |  | ||||||
|  |         switch(id) { | ||||||
|  |             case android.R.id.home: { | ||||||
|  |                 Intent intent = new Intent(this, MainActivity.class); | ||||||
|  |                 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); | ||||||
|  |                 NavUtils.navigateUpTo(this, intent); | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |             case R.id.action_settings: { | ||||||
|  |                 Intent intent = new Intent(this, SettingsActivity.class); | ||||||
|  |                 startActivity(intent); | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |             case R.id.action_show_downloads: { | ||||||
|  |                 Intent intent = new Intent(this, org.schabi.newpipe.download.MainActivity.class); | ||||||
|  |                 startActivity(intent); | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |             default: | ||||||
|  |                 return mainFragment.onOptionsItemSelected(item) || | ||||||
|  |                         super.onOptionsItemSelected(item); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,411 +0,0 @@ | |||||||
| package org.schabi.newpipe; |  | ||||||
|  |  | ||||||
| import android.content.Context; |  | ||||||
| import android.content.Intent; |  | ||||||
| import android.content.SharedPreferences; |  | ||||||
| import android.media.AudioManager; |  | ||||||
| import android.os.Bundle; |  | ||||||
| import android.os.Handler; |  | ||||||
| import android.preference.PreferenceManager; |  | ||||||
| import android.support.v4.app.NavUtils; |  | ||||||
| import android.support.v7.app.AppCompatActivity; |  | ||||||
| import android.support.v7.widget.SearchView; |  | ||||||
| import android.util.Log; |  | ||||||
| import android.view.Menu; |  | ||||||
| import android.view.MenuInflater; |  | ||||||
| import android.view.MenuItem; |  | ||||||
| import android.view.View; |  | ||||||
| import android.view.inputmethod.InputMethodManager; |  | ||||||
| import android.widget.Toast; |  | ||||||
|  |  | ||||||
| import org.schabi.newpipe.extractor.ExtractionException; |  | ||||||
| import org.schabi.newpipe.extractor.SearchEngine; |  | ||||||
| import org.schabi.newpipe.extractor.ServiceList; |  | ||||||
| import org.schabi.newpipe.extractor.StreamingService; |  | ||||||
|  |  | ||||||
| import java.io.IOException; |  | ||||||
| import java.util.List; |  | ||||||
| import java.util.Vector; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org> |  | ||||||
|  * VideoItemListActivity.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 VideoItemListActivity extends AppCompatActivity |  | ||||||
|         implements VideoItemListFragment.Callbacks { |  | ||||||
|  |  | ||||||
|     private static final String TAG = VideoItemListFragment.class.toString(); |  | ||||||
|  |  | ||||||
|     // arguments to give to this activity |  | ||||||
|     public static final String VIDEO_INFO_ITEMS = "video_info_items"; |  | ||||||
|  |  | ||||||
|     // savedInstanceBundle arguments |  | ||||||
|     private static final String QUERY = "query"; |  | ||||||
|     private static final String STREAMING_SERVICE = "streaming_service"; |  | ||||||
|  |  | ||||||
|     // activity modes |  | ||||||
|     private static final int SEARCH_MODE = 0; |  | ||||||
|     private static final int PRESENT_VIDEOS_MODE = 1; |  | ||||||
|  |  | ||||||
|     private int mode; |  | ||||||
|     private int currentStreamingServiceId = -1; |  | ||||||
|     private String searchQuery = ""; |  | ||||||
|  |  | ||||||
|     private VideoItemListFragment listFragment; |  | ||||||
|     private VideoItemDetailFragment videoFragment; |  | ||||||
|     private Menu menu; |  | ||||||
|  |  | ||||||
|     private SuggestionListAdapter suggestionListAdapter; |  | ||||||
|     private SuggestionSearchRunnable suggestionSearchRunnable; |  | ||||||
|     private Thread searchThread; |  | ||||||
|  |  | ||||||
|     private class SearchVideoQueryListener implements SearchView.OnQueryTextListener { |  | ||||||
|  |  | ||||||
|         @Override |  | ||||||
|         public boolean onQueryTextSubmit(String query) { |  | ||||||
|             try { |  | ||||||
|                 searchQuery = query; |  | ||||||
|                 listFragment.search(query); |  | ||||||
|  |  | ||||||
|                 // hide virtual keyboard |  | ||||||
|                 InputMethodManager inputManager = |  | ||||||
|                         (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); |  | ||||||
|                 try { |  | ||||||
|                     //noinspection ConstantConditions |  | ||||||
|                     inputManager.hideSoftInputFromWindow( |  | ||||||
|                             getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); |  | ||||||
|                 } catch(NullPointerException e) { |  | ||||||
|                     Log.e(TAG, "Could not get widget with focus"); |  | ||||||
|                     Toast.makeText(VideoItemListActivity.this, "Could not get widget with focus", |  | ||||||
|                             Toast.LENGTH_SHORT).show(); |  | ||||||
|                     e.printStackTrace(); |  | ||||||
|                 } |  | ||||||
|                 // 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 |  | ||||||
|                 getCurrentFocus().clearFocus(); |  | ||||||
|             } catch(Exception e) { |  | ||||||
|                 e.printStackTrace(); |  | ||||||
|             } |  | ||||||
|             View bg = findViewById(R.id.mainBG); |  | ||||||
|             bg.setVisibility(View.GONE); |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         @Override |  | ||||||
|         public boolean onQueryTextChange(String newText) { |  | ||||||
|             if(!newText.isEmpty()) { |  | ||||||
|                 searchSuggestions(newText); |  | ||||||
|             } |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|     } |  | ||||||
|     private class SearchSuggestionListener implements SearchView.OnSuggestionListener{ |  | ||||||
|  |  | ||||||
|         private SearchView searchView; |  | ||||||
|  |  | ||||||
|         private SearchSuggestionListener(SearchView searchView) { |  | ||||||
|             this.searchView = searchView; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         @Override |  | ||||||
|         public boolean onSuggestionSelect(int position) { |  | ||||||
|             String suggestion = suggestionListAdapter.getSuggestion(position); |  | ||||||
|             searchView.setQuery(suggestion,true); |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         @Override |  | ||||||
|         public boolean onSuggestionClick(int position) { |  | ||||||
|             String suggestion = suggestionListAdapter.getSuggestion(position); |  | ||||||
|             searchView.setQuery(suggestion,true); |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private class SuggestionResultRunnable implements Runnable{ |  | ||||||
|  |  | ||||||
|         private List<String> suggestions; |  | ||||||
|  |  | ||||||
|         private SuggestionResultRunnable(List<String> suggestions) { |  | ||||||
|             this.suggestions = suggestions; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         @Override |  | ||||||
|         public void run() { |  | ||||||
|             suggestionListAdapter.updateAdapter(suggestions); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private class SuggestionSearchRunnable implements Runnable{ |  | ||||||
|         private final int serviceId; |  | ||||||
|         private final String query; |  | ||||||
|         final Handler h = new Handler(); |  | ||||||
|         private Context context; |  | ||||||
|         private SuggestionSearchRunnable(int serviceId, String query) { |  | ||||||
|             this.serviceId = serviceId; |  | ||||||
|             this.query = query; |  | ||||||
|             context = VideoItemListActivity.this; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         @Override |  | ||||||
|         public void run() { |  | ||||||
|             try { |  | ||||||
|                 SearchEngine engine = |  | ||||||
|                         ServiceList.getService(serviceId).getSearchEngineInstance(new Downloader()); |  | ||||||
|                 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); |  | ||||||
|                 String searchLanguageKey = context.getString(R.string.search_language_key); |  | ||||||
|                 String searchLanguage = sp.getString(searchLanguageKey, |  | ||||||
|                         getString(R.string.default_language_value)); |  | ||||||
|                 List<String> suggestions = engine.suggestionList(query,searchLanguage,new Downloader()); |  | ||||||
|                 h.post(new SuggestionResultRunnable(suggestions)); |  | ||||||
|             } catch (ExtractionException e) { |  | ||||||
|                 ErrorActivity.reportError(h, VideoItemListActivity.this, e, null, findViewById(R.id.videoitem_list), |  | ||||||
|                         ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED, |  | ||||||
|                                 ServiceList.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, VideoItemListActivity.this, e, null, findViewById(R.id.videoitem_list), |  | ||||||
|                         ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED, |  | ||||||
|                                 ServiceList.getNameOfService(serviceId), query, R.string.general_error)); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     /** |  | ||||||
|      * Whether or not the activity is in two-pane mode, i.e. running on a tablet |  | ||||||
|      * device. |  | ||||||
|      */ |  | ||||||
|     private boolean mTwoPane; |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     protected void onCreate(Bundle savedInstanceState) { |  | ||||||
|         setTheme(R.style.AppTheme); |  | ||||||
|         super.onCreate(savedInstanceState); |  | ||||||
|         setContentView(R.layout.activity_videoitem_list); |  | ||||||
|         StreamingService streamingService = null; |  | ||||||
|  |  | ||||||
|         View bg = findViewById(R.id.mainBG); |  | ||||||
|         bg.setVisibility(View.VISIBLE); |  | ||||||
|  |  | ||||||
|         try { |  | ||||||
|             //------ todo: remove this line when multiservice support is implemented ------ |  | ||||||
|             currentStreamingServiceId = ServiceList.getIdOfService("Youtube"); |  | ||||||
|             streamingService = ServiceList.getService(currentStreamingServiceId); |  | ||||||
|         } catch (Exception e) { |  | ||||||
|             e.printStackTrace(); |  | ||||||
|             ErrorActivity.reportError(VideoItemListActivity.this, e, null, findViewById(R.id.videoitem_list), |  | ||||||
|                     ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED, |  | ||||||
|                             ServiceList.getNameOfService(currentStreamingServiceId), "", R.string.general_error)); |  | ||||||
|         } |  | ||||||
|         setVolumeControlStream(AudioManager.STREAM_MUSIC); |  | ||||||
|         listFragment = (VideoItemListFragment) getSupportFragmentManager() |  | ||||||
|                 .findFragmentById(R.id.videoitem_list); |  | ||||||
|         listFragment.setStreamingService(streamingService); |  | ||||||
|  |  | ||||||
|         if(savedInstanceState != null |  | ||||||
|                 && mode != PRESENT_VIDEOS_MODE) { |  | ||||||
|             searchQuery = savedInstanceState.getString(QUERY); |  | ||||||
|             currentStreamingServiceId = savedInstanceState.getInt(STREAMING_SERVICE); |  | ||||||
|             if(!searchQuery.isEmpty()) { |  | ||||||
|                 listFragment.search(searchQuery); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (findViewById(R.id.videoitem_detail_container) != null) { |  | ||||||
|             // The detail container view will be present only in the |  | ||||||
|             // large-screen layouts (res/values-large and |  | ||||||
|             // res/values-sw600dp). If this view is present, then the |  | ||||||
|             // activity should be in two-pane mode. |  | ||||||
|             mTwoPane = true; |  | ||||||
|  |  | ||||||
|             // In two-pane mode, list items should be given the |  | ||||||
|             // 'activated' state when touched. |  | ||||||
|  |  | ||||||
|             ((VideoItemListFragment) getSupportFragmentManager() |  | ||||||
|                     .findFragmentById(R.id.videoitem_list)) |  | ||||||
|                     .setActivateOnItemClick(true); |  | ||||||
|  |  | ||||||
|             SearchView searchView = (SearchView)findViewById(R.id.searchViewTablet); |  | ||||||
|             if(mode != PRESENT_VIDEOS_MODE) { |  | ||||||
|                 // Somehow the seticonifiedbydefault property set by the layout xml is not working on |  | ||||||
|                 // the support version on SearchView, so it needs to be set programmatically. |  | ||||||
|                 searchView.setIconifiedByDefault(false); |  | ||||||
|                 searchView.setIconified(false); |  | ||||||
|                 if(!searchQuery.isEmpty()) { |  | ||||||
|                     searchView.setQuery(searchQuery,false); |  | ||||||
|                 } |  | ||||||
|                 searchView.setOnQueryTextListener(new SearchVideoQueryListener()); |  | ||||||
|                 suggestionListAdapter = new SuggestionListAdapter(this); |  | ||||||
|                 searchView.setSuggestionsAdapter(suggestionListAdapter); |  | ||||||
|                 searchView.setOnSuggestionListener(new SearchSuggestionListener(searchView)); |  | ||||||
|             } else { |  | ||||||
|                 searchView.setVisibility(View.GONE); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         PreferenceManager.setDefaultValues(this, R.xml.settings, false); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void onResume() { |  | ||||||
|         super.onResume(); |  | ||||||
|         App.checkStartTor(this); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Callback method from {@link VideoItemListFragment.Callbacks} |  | ||||||
|      * indicating that the item with the given ID was selected. |  | ||||||
|      */ |  | ||||||
|     @Override |  | ||||||
|     public void onItemSelected(String id) { |  | ||||||
|         VideoListAdapter listAdapter = (VideoListAdapter) ((VideoItemListFragment) |  | ||||||
|                 getSupportFragmentManager() |  | ||||||
|                         .findFragmentById(R.id.videoitem_list)) |  | ||||||
|                 .getListAdapter(); |  | ||||||
|         String webpageUrl = listAdapter.getVideoList().get((int) Long.parseLong(id)).webpage_url; |  | ||||||
|         if (mTwoPane) { |  | ||||||
|             // In two-pane mode, show the detail view in this activity by |  | ||||||
|             // adding or replacing the detail fragment using a |  | ||||||
|             // fragment transaction. |  | ||||||
|             Bundle arguments = new Bundle(); |  | ||||||
|             //arguments.putString(VideoItemDetailFragment.ARG_ITEM_ID, id); |  | ||||||
|             arguments.putString(VideoItemDetailFragment.VIDEO_URL, webpageUrl); |  | ||||||
|             arguments.putInt(VideoItemDetailFragment.STREAMING_SERVICE, currentStreamingServiceId); |  | ||||||
|             videoFragment = new VideoItemDetailFragment(); |  | ||||||
|             videoFragment.setArguments(arguments); |  | ||||||
|             videoFragment.setOnInvokeCreateOptionsMenuListener(new VideoItemDetailFragment.OnInvokeCreateOptionsMenuListener() { |  | ||||||
|                 @Override |  | ||||||
|                 public void createOptionsMenu() { |  | ||||||
|                     menu.clear(); |  | ||||||
|                     onCreateOptionsMenu(menu); |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
|             getSupportFragmentManager().beginTransaction() |  | ||||||
|                     .replace(R.id.videoitem_detail_container, videoFragment) |  | ||||||
|                     .commit(); |  | ||||||
|         } else { |  | ||||||
|             // In single-pane mode, simply start the detail activity |  | ||||||
|             // for the selected item ID. |  | ||||||
|             Intent detailIntent = new Intent(this, VideoItemDetailActivity.class); |  | ||||||
|             //detailIntent.putExtra(VideoItemDetailFragment.ARG_ITEM_ID, id); |  | ||||||
|             detailIntent.putExtra(VideoItemDetailFragment.VIDEO_URL, webpageUrl); |  | ||||||
|             detailIntent.putExtra(VideoItemDetailFragment.STREAMING_SERVICE, currentStreamingServiceId); |  | ||||||
|             startActivity(detailIntent); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     public boolean onCreateOptionsMenu(Menu menu) { |  | ||||||
|         super.onCreateOptionsMenu(menu); |  | ||||||
|         this.menu = menu; |  | ||||||
|         MenuInflater inflater = getMenuInflater(); |  | ||||||
|         if(mode != PRESENT_VIDEOS_MODE && |  | ||||||
|                 findViewById(R.id.videoitem_detail_container) == null) { |  | ||||||
|             inflater.inflate(R.menu.videoitem_list, menu); |  | ||||||
|             MenuItem searchItem = menu.findItem(R.id.action_search); |  | ||||||
|             SearchView searchView = (SearchView) searchItem.getActionView(); |  | ||||||
|             searchView.setFocusable(false); |  | ||||||
|             searchView.setOnQueryTextListener( |  | ||||||
|                     new SearchVideoQueryListener()); |  | ||||||
|             suggestionListAdapter = new SuggestionListAdapter(this); |  | ||||||
|             searchView.setSuggestionsAdapter(suggestionListAdapter); |  | ||||||
|             searchView.setOnSuggestionListener(new SearchSuggestionListener(searchView)); |  | ||||||
|             if(!searchQuery.isEmpty()) { |  | ||||||
|                 searchView.setQuery(searchQuery,false); |  | ||||||
|                 searchView.setIconifiedByDefault(false); |  | ||||||
|             } |  | ||||||
|         } else if (videoFragment != null){ |  | ||||||
|             videoFragment.onCreateOptionsMenu(menu, inflater); |  | ||||||
|         } else { |  | ||||||
|             inflater.inflate(R.menu.videoitem_two_pannel, menu); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public boolean onOptionsItemSelected(MenuItem item) { |  | ||||||
|         int id = item.getItemId(); |  | ||||||
|  |  | ||||||
|         switch(id) { |  | ||||||
|             case android.R.id.home: { |  | ||||||
|                 Intent intent = new Intent(this, VideoItemListActivity.class); |  | ||||||
|                 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); |  | ||||||
|                 NavUtils.navigateUpTo(this, intent); |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
|             case R.id.action_settings: { |  | ||||||
|                 Intent intent = new Intent(this, SettingsActivity.class); |  | ||||||
|                 startActivity(intent); |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
|             case R.id.action_report_error: { |  | ||||||
|                 ErrorActivity.reportError(VideoItemListActivity.this, new Vector<Exception>(), |  | ||||||
|                         null, null, |  | ||||||
|                         ErrorActivity.ErrorInfo.make(ErrorActivity.USER_REPORT, |  | ||||||
|                                 ServiceList.getNameOfService(currentStreamingServiceId), |  | ||||||
|                                 "user_report", R.string.user_report)); |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
|             case R.id.action_show_downloads: { |  | ||||||
|                 Intent intent = new Intent(this, org.schabi.newpipe.download.MainActivity.class); |  | ||||||
|                 startActivity(intent); |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
|             default: |  | ||||||
|                 return videoFragment.onOptionsItemSelected(item) || |  | ||||||
|                     super.onOptionsItemSelected(item); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void onSaveInstanceState(Bundle outState) { |  | ||||||
|         super.onSaveInstanceState(outState); |  | ||||||
|         /* |  | ||||||
|         if (mActivatedPosition != ListView.INVALID_POSITION) { |  | ||||||
|             // Serialize and persist the activated item position. |  | ||||||
|             outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition); |  | ||||||
|         } |  | ||||||
|         */ |  | ||||||
|         outState.putString(QUERY, searchQuery); |  | ||||||
|         outState.putInt(STREAMING_SERVICE, currentStreamingServiceId); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void searchSuggestions(String query) { |  | ||||||
|         suggestionSearchRunnable = |  | ||||||
|                 new SuggestionSearchRunnable(currentStreamingServiceId, query); |  | ||||||
|         searchThread = new Thread(suggestionSearchRunnable); |  | ||||||
|         searchThread.start(); |  | ||||||
|  |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void postNewErrorToast(Handler h, final int stringResource) { |  | ||||||
|         h.post(new Runnable() { |  | ||||||
|             @Override |  | ||||||
|             public void run() { |  | ||||||
|                 Toast.makeText(VideoItemListActivity.this, getString(stringResource), |  | ||||||
|                         Toast.LENGTH_SHORT).show(); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,370 +0,0 @@ | |||||||
| package org.schabi.newpipe; |  | ||||||
|  |  | ||||||
| import android.app.Activity; |  | ||||||
| import android.content.Context; |  | ||||||
| import android.content.SharedPreferences; |  | ||||||
| import android.os.Bundle; |  | ||||||
| import android.os.Handler; |  | ||||||
| import android.preference.PreferenceManager; |  | ||||||
| import android.support.v4.app.ListFragment; |  | ||||||
| import android.util.Log; |  | ||||||
| import android.view.LayoutInflater; |  | ||||||
| import android.view.View; |  | ||||||
| import android.widget.AbsListView; |  | ||||||
| import android.widget.ListView; |  | ||||||
| import android.widget.Toast; |  | ||||||
|  |  | ||||||
| import java.io.IOException; |  | ||||||
| import java.util.List; |  | ||||||
|  |  | ||||||
| import org.schabi.newpipe.extractor.ExtractionException; |  | ||||||
| import org.schabi.newpipe.extractor.SearchResult; |  | ||||||
| import org.schabi.newpipe.extractor.StreamPreviewInfo; |  | ||||||
| import org.schabi.newpipe.extractor.SearchEngine; |  | ||||||
| import org.schabi.newpipe.extractor.StreamingService; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org> |  | ||||||
|  * VideoItemListFragment.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 VideoItemListFragment extends ListFragment { |  | ||||||
|  |  | ||||||
|     private static final String TAG = VideoItemListFragment.class.toString(); |  | ||||||
|  |  | ||||||
|     private StreamingService streamingService; |  | ||||||
|     private VideoListAdapter videoListAdapter; |  | ||||||
|  |  | ||||||
|     // activity modes |  | ||||||
|     private static final int SEARCH_MODE = 0; |  | ||||||
|     private static final int PRESENT_VIDEOS_MODE = 1; |  | ||||||
|  |  | ||||||
|     private int mode; |  | ||||||
|     private String query = ""; |  | ||||||
|     private int lastPage; |  | ||||||
|  |  | ||||||
|     private Thread searchThread; |  | ||||||
|     private SearchRunnable searchRunnable; |  | ||||||
|     // used to track down if results posted by threads ar still valid |  | ||||||
|     private int currentRequestId = -1; |  | ||||||
|     private ListView list; |  | ||||||
|  |  | ||||||
|     private View footer; |  | ||||||
|  |  | ||||||
|     // used to suppress request for loading a new page while another page is already loading. |  | ||||||
|     private boolean loadingNextPage = true; |  | ||||||
|  |  | ||||||
|     private class ResultRunnable implements Runnable { |  | ||||||
|         private final SearchResult result; |  | ||||||
|         private final int requestId; |  | ||||||
|         public ResultRunnable(SearchResult result, int requestId) { |  | ||||||
|             this.result = result; |  | ||||||
|             this.requestId = requestId; |  | ||||||
|         } |  | ||||||
|         @Override |  | ||||||
|         public void run() { |  | ||||||
|             updateListOnResult(result, requestId); |  | ||||||
|             if (android.os.Build.VERSION.SDK_INT >= 19) { |  | ||||||
|                 getListView().removeFooterView(footer); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private class SearchRunnable implements Runnable { |  | ||||||
|         public static final String YOUTUBE = "Youtube"; |  | ||||||
|         private final SearchEngine engine; |  | ||||||
|         private final String query; |  | ||||||
|         private final int page; |  | ||||||
|         final Handler h = new Handler(); |  | ||||||
|         private volatile boolean runs = true; |  | ||||||
|         private final int requestId; |  | ||||||
|         public SearchRunnable(SearchEngine engine, String query, int page, int requestId) { |  | ||||||
|             this.engine = engine; |  | ||||||
|             this.query = query; |  | ||||||
|             this.page = page; |  | ||||||
|             this.requestId = requestId; |  | ||||||
|         } |  | ||||||
|         void terminate() { |  | ||||||
|             runs = false; |  | ||||||
|         } |  | ||||||
|         @Override |  | ||||||
|         public void run() { |  | ||||||
|             SearchResult result = null; |  | ||||||
|             try { |  | ||||||
|                 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); |  | ||||||
|                 String searchLanguageKey = getContext().getString(R.string.search_language_key); |  | ||||||
|                 String searchLanguage = sp.getString(searchLanguageKey, |  | ||||||
|                         getString(R.string.default_language_value)); |  | ||||||
|                 result = SearchResult |  | ||||||
|                         .getSearchResult(engine, query, page, searchLanguage, new Downloader()); |  | ||||||
|  |  | ||||||
|                 if(runs) { |  | ||||||
|                     h.post(new ResultRunnable(result, requestId)); |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 // look for errors during extraction |  | ||||||
|                 // soft errors: |  | ||||||
|                 if(result != null && |  | ||||||
|                         !result.errors.isEmpty()) { |  | ||||||
|                     Log.e(TAG, "OCCURRED ERRORS DURING SEARCH EXTRACTION:"); |  | ||||||
|                     for(Exception e : result.errors) { |  | ||||||
|                         e.printStackTrace(); |  | ||||||
|                         Log.e(TAG, "------"); |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     Activity a = getActivity(); |  | ||||||
|                     View rootView = a.findViewById(R.id.videoitem_list); |  | ||||||
|                     ErrorActivity.reportError(h, getActivity(), result.errors, null, rootView, |  | ||||||
|                             ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED, |  | ||||||
|                         /* todo: this shoudl not be assigned static */  YOUTUBE, query, R.string.light_parsing_error)); |  | ||||||
|  |  | ||||||
|                 } |  | ||||||
|                 // hard errors: |  | ||||||
|             } catch(IOException e) { |  | ||||||
|                 postNewNothingFoundToast(h, R.string.network_error); |  | ||||||
|                 e.printStackTrace(); |  | ||||||
|             } catch(SearchEngine.NothingFoundException e) { |  | ||||||
|                 postNewErrorToast(h, e.getMessage()); |  | ||||||
|             } catch(ExtractionException e) { |  | ||||||
|                 ErrorActivity.reportError(h, getActivity(), e, null, null, |  | ||||||
|                         ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED, |  | ||||||
|                         /* todo: this shoudl not be assigned static */ |  | ||||||
|                                 YOUTUBE, query, R.string.parsing_error)); |  | ||||||
|                 //postNewErrorToast(h, R.string.parsing_error); |  | ||||||
|                 e.printStackTrace(); |  | ||||||
|  |  | ||||||
|             } catch(Exception e) { |  | ||||||
|                 ErrorActivity.reportError(h, getActivity(), e, null, null, |  | ||||||
|                         ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED, |  | ||||||
|                         /* todo: this shoudl not be assigned static */ YOUTUBE, query, R.string.general_error)); |  | ||||||
|  |  | ||||||
|                 e.printStackTrace(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public void present(List<StreamPreviewInfo> videoList) { |  | ||||||
|         mode = PRESENT_VIDEOS_MODE; |  | ||||||
|         setListShown(true); |  | ||||||
|         getListView().smoothScrollToPosition(0); |  | ||||||
|  |  | ||||||
|         updateList(videoList); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public void search(String query) { |  | ||||||
|         mode = SEARCH_MODE; |  | ||||||
|         this.query = query; |  | ||||||
|         this.lastPage = 1; |  | ||||||
|         videoListAdapter.clearVideoList(); |  | ||||||
|         setListShown(false); |  | ||||||
|         startSearch(query, lastPage); |  | ||||||
|         //todo: Somehow this command is not working on older devices, |  | ||||||
|         // although it was introduced with API level 8. Test this and find a solution. |  | ||||||
|         getListView().smoothScrollToPosition(0); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void nextPage() { |  | ||||||
|         loadingNextPage = true; |  | ||||||
|         lastPage++; |  | ||||||
|         Log.d(TAG, getString(R.string.search_page) + Integer.toString(lastPage)); |  | ||||||
|         startSearch(query, lastPage); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void startSearch(String query, int page) { |  | ||||||
|         currentRequestId++; |  | ||||||
|         terminateThreads(); |  | ||||||
|         searchRunnable = new SearchRunnable(streamingService.getSearchEngineInstance(new Downloader()), |  | ||||||
|                                             query, page, currentRequestId); |  | ||||||
|         searchThread = new Thread(searchRunnable); |  | ||||||
|         searchThread.start(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public void setStreamingService(StreamingService streamingService) { |  | ||||||
|         this.streamingService = streamingService; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void updateListOnResult(SearchResult result, int requestId) { |  | ||||||
|         if(requestId == currentRequestId) { |  | ||||||
|             setListShown(true); |  | ||||||
|             updateList(result.resultList); |  | ||||||
|             if(!result.suggestion.isEmpty()) { |  | ||||||
|                 Toast.makeText(getActivity(), |  | ||||||
|                         String.format(getString(R.string.did_you_mean), result.suggestion), |  | ||||||
|                         Toast.LENGTH_LONG).show(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void updateList(List<StreamPreviewInfo> list) { |  | ||||||
|         try { |  | ||||||
|             videoListAdapter.addVideoList(list); |  | ||||||
|             terminateThreads(); |  | ||||||
|         } catch(java.lang.IllegalStateException e) { |  | ||||||
|             Toast.makeText(getActivity(), "Trying to set value while activity doesn't exist anymore.", |  | ||||||
|                     Toast.LENGTH_SHORT).show(); |  | ||||||
|             Log.w(TAG, "Trying to set value while activity doesn't exist anymore."); |  | ||||||
|         } catch(Exception e) { |  | ||||||
|             Toast.makeText(getActivity(), getString(R.string.general_error), |  | ||||||
|                     Toast.LENGTH_SHORT).show(); |  | ||||||
|             e.printStackTrace(); |  | ||||||
|         } finally { |  | ||||||
|             loadingNextPage = false; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void terminateThreads() { |  | ||||||
|         if(searchThread != null) { |  | ||||||
|             searchRunnable.terminate(); |  | ||||||
|             // No need to join, since we don't really terminate the thread. We just demand |  | ||||||
|             // it to post its result runnable into the gui main loop. |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * The serialization (saved instance state) Bundle key representing the |  | ||||||
|      * activated item position. Only used on tablets. |  | ||||||
|      */ |  | ||||||
|     private static final String STATE_ACTIVATED_POSITION = "activated_position"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * The current activated item position. Only used on tablets. |  | ||||||
|      */ |  | ||||||
|     private int mActivatedPosition = ListView.INVALID_POSITION; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * A callback interface that all activities containing this fragment must |  | ||||||
|      * implement. This mechanism allows activities to be notified of item |  | ||||||
|      * selections. |  | ||||||
|      */ |  | ||||||
|     public interface Callbacks { |  | ||||||
|         /** |  | ||||||
|          * Callback for when an item has been selected. |  | ||||||
|          */ |  | ||||||
|         void onItemSelected(String id); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private Callbacks mCallbacks; |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void onViewCreated(View view, Bundle savedInstanceState) { |  | ||||||
|         super.onViewCreated(view, savedInstanceState); |  | ||||||
|         list = getListView(); |  | ||||||
|         videoListAdapter = new VideoListAdapter(getActivity(), this); |  | ||||||
|         footer = ((LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE)) |  | ||||||
|                 .inflate(R.layout.paginate_footer, null, false); |  | ||||||
|  |  | ||||||
|  |  | ||||||
|         setListAdapter(videoListAdapter); |  | ||||||
|  |  | ||||||
|         // Restore the previously serialized activated item position. |  | ||||||
|         if (savedInstanceState != null |  | ||||||
|  |  | ||||||
|                 && savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) { |  | ||||||
|             setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION)); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|         getListView().setOnScrollListener(new AbsListView.OnScrollListener() { |  | ||||||
|             long lastScrollDate; |  | ||||||
|  |  | ||||||
|             @Override |  | ||||||
|             public void onScrollStateChanged(AbsListView view, int scrollState) { |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             @Override |  | ||||||
|             public void onScroll(AbsListView view, int firstVisibleItem, |  | ||||||
|                                  int visibleItemCount, int totalItemCount) { |  | ||||||
|                 if (mode != PRESENT_VIDEOS_MODE |  | ||||||
|                         && list.getChildAt(0) != null |  | ||||||
|                         && list.getLastVisiblePosition() == list.getAdapter().getCount() - 1 |  | ||||||
|                         && list.getChildAt(list.getChildCount() - 1).getBottom() >= list.getHeight()) { |  | ||||||
|                     long time = System.currentTimeMillis(); |  | ||||||
|                     if ((time - lastScrollDate) > 200 |  | ||||||
|                             && !loadingNextPage) { |  | ||||||
|                         lastScrollDate = time; |  | ||||||
|                         getListView().addFooterView(footer); |  | ||||||
|                         nextPage(); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void onAttach(Context context) { |  | ||||||
|         super.onAttach(context); |  | ||||||
|  |  | ||||||
|         // Activities containing this fragment must implement its callbacks. |  | ||||||
|         if (!(context instanceof Callbacks)) { |  | ||||||
|             throw new IllegalStateException("Activity must implement fragment's callbacks."); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         mCallbacks = (Callbacks) context; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void onListItemClick(ListView listView, View view, int position, long id) { |  | ||||||
|         super.onListItemClick(listView, view, position, id); |  | ||||||
|         setActivatedPosition(position); |  | ||||||
|         mCallbacks.onItemSelected(Long.toString(id)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Turns on activate-on-click mode. When this mode is on, list items will be |  | ||||||
|      * given the 'activated' state when touched. |  | ||||||
|      */ |  | ||||||
|     public void setActivateOnItemClick(@SuppressWarnings("SameParameterValue") boolean activateOnItemClick) { |  | ||||||
|         // When setting CHOICE_MODE_SINGLE, ListView will automatically |  | ||||||
|         // give items the 'activated' state when touched. |  | ||||||
|         getListView().setChoiceMode(activateOnItemClick |  | ||||||
|                 ? ListView.CHOICE_MODE_SINGLE |  | ||||||
|                 : ListView.CHOICE_MODE_NONE); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void setActivatedPosition(int position) { |  | ||||||
|         if (position == ListView.INVALID_POSITION) { |  | ||||||
|             getListView().setItemChecked(mActivatedPosition, false); |  | ||||||
|         } else { |  | ||||||
|             getListView().setItemChecked(position, true); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         mActivatedPosition = position; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void postNewErrorToast(Handler h, final String message) { |  | ||||||
|         h.post(new Runnable() { |  | ||||||
|             @Override |  | ||||||
|             public void run() { |  | ||||||
|                 setListShown(true); |  | ||||||
|                 Toast.makeText(getActivity(), message, |  | ||||||
|                         Toast.LENGTH_SHORT).show(); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void postNewNothingFoundToast(Handler h, final int stringResource) { |  | ||||||
|         h.post(new Runnable() { |  | ||||||
|             @Override |  | ||||||
|             public void run() { |  | ||||||
|                 setListShown(true); |  | ||||||
|                 Toast.makeText(getActivity(), getString(stringResource), |  | ||||||
|                         Toast.LENGTH_LONG).show(); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,91 +0,0 @@ | |||||||
| package org.schabi.newpipe; |  | ||||||
|  |  | ||||||
| import android.content.Context; |  | ||||||
| import android.support.v4.content.ContextCompat; |  | ||||||
| import android.view.LayoutInflater; |  | ||||||
| import android.view.View; |  | ||||||
| import android.view.ViewGroup; |  | ||||||
| import android.widget.BaseAdapter; |  | ||||||
| import android.widget.ListView; |  | ||||||
|  |  | ||||||
| import org.schabi.newpipe.extractor.StreamPreviewInfo; |  | ||||||
|  |  | ||||||
| import java.util.List; |  | ||||||
| import java.util.Vector; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Created by Christian Schabesberger on 11.08.15. |  | ||||||
|  * |  | ||||||
|  * Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org> |  | ||||||
|  * VideoListAdapter.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/>. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| class VideoListAdapter extends BaseAdapter { |  | ||||||
|     private final Context context; |  | ||||||
|     private final VideoInfoItemViewCreator viewCreator; |  | ||||||
|     private Vector<StreamPreviewInfo> videoList = new Vector<>(); |  | ||||||
|     private final ListView listView; |  | ||||||
|  |  | ||||||
|     public VideoListAdapter(Context context, VideoItemListFragment videoListFragment) { |  | ||||||
|         viewCreator = new VideoInfoItemViewCreator(LayoutInflater.from(context)); |  | ||||||
|         this.listView = videoListFragment.getListView(); |  | ||||||
|         this.listView.setDivider(null); |  | ||||||
|         this.listView.setDividerHeight(0); |  | ||||||
|         this.context = context; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public void addVideoList(List<StreamPreviewInfo> videos) { |  | ||||||
|         videoList.addAll(videos); |  | ||||||
|         notifyDataSetChanged(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public void clearVideoList() { |  | ||||||
|         videoList = new Vector<>(); |  | ||||||
|         notifyDataSetChanged(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public Vector<StreamPreviewInfo> getVideoList() { |  | ||||||
|         return videoList; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public int getCount() { |  | ||||||
|         return videoList.size(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public Object getItem(int position) { |  | ||||||
|         return videoList.get(position); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public long getItemId(int position) { |  | ||||||
|         return position; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public View getView(int position, View convertView, ViewGroup parent) { |  | ||||||
|         convertView = viewCreator.getViewFromVideoInfoItem(convertView, parent, videoList.get(position)); |  | ||||||
|  |  | ||||||
|         if(listView.isItemChecked(position)) { |  | ||||||
|             convertView.setBackgroundColor(ContextCompat.getColor(context,R.color.light_youtube_primary_color)); |  | ||||||
|         } else { |  | ||||||
|             convertView.setBackgroundColor(0); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return convertView; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe; | package org.schabi.newpipe.detail; | ||||||
| 
 | 
 | ||||||
| import android.content.Intent; | import android.content.Intent; | ||||||
| import android.content.SharedPreferences; | import android.content.SharedPreferences; | ||||||
| @@ -11,8 +11,9 @@ import android.view.MenuInflater; | |||||||
| import android.view.MenuItem; | import android.view.MenuItem; | ||||||
| import android.widget.ArrayAdapter; | import android.widget.ArrayAdapter; | ||||||
| 
 | 
 | ||||||
|  | import org.schabi.newpipe.R; | ||||||
|  | import org.schabi.newpipe.settings.SettingsActivity; | ||||||
| import org.schabi.newpipe.extractor.MediaFormat; | import org.schabi.newpipe.extractor.MediaFormat; | ||||||
| import org.schabi.newpipe.extractor.StreamInfo; |  | ||||||
| import org.schabi.newpipe.extractor.VideoStream; | import org.schabi.newpipe.extractor.VideoStream; | ||||||
| 
 | 
 | ||||||
| import java.util.List; | import java.util.List; | ||||||
| @@ -0,0 +1,204 @@ | |||||||
|  | package org.schabi.newpipe.detail; | ||||||
|  |  | ||||||
|  | import android.app.Activity; | ||||||
|  | import android.os.Handler; | ||||||
|  | import android.util.Log; | ||||||
|  | import android.view.View; | ||||||
|  |  | ||||||
|  | import org.schabi.newpipe.Downloader; | ||||||
|  | import org.schabi.newpipe.ErrorActivity; | ||||||
|  | import org.schabi.newpipe.R; | ||||||
|  | import org.schabi.newpipe.extractor.ParsingException; | ||||||
|  | import org.schabi.newpipe.extractor.ServiceList; | ||||||
|  | import org.schabi.newpipe.extractor.StreamExtractor; | ||||||
|  | import org.schabi.newpipe.extractor.StreamInfo; | ||||||
|  | import org.schabi.newpipe.extractor.StreamingService; | ||||||
|  | import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor; | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Created by the-scrabi on 02.08.16. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | public class StreamInfoWorker { | ||||||
|  |  | ||||||
|  |     private static final String TAG = StreamInfoWorker.class.toString(); | ||||||
|  |  | ||||||
|  |     public interface OnStreamInfoReceivedListener { | ||||||
|  |         void onReceive(StreamInfo info); | ||||||
|  |         void onError(int messageId); | ||||||
|  |         void onBlockedByGemaError(); | ||||||
|  |         void onContentErrorWithMessage(int messageId); | ||||||
|  |         void onContentError(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private class StreamExtractorRunnable implements Runnable { | ||||||
|  |         private final Handler h = new Handler(); | ||||||
|  |         private StreamExtractor streamExtractor; | ||||||
|  |         private final int serviceId; | ||||||
|  |         private final String videoUrl; | ||||||
|  |         private Activity a; | ||||||
|  |  | ||||||
|  |         public StreamExtractorRunnable(Activity a, String videoUrl, int serviceId) { | ||||||
|  |             this.serviceId = serviceId; | ||||||
|  |             this.videoUrl = videoUrl; | ||||||
|  |             this.a = a; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         @Override | ||||||
|  |         public void run() { | ||||||
|  |             StreamInfo streamInfo = null; | ||||||
|  |             StreamingService service = null; | ||||||
|  |             try { | ||||||
|  |                 service = ServiceList.getService(serviceId); | ||||||
|  |             } catch (Exception e) { | ||||||
|  |                 e.printStackTrace(); | ||||||
|  |                 ErrorActivity.reportError(h, a, e, VideoItemDetailFragment.class, null, | ||||||
|  |                         ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, | ||||||
|  |                                 "", videoUrl, R.string.could_not_get_stream)); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             try { | ||||||
|  |                 streamExtractor = service.getExtractorInstance(videoUrl, new Downloader()); | ||||||
|  |                 streamInfo = StreamInfo.getVideoInfo(streamExtractor, new Downloader()); | ||||||
|  |  | ||||||
|  |                 final StreamInfo info = streamInfo; | ||||||
|  |                 h.post(new Runnable() { | ||||||
|  |                     @Override | ||||||
|  |                     public void run() { | ||||||
|  |                         onStreamInfoReceivedListener.onReceive(info); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |  | ||||||
|  |                 // look for errors during extraction | ||||||
|  |                 // this if statement only covers extra information. | ||||||
|  |                 // if these are not available or caused an error, they are just not available | ||||||
|  |                 // but don't render the stream information unusalbe. | ||||||
|  |                 if(streamInfo != null && | ||||||
|  |                         !streamInfo.errors.isEmpty()) { | ||||||
|  |                     Log.e(TAG, "OCCURRED ERRORS DURING EXTRACTION:"); | ||||||
|  |                     for (Throwable e : streamInfo.errors) { | ||||||
|  |                         e.printStackTrace(); | ||||||
|  |                         Log.e(TAG, "------"); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     View rootView = a != null ? a.findViewById(R.id.video_item_detail) : null; | ||||||
|  |                     ErrorActivity.reportError(h, a, | ||||||
|  |                             streamInfo.errors, null, rootView, | ||||||
|  |                             ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, | ||||||
|  |                                     service.getServiceInfo().name, videoUrl, 0 /* no message for the user */)); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 // These errors render the stream information unusable. | ||||||
|  |             } catch (IOException e) { | ||||||
|  |                 h.post(new Runnable() { | ||||||
|  |                     @Override | ||||||
|  |                     public void run() { | ||||||
|  |                         onStreamInfoReceivedListener.onError(R.string.network_error); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |                 e.printStackTrace(); | ||||||
|  |             } | ||||||
|  |             // custom service related exceptions | ||||||
|  |             catch (YoutubeStreamExtractor.DecryptException de) { | ||||||
|  |                 h.post(new Runnable() { | ||||||
|  |                     @Override | ||||||
|  |                     public void run() { | ||||||
|  |                         onStreamInfoReceivedListener.onError(R.string.youtube_signature_decryption_error); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |                 de.printStackTrace(); | ||||||
|  |             } catch (YoutubeStreamExtractor.GemaException ge) { | ||||||
|  |                 h.post(new Runnable() { | ||||||
|  |                     @Override | ||||||
|  |                     public void run() { | ||||||
|  |                         onStreamInfoReceivedListener.onBlockedByGemaError(); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |             } catch(YoutubeStreamExtractor.LiveStreamException e) { | ||||||
|  |                 h.post(new Runnable() { | ||||||
|  |                     @Override | ||||||
|  |                     public void run() { | ||||||
|  |                         onStreamInfoReceivedListener | ||||||
|  |                                 .onContentErrorWithMessage(R.string.live_streams_not_supported); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |             // ---------------------------------------- | ||||||
|  |             catch(StreamExtractor.ContentNotAvailableException e) { | ||||||
|  |                 h.post(new Runnable() { | ||||||
|  |                     @Override | ||||||
|  |                     public void run() { | ||||||
|  |                         onStreamInfoReceivedListener | ||||||
|  |                                 .onContentError(); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |                 e.printStackTrace(); | ||||||
|  |             } catch(StreamInfo.StreamExctractException e) { | ||||||
|  |                 if(!streamInfo.errors.isEmpty()) { | ||||||
|  |                     // !!! if this case ever kicks in someone gets kicked out !!! | ||||||
|  |                     ErrorActivity.reportError(h, a, e, VideoItemDetailFragment.class, null, | ||||||
|  |                             ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, | ||||||
|  |                                     service.getServiceInfo().name, videoUrl, R.string.could_not_get_stream)); | ||||||
|  |                 } else { | ||||||
|  |                     ErrorActivity.reportError(h, a, streamInfo.errors, VideoItemDetailFragment.class, null, | ||||||
|  |                             ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, | ||||||
|  |                                     service.getServiceInfo().name, videoUrl, R.string.could_not_get_stream)); | ||||||
|  |                 } | ||||||
|  |                 h.post(new Runnable() { | ||||||
|  |                     @Override | ||||||
|  |                     public void run() { | ||||||
|  |                         a.finish(); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |                 e.printStackTrace(); | ||||||
|  |             } catch (ParsingException e) { | ||||||
|  |                 ErrorActivity.reportError(h, a, e, VideoItemDetailFragment.class, null, | ||||||
|  |                         ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, | ||||||
|  |                                 service.getServiceInfo().name, videoUrl, R.string.parsing_error)); | ||||||
|  |                 h.post(new Runnable() { | ||||||
|  |                     @Override | ||||||
|  |                     public void run() { | ||||||
|  |                         a.finish(); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |                 e.printStackTrace(); | ||||||
|  |             } catch(Exception e) { | ||||||
|  |                 ErrorActivity.reportError(h, a, e, VideoItemDetailFragment.class, null, | ||||||
|  |                         ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, | ||||||
|  |                                 service.getServiceInfo().name, videoUrl, R.string.general_error)); | ||||||
|  |                 h.post(new Runnable() { | ||||||
|  |                     @Override | ||||||
|  |                     public void run() { | ||||||
|  |                         a.finish(); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |                 e.printStackTrace(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static StreamInfoWorker streamInfoWorker = null; | ||||||
|  |     private StreamExtractorRunnable runnable = null; | ||||||
|  |     private OnStreamInfoReceivedListener onStreamInfoReceivedListener = null; | ||||||
|  |  | ||||||
|  |     private StreamInfoWorker() { | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static StreamInfoWorker getInstance() { | ||||||
|  |         return streamInfoWorker == null ? (streamInfoWorker = new StreamInfoWorker()) : streamInfoWorker; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void search(int serviceId, String url, Activity a) { | ||||||
|  |         runnable = new StreamExtractorRunnable(a, url, serviceId); | ||||||
|  |         Thread thread = new Thread(runnable); | ||||||
|  |         thread.start(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void setOnStreamInfoReceivedListener( | ||||||
|  |             OnStreamInfoReceivedListener onStreamInfoReceivedListener) { | ||||||
|  |         this.onStreamInfoReceivedListener = onStreamInfoReceivedListener; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe; | package org.schabi.newpipe.detail; | ||||||
| 
 | 
 | ||||||
| import android.content.Intent; | import android.content.Intent; | ||||||
| import android.media.AudioManager; | import android.media.AudioManager; | ||||||
| @@ -11,6 +11,9 @@ import android.view.Menu; | |||||||
| import android.view.MenuItem; | import android.view.MenuItem; | ||||||
| import android.widget.Toast; | import android.widget.Toast; | ||||||
| 
 | 
 | ||||||
|  | import org.schabi.newpipe.App; | ||||||
|  | import org.schabi.newpipe.MainActivity; | ||||||
|  | import org.schabi.newpipe.R; | ||||||
| import org.schabi.newpipe.extractor.ServiceList; | import org.schabi.newpipe.extractor.ServiceList; | ||||||
| import org.schabi.newpipe.extractor.StreamingService; | import org.schabi.newpipe.extractor.StreamingService; | ||||||
| 
 | 
 | ||||||
| @@ -85,7 +88,7 @@ public class VideoItemDetailActivity extends AppCompatActivity { | |||||||
|                             .show(); |                             .show(); | ||||||
|                 } |                 } | ||||||
|                 //arguments.putString(VideoItemDetailFragment.VIDEO_URL, |                 //arguments.putString(VideoItemDetailFragment.VIDEO_URL, | ||||||
|                 //        videoExtractor.getVideoUrl(videoExtractor.getVideoId(videoUrl)));//cleans URL |                 //        videoExtractor.getUrl(videoExtractor.getId(videoUrl)));//cleans URL | ||||||
|                 arguments.putString(VideoItemDetailFragment.VIDEO_URL, videoUrl); |                 arguments.putString(VideoItemDetailFragment.VIDEO_URL, videoUrl); | ||||||
| 
 | 
 | ||||||
|                 arguments.putBoolean(VideoItemDetailFragment.AUTO_PLAY, |                 arguments.putBoolean(VideoItemDetailFragment.AUTO_PLAY, | ||||||
| @@ -138,7 +141,7 @@ public class VideoItemDetailActivity extends AppCompatActivity { | |||||||
| 
 | 
 | ||||||
|             // http://developer.android.com/design/patterns/navigation.html#up-vs-back |             // http://developer.android.com/design/patterns/navigation.html#up-vs-back | ||||||
| 
 | 
 | ||||||
|             Intent intent = new Intent(this, VideoItemListActivity.class); |             Intent intent = new Intent(this, MainActivity.class); | ||||||
|             intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); |             intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); | ||||||
|             NavUtils.navigateUpTo(this, intent); |             NavUtils.navigateUpTo(this, intent); | ||||||
|             return true; |             return true; | ||||||
| @@ -1,22 +1,21 @@ | |||||||
| package org.schabi.newpipe; | package org.schabi.newpipe.detail; | ||||||
| 
 | 
 | ||||||
| import android.app.Activity; | import android.app.Activity; | ||||||
| import android.content.Context; |  | ||||||
| import android.content.DialogInterface; | import android.content.DialogInterface; | ||||||
| import android.content.Intent; | import android.content.Intent; | ||||||
| import android.content.res.TypedArray; |  | ||||||
| import android.graphics.Bitmap; | import android.graphics.Bitmap; | ||||||
| import android.graphics.BitmapFactory; | import android.graphics.BitmapFactory; | ||||||
| import android.graphics.Point; | import android.graphics.Point; | ||||||
| import android.net.Uri; | import android.net.Uri; | ||||||
| import android.os.Build; | import android.os.Build; | ||||||
| import android.os.Bundle; | import android.os.Bundle; | ||||||
| import android.os.Handler; |  | ||||||
| import android.preference.PreferenceManager; | import android.preference.PreferenceManager; | ||||||
| import android.support.design.widget.FloatingActionButton; | import android.support.design.widget.FloatingActionButton; | ||||||
| import android.support.v4.app.Fragment; | import android.support.v4.app.Fragment; | ||||||
| import android.support.v7.app.AlertDialog; | import android.support.v7.app.AlertDialog; | ||||||
| import android.support.v7.app.AppCompatActivity; | import android.support.v7.app.AppCompatActivity; | ||||||
|  | import android.support.v7.widget.LinearLayoutManager; | ||||||
|  | import android.support.v7.widget.RecyclerView; | ||||||
| import android.text.Html; | import android.text.Html; | ||||||
| import android.text.method.LinkMovementMethod; | import android.text.method.LinkMovementMethod; | ||||||
| import android.util.Log; | import android.util.Log; | ||||||
| @@ -27,38 +26,34 @@ import android.view.MotionEvent; | |||||||
| import android.view.View; | import android.view.View; | ||||||
| import android.view.ViewGroup; | import android.view.ViewGroup; | ||||||
| import android.widget.Button; | import android.widget.Button; | ||||||
| import android.widget.FrameLayout; |  | ||||||
| import android.widget.ImageView; | import android.widget.ImageView; | ||||||
| import android.widget.LinearLayout; |  | ||||||
| import android.widget.ProgressBar; | import android.widget.ProgressBar; | ||||||
| import android.widget.RelativeLayout; | import android.widget.RelativeLayout; | ||||||
| import android.widget.TextView; | import android.widget.TextView; | ||||||
| import android.view.MenuItem; | import android.view.MenuItem; | ||||||
| import android.widget.Toast; | import android.widget.Toast; | ||||||
| 
 | 
 | ||||||
| import java.io.IOException; |  | ||||||
| 
 |  | ||||||
| import com.google.android.exoplayer.util.Util; | import com.google.android.exoplayer.util.Util; | ||||||
| import com.nostra13.universalimageloader.core.DisplayImageOptions; | import com.nostra13.universalimageloader.core.DisplayImageOptions; | ||||||
| import com.nostra13.universalimageloader.core.ImageLoader; | import com.nostra13.universalimageloader.core.ImageLoader; | ||||||
| import com.nostra13.universalimageloader.core.assist.FailReason; | import com.nostra13.universalimageloader.core.assist.FailReason; | ||||||
| import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; | import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; | ||||||
| 
 | 
 | ||||||
| import java.util.ArrayList; |  | ||||||
| import java.util.Vector; | import java.util.Vector; | ||||||
| 
 | 
 | ||||||
| 
 | import org.schabi.newpipe.ActivityCommunicator; | ||||||
|  | import org.schabi.newpipe.ChannelActivity; | ||||||
|  | import org.schabi.newpipe.ErrorActivity; | ||||||
|  | import org.schabi.newpipe.ImageErrorLoadingListener; | ||||||
|  | import org.schabi.newpipe.Localization; | ||||||
|  | import org.schabi.newpipe.R; | ||||||
| import org.schabi.newpipe.download.DownloadDialog; | import org.schabi.newpipe.download.DownloadDialog; | ||||||
| import org.schabi.newpipe.extractor.AudioStream; | import org.schabi.newpipe.extractor.AudioStream; | ||||||
| import org.schabi.newpipe.extractor.MediaFormat; | import org.schabi.newpipe.extractor.MediaFormat; | ||||||
| import org.schabi.newpipe.extractor.ParsingException; |  | ||||||
| import org.schabi.newpipe.extractor.ServiceList; | import org.schabi.newpipe.extractor.ServiceList; | ||||||
| import org.schabi.newpipe.extractor.StreamExtractor; |  | ||||||
| import org.schabi.newpipe.extractor.StreamInfo; | import org.schabi.newpipe.extractor.StreamInfo; | ||||||
| import org.schabi.newpipe.extractor.StreamPreviewInfo; |  | ||||||
| import org.schabi.newpipe.extractor.StreamingService; |  | ||||||
| import org.schabi.newpipe.extractor.VideoStream; | import org.schabi.newpipe.extractor.VideoStream; | ||||||
| import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor; | import org.schabi.newpipe.info_list.InfoListAdapter; | ||||||
| import org.schabi.newpipe.player.BackgroundPlayer; | import org.schabi.newpipe.player.BackgroundPlayer; | ||||||
| import org.schabi.newpipe.player.PlayVideoActivity; | import org.schabi.newpipe.player.PlayVideoActivity; | ||||||
| import org.schabi.newpipe.player.ExoPlayerActivity; | import org.schabi.newpipe.player.ExoPlayerActivity; | ||||||
| @@ -102,19 +97,20 @@ public class VideoItemDetailFragment extends Fragment { | |||||||
|     private int streamingServiceId = -1; |     private int streamingServiceId = -1; | ||||||
| 
 | 
 | ||||||
|     private boolean autoPlayEnabled; |     private boolean autoPlayEnabled; | ||||||
|     private boolean showNextVideoItem; |     private boolean showNextStreamItem; | ||||||
|     private Bitmap videoThumbnail; |  | ||||||
| 
 | 
 | ||||||
|     private View thumbnailWindowLayout; |     private View thumbnailWindowLayout; | ||||||
|     //this only remains due to downwards compatibility |     //this only remains due to downwards compatibility | ||||||
|     private FloatingActionButton playVideoButton; |     private FloatingActionButton playVideoButton; | ||||||
|     private final Point initialThumbnailPos = new Point(0, 0); |     private final Point initialThumbnailPos = new Point(0, 0); | ||||||
| 
 |     private View rootView = null; | ||||||
|  |     private Bitmap streamThumbnail = null; | ||||||
| 
 | 
 | ||||||
|     private ImageLoader imageLoader = ImageLoader.getInstance(); |     private ImageLoader imageLoader = ImageLoader.getInstance(); | ||||||
|     private DisplayImageOptions displayImageOptions = |     private DisplayImageOptions displayImageOptions = | ||||||
|             new DisplayImageOptions.Builder().cacheInMemory(true).build(); |             new DisplayImageOptions.Builder().cacheInMemory(true).build(); | ||||||
| 
 | 
 | ||||||
|  |     private InfoListAdapter similarStreamsAdapter = null; | ||||||
| 
 | 
 | ||||||
|     public interface OnInvokeCreateOptionsMenuListener { |     public interface OnInvokeCreateOptionsMenuListener { | ||||||
|         void createOptionsMenu(); |         void createOptionsMenu(); | ||||||
| @@ -122,215 +118,57 @@ public class VideoItemDetailFragment extends Fragment { | |||||||
| 
 | 
 | ||||||
|     private OnInvokeCreateOptionsMenuListener onInvokeCreateOptionsMenuListener; |     private OnInvokeCreateOptionsMenuListener onInvokeCreateOptionsMenuListener; | ||||||
| 
 | 
 | ||||||
|     private class VideoExtractorRunnable implements Runnable { |  | ||||||
|         private final Handler h = new Handler(); |  | ||||||
|         private StreamExtractor streamExtractor; |  | ||||||
|         private final StreamingService service; |  | ||||||
|         private final String videoUrl; |  | ||||||
| 
 |  | ||||||
|         public VideoExtractorRunnable(String videoUrl, StreamingService service) { |  | ||||||
|             this.service = service; |  | ||||||
|             this.videoUrl = videoUrl; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void run() { |  | ||||||
|             StreamInfo streamInfo = null; |  | ||||||
|             try { |  | ||||||
|                 streamExtractor = service.getExtractorInstance(videoUrl, new Downloader()); |  | ||||||
|                 streamInfo = StreamInfo.getVideoInfo(streamExtractor, new Downloader()); |  | ||||||
| 
 |  | ||||||
|                 h.post(new VideoResultReturnedRunnable(streamInfo)); |  | ||||||
| 
 |  | ||||||
|                 // look for errors during extraction |  | ||||||
|                 // this if statement only covers extra information. |  | ||||||
|                 // if these are not available or caused an error, they are just not available |  | ||||||
|                 // but don't render the stream information unusalbe. |  | ||||||
|                 if(streamInfo != null && |  | ||||||
|                         !streamInfo.errors.isEmpty()) { |  | ||||||
|                     Log.e(TAG, "OCCURRED ERRORS DURING EXTRACTION:"); |  | ||||||
|                     for (Exception e : streamInfo.errors) { |  | ||||||
|                         e.printStackTrace(); |  | ||||||
|                         Log.e(TAG, "------"); |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     Activity a = getActivity(); |  | ||||||
|                     View rootView = a != null ? a.findViewById(R.id.videoitem_detail) : null; |  | ||||||
|                     ErrorActivity.reportError(h, getActivity(), |  | ||||||
|                             streamInfo.errors, null, rootView, |  | ||||||
|                             ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, |  | ||||||
|                                     service.getServiceInfo().name, videoUrl, 0 /* no message for the user */)); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 // These errors render the stream information unusable. |  | ||||||
|             } catch (IOException e) { |  | ||||||
|                 postNewErrorToast(h, R.string.network_error); |  | ||||||
|                 e.printStackTrace(); |  | ||||||
|             } |  | ||||||
|             // custom service related exceptions |  | ||||||
|             catch (YoutubeStreamExtractor.DecryptException de) { |  | ||||||
|                 postNewErrorToast(h, R.string.youtube_signature_decryption_error); |  | ||||||
|                 de.printStackTrace(); |  | ||||||
|             } catch (YoutubeStreamExtractor.GemaException ge) { |  | ||||||
|                 h.post(new Runnable() { |  | ||||||
|                     @Override |  | ||||||
|                     public void run() { |  | ||||||
|                         onErrorBlockedByGema(); |  | ||||||
|                     } |  | ||||||
|                 }); |  | ||||||
|             } catch(YoutubeStreamExtractor.LiveStreamException e) { |  | ||||||
|                 h.post(new Runnable() { |  | ||||||
|                     @Override |  | ||||||
|                     public void run() { |  | ||||||
|                         onNotSpecifiedContentErrorWithMessage(R.string.live_streams_not_supported); |  | ||||||
|                     } |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
|             // ---------------------------------------- |  | ||||||
|             catch(StreamExtractor.ContentNotAvailableException e) { |  | ||||||
|                 h.post(new Runnable() { |  | ||||||
|                     @Override |  | ||||||
|                     public void run() { |  | ||||||
|                         onNotSpecifiedContentError(); |  | ||||||
|                     } |  | ||||||
|                 }); |  | ||||||
|                 e.printStackTrace(); |  | ||||||
|             } catch(StreamInfo.StreamExctractException e) { |  | ||||||
|                 if(!streamInfo.errors.isEmpty()) { |  | ||||||
|                     // !!! if this case ever kicks in someone gets kicked out !!! |  | ||||||
|                     ErrorActivity.reportError(h, getActivity(), e, VideoItemListActivity.class, null, |  | ||||||
|                             ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, |  | ||||||
|                                     service.getServiceInfo().name, videoUrl, R.string.could_not_get_stream)); |  | ||||||
|                 } else { |  | ||||||
|                     ErrorActivity.reportError(h, getActivity(), streamInfo.errors, VideoItemListActivity.class, null, |  | ||||||
|                             ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, |  | ||||||
|                                     service.getServiceInfo().name, videoUrl, R.string.could_not_get_stream)); |  | ||||||
|                 } |  | ||||||
|                 h.post(new Runnable() { |  | ||||||
|                     @Override |  | ||||||
|                     public void run() { |  | ||||||
|                         getActivity().finish(); |  | ||||||
|                     } |  | ||||||
|                 }); |  | ||||||
|                 e.printStackTrace(); |  | ||||||
|             } catch (ParsingException e) { |  | ||||||
|                 ErrorActivity.reportError(h, getActivity(), e, VideoItemListActivity.class, null, |  | ||||||
|                         ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, |  | ||||||
|                                 service.getServiceInfo().name, videoUrl, R.string.parsing_error)); |  | ||||||
|                         h.post(new Runnable() { |  | ||||||
|                             @Override |  | ||||||
|                             public void run() { |  | ||||||
|                                 getActivity().finish(); |  | ||||||
|                             } |  | ||||||
|                         }); |  | ||||||
|                 e.printStackTrace(); |  | ||||||
|             } catch(Exception e) { |  | ||||||
|                 ErrorActivity.reportError(h, getActivity(), e, VideoItemListActivity.class, null, |  | ||||||
|                         ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, |  | ||||||
|                                 service.getServiceInfo().name, videoUrl, R.string.general_error)); |  | ||||||
|                         h.post(new Runnable() { |  | ||||||
|                             @Override |  | ||||||
|                             public void run() { |  | ||||||
|                                 getActivity().finish(); |  | ||||||
|                             } |  | ||||||
|                         }); |  | ||||||
|                 e.printStackTrace(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private class VideoResultReturnedRunnable implements Runnable { |  | ||||||
|         private final StreamInfo streamInfo; |  | ||||||
|         public VideoResultReturnedRunnable(StreamInfo streamInfo) { |  | ||||||
|             this.streamInfo = streamInfo; |  | ||||||
|         } |  | ||||||
|         @Override |  | ||||||
|         public void run() { |  | ||||||
|             Activity a = getActivity(); |  | ||||||
|             if(a != null) { |  | ||||||
|                 boolean showAgeRestrictedContent = PreferenceManager.getDefaultSharedPreferences(a) |  | ||||||
|                         .getBoolean(activity.getString(R.string.show_age_restricted_content), false); |  | ||||||
|                 if (streamInfo.age_limit == 0 || showAgeRestrictedContent) { |  | ||||||
|                     updateInfo(streamInfo); |  | ||||||
|                 } else { |  | ||||||
|                     onNotSpecifiedContentErrorWithMessage(R.string.video_is_age_restricted); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private class ThumbnailLoadingListener implements ImageLoadingListener { |  | ||||||
|         @Override |  | ||||||
|         public void onLoadingStarted(String imageUri, View view) {} |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onLoadingFailed(String imageUri, View view, FailReason failReason) { |  | ||||||
|             if(getContext() != null) { |  | ||||||
|                 Toast.makeText(VideoItemDetailFragment.this.getActivity(), |  | ||||||
|                         R.string.could_not_load_thumbnails, Toast.LENGTH_LONG).show(); |  | ||||||
|             } |  | ||||||
|             failReason.getCause().printStackTrace(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {} |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onLoadingCancelled(String imageUri, View view) {} |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private void updateInfo(final StreamInfo info) { |     private void updateInfo(final StreamInfo info) { | ||||||
|         try { |         try { | ||||||
|             Context c = getContext(); |             Activity a = getActivity(); | ||||||
|             VideoInfoItemViewCreator videoItemViewCreator = |  | ||||||
|                     new VideoInfoItemViewCreator(LayoutInflater.from(getActivity())); |  | ||||||
| 
 | 
 | ||||||
|             RelativeLayout textContentLayout = |             RelativeLayout textContentLayout = | ||||||
|                     (RelativeLayout) activity.findViewById(R.id.detailTextContentLayout); |                     (RelativeLayout) activity.findViewById(R.id.detail_text_content_layout); | ||||||
|             final TextView videoTitleView = |             final TextView videoTitleView = | ||||||
|                     (TextView) activity.findViewById(R.id.detailVideoTitleView); |                     (TextView) activity.findViewById(R.id.detail_video_title_view); | ||||||
|             TextView uploaderView = (TextView) activity.findViewById(R.id.detailUploaderView); |             TextView uploaderView = (TextView) activity.findViewById(R.id.detail_uploader_view); | ||||||
|             TextView viewCountView = (TextView) activity.findViewById(R.id.detailViewCountView); |             TextView viewCountView = (TextView) activity.findViewById(R.id.detail_view_count_view); | ||||||
|             TextView thumbsUpView = (TextView) activity.findViewById(R.id.detailThumbsUpCountView); |             TextView thumbsUpView = (TextView) activity.findViewById(R.id.detail_thumbs_up_count_view); | ||||||
|             TextView thumbsDownView = |             TextView thumbsDownView = | ||||||
|                     (TextView) activity.findViewById(R.id.detailThumbsDownCountView); |                     (TextView) activity.findViewById(R.id.detail_thumbs_down_count_view); | ||||||
|             TextView uploadDateView = (TextView) activity.findViewById(R.id.detailUploadDateView); |             TextView uploadDateView = (TextView) activity.findViewById(R.id.detail_upload_date_view); | ||||||
|             TextView descriptionView = (TextView) activity.findViewById(R.id.detailDescriptionView); |             TextView descriptionView = (TextView) activity.findViewById(R.id.detail_description_view); | ||||||
|             FrameLayout nextVideoFrame = |             RecyclerView nextStreamView = | ||||||
|                     (FrameLayout) activity.findViewById(R.id.detailNextVideoFrame); |                     (RecyclerView) activity.findViewById(R.id.detail_next_stream_content); | ||||||
|             RelativeLayout nextVideoRootFrame = |             RelativeLayout nextVideoRootFrame = | ||||||
|                     (RelativeLayout) activity.findViewById(R.id.detailNextVideoRootLayout); |                     (RelativeLayout) activity.findViewById(R.id.detail_next_stream_root_layout); | ||||||
|             Button nextVideoButton = (Button) activity.findViewById(R.id.detailNextVideoButton); |             TextView similarTitle = (TextView) activity.findViewById(R.id.detail_similar_title); | ||||||
|             TextView similarTitle = (TextView) activity.findViewById(R.id.detailSimilarTitle); |  | ||||||
|             Button backgroundButton = (Button) |             Button backgroundButton = (Button) | ||||||
|                     activity.findViewById(R.id.detailVideoThumbnailWindowBackgroundButton); |                     activity.findViewById(R.id.detail_stream_thumbnail_window_background_button); | ||||||
|             View topView = activity.findViewById(R.id.detailTopView); |             View topView = activity.findViewById(R.id.detailTopView); | ||||||
|             View nextVideoView = null; |             Button channelButton = (Button) activity.findViewById(R.id.channel_button); | ||||||
|             if(info.next_video != null) { |  | ||||||
|                 nextVideoView = videoItemViewCreator |  | ||||||
|                         .getViewFromVideoInfoItem(null, nextVideoFrame, info.next_video); |  | ||||||
|             } else { |  | ||||||
|                 activity.findViewById(R.id.detailNextVidButtonAndContentLayout).setVisibility(View.GONE); |  | ||||||
|                 activity.findViewById(R.id.detailNextVideoTitle).setVisibility(View.GONE); |  | ||||||
|                 activity.findViewById(R.id.detailNextVideoButton).setVisibility(View.GONE); |  | ||||||
|             } |  | ||||||
| 
 | 
 | ||||||
|             progressBar.setVisibility(View.GONE); |             progressBar.setVisibility(View.GONE); | ||||||
|             if(nextVideoView != null) { |             if(info.next_video != null) { | ||||||
|                 nextVideoFrame.addView(nextVideoView); |                 InfoListAdapter adapter = new InfoListAdapter(a, rootView); | ||||||
|  |                 nextStreamView.setAdapter(adapter); | ||||||
|  |                 nextStreamView.setLayoutManager(new LinearLayoutManager(a)); | ||||||
|  |                 adapter.setOnItemSelectedListener(new InfoListAdapter.OnItemSelectedListener() { | ||||||
|  |                     @Override | ||||||
|  |                     public void selected(String url) { | ||||||
|  |                         openStreamUrl(url); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |             } else { | ||||||
|  |                 nextStreamView.setVisibility(View.GONE); | ||||||
|  |                 activity.findViewById(R.id.detail_similar_title).setVisibility(View.GONE); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             initThumbnailViews(info, nextVideoFrame); |  | ||||||
| 
 | 
 | ||||||
|             textContentLayout.setVisibility(View.VISIBLE); |             textContentLayout.setVisibility(View.VISIBLE); | ||||||
|             if (android.os.Build.VERSION.SDK_INT < 18) { |             if (android.os.Build.VERSION.SDK_INT < 18) { | ||||||
|                 playVideoButton.setVisibility(View.VISIBLE); |                 playVideoButton.setVisibility(View.VISIBLE); | ||||||
|             } else { |             } else { | ||||||
|                 ImageView playArrowView = (ImageView) activity.findViewById(R.id.playArrowView); |                 ImageView playArrowView = (ImageView) activity.findViewById(R.id.play_arrow_view); | ||||||
|                 playArrowView.setVisibility(View.VISIBLE); |                 playArrowView.setVisibility(View.VISIBLE); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if (!showNextVideoItem) { |             if (!showNextStreamItem) { | ||||||
|                 nextVideoRootFrame.setVisibility(View.GONE); |                 nextVideoRootFrame.setVisibility(View.GONE); | ||||||
|                 similarTitle.setVisibility(View.GONE); |                 similarTitle.setVisibility(View.GONE); | ||||||
|             } |             } | ||||||
| @@ -341,7 +179,7 @@ public class VideoItemDetailFragment extends Fragment { | |||||||
|                 @Override |                 @Override | ||||||
|                 public boolean onTouch(View v, MotionEvent event) { |                 public boolean onTouch(View v, MotionEvent event) { | ||||||
|                     if (event.getAction() == android.view.MotionEvent.ACTION_UP) { |                     if (event.getAction() == android.view.MotionEvent.ACTION_UP) { | ||||||
|                         ImageView arrow = (ImageView) activity.findViewById(R.id.toggleDescriptionView); |                         ImageView arrow = (ImageView) activity.findViewById(R.id.toggle_description_view); | ||||||
|                         View extra = activity.findViewById(R.id.detailExtraView); |                         View extra = activity.findViewById(R.id.detailExtraView); | ||||||
|                         if (extra.getVisibility() == View.VISIBLE) { |                         if (extra.getVisibility() == View.VISIBLE) { | ||||||
|                             extra.setVisibility(View.GONE); |                             extra.setVisibility(View.GONE); | ||||||
| @@ -361,29 +199,29 @@ public class VideoItemDetailFragment extends Fragment { | |||||||
|             if(!info.uploader.isEmpty()) { |             if(!info.uploader.isEmpty()) { | ||||||
|                 uploaderView.setText(info.uploader); |                 uploaderView.setText(info.uploader); | ||||||
|             } else { |             } else { | ||||||
|                 activity.findViewById(R.id.detailUploaderWrapView).setVisibility(View.GONE); |                 activity.findViewById(R.id.detail_uploader_view).setVisibility(View.GONE); | ||||||
|             } |             } | ||||||
|             if(info.view_count >= 0) { |             if(info.view_count >= 0) { | ||||||
|                 viewCountView.setText(Localization.localizeViewCount(info.view_count, c)); |                 viewCountView.setText(Localization.localizeViewCount(info.view_count, a)); | ||||||
|             } else { |             } else { | ||||||
|                 viewCountView.setVisibility(View.GONE); |                 viewCountView.setVisibility(View.GONE); | ||||||
|             } |             } | ||||||
|             if(info.dislike_count >= 0) { |             if(info.dislike_count >= 0) { | ||||||
|                 thumbsDownView.setText(Localization.localizeNumber(info.dislike_count, c)); |                 thumbsDownView.setText(Localization.localizeNumber(info.dislike_count, a)); | ||||||
|             } else { |             } else { | ||||||
|                 thumbsDownView.setVisibility(View.INVISIBLE); |                 thumbsDownView.setVisibility(View.INVISIBLE); | ||||||
|                 activity.findViewById(R.id.detailThumbsDownImgView).setVisibility(View.GONE); |                 activity.findViewById(R.id.detail_thumbs_down_count_view).setVisibility(View.GONE); | ||||||
|             } |             } | ||||||
|             if(info.like_count >= 0) { |             if(info.like_count >= 0) { | ||||||
|                 thumbsUpView.setText(Localization.localizeNumber(info.like_count, c)); |                 thumbsUpView.setText(Localization.localizeNumber(info.like_count, a)); | ||||||
|             } else { |             } else { | ||||||
|                 thumbsUpView.setVisibility(View.GONE); |                 thumbsUpView.setVisibility(View.GONE); | ||||||
|                 activity.findViewById(R.id.detailThumbsUpImgView).setVisibility(View.GONE); |                 activity.findViewById(R.id.detail_thumbs_up_img_view).setVisibility(View.GONE); | ||||||
|                 thumbsDownView.setVisibility(View.GONE); |                 thumbsDownView.setVisibility(View.GONE); | ||||||
|                 activity.findViewById(R.id.detailThumbsDownImgView).setVisibility(View.GONE); |                 activity.findViewById(R.id.detail_thumbs_down_img_view).setVisibility(View.GONE); | ||||||
|             } |             } | ||||||
|             if(!info.upload_date.isEmpty()) { |             if(!info.upload_date.isEmpty()) { | ||||||
|                 uploadDateView.setText(Localization.localizeDate(info.upload_date, c)); |                 uploadDateView.setText(Localization.localizeDate(info.upload_date, a)); | ||||||
|             } else { |             } else { | ||||||
|                 uploadDateView.setVisibility(View.GONE); |                 uploadDateView.setVisibility(View.GONE); | ||||||
|             } |             } | ||||||
| @@ -403,26 +241,17 @@ public class VideoItemDetailFragment extends Fragment { | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             nextVideoButton.setOnClickListener(new View.OnClickListener() { |  | ||||||
|                 @Override |  | ||||||
|                 public void onClick(View v) { |  | ||||||
|                     Intent detailIntent = |  | ||||||
|                             new Intent(getActivity(), VideoItemDetailActivity.class); |  | ||||||
|                         /*detailIntent.putExtra( |  | ||||||
|                                 VideoItemDetailFragment.ARG_ITEM_ID, currentVideoInfo.nextVideo.id); */ |  | ||||||
|                     detailIntent.putExtra( |  | ||||||
|                             VideoItemDetailFragment.VIDEO_URL, info.next_video.webpage_url); |  | ||||||
|                     detailIntent.putExtra(VideoItemDetailFragment.STREAMING_SERVICE, streamingServiceId); |  | ||||||
|                     startActivity(detailIntent); |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
|             textContentLayout.setVisibility(View.VISIBLE); |             textContentLayout.setVisibility(View.VISIBLE); | ||||||
| 
 | 
 | ||||||
|             if(info.related_videos != null && !info.related_videos.isEmpty()) { |             if(info.next_video == null) { | ||||||
|                 initSimilarVideos(info, videoItemViewCreator); |                 activity.findViewById(R.id.detail_next_stream_title).setVisibility(View.GONE); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if(info.related_streams != null && !info.related_streams.isEmpty()) { | ||||||
|  |                 initSimilarVideos(info); | ||||||
|             } else { |             } else { | ||||||
|                 activity.findViewById(R.id.detailSimilarTitle).setVisibility(View.GONE); |                 activity.findViewById(R.id.detail_similar_title).setVisibility(View.GONE); | ||||||
|                 activity.findViewById(R.id.similarVideosView).setVisibility(View.GONE); |                 activity.findViewById(R.id.similar_streams_view).setVisibility(View.GONE); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             setupActionBarHandler(info); |             setupActionBarHandler(info); | ||||||
| @@ -447,18 +276,32 @@ public class VideoItemDetailFragment extends Fragment { | |||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
| 
 | 
 | ||||||
|  |             if(info.channel_url != null && info.channel_url != "") { | ||||||
|  |                 channelButton.setOnClickListener(new View.OnClickListener() { | ||||||
|  |                     @Override | ||||||
|  |                     public void onClick(View view) { | ||||||
|  |                         Intent i = new Intent(activity, ChannelActivity.class); | ||||||
|  |                         i.putExtra(ChannelActivity.CHANNEL_URL, info.channel_url); | ||||||
|  |                         i.putExtra(ChannelActivity.SERVICE_ID, info.service_id); | ||||||
|  |                         startActivity(i); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |             } else { | ||||||
|  |                 channelButton.setVisibility(Button.GONE); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             initThumbnailViews(info); | ||||||
|  |              | ||||||
|         } catch (java.lang.NullPointerException e) { |         } catch (java.lang.NullPointerException e) { | ||||||
|             Log.w(TAG, "updateInfo(): Fragment closed before thread ended work... or else"); |             Log.w(TAG, "updateInfo(): Fragment closed before thread ended work... or else"); | ||||||
|             e.printStackTrace(); |             e.printStackTrace(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void initThumbnailViews(StreamInfo info, View nextVideoFrame) { |     private void initThumbnailViews(final StreamInfo info) { | ||||||
|         ImageView videoThumbnailView = (ImageView) activity.findViewById(R.id.detailThumbnailView); |         ImageView videoThumbnailView = (ImageView) activity.findViewById(R.id.detail_thumbnail_view); | ||||||
|         ImageView uploaderThumb |         ImageView uploaderThumb | ||||||
|                 = (ImageView) activity.findViewById(R.id.detailUploaderThumbnailView); |                 = (ImageView) activity.findViewById(R.id.detail_uploader_thumbnail_view); | ||||||
|         ImageView nextVideoThumb = |  | ||||||
|                 (ImageView) nextVideoFrame.findViewById(R.id.itemThumbnailView); |  | ||||||
| 
 | 
 | ||||||
|         if(info.thumbnail_url != null && !info.thumbnail_url.isEmpty()) { |         if(info.thumbnail_url != null && !info.thumbnail_url.isEmpty()) { | ||||||
|             imageLoader.displayImage(info.thumbnail_url, videoThumbnailView, |             imageLoader.displayImage(info.thumbnail_url, videoThumbnailView, | ||||||
| @@ -469,14 +312,16 @@ public class VideoItemDetailFragment extends Fragment { | |||||||
| 
 | 
 | ||||||
|                         @Override |                         @Override | ||||||
|                         public void onLoadingFailed(String imageUri, View view, FailReason failReason) { |                         public void onLoadingFailed(String imageUri, View view, FailReason failReason) { | ||||||
|                             Toast.makeText(VideoItemDetailFragment.this.getActivity(), |                             ErrorActivity.reportError(getActivity(), | ||||||
|                                     R.string.could_not_load_thumbnails, Toast.LENGTH_LONG).show(); |                                     failReason.getCause(), null, rootView, | ||||||
|                             failReason.getCause().printStackTrace(); |                                     ErrorActivity.ErrorInfo.make(ErrorActivity.LOAD_IMAGE, | ||||||
|  |                                             ServiceList.getNameOfService(info.service_id), imageUri, | ||||||
|  |                                             R.string.could_not_load_thumbnails)); | ||||||
|                         } |                         } | ||||||
| 
 | 
 | ||||||
|                         @Override |                         @Override | ||||||
|                         public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { |                         public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { | ||||||
|                             videoThumbnail = loadedImage; |                             streamThumbnail = loadedImage; | ||||||
|                         } |                         } | ||||||
| 
 | 
 | ||||||
|                         @Override |                         @Override | ||||||
| @@ -488,11 +333,8 @@ public class VideoItemDetailFragment extends Fragment { | |||||||
|         } |         } | ||||||
|         if(info.uploader_thumbnail_url != null && !info.uploader_thumbnail_url.isEmpty()) { |         if(info.uploader_thumbnail_url != null && !info.uploader_thumbnail_url.isEmpty()) { | ||||||
|             imageLoader.displayImage(info.uploader_thumbnail_url, |             imageLoader.displayImage(info.uploader_thumbnail_url, | ||||||
|                     uploaderThumb, displayImageOptions, new ThumbnailLoadingListener()); |                     uploaderThumb, displayImageOptions, | ||||||
|         } |                     new ImageErrorLoadingListener(activity, rootView, info.service_id)); | ||||||
|         if(info.thumbnail_url != null && !info.thumbnail_url.isEmpty() && info.next_video != null) { |  | ||||||
|             imageLoader.displayImage(info.next_video.thumbnail_url, |  | ||||||
|                     nextVideoThumb, displayImageOptions, new ThumbnailLoadingListener()); |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @@ -602,9 +444,9 @@ public class VideoItemDetailFragment extends Fragment { | |||||||
|                             info.audio_streams.get(getPreferredAudioStreamId(info)); |                             info.audio_streams.get(getPreferredAudioStreamId(info)); | ||||||
|                     if (!useExternalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 18) { |                     if (!useExternalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 18) { | ||||||
|                         //internal music player: explicit intent |                         //internal music player: explicit intent | ||||||
|                         if (!BackgroundPlayer.isRunning && videoThumbnail != null) { |                         if (!BackgroundPlayer.isRunning && streamThumbnail != null) { | ||||||
|                             ActivityCommunicator.getCommunicator() |                             ActivityCommunicator.getCommunicator() | ||||||
|                                     .backgroundPlayerThumbnail = videoThumbnail; |                                     .backgroundPlayerThumbnail = streamThumbnail; | ||||||
|                             intent = new Intent(activity, BackgroundPlayer.class); |                             intent = new Intent(activity, BackgroundPlayer.class); | ||||||
| 
 | 
 | ||||||
|                             intent.setAction(Intent.ACTION_VIEW); |                             intent.setAction(Intent.ACTION_VIEW); | ||||||
| @@ -684,47 +526,14 @@ public class VideoItemDetailFragment extends Fragment { | |||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void initSimilarVideos(final StreamInfo info, VideoInfoItemViewCreator videoItemViewCreator) { |     private void initSimilarVideos(final StreamInfo info) { | ||||||
|         LinearLayout similarLayout = (LinearLayout) activity.findViewById(R.id.similarVideosView); |         similarStreamsAdapter.addStreamItemList(info.related_streams); | ||||||
|         ArrayList<StreamPreviewInfo> similar = new ArrayList<>(info.related_videos); |  | ||||||
|         for (final StreamPreviewInfo item : similar) { |  | ||||||
|             View similarView = videoItemViewCreator |  | ||||||
|                     .getViewFromVideoInfoItem(null, similarLayout, item); |  | ||||||
| 
 |  | ||||||
|             similarView.setClickable(true); |  | ||||||
|             similarView.setFocusable(true); |  | ||||||
|             int[] attrs = new int[]{R.attr.selectableItemBackground}; |  | ||||||
|             TypedArray typedArray = activity.obtainStyledAttributes(attrs); |  | ||||||
|             int backgroundResource = typedArray.getResourceId(0, 0); |  | ||||||
|             similarView.setBackgroundResource(backgroundResource); |  | ||||||
|             typedArray.recycle(); |  | ||||||
| 
 |  | ||||||
|             similarView.setOnTouchListener(new View.OnTouchListener() { |  | ||||||
|                 @Override |  | ||||||
|                 public boolean onTouch(View v, MotionEvent event) { |  | ||||||
|                     if (event.getAction() == MotionEvent.ACTION_UP) { |  | ||||||
|                         Intent detailIntent = new Intent(activity, VideoItemDetailActivity.class); |  | ||||||
|                         detailIntent.putExtra(VideoItemDetailFragment.VIDEO_URL, item.webpage_url); |  | ||||||
|                         detailIntent.putExtra( |  | ||||||
|                                 VideoItemDetailFragment.STREAMING_SERVICE, streamingServiceId); |  | ||||||
|                         startActivity(detailIntent); |  | ||||||
|                         return true; |  | ||||||
|                     } |  | ||||||
|                     return false; |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
| 
 |  | ||||||
|             similarLayout.addView(similarView); |  | ||||||
|             ImageView rthumb = (ImageView)similarView.findViewById(R.id.itemThumbnailView); |  | ||||||
|             imageLoader.displayImage(item.thumbnail_url, rthumb, |  | ||||||
|                     displayImageOptions, new ThumbnailLoadingListener()); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void onErrorBlockedByGema() { |     private void onErrorBlockedByGema() { | ||||||
|         Button backgroundButton = (Button) |         Button backgroundButton = (Button) | ||||||
|                 activity.findViewById(R.id.detailVideoThumbnailWindowBackgroundButton); |                 activity.findViewById(R.id.detail_stream_thumbnail_window_background_button); | ||||||
|         ImageView thumbnailView = (ImageView) activity.findViewById(R.id.detailThumbnailView); |         ImageView thumbnailView = (ImageView) activity.findViewById(R.id.detail_thumbnail_view); | ||||||
| 
 | 
 | ||||||
|         progressBar.setVisibility(View.GONE); |         progressBar.setVisibility(View.GONE); | ||||||
|         thumbnailView.setImageBitmap(BitmapFactory.decodeResource( |         thumbnailView.setImageBitmap(BitmapFactory.decodeResource( | ||||||
| @@ -744,7 +553,7 @@ public class VideoItemDetailFragment extends Fragment { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void onNotSpecifiedContentError() { |     private void onNotSpecifiedContentError() { | ||||||
|         ImageView thumbnailView = (ImageView) activity.findViewById(R.id.detailThumbnailView); |         ImageView thumbnailView = (ImageView) activity.findViewById(R.id.detail_thumbnail_view); | ||||||
|         progressBar.setVisibility(View.GONE); |         progressBar.setVisibility(View.GONE); | ||||||
|         thumbnailView.setImageBitmap(BitmapFactory.decodeResource( |         thumbnailView.setImageBitmap(BitmapFactory.decodeResource( | ||||||
|                 getResources(), R.drawable.not_available_monkey)); |                 getResources(), R.drawable.not_available_monkey)); | ||||||
| @@ -753,7 +562,7 @@ public class VideoItemDetailFragment extends Fragment { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void onNotSpecifiedContentErrorWithMessage(int resourceId) { |     private void onNotSpecifiedContentErrorWithMessage(int resourceId) { | ||||||
|         ImageView thumbnailView = (ImageView) activity.findViewById(R.id.detailThumbnailView); |         ImageView thumbnailView = (ImageView) activity.findViewById(R.id.detail_thumbnail_view); | ||||||
|         progressBar.setVisibility(View.GONE); |         progressBar.setVisibility(View.GONE); | ||||||
|         thumbnailView.setImageBitmap(BitmapFactory.decodeResource( |         thumbnailView.setImageBitmap(BitmapFactory.decodeResource( | ||||||
|                 getResources(), R.drawable.not_available_monkey)); |                 getResources(), R.drawable.not_available_monkey)); | ||||||
| @@ -779,16 +588,44 @@ public class VideoItemDetailFragment extends Fragment { | |||||||
|     public void onCreate(Bundle savedInstanceState) { |     public void onCreate(Bundle savedInstanceState) { | ||||||
|         super.onCreate(savedInstanceState); |         super.onCreate(savedInstanceState); | ||||||
|         activity = (AppCompatActivity) getActivity(); |         activity = (AppCompatActivity) getActivity(); | ||||||
|         showNextVideoItem = PreferenceManager.getDefaultSharedPreferences(getActivity()) |         showNextStreamItem = PreferenceManager.getDefaultSharedPreferences(getActivity()) | ||||||
|                 .getBoolean(activity.getString(R.string.show_next_video_key), true); |                 .getBoolean(activity.getString(R.string.show_next_video_key), true); | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  |         StreamInfoWorker siw = StreamInfoWorker.getInstance(); | ||||||
|  |         siw.setOnStreamInfoReceivedListener(new StreamInfoWorker.OnStreamInfoReceivedListener() { | ||||||
|  |             @Override | ||||||
|  |             public void onReceive(StreamInfo info) { | ||||||
|  |                 updateInfo(info); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             @Override | ||||||
|  |             public void onError(int messageId) { | ||||||
|  |                 postNewErrorToast(messageId); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             @Override | ||||||
|  |             public void onBlockedByGemaError() { | ||||||
|  |                 onErrorBlockedByGema(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             @Override | ||||||
|  |             public void onContentErrorWithMessage(int messageId) { | ||||||
|  |                 onNotSpecifiedContentErrorWithMessage(messageId); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             @Override | ||||||
|  |             public void onContentError() { | ||||||
|  |                 onNotSpecifiedContentError(); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public View onCreateView(LayoutInflater inflater, ViewGroup container, |     public View onCreateView(LayoutInflater inflater, ViewGroup container, | ||||||
|                              Bundle savedInstanceState) { |                              Bundle savedInstanceState) { | ||||||
|         View rootView = inflater.inflate(R.layout.fragment_videoitem_detail, container, false); |         rootView = inflater.inflate(R.layout.fragment_videoitem_detail, container, false); | ||||||
|         progressBar = (ProgressBar) rootView.findViewById(R.id.detailProgressBar); |         progressBar = (ProgressBar) rootView.findViewById(R.id.detail_progress_bar); | ||||||
| 
 | 
 | ||||||
|         actionBarHandler = new ActionBarHandler(activity); |         actionBarHandler = new ActionBarHandler(activity); | ||||||
|         actionBarHandler.setupNavMenu(activity); |         actionBarHandler.setupNavMenu(activity); | ||||||
| @@ -804,30 +641,25 @@ public class VideoItemDetailFragment extends Fragment { | |||||||
|         super.onActivityCreated(savedInstanceBundle); |         super.onActivityCreated(savedInstanceBundle); | ||||||
|         Activity a = getActivity(); |         Activity a = getActivity(); | ||||||
|         if (android.os.Build.VERSION.SDK_INT < 18) { |         if (android.os.Build.VERSION.SDK_INT < 18) { | ||||||
|             playVideoButton = (FloatingActionButton) a.findViewById(R.id.playVideoButton); |             playVideoButton = (FloatingActionButton) a.findViewById(R.id.play_video_button); | ||||||
|         } |         } | ||||||
|         thumbnailWindowLayout = a.findViewById(R.id.detailVideoThumbnailWindowLayout); |         thumbnailWindowLayout = a.findViewById(R.id.detail_stream_thumbnail_window_layout); | ||||||
|         Button backgroundButton = (Button) |         Button backgroundButton = (Button) | ||||||
|                 a.findViewById(R.id.detailVideoThumbnailWindowBackgroundButton); |                 a.findViewById(R.id.detail_stream_thumbnail_window_background_button); | ||||||
| 
 | 
 | ||||||
|         // Sometimes when this fragment is not visible it still gets initiated |         // Sometimes when this fragment is not visible it still gets initiated | ||||||
|         // then we must not try to access objects of this fragment. |         // then we must not try to access objects of this fragment. | ||||||
|         // Otherwise the applications would crash. |         // Otherwise the applications would crash. | ||||||
|         if(backgroundButton != null) { |         if(backgroundButton != null) { | ||||||
|             try { |             streamingServiceId = getArguments().getInt(STREAMING_SERVICE); | ||||||
|                 streamingServiceId = getArguments().getInt(STREAMING_SERVICE); |             String videoUrl = getArguments().getString(VIDEO_URL); | ||||||
|                 StreamingService streamingService = ServiceList.getService(streamingServiceId); |             StreamInfoWorker siw = StreamInfoWorker.getInstance(); | ||||||
|                 Thread videoExtractorThread = new Thread(new VideoExtractorRunnable( |             siw.search(streamingServiceId, videoUrl, getActivity()); | ||||||
|                         getArguments().getString(VIDEO_URL), streamingService)); |  | ||||||
| 
 | 
 | ||||||
|                 autoPlayEnabled = getArguments().getBoolean(AUTO_PLAY); |             autoPlayEnabled = getArguments().getBoolean(AUTO_PLAY); | ||||||
|                 videoExtractorThread.start(); |  | ||||||
|             } catch (Exception e) { |  | ||||||
|                 e.printStackTrace(); |  | ||||||
|             } |  | ||||||
| 
 | 
 | ||||||
|             if(Build.VERSION.SDK_INT >= 18) { |             if(Build.VERSION.SDK_INT >= 18) { | ||||||
|                 ImageView thumbnailView = (ImageView) activity.findViewById(R.id.detailThumbnailView); |                 ImageView thumbnailView = (ImageView) activity.findViewById(R.id.detail_thumbnail_view); | ||||||
|                 thumbnailView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { |                 thumbnailView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { | ||||||
|                     // This is used to synchronize the thumbnailWindowButton and the playVideoButton |                     // This is used to synchronize the thumbnailWindowButton and the playVideoButton | ||||||
|                     // inside the ScrollView with the actual size of the thumbnail. |                     // inside the ScrollView with the actual size of the thumbnail. | ||||||
| @@ -849,6 +681,17 @@ public class VideoItemDetailFragment extends Fragment { | |||||||
|                     } |                     } | ||||||
|                 }); |                 }); | ||||||
|             } |             } | ||||||
|  | 
 | ||||||
|  |             similarStreamsAdapter = new InfoListAdapter(getActivity(), rootView); | ||||||
|  |             RecyclerView rv = (RecyclerView) getActivity().findViewById(R.id.similar_streams_view); | ||||||
|  |             rv.setLayoutManager(new LinearLayoutManager(getActivity())); | ||||||
|  |             rv.setAdapter(similarStreamsAdapter); | ||||||
|  |             similarStreamsAdapter.setOnItemSelectedListener(new InfoListAdapter.OnItemSelectedListener() { | ||||||
|  |                 @Override | ||||||
|  |                 public void selected(String url) { | ||||||
|  |                     openStreamUrl(url); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @@ -946,23 +789,16 @@ public class VideoItemDetailFragment extends Fragment { | |||||||
|         this.onInvokeCreateOptionsMenuListener = listener; |         this.onInvokeCreateOptionsMenuListener = listener; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void postNewErrorToast(Handler h, final int stringResource) { |     private void postNewErrorToast(final int stringResource) { | ||||||
|         h.post(new Runnable() { |         Toast.makeText(VideoItemDetailFragment.this.getActivity(), | ||||||
|             @Override |                 stringResource, Toast.LENGTH_LONG).show(); | ||||||
|             public void run() { |  | ||||||
|                 Toast.makeText(VideoItemDetailFragment.this.getActivity(), |  | ||||||
|                         stringResource, Toast.LENGTH_LONG).show(); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void postNewErrorToast(Handler h, final String message) { |     private void openStreamUrl(String url) { | ||||||
|         h.post(new Runnable() { |         Intent detailIntent = new Intent(activity, VideoItemDetailActivity.class); | ||||||
|             @Override |         detailIntent.putExtra(VideoItemDetailFragment.VIDEO_URL, url); | ||||||
|             public void run() { |         detailIntent.putExtra( | ||||||
|                 Toast.makeText(VideoItemDetailFragment.this.getActivity(), |                 VideoItemDetailFragment.STREAMING_SERVICE, streamingServiceId); | ||||||
|                         message, Toast.LENGTH_LONG).show(); |         activity.startActivity(detailIntent); | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -25,7 +25,6 @@ import android.widget.SeekBar; | |||||||
| import android.widget.TextView; | import android.widget.TextView; | ||||||
|  |  | ||||||
| import org.schabi.newpipe.App; | import org.schabi.newpipe.App; | ||||||
| import org.schabi.newpipe.NewPipeSettings; |  | ||||||
| import org.schabi.newpipe.R; | import org.schabi.newpipe.R; | ||||||
|  |  | ||||||
| import java.io.File; | import java.io.File; | ||||||
|   | |||||||
| @@ -8,14 +8,12 @@ import android.content.Context; | |||||||
| import android.content.Intent; | import android.content.Intent; | ||||||
| import android.content.ServiceConnection; | import android.content.ServiceConnection; | ||||||
| import android.content.SharedPreferences; | import android.content.SharedPreferences; | ||||||
| import android.os.AsyncTask; |  | ||||||
| import android.os.Bundle; | import android.os.Bundle; | ||||||
| import android.os.IBinder; | import android.os.IBinder; | ||||||
| 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.support.v7.widget.Toolbar; | ||||||
| import android.util.Log; |  | ||||||
| import android.view.LayoutInflater; | import android.view.LayoutInflater; | ||||||
| import android.view.Menu; | import android.view.Menu; | ||||||
| import android.view.MenuInflater; | import android.view.MenuInflater; | ||||||
| @@ -23,25 +21,16 @@ import android.view.MenuItem; | |||||||
| import android.view.View; | import android.view.View; | ||||||
| import android.view.ViewTreeObserver; | import android.view.ViewTreeObserver; | ||||||
| import android.widget.AdapterView; | import android.widget.AdapterView; | ||||||
| import android.widget.Button; |  | ||||||
| import android.widget.EditText; | import android.widget.EditText; | ||||||
| import android.widget.SeekBar; | import android.widget.SeekBar; | ||||||
| import android.widget.TextView; | import android.widget.TextView; | ||||||
| import android.widget.Toast; | import android.widget.Toast; | ||||||
| import android.support.v7.widget.SearchView; |  | ||||||
|  |  | ||||||
| import org.schabi.newpipe.ErrorActivity; | import org.schabi.newpipe.ErrorActivity; | ||||||
| import org.schabi.newpipe.R; | import org.schabi.newpipe.R; | ||||||
| import org.schabi.newpipe.SettingsActivity; | import org.schabi.newpipe.settings.SettingsActivity; | ||||||
| import org.schabi.newpipe.VideoItemDetailActivity; |  | ||||||
| import org.schabi.newpipe.VideoItemListActivity; |  | ||||||
| import org.schabi.newpipe.extractor.ServiceList; |  | ||||||
|  |  | ||||||
| import java.io.File; | import java.io.File; | ||||||
| import java.io.IOException; |  | ||||||
| import java.net.HttpURLConnection; |  | ||||||
| import java.net.MalformedURLException; |  | ||||||
| import java.net.URL; |  | ||||||
| import java.util.Vector; | import java.util.Vector; | ||||||
|  |  | ||||||
| import us.shandian.giga.get.DownloadManager; | import us.shandian.giga.get.DownloadManager; | ||||||
| @@ -257,7 +246,7 @@ public class MainActivity extends AppCompatActivity implements AdapterView.OnIte | |||||||
|  |  | ||||||
|         switch (id) { |         switch (id) { | ||||||
|             case android.R.id.home: { |             case android.R.id.home: { | ||||||
|                 Intent intent = new Intent(this, VideoItemListActivity.class); |                 Intent intent = new Intent(this, org.schabi.newpipe.MainActivity.class); | ||||||
|                 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); |                 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); | ||||||
|                 NavUtils.navigateUpTo(this, intent); |                 NavUtils.navigateUpTo(this, intent); | ||||||
|                 return true; |                 return true; | ||||||
| @@ -268,7 +257,7 @@ public class MainActivity extends AppCompatActivity implements AdapterView.OnIte | |||||||
|                 return true; |                 return true; | ||||||
|             } |             } | ||||||
|             case R.id.action_report_error: { |             case R.id.action_report_error: { | ||||||
|                 ErrorActivity.reportError(MainActivity.this, new Vector<Exception>(), |                 ErrorActivity.reportError(MainActivity.this, new Vector<Throwable>(), | ||||||
|                         null, null, |                         null, null, | ||||||
|                         ErrorActivity.ErrorInfo.make(ErrorActivity.USER_REPORT, |                         ErrorActivity.ErrorInfo.make(ErrorActivity.USER_REPORT, | ||||||
|                                 null, |                                 null, | ||||||
|   | |||||||
| @@ -0,0 +1,55 @@ | |||||||
|  | package org.schabi.newpipe.extractor; | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Created by Christian Schabesberger on 25.07.16. | ||||||
|  |  * | ||||||
|  |  * Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org> | ||||||
|  |  * ChannelExtractor.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 abstract class ChannelExtractor { | ||||||
|  |     private int serviceId; | ||||||
|  |     private String url; | ||||||
|  |     private UrlIdHandler urlIdHandler; | ||||||
|  |     private Downloader downloader; | ||||||
|  |     private StreamPreviewInfoCollector previewInfoCollector; | ||||||
|  |  | ||||||
|  |     public ChannelExtractor(UrlIdHandler urlIdHandler, String url, int page, Downloader dl, int serviceId) | ||||||
|  |             throws ExtractionException, IOException { | ||||||
|  |         this.serviceId = serviceId; | ||||||
|  |         this.urlIdHandler = urlIdHandler; | ||||||
|  |         previewInfoCollector = new StreamPreviewInfoCollector(urlIdHandler, serviceId); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public String getUrl() { return url; } | ||||||
|  |     public UrlIdHandler getUrlIdHandler() { return urlIdHandler; } | ||||||
|  |     public Downloader getDownloader() { return downloader; } | ||||||
|  |     public StreamPreviewInfoCollector getStreamPreviewInfoCollector() { | ||||||
|  |         return previewInfoCollector; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public abstract String getChannelName() throws ParsingException; | ||||||
|  |     public abstract String getAvatarUrl() throws ParsingException; | ||||||
|  |     public abstract String getBannerUrl() throws ParsingException; | ||||||
|  |     public abstract String getFeedUrl() throws ParsingException; | ||||||
|  |     public abstract StreamPreviewInfoCollector getStreams() throws ParsingException; | ||||||
|  |     public abstract boolean hasNextPage() throws ParsingException; | ||||||
|  |     public int getServiceId() { | ||||||
|  |         return serviceId; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,79 @@ | |||||||
|  | package org.schabi.newpipe.extractor; | ||||||
|  |  | ||||||
|  | import android.util.Log; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Vector; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Created by Christian Schabesberger on 31.07.16. | ||||||
|  |  * | ||||||
|  |  * Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org> | ||||||
|  |  * ChannelInfo.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 ChannelInfo { | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     public void addException(Exception e) { | ||||||
|  |         errors.add(e); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static ChannelInfo getInfo(ChannelExtractor extractor, Downloader dl) | ||||||
|  |         throws ParsingException { | ||||||
|  |         ChannelInfo info = new ChannelInfo(); | ||||||
|  |  | ||||||
|  |         // importand data | ||||||
|  |         info.service_id = extractor.getServiceId(); | ||||||
|  |         info.channel_name = extractor.getChannelName(); | ||||||
|  |         info.hasNextPage = extractor.hasNextPage(); | ||||||
|  |  | ||||||
|  |         try { | ||||||
|  |             info.avatar_url = extractor.getAvatarUrl(); | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             info.errors.add(e); | ||||||
|  |         } | ||||||
|  |         try { | ||||||
|  |             info.banner_url = extractor.getBannerUrl(); | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             info.errors.add(e); | ||||||
|  |         } | ||||||
|  |         try { | ||||||
|  |             info.feed_url = extractor.getFeedUrl(); | ||||||
|  |         } catch(Exception e) { | ||||||
|  |             info.errors.add(e); | ||||||
|  |         } | ||||||
|  |         try { | ||||||
|  |             StreamPreviewInfoCollector c = extractor.getStreams(); | ||||||
|  |             info.related_streams = c.getItemList(); | ||||||
|  |             info.errors.addAll(c.getErrors()); | ||||||
|  |         } catch(Exception e) { | ||||||
|  |             info.errors.add(e); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return info; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public int service_id = -1; | ||||||
|  |     public String channel_name = ""; | ||||||
|  |     public String avatar_url = ""; | ||||||
|  |     public String banner_url = ""; | ||||||
|  |     public String feed_url = ""; | ||||||
|  |     public List<StreamPreviewInfo> related_streams = null; | ||||||
|  |     public boolean hasNextPage = false; | ||||||
|  |  | ||||||
|  |     public List<Throwable> errors = new Vector<>(); | ||||||
|  | } | ||||||
| @@ -1,6 +1,7 @@ | |||||||
| package org.schabi.newpipe.extractor; | package org.schabi.newpipe.extractor; | ||||||
|  |  | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
|  | import java.util.Map; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Created by Christian Schabesberger on 28.01.16. |  * Created by Christian Schabesberger on 28.01.16. | ||||||
| @@ -32,6 +33,14 @@ public interface Downloader { | |||||||
|      * @throws IOException*/ |      * @throws IOException*/ | ||||||
|     String download(String siteUrl, String language) throws IOException; |     String download(String siteUrl, String language) throws IOException; | ||||||
|  |  | ||||||
|  |     /**Download the text file at the supplied URL as in download(String), | ||||||
|  |      * but set the HTTP header field "Accept-Language" to the supplied string. | ||||||
|  |      * @param siteUrl the URL of the text file to return the contents of | ||||||
|  |      * @param customProperties set request header properties | ||||||
|  |      * @return the contents of the specified text file | ||||||
|  |      * @throws IOException*/ | ||||||
|  |     String download(String siteUrl, Map<String, String> customProperties) throws IOException; | ||||||
|  |  | ||||||
|     /**Download (via HTTP) the text file located at the supplied URL, and return its contents. |     /**Download (via HTTP) the text file located at the supplied URL, and return its contents. | ||||||
|      * Primarily intended for downloading web pages. |      * Primarily intended for downloading web pages. | ||||||
|      * @param siteUrl the URL of the text file to download |      * @param siteUrl the URL of the text file to download | ||||||
|   | |||||||
| @@ -33,7 +33,7 @@ public abstract class SearchEngine { | |||||||
|  |  | ||||||
|     private StreamPreviewInfoSearchCollector collector; |     private StreamPreviewInfoSearchCollector collector; | ||||||
|  |  | ||||||
|     public SearchEngine(StreamUrlIdHandler urlIdHandler, int serviceId) { |     public SearchEngine(UrlIdHandler urlIdHandler, int serviceId) { | ||||||
|         collector = new StreamPreviewInfoSearchCollector(urlIdHandler, serviceId); |         collector = new StreamPreviewInfoSearchCollector(urlIdHandler, serviceId); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -43,5 +43,5 @@ public class SearchResult { | |||||||
|  |  | ||||||
|     public String suggestion = ""; |     public String suggestion = ""; | ||||||
|     public List<StreamPreviewInfo> resultList = new Vector<>(); |     public List<StreamPreviewInfo> resultList = new Vector<>(); | ||||||
|     public List<Exception> errors = new Vector<>(); |     public List<Throwable> errors = new Vector<>(); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -40,7 +40,7 @@ public class ServiceList { | |||||||
|     public static StreamingService[] getServices() { |     public static StreamingService[] getServices() { | ||||||
|         return services; |         return services; | ||||||
|     } |     } | ||||||
|     public static StreamingService getService(int serviceId) throws ExtractionException { |     public static StreamingService getService(int serviceId)throws ExtractionException { | ||||||
|         for(StreamingService s : services) { |         for(StreamingService s : services) { | ||||||
|             if(s.getServiceId() == serviceId) { |             if(s.getServiceId() == serviceId) { | ||||||
|                 return s; |                 return s; | ||||||
|   | |||||||
| @@ -30,7 +30,7 @@ public abstract class StreamExtractor { | |||||||
|  |  | ||||||
|     private int serviceId; |     private int serviceId; | ||||||
|     private String url; |     private String url; | ||||||
|     private StreamUrlIdHandler urlIdHandler; |     private UrlIdHandler urlIdHandler; | ||||||
|     private Downloader downloader; |     private Downloader downloader; | ||||||
|     private StreamPreviewInfoCollector previewInfoCollector; |     private StreamPreviewInfoCollector previewInfoCollector; | ||||||
|  |  | ||||||
| @@ -55,7 +55,7 @@ public abstract class StreamExtractor { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public StreamExtractor(StreamUrlIdHandler urlIdHandler, String url, Downloader dl, int serviceId) { |     public StreamExtractor(UrlIdHandler urlIdHandler, String url, Downloader dl, int serviceId) { | ||||||
|         this.serviceId = serviceId; |         this.serviceId = serviceId; | ||||||
|         this.urlIdHandler = urlIdHandler; |         this.urlIdHandler = urlIdHandler; | ||||||
|         previewInfoCollector = new StreamPreviewInfoCollector(urlIdHandler, serviceId); |         previewInfoCollector = new StreamPreviewInfoCollector(urlIdHandler, serviceId); | ||||||
| @@ -69,7 +69,7 @@ public abstract class StreamExtractor { | |||||||
|         return url; |         return url; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public StreamUrlIdHandler getUrlIdHandler() { |     public UrlIdHandler getUrlIdHandler() { | ||||||
|         return urlIdHandler; |         return urlIdHandler; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -81,6 +81,7 @@ public abstract class StreamExtractor { | |||||||
|     public abstract String getTitle() throws ParsingException; |     public abstract String getTitle() throws ParsingException; | ||||||
|     public abstract String getDescription() throws ParsingException; |     public abstract String getDescription() throws ParsingException; | ||||||
|     public abstract String getUploader() throws ParsingException; |     public abstract String getUploader() throws ParsingException; | ||||||
|  |     public abstract String getChannelUrl() throws ParsingException; | ||||||
|     public abstract int getLength() throws ParsingException; |     public abstract int getLength() throws ParsingException; | ||||||
|     public abstract long getViewCount() throws ParsingException; |     public abstract long getViewCount() throws ParsingException; | ||||||
|     public abstract String getUploadDate() throws ParsingException; |     public abstract String getUploadDate() throws ParsingException; | ||||||
|   | |||||||
| @@ -85,12 +85,12 @@ public class StreamInfo extends AbstractVideoInfo { | |||||||
|         /* ---- importand data, withoug the video can't be displayed goes here: ---- */ |         /* ---- importand data, withoug the video can't be displayed goes here: ---- */ | ||||||
|         // if one of these is not available an exception is ment to be thrown directly into the frontend. |         // if one of these is not available an exception is ment to be thrown directly into the frontend. | ||||||
|  |  | ||||||
|         StreamUrlIdHandler uiconv = extractor.getUrlIdHandler(); |         UrlIdHandler uiconv = extractor.getUrlIdHandler(); | ||||||
|  |  | ||||||
|         streamInfo.service_id = extractor.getServiceId(); |         streamInfo.service_id = extractor.getServiceId(); | ||||||
|         streamInfo.webpage_url = extractor.getPageUrl(); |         streamInfo.webpage_url = extractor.getPageUrl(); | ||||||
|         streamInfo.stream_type = extractor.getStreamType(); |         streamInfo.stream_type = extractor.getStreamType(); | ||||||
|         streamInfo.id = uiconv.getVideoId(extractor.getPageUrl()); |         streamInfo.id = uiconv.getId(extractor.getPageUrl()); | ||||||
|         streamInfo.title = extractor.getTitle(); |         streamInfo.title = extractor.getTitle(); | ||||||
|         streamInfo.age_limit = extractor.getAgeLimit(); |         streamInfo.age_limit = extractor.getAgeLimit(); | ||||||
|  |  | ||||||
| @@ -188,6 +188,11 @@ public class StreamInfo extends AbstractVideoInfo { | |||||||
|         } catch(Exception e) { |         } catch(Exception e) { | ||||||
|             streamInfo.addException(e); |             streamInfo.addException(e); | ||||||
|         } |         } | ||||||
|  |         try { | ||||||
|  |             streamInfo.channel_url = extractor.getChannelUrl(); | ||||||
|  |         } catch(Exception e) { | ||||||
|  |             streamInfo.addException(e); | ||||||
|  |         } | ||||||
|         try { |         try { | ||||||
|             streamInfo.description = extractor.getDescription(); |             streamInfo.description = extractor.getDescription(); | ||||||
|         } catch(Exception e) { |         } catch(Exception e) { | ||||||
| @@ -248,7 +253,7 @@ public class StreamInfo extends AbstractVideoInfo { | |||||||
|         try { |         try { | ||||||
|             // get related videos |             // get related videos | ||||||
|             StreamPreviewInfoCollector c = extractor.getRelatedVideos(); |             StreamPreviewInfoCollector c = extractor.getRelatedVideos(); | ||||||
|             streamInfo.related_videos = c.getItemList(); |             streamInfo.related_streams = c.getItemList(); | ||||||
|             streamInfo.errors.addAll(c.getErrors()); |             streamInfo.errors.addAll(c.getErrors()); | ||||||
|         } catch(Exception e) { |         } catch(Exception e) { | ||||||
|             streamInfo.addException(e); |             streamInfo.addException(e); | ||||||
| @@ -258,6 +263,7 @@ public class StreamInfo extends AbstractVideoInfo { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public String uploader_thumbnail_url = ""; |     public String uploader_thumbnail_url = ""; | ||||||
|  |     public String channel_url = ""; | ||||||
|     public String description = ""; |     public String description = ""; | ||||||
|  |  | ||||||
|     public List<VideoStream> video_streams = null; |     public List<VideoStream> video_streams = null; | ||||||
| @@ -275,9 +281,9 @@ public class StreamInfo extends AbstractVideoInfo { | |||||||
|     public int dislike_count = -1; |     public int dislike_count = -1; | ||||||
|     public String average_rating = ""; |     public String average_rating = ""; | ||||||
|     public StreamPreviewInfo next_video = null; |     public StreamPreviewInfo next_video = null; | ||||||
|     public List<StreamPreviewInfo> related_videos = null; |     public List<StreamPreviewInfo> related_streams = null; | ||||||
|     //in seconds. some metadata is not passed using a StreamInfo object! |     //in seconds. some metadata is not passed using a StreamInfo object! | ||||||
|     public int start_position = 0; |     public int start_position = 0; | ||||||
|  |  | ||||||
|     public List<Exception> errors = new Vector<>(); |     public List<Throwable> errors = new Vector<>(); | ||||||
| } | } | ||||||
| @@ -27,11 +27,11 @@ import java.util.Vector; | |||||||
|  |  | ||||||
| public class StreamPreviewInfoCollector { | public class StreamPreviewInfoCollector { | ||||||
|     private List<StreamPreviewInfo> itemList = new Vector<>(); |     private List<StreamPreviewInfo> itemList = new Vector<>(); | ||||||
|     private List<Exception> errors = new Vector<>(); |     private List<Throwable> errors = new Vector<>(); | ||||||
|     private StreamUrlIdHandler urlIdHandler; |     private UrlIdHandler urlIdHandler; | ||||||
|     private int serviceId = -1; |     private int serviceId = -1; | ||||||
|  |  | ||||||
|     public StreamPreviewInfoCollector(StreamUrlIdHandler handler, int serviceId) { |     public StreamPreviewInfoCollector(UrlIdHandler handler, int serviceId) { | ||||||
|         urlIdHandler = handler; |         urlIdHandler = handler; | ||||||
|         this.serviceId = serviceId; |         this.serviceId = serviceId; | ||||||
|     } |     } | ||||||
| @@ -40,7 +40,7 @@ public class StreamPreviewInfoCollector { | |||||||
|         return itemList; |         return itemList; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public List<Exception> getErrors() { |     public List<Throwable> getErrors() { | ||||||
|         return errors; |         return errors; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -57,7 +57,7 @@ public class StreamPreviewInfoCollector { | |||||||
|             if (urlIdHandler == null) { |             if (urlIdHandler == null) { | ||||||
|                 throw new ParsingException("Error: UrlIdHandler not set"); |                 throw new ParsingException("Error: UrlIdHandler not set"); | ||||||
|             } else if(!resultItem.webpage_url.isEmpty()) { |             } else if(!resultItem.webpage_url.isEmpty()) { | ||||||
|                 resultItem.id = (new YoutubeStreamUrlIdHandler()).getVideoId(resultItem.webpage_url); |                 resultItem.id = (new YoutubeStreamUrlIdHandler()).getId(resultItem.webpage_url); | ||||||
|             } |             } | ||||||
|             resultItem.title = extractor.getTitle(); |             resultItem.title = extractor.getTitle(); | ||||||
|             resultItem.stream_type = extractor.getStreamType(); |             resultItem.stream_type = extractor.getStreamType(); | ||||||
|   | |||||||
| @@ -24,7 +24,7 @@ public class StreamPreviewInfoSearchCollector extends StreamPreviewInfoCollector | |||||||
|  |  | ||||||
|     private String suggestion = ""; |     private String suggestion = ""; | ||||||
|  |  | ||||||
|     public StreamPreviewInfoSearchCollector(StreamUrlIdHandler handler, int serviceId) { |     public StreamPreviewInfoSearchCollector(UrlIdHandler handler, int serviceId) { | ||||||
|         super(handler, serviceId); |         super(handler, serviceId); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -38,7 +38,10 @@ public abstract class StreamingService { | |||||||
|     public abstract StreamExtractor getExtractorInstance(String url, Downloader downloader) |     public abstract StreamExtractor getExtractorInstance(String url, Downloader downloader) | ||||||
|             throws IOException, ExtractionException; |             throws IOException, ExtractionException; | ||||||
|     public abstract SearchEngine getSearchEngineInstance(Downloader downloader); |     public abstract SearchEngine getSearchEngineInstance(Downloader downloader); | ||||||
|     public abstract StreamUrlIdHandler getUrlIdHandlerInstance(); |     public abstract UrlIdHandler getUrlIdHandlerInstance(); | ||||||
|  |     public abstract UrlIdHandler getChannelUrlIdHandlerInstance(); | ||||||
|  |     public abstract ChannelExtractor getChannelExtractorInstance(String url, int page, Downloader downloader) | ||||||
|  |             throws ExtractionException, IOException; | ||||||
|  |  | ||||||
|     public final int getServiceId() { |     public final int getServiceId() { | ||||||
|         return serviceId; |         return serviceId; | ||||||
|   | |||||||
| @@ -1,10 +1,10 @@ | |||||||
| package org.schabi.newpipe.extractor; | package org.schabi.newpipe.extractor; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Created by Christian Schabesberger on 02.02.16. |  * Created by Christian Schabesberger on 26.07.16. | ||||||
|  * |  * | ||||||
|  * Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org> |  * Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org> | ||||||
|  * StreamUrlIdHandler.java is part of NewPipe. |  * UrlIdHandler.java is part of NewPipe. | ||||||
|  * |  * | ||||||
|  * 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 | ||||||
| @@ -20,9 +20,9 @@ package org.schabi.newpipe.extractor; | |||||||
|  * along with NewPipe.  If not, see <http://www.gnu.org/licenses/>. |  * along with NewPipe.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| public interface StreamUrlIdHandler { | public interface UrlIdHandler { | ||||||
|     String getVideoUrl(String videoId); |     String getUrl(String videoId); | ||||||
|     String getVideoId(String siteUrl) throws ParsingException; |     String getId(String siteUrl) throws ParsingException; | ||||||
|     String cleanUrl(String siteUrl) throws ParsingException; |     String cleanUrl(String siteUrl) throws ParsingException; | ||||||
| 
 | 
 | ||||||
|     /**When a VIEW_ACTION is caught this function will test if the url delivered within the calling |     /**When a VIEW_ACTION is caught this function will test if the url delivered within the calling | ||||||
| @@ -0,0 +1,331 @@ | |||||||
|  | package org.schabi.newpipe.extractor.services.youtube; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | import org.json.JSONException; | ||||||
|  | import org.json.JSONObject; | ||||||
|  | import org.jsoup.Jsoup; | ||||||
|  | import org.jsoup.nodes.Document; | ||||||
|  | import org.jsoup.nodes.Element; | ||||||
|  | import org.schabi.newpipe.extractor.AbstractVideoInfo; | ||||||
|  | import org.schabi.newpipe.extractor.ChannelExtractor; | ||||||
|  | import org.schabi.newpipe.extractor.Downloader; | ||||||
|  | import org.schabi.newpipe.extractor.ExtractionException; | ||||||
|  | import org.schabi.newpipe.extractor.Parser; | ||||||
|  | import org.schabi.newpipe.extractor.ParsingException; | ||||||
|  | import org.schabi.newpipe.extractor.StreamPreviewInfoCollector; | ||||||
|  | import org.schabi.newpipe.extractor.StreamPreviewInfoExtractor; | ||||||
|  | import org.schabi.newpipe.extractor.UrlIdHandler; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.util.HashMap; | ||||||
|  | import java.util.Map; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Created by Christian Schabesberger on 25.07.16. | ||||||
|  |  * | ||||||
|  |  * Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org> | ||||||
|  |  * YoutubeChannelExtractor.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 YoutubeChannelExtractor extends ChannelExtractor { | ||||||
|  |  | ||||||
|  |     private static final String TAG = YoutubeChannelExtractor.class.toString(); | ||||||
|  |  | ||||||
|  |     // private CSSOMParser cssParser = new CSSOMParser(new SACParserCSS3()); | ||||||
|  |  | ||||||
|  |     private Downloader downloader; | ||||||
|  |     private Document doc = null; | ||||||
|  |  | ||||||
|  |     private boolean isAjaxPage = false; | ||||||
|  |     private static String userUrl = ""; | ||||||
|  |     private static String channelName = ""; | ||||||
|  |     private static String avatarUrl = ""; | ||||||
|  |     private static String bannerUrl = ""; | ||||||
|  |     private static String feedUrl = ""; | ||||||
|  |     // the fist page is html all other pages are ajax. Every new page can be requested by sending | ||||||
|  |     // this request url. | ||||||
|  |     private static String nextPageUrl = ""; | ||||||
|  |  | ||||||
|  |     public YoutubeChannelExtractor(UrlIdHandler urlIdHandler, String url, int page, Downloader dl, int serviceId) | ||||||
|  |             throws ExtractionException, IOException { | ||||||
|  |         super(urlIdHandler, url, page, dl, serviceId); | ||||||
|  |  | ||||||
|  |         url = urlIdHandler.cleanUrl(url) ; //+ "/video?veiw=0&flow=list&sort=dd"; | ||||||
|  |         downloader = dl; | ||||||
|  |  | ||||||
|  |         if(page == 0) { | ||||||
|  |             if (isUserUrl(url)) { | ||||||
|  |                 userUrl = url; | ||||||
|  |             } else { | ||||||
|  |                 // we first need to get the user url. Otherwise we can't find videos | ||||||
|  |                 String channelPageContent = downloader.download(url); | ||||||
|  |                 Document channelDoc = Jsoup.parse(channelPageContent, url); | ||||||
|  |                 userUrl = getUserUrl(channelDoc); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             userUrl = userUrl + "/videos?veiw=0&flow=list&sort=dd&live_view=10000"; | ||||||
|  |             String pageContent = downloader.download(userUrl); | ||||||
|  |             doc = Jsoup.parse(pageContent, userUrl); | ||||||
|  |             nextPageUrl = getNextPageUrl(doc); | ||||||
|  |             isAjaxPage = false; | ||||||
|  |         } else { | ||||||
|  |             String ajaxDataRaw = downloader.download(nextPageUrl); | ||||||
|  |             JSONObject ajaxData; | ||||||
|  |             try { | ||||||
|  |                 ajaxData = new JSONObject(ajaxDataRaw); | ||||||
|  |                 String htmlDataRaw = ajaxData.getString("content_html"); | ||||||
|  |                 doc = Jsoup.parse(htmlDataRaw, nextPageUrl); | ||||||
|  |  | ||||||
|  |                 String nextPageHtmlDataRaw = ajaxData.getString("load_more_widget_html"); | ||||||
|  |                 if(!nextPageHtmlDataRaw.isEmpty()) { | ||||||
|  |                     Document nextPageData = Jsoup.parse(nextPageHtmlDataRaw, nextPageUrl); | ||||||
|  |                     nextPageUrl = getNextPageUrl(nextPageData); | ||||||
|  |                 } else { | ||||||
|  |                     nextPageUrl = ""; | ||||||
|  |                 } | ||||||
|  |             } catch (JSONException e) { | ||||||
|  |                 throw new ParsingException("Could not parse json data for next page", e); | ||||||
|  |             } | ||||||
|  |             isAjaxPage = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String getChannelName() throws ParsingException { | ||||||
|  |         try { | ||||||
|  |             if(!isAjaxPage) { | ||||||
|  |                 channelName = doc.select("span[class=\"qualified-channel-title-text\"]").first() | ||||||
|  |                         .select("a").first().text(); | ||||||
|  |             } | ||||||
|  |             return channelName; | ||||||
|  |         } catch(Exception e) { | ||||||
|  |             throw new ParsingException("Could not get channel name"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String getAvatarUrl() throws ParsingException { | ||||||
|  |         try { | ||||||
|  |             if(!isAjaxPage) { | ||||||
|  |                 avatarUrl = doc.select("img[class=\"channel-header-profile-image\"]") | ||||||
|  |                         .first().attr("abs:src"); | ||||||
|  |             } | ||||||
|  |             return avatarUrl; | ||||||
|  |         } catch(Exception e) { | ||||||
|  |             throw new ParsingException("Could not get avatar", e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String getBannerUrl() throws ParsingException { | ||||||
|  |         try { | ||||||
|  |             if(!isAjaxPage) { | ||||||
|  |                 Element el = doc.select("div[id=\"gh-banner\"]").first().select("style").first(); | ||||||
|  |                 String cssContent = el.html(); | ||||||
|  |                 String url = "https:" + Parser.matchGroup1("url\\((.*)\\)", cssContent); | ||||||
|  |                 if (url.contains("s.ytimg.com")) { | ||||||
|  |                     bannerUrl = null; | ||||||
|  |                 } else { | ||||||
|  |                     bannerUrl = url; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return bannerUrl; | ||||||
|  |         } catch(Exception e) { | ||||||
|  |             throw new ParsingException("Could not get Banner", e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public StreamPreviewInfoCollector getStreams() throws ParsingException { | ||||||
|  |         StreamPreviewInfoCollector collector = getStreamPreviewInfoCollector(); | ||||||
|  |         Element ul = null; | ||||||
|  |         if(isAjaxPage) { | ||||||
|  |             ul = doc.select("body").first(); | ||||||
|  |         } else { | ||||||
|  |             ul = doc.select("ul[id=\"browse-items-primary\"]").first(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         for(final Element li : ul.children()) { | ||||||
|  |             if (li.select("div[class=\"feed-item-dismissable\"]").first() != null) { | ||||||
|  |                 collector.commit(new StreamPreviewInfoExtractor() { | ||||||
|  |                     @Override | ||||||
|  |                     public AbstractVideoInfo.StreamType getStreamType() throws ParsingException { | ||||||
|  |                         return AbstractVideoInfo.StreamType.VIDEO_STREAM; | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     @Override | ||||||
|  |                     public String getWebPageUrl() throws ParsingException { | ||||||
|  |                         try { | ||||||
|  |                             Element el = li.select("div[class=\"feed-item-dismissable\"]").first(); | ||||||
|  |                             Element dl = el.select("h3").first().select("a").first(); | ||||||
|  |                             return dl.attr("abs:href"); | ||||||
|  |                         } catch (Exception e) { | ||||||
|  |                             throw new ParsingException("Could not get web page url for the video", e); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     @Override | ||||||
|  |                     public String getTitle() throws ParsingException { | ||||||
|  |                         try { | ||||||
|  |                             Element el = li.select("div[class=\"feed-item-dismissable\"]").first(); | ||||||
|  |                             Element dl = el.select("h3").first().select("a").first(); | ||||||
|  |                             return dl.text(); | ||||||
|  |                         } catch (Exception e) { | ||||||
|  |                             throw new ParsingException("Could not get title", e); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     @Override | ||||||
|  |                     public int getDuration() throws ParsingException { | ||||||
|  |                         try { | ||||||
|  |                             return YoutubeParsingHelper.parseDurationString( | ||||||
|  |                                     li.select("span[class=\"video-time\"]").first().text()); | ||||||
|  |                         } catch(Exception e) { | ||||||
|  |                             if(isLiveStream(li)) { | ||||||
|  |                                 // -1 for no duration | ||||||
|  |                                 return -1; | ||||||
|  |                             } else { | ||||||
|  |                                 throw new ParsingException("Could not get Duration: " + getTitle(), e); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     @Override | ||||||
|  |                     public String getUploader() throws ParsingException { | ||||||
|  |                         return getChannelName(); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     @Override | ||||||
|  |                     public String getUploadDate() throws ParsingException { | ||||||
|  |                         try { | ||||||
|  |                             return li.select("div[class=\"yt-lockup-meta\"]").first() | ||||||
|  |                                     .select("li").first() | ||||||
|  |                                     .text(); | ||||||
|  |                         } catch(Exception e) { | ||||||
|  |                             throw new ParsingException("Could not get uplaod date", e); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     @Override | ||||||
|  |                     public long getViewCount() throws ParsingException { | ||||||
|  |                         String output; | ||||||
|  |                         String input; | ||||||
|  |                         try { | ||||||
|  |                             input = li.select("div[class=\"yt-lockup-meta\"]").first() | ||||||
|  |                                     .select("li").get(1) | ||||||
|  |                                     .text(); | ||||||
|  |                         } catch (IndexOutOfBoundsException e) { | ||||||
|  |                             if(isLiveStream(li)) { | ||||||
|  |                                 // -1 for no view count | ||||||
|  |                                 return -1; | ||||||
|  |                             } else { | ||||||
|  |                                 throw new ParsingException( | ||||||
|  |                                         "Could not parse yt-lockup-meta although available: " + getTitle(), e); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |  | ||||||
|  |                         output = Parser.matchGroup1("([0-9,\\. ]*)", input) | ||||||
|  |                                 .replace(" ", "") | ||||||
|  |                                 .replace(".", "") | ||||||
|  |                                 .replace(",", ""); | ||||||
|  |  | ||||||
|  |                         try { | ||||||
|  |                             return Long.parseLong(output); | ||||||
|  |                         } catch (NumberFormatException e) { | ||||||
|  |                             // if this happens the video probably has no views | ||||||
|  |                             if(!input.isEmpty()) { | ||||||
|  |                                 return 0; | ||||||
|  |                             } else { | ||||||
|  |                                 throw new ParsingException("Could not handle input: " + input, e); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     @Override | ||||||
|  |                     public String getThumbnailUrl() throws ParsingException { | ||||||
|  |                         try { | ||||||
|  |                             String url; | ||||||
|  |                             Element te = li.select("span[class=\"yt-thumb-clip\"]").first() | ||||||
|  |                                     .select("img").first(); | ||||||
|  |                             url = te.attr("abs:src"); | ||||||
|  |                             // Sometimes youtube sends links to gif files which somehow seem to not exist | ||||||
|  |                             // anymore. Items with such gif also offer a secondary image source. So we are going | ||||||
|  |                             // to use that if we've caught such an item. | ||||||
|  |                             if (url.contains(".gif")) { | ||||||
|  |                                 url = te.attr("abs:data-thumb"); | ||||||
|  |                             } | ||||||
|  |                             return url; | ||||||
|  |                         } catch (Exception e) { | ||||||
|  |                             throw new ParsingException("Could not get thumbnail url", e); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     private boolean isLiveStream(Element item) { | ||||||
|  |                         Element bla = item.select("span[class*=\"yt-badge-live\"]").first(); | ||||||
|  |  | ||||||
|  |                         if(bla == null) { | ||||||
|  |                             // sometimes livestreams dont have badges but sill are live streams | ||||||
|  |                             // if video time is not available we most likly have an offline livestream | ||||||
|  |                             if(item.select("span[class*=\"video-time\"]").first() == null) { | ||||||
|  |                                 return true; | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         return bla != null; | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return collector; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String getFeedUrl() throws ParsingException { | ||||||
|  |         try { | ||||||
|  |             if(!isAjaxPage) { | ||||||
|  |                 feedUrl = doc.select("link[title=\"RSS\"]").first().attr("abs:href"); | ||||||
|  |             } | ||||||
|  |             return feedUrl; | ||||||
|  |         } catch(Exception e) { | ||||||
|  |             throw new ParsingException("Could not get feed url", e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean hasNextPage() throws ParsingException { | ||||||
|  |         return !nextPageUrl.isEmpty(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private String getUserUrl(Document d) throws ParsingException { | ||||||
|  |         return d.select("span[class=\"qualified-channel-title-text\"]").first() | ||||||
|  |                 .select("a").first().attr("abs:href"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private boolean isUserUrl(String url) throws ParsingException { | ||||||
|  |         return url.contains("/user/"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private String getNextPageUrl(Document d) throws ParsingException { | ||||||
|  |         try { | ||||||
|  |             Element button = d.select("button[class*=\"yt-uix-load-more\"]").first(); | ||||||
|  |             return button.attr("abs:data-uix-load-more-href"); | ||||||
|  |         } catch(Exception e) { | ||||||
|  |             throw new ParsingException("could not load next page url", e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,47 @@ | |||||||
|  | package org.schabi.newpipe.extractor.services.youtube; | ||||||
|  |  | ||||||
|  | import org.schabi.newpipe.extractor.Parser; | ||||||
|  | import org.schabi.newpipe.extractor.ParsingException; | ||||||
|  | import org.schabi.newpipe.extractor.UrlIdHandler; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Created by Christian Schabesberger on 25.07.16. | ||||||
|  |  * | ||||||
|  |  * Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org> | ||||||
|  |  * YoutubeChannelUrlIdHandler.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 YoutubeChannelUrlIdHandler implements UrlIdHandler { | ||||||
|  |  | ||||||
|  |     public String getUrl(String channelId) { | ||||||
|  |         return "https://www.youtube.com/" + channelId; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public String getId(String siteUrl) throws ParsingException { | ||||||
|  |         return Parser.matchGroup1("/(user/[A-Za-z0-9_-]*|channel/[A-Za-z0-9_-]*)", siteUrl); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public String cleanUrl(String siteUrl) throws ParsingException { | ||||||
|  |        return getUrl(getId(siteUrl)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public boolean acceptUrl(String videoUrl) { | ||||||
|  |         return (videoUrl.contains("youtube") || | ||||||
|  |                 videoUrl.contains("youtu.be")) && | ||||||
|  |                 ( videoUrl.contains("/user/") || | ||||||
|  |                         videoUrl.contains("/channel/")); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -14,7 +14,7 @@ import org.schabi.newpipe.extractor.SearchEngine; | |||||||
| import org.schabi.newpipe.extractor.StreamPreviewInfoCollector; | import org.schabi.newpipe.extractor.StreamPreviewInfoCollector; | ||||||
| import org.schabi.newpipe.extractor.StreamPreviewInfoExtractor; | import org.schabi.newpipe.extractor.StreamPreviewInfoExtractor; | ||||||
| import org.schabi.newpipe.extractor.StreamPreviewInfoSearchCollector; | import org.schabi.newpipe.extractor.StreamPreviewInfoSearchCollector; | ||||||
| import org.schabi.newpipe.extractor.StreamUrlIdHandler; | import org.schabi.newpipe.extractor.UrlIdHandler; | ||||||
| import org.w3c.dom.Node; | import org.w3c.dom.Node; | ||||||
| import org.w3c.dom.NodeList; | import org.w3c.dom.NodeList; | ||||||
| import org.xml.sax.InputSource; | import org.xml.sax.InputSource; | ||||||
| @@ -55,7 +55,7 @@ public class YoutubeSearchEngine extends SearchEngine { | |||||||
|     private static final String TAG = YoutubeSearchEngine.class.toString(); |     private static final String TAG = YoutubeSearchEngine.class.toString(); | ||||||
|     public static final String CHARSET_UTF_8 = "UTF-8"; |     public static final String CHARSET_UTF_8 = "UTF-8"; | ||||||
|  |  | ||||||
|     public YoutubeSearchEngine(StreamUrlIdHandler urlIdHandler, int serviceId) { |     public YoutubeSearchEngine(UrlIdHandler urlIdHandler, int serviceId) { | ||||||
|         super(urlIdHandler, serviceId); |         super(urlIdHandler, serviceId); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -91,7 +91,6 @@ public class YoutubeSearchEngine extends SearchEngine { | |||||||
|             site = downloader.download(url); |             site = downloader.download(url); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |  | ||||||
|         Document doc = Jsoup.parse(site, url); |         Document doc = Jsoup.parse(site, url); | ||||||
|         Element list = doc.select("ol[class=\"item-section\"]").first(); |         Element list = doc.select("ol[class=\"item-section\"]").first(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,10 +1,11 @@ | |||||||
| package org.schabi.newpipe.extractor.services.youtube; | package org.schabi.newpipe.extractor.services.youtube; | ||||||
|  |  | ||||||
|  | import org.schabi.newpipe.extractor.ChannelExtractor; | ||||||
| import org.schabi.newpipe.extractor.ExtractionException; | import org.schabi.newpipe.extractor.ExtractionException; | ||||||
| import org.schabi.newpipe.extractor.Downloader; | import org.schabi.newpipe.extractor.Downloader; | ||||||
| import org.schabi.newpipe.extractor.StreamExtractor; | import org.schabi.newpipe.extractor.StreamExtractor; | ||||||
| import org.schabi.newpipe.extractor.StreamingService; | import org.schabi.newpipe.extractor.StreamingService; | ||||||
| import org.schabi.newpipe.extractor.StreamUrlIdHandler; | import org.schabi.newpipe.extractor.UrlIdHandler; | ||||||
| import org.schabi.newpipe.extractor.SearchEngine; | import org.schabi.newpipe.extractor.SearchEngine; | ||||||
|  |  | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| @@ -45,7 +46,7 @@ public class YoutubeService extends StreamingService { | |||||||
|     @Override |     @Override | ||||||
|     public StreamExtractor getExtractorInstance(String url, Downloader downloader) |     public StreamExtractor getExtractorInstance(String url, Downloader downloader) | ||||||
|             throws ExtractionException, IOException { |             throws ExtractionException, IOException { | ||||||
|         StreamUrlIdHandler urlIdHandler = new YoutubeStreamUrlIdHandler(); |         UrlIdHandler urlIdHandler = new YoutubeStreamUrlIdHandler(); | ||||||
|         if(urlIdHandler.acceptUrl(url)) { |         if(urlIdHandler.acceptUrl(url)) { | ||||||
|             return new YoutubeStreamExtractor(urlIdHandler, url, downloader, getServiceId()); |             return new YoutubeStreamExtractor(urlIdHandler, url, downloader, getServiceId()); | ||||||
|         } |         } | ||||||
| @@ -59,7 +60,18 @@ public class YoutubeService extends StreamingService { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public StreamUrlIdHandler getUrlIdHandlerInstance() { |     public UrlIdHandler getUrlIdHandlerInstance() { | ||||||
|         return new YoutubeStreamUrlIdHandler(); |         return new YoutubeStreamUrlIdHandler(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public UrlIdHandler getChannelUrlIdHandlerInstance() { | ||||||
|  |         return new YoutubeChannelUrlIdHandler(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public ChannelExtractor getChannelExtractorInstance(String url, int page, Downloader downloader) | ||||||
|  |         throws ExtractionException, IOException { | ||||||
|  |         return new YoutubeChannelExtractor(getChannelUrlIdHandlerInstance(), url, page, downloader, getServiceId()); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -15,10 +15,9 @@ import org.schabi.newpipe.extractor.Downloader; | |||||||
| import org.schabi.newpipe.extractor.Parser; | import org.schabi.newpipe.extractor.Parser; | ||||||
| import org.schabi.newpipe.extractor.ParsingException; | import org.schabi.newpipe.extractor.ParsingException; | ||||||
| import org.schabi.newpipe.extractor.StreamInfo; | import org.schabi.newpipe.extractor.StreamInfo; | ||||||
| import org.schabi.newpipe.extractor.StreamPreviewInfo; |  | ||||||
| import org.schabi.newpipe.extractor.StreamPreviewInfoCollector; | import org.schabi.newpipe.extractor.StreamPreviewInfoCollector; | ||||||
| import org.schabi.newpipe.extractor.StreamPreviewInfoExtractor; | import org.schabi.newpipe.extractor.StreamPreviewInfoExtractor; | ||||||
| import org.schabi.newpipe.extractor.StreamUrlIdHandler; | import org.schabi.newpipe.extractor.UrlIdHandler; | ||||||
| import org.schabi.newpipe.extractor.StreamExtractor; | import org.schabi.newpipe.extractor.StreamExtractor; | ||||||
| import org.schabi.newpipe.extractor.MediaFormat; | import org.schabi.newpipe.extractor.MediaFormat; | ||||||
| import org.schabi.newpipe.extractor.VideoStream; | import org.schabi.newpipe.extractor.VideoStream; | ||||||
| @@ -183,12 +182,12 @@ public class YoutubeStreamExtractor extends StreamExtractor { | |||||||
|     // cached values |     // cached values | ||||||
|     private static volatile String decryptionCode = ""; |     private static volatile String decryptionCode = ""; | ||||||
|  |  | ||||||
|     StreamUrlIdHandler urlidhandler = new YoutubeStreamUrlIdHandler(); |     UrlIdHandler urlidhandler = new YoutubeStreamUrlIdHandler(); | ||||||
|     String pageUrl = ""; |     String pageUrl = ""; | ||||||
|  |  | ||||||
|     private Downloader downloader; |     private Downloader downloader; | ||||||
|  |  | ||||||
|     public YoutubeStreamExtractor(StreamUrlIdHandler urlIdHandler, String pageUrl, |     public YoutubeStreamExtractor(UrlIdHandler urlIdHandler, String pageUrl, | ||||||
|                                   Downloader dl, int serviceId) |                                   Downloader dl, int serviceId) | ||||||
|             throws ExtractionException, IOException { |             throws ExtractionException, IOException { | ||||||
|         super(urlIdHandler ,pageUrl, dl, serviceId); |         super(urlIdHandler ,pageUrl, dl, serviceId); | ||||||
| @@ -203,7 +202,7 @@ public class YoutubeStreamExtractor extends StreamExtractor { | |||||||
|         // Check if the video is age restricted |         // Check if the video is age restricted | ||||||
|         if (pageContent.contains("<meta property=\"og:restrictions:age")) { |         if (pageContent.contains("<meta property=\"og:restrictions:age")) { | ||||||
|             String videoInfoUrl = GET_VIDEO_INFO_URL.replace("%%video_id%%", |             String videoInfoUrl = GET_VIDEO_INFO_URL.replace("%%video_id%%", | ||||||
|                     urlidhandler.getVideoId(pageUrl)).replace("$$el_type$$", "&" + EL_INFO); |                     urlidhandler.getId(pageUrl)).replace("$$el_type$$", "&" + EL_INFO); | ||||||
|             String videoInfoPageString = downloader.download(videoInfoUrl); |             String videoInfoPageString = downloader.download(videoInfoUrl); | ||||||
|             videoInfoPage = Parser.compatParseMap(videoInfoPageString); |             videoInfoPage = Parser.compatParseMap(videoInfoPageString); | ||||||
|             playerUrl = getPlayerUrlFromRestrictedVideo(pageUrl); |             playerUrl = getPlayerUrlFromRestrictedVideo(pageUrl); | ||||||
| @@ -286,7 +285,7 @@ public class YoutubeStreamExtractor extends StreamExtractor { | |||||||
|     private String getPlayerUrlFromRestrictedVideo(String pageUrl) throws ParsingException { |     private String getPlayerUrlFromRestrictedVideo(String pageUrl) throws ParsingException { | ||||||
|         try { |         try { | ||||||
|             String playerUrl = ""; |             String playerUrl = ""; | ||||||
|             String videoId = urlidhandler.getVideoId(pageUrl); |             String videoId = urlidhandler.getId(pageUrl); | ||||||
|             String embedUrl = "https://www.youtube.com/embed/" + videoId; |             String embedUrl = "https://www.youtube.com/embed/" + videoId; | ||||||
|             String embedPageContent = downloader.download(embedUrl); |             String embedPageContent = downloader.download(embedUrl); | ||||||
|             //todo: find out if this can be reapaced by Parser.matchGroup1() |             //todo: find out if this can be reapaced by Parser.matchGroup1() | ||||||
| @@ -686,6 +685,16 @@ public class YoutubeStreamExtractor extends StreamExtractor { | |||||||
|         return pageUrl; |         return pageUrl; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String getChannelUrl() throws ParsingException { | ||||||
|  |         try { | ||||||
|  |             return doc.select("div[class=\"yt-user-info\"]").first().children() | ||||||
|  |                     .select("a").first().attr("abs:href"); | ||||||
|  |         } catch(Exception e) { | ||||||
|  |             throw new ParsingException("Could not get channel link", e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public StreamInfo.StreamType getStreamType() throws ParsingException { |     public StreamInfo.StreamType getStreamType() throws ParsingException { | ||||||
|         //todo: if implementing livestream support this value should be generated dynamically |         //todo: if implementing livestream support this value should be generated dynamically | ||||||
|   | |||||||
| @@ -66,8 +66,6 @@ public class YoutubeStreamPreviewInfoExtractor implements StreamPreviewInfoExtra | |||||||
|             } else { |             } else { | ||||||
|                 throw new ParsingException("Could not get Duration: " + getTitle(), e); |                 throw new ParsingException("Could not get Duration: " + getTitle(), e); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ package org.schabi.newpipe.extractor.services.youtube; | |||||||
|  |  | ||||||
| import org.schabi.newpipe.extractor.Parser; | import org.schabi.newpipe.extractor.Parser; | ||||||
| import org.schabi.newpipe.extractor.ParsingException; | import org.schabi.newpipe.extractor.ParsingException; | ||||||
| import org.schabi.newpipe.extractor.StreamUrlIdHandler; | import org.schabi.newpipe.extractor.UrlIdHandler; | ||||||
|  |  | ||||||
| import java.io.UnsupportedEncodingException; | import java.io.UnsupportedEncodingException; | ||||||
| import java.net.URLDecoder; | import java.net.URLDecoder; | ||||||
| @@ -27,16 +27,16 @@ import java.net.URLDecoder; | |||||||
|  * along with NewPipe.  If not, see <http://www.gnu.org/licenses/>. |  * along with NewPipe.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| public class YoutubeStreamUrlIdHandler implements StreamUrlIdHandler { | public class YoutubeStreamUrlIdHandler implements UrlIdHandler { | ||||||
|     @SuppressWarnings("WeakerAccess") |     @SuppressWarnings("WeakerAccess") | ||||||
|     @Override |     @Override | ||||||
|     public String getVideoUrl(String videoId) { |     public String getUrl(String videoId) { | ||||||
|         return "https://www.youtube.com/watch?v=" + videoId; |         return "https://www.youtube.com/watch?v=" + videoId; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @SuppressWarnings("WeakerAccess") |     @SuppressWarnings("WeakerAccess") | ||||||
|     @Override |     @Override | ||||||
|     public String getVideoId(String url) throws ParsingException, IllegalArgumentException { |     public String getId(String url) throws ParsingException, IllegalArgumentException { | ||||||
|         if(url.isEmpty()) |         if(url.isEmpty()) | ||||||
|         { |         { | ||||||
|             throw new IllegalArgumentException("The url parameter should not be empty"); |             throw new IllegalArgumentException("The url parameter should not be empty"); | ||||||
| @@ -81,7 +81,7 @@ public class YoutubeStreamUrlIdHandler implements StreamUrlIdHandler { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public String cleanUrl(String complexUrl) throws ParsingException { |     public String cleanUrl(String complexUrl) throws ParsingException { | ||||||
|         return getVideoUrl(getVideoId(complexUrl)); |         return getUrl(getId(complexUrl)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|   | |||||||
| @@ -0,0 +1,34 @@ | |||||||
|  | package org.schabi.newpipe.info_list; | ||||||
|  |  | ||||||
|  | import android.support.v7.widget.RecyclerView; | ||||||
|  | import android.view.View; | ||||||
|  | import android.widget.Button; | ||||||
|  | import android.widget.ImageView; | ||||||
|  | import android.widget.TextView; | ||||||
|  |  | ||||||
|  | import org.schabi.newpipe.R; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Created by the-scrabi on 01.08.16. | ||||||
|  |  */ | ||||||
|  | public class InfoItemHolder extends RecyclerView.ViewHolder { | ||||||
|  |  | ||||||
|  |     public ImageView itemThumbnailView; | ||||||
|  |     public TextView itemVideoTitleView, | ||||||
|  |             itemUploaderView, | ||||||
|  |             itemDurationView, | ||||||
|  |             itemUploadDateView, | ||||||
|  |             itemViewCountView; | ||||||
|  |     public Button itemButton; | ||||||
|  |  | ||||||
|  |     public InfoItemHolder(View v) { | ||||||
|  |         super(v); | ||||||
|  |         itemThumbnailView = (ImageView) v.findViewById(R.id.itemThumbnailView); | ||||||
|  |         itemVideoTitleView = (TextView) v.findViewById(R.id.itemVideoTitleView); | ||||||
|  |         itemUploaderView = (TextView) v.findViewById(R.id.itemUploaderView); | ||||||
|  |         itemDurationView = (TextView) v.findViewById(R.id.itemDurationView); | ||||||
|  |         itemUploadDateView = (TextView) v.findViewById(R.id.itemUploadDateView); | ||||||
|  |         itemViewCountView = (TextView) v.findViewById(R.id.itemViewCountView); | ||||||
|  |         itemButton = (Button) v.findViewById(R.id.item_button); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,73 +1,79 @@ | |||||||
| package org.schabi.newpipe; | package org.schabi.newpipe.info_list; | ||||||
| 
 | 
 | ||||||
|  | import android.app.Activity; | ||||||
|  | import android.support.v7.widget.RecyclerView; | ||||||
| import android.view.LayoutInflater; | import android.view.LayoutInflater; | ||||||
| import android.view.View; | import android.view.View; | ||||||
| import android.view.ViewGroup; | import android.view.ViewGroup; | ||||||
| import android.widget.ImageView; |  | ||||||
| import android.widget.TextView; |  | ||||||
| 
 | 
 | ||||||
| import com.nostra13.universalimageloader.core.DisplayImageOptions; | import com.nostra13.universalimageloader.core.DisplayImageOptions; | ||||||
| import com.nostra13.universalimageloader.core.ImageLoader; | import com.nostra13.universalimageloader.core.ImageLoader; | ||||||
| 
 | 
 | ||||||
|  | import org.schabi.newpipe.ImageErrorLoadingListener; | ||||||
|  | import org.schabi.newpipe.R; | ||||||
| import org.schabi.newpipe.extractor.AbstractVideoInfo; | import org.schabi.newpipe.extractor.AbstractVideoInfo; | ||||||
| import org.schabi.newpipe.extractor.StreamPreviewInfo; | import org.schabi.newpipe.extractor.StreamPreviewInfo; | ||||||
| 
 | 
 | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Vector; | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * Created by Christian Schabesberger on 24.10.15. |  * Created by the-scrabi on 01.08.16. | ||||||
|  * |  | ||||||
|  * Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org> |  | ||||||
|  * VideoInfoItemViewCreator.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 InfoListAdapter extends RecyclerView.Adapter<InfoItemHolder> { | ||||||
| 
 | 
 | ||||||
| public class VideoInfoItemViewCreator { |     public interface OnItemSelectedListener { | ||||||
|     private final LayoutInflater inflater; |         void selected(String url); | ||||||
|     private ImageLoader imageLoader = ImageLoader.getInstance(); |  | ||||||
|     private DisplayImageOptions displayImageOptions = new DisplayImageOptions.Builder().cacheInMemory(true).build(); |  | ||||||
| 
 |  | ||||||
|     public VideoInfoItemViewCreator(LayoutInflater inflater) { |  | ||||||
|         this.inflater = inflater; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public View getViewFromVideoInfoItem(View convertView, ViewGroup parent, StreamPreviewInfo info) { |     private Activity activity = null; | ||||||
|         ViewHolder holder; |     private View rootView = null; | ||||||
|  |     private List<StreamPreviewInfo> streamList = new Vector<>(); | ||||||
|  |     private ImageLoader imageLoader = ImageLoader.getInstance(); | ||||||
|  |     private DisplayImageOptions displayImageOptions = | ||||||
|  |             new DisplayImageOptions.Builder().cacheInMemory(true).build(); | ||||||
|  |     private OnItemSelectedListener onItemSelectedListener; | ||||||
| 
 | 
 | ||||||
|         // generate holder | 
 | ||||||
|         if(convertView == null) { | 
 | ||||||
|             convertView = inflater.inflate(R.layout.video_item, parent, false); |     public InfoListAdapter(Activity a, View rootView) { | ||||||
|             holder = new ViewHolder(); |         activity = a; | ||||||
|             holder.itemThumbnailView = (ImageView) convertView.findViewById(R.id.itemThumbnailView); |         this.rootView = rootView; | ||||||
|             holder.itemVideoTitleView = (TextView) convertView.findViewById(R.id.itemVideoTitleView); |     } | ||||||
|             holder.itemUploaderView = (TextView) convertView.findViewById(R.id.itemUploaderView); | 
 | ||||||
|             holder.itemDurationView = (TextView) convertView.findViewById(R.id.itemDurationView); |     public void setOnItemSelectedListener(OnItemSelectedListener onItemSelectedListener) { | ||||||
|             holder.itemUploadDateView = (TextView) convertView.findViewById(R.id.itemUploadDateView); |         this.onItemSelectedListener = onItemSelectedListener; | ||||||
|             holder.itemViewCountView = (TextView) convertView.findViewById(R.id.itemViewCountView); |     } | ||||||
|             convertView.setTag(holder); | 
 | ||||||
|         } else { |     public void addStreamItemList(List<StreamPreviewInfo> videos) { | ||||||
|             holder = (ViewHolder) convertView.getTag(); |         if(videos!= null) { | ||||||
|  |             streamList.addAll(videos); | ||||||
|  |             notifyDataSetChanged(); | ||||||
|         } |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|         // fill with information |     public void clearSteamItemList() { | ||||||
|  |         streamList = new Vector<>(); | ||||||
|  |         notifyDataSetChanged(); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|         /* |     @Override | ||||||
|         if(info.thumbnail == null) { |     public int getItemCount() { | ||||||
|             holder.itemThumbnailView.setImageResource(R.drawable.dummy_thumbnail); |         return streamList.size(); | ||||||
|         } else { |     } | ||||||
|             holder.itemThumbnailView.setImageBitmap(info.thumbnail); | 
 | ||||||
|         } |     @Override | ||||||
|         */ |     public InfoItemHolder onCreateViewHolder(ViewGroup parent, int i) { | ||||||
|  |         View itemView = LayoutInflater.from(parent.getContext()) | ||||||
|  |                 .inflate(R.layout.video_item, parent, false); | ||||||
|  | 
 | ||||||
|  |         return new InfoItemHolder(itemView); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public void onBindViewHolder(InfoItemHolder holder, int i) { | ||||||
|  |         final StreamPreviewInfo info = streamList.get(i); | ||||||
|  |         // fill holder with information | ||||||
|         holder.itemVideoTitleView.setText(info.title); |         holder.itemVideoTitleView.setText(info.title); | ||||||
|         if(info.uploader != null && !info.uploader.isEmpty()) { |         if(info.uploader != null && !info.uploader.isEmpty()) { | ||||||
|             holder.itemUploaderView.setText(info.uploader); |             holder.itemUploaderView.setText(info.uploader); | ||||||
| @@ -94,18 +100,22 @@ public class VideoInfoItemViewCreator { | |||||||
| 
 | 
 | ||||||
|         holder.itemThumbnailView.setImageResource(R.drawable.dummy_thumbnail); |         holder.itemThumbnailView.setImageResource(R.drawable.dummy_thumbnail); | ||||||
|         if(info.thumbnail_url != null && !info.thumbnail_url.isEmpty()) { |         if(info.thumbnail_url != null && !info.thumbnail_url.isEmpty()) { | ||||||
|             imageLoader.displayImage(info.thumbnail_url, holder.itemThumbnailView, displayImageOptions); |             imageLoader.displayImage(info.thumbnail_url, | ||||||
|  |                     holder.itemThumbnailView, | ||||||
|  |                     displayImageOptions, | ||||||
|  |                     new ImageErrorLoadingListener(activity, rootView, info.service_id)); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return convertView; |         holder.itemButton.setOnClickListener(new View.OnClickListener() { | ||||||
|  |             @Override | ||||||
|  |             public void onClick(View view) { | ||||||
|  |                 onItemSelectedListener.selected(info.webpage_url); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private class ViewHolder { |  | ||||||
|         public ImageView itemThumbnailView; |  | ||||||
|         public TextView itemVideoTitleView, itemUploaderView, itemDurationView, itemUploadDateView, itemViewCountView; |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     private String shortViewCount(Long viewCount){ |     public static String shortViewCount(Long viewCount){ | ||||||
|         if(viewCount >= 1000000000){ |         if(viewCount >= 1000000000){ | ||||||
|             return Long.toString(viewCount/1000000000)+"B views"; |             return Long.toString(viewCount/1000000000)+"B views"; | ||||||
|         }else if(viewCount>=1000000){ |         }else if(viewCount>=1000000){ | ||||||
| @@ -23,8 +23,8 @@ import android.widget.Toast; | |||||||
| import org.schabi.newpipe.ActivityCommunicator; | import org.schabi.newpipe.ActivityCommunicator; | ||||||
| import org.schabi.newpipe.BuildConfig; | import org.schabi.newpipe.BuildConfig; | ||||||
| import org.schabi.newpipe.R; | import org.schabi.newpipe.R; | ||||||
| import org.schabi.newpipe.VideoItemDetailActivity; | import org.schabi.newpipe.detail.VideoItemDetailActivity; | ||||||
| import org.schabi.newpipe.VideoItemDetailFragment; | import org.schabi.newpipe.detail.VideoItemDetailFragment; | ||||||
|  |  | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -0,0 +1,265 @@ | |||||||
|  | package org.schabi.newpipe.search_fragment; | ||||||
|  |  | ||||||
|  | import android.app.Activity; | ||||||
|  | import android.content.Context; | ||||||
|  | import android.content.Intent; | ||||||
|  | import android.os.Bundle; | ||||||
|  | import android.support.v4.app.Fragment; | ||||||
|  | import android.support.v7.widget.LinearLayoutManager; | ||||||
|  | import android.support.v7.widget.RecyclerView; | ||||||
|  | import android.support.v7.widget.SearchView; | ||||||
|  | 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 android.view.inputmethod.InputMethodManager; | ||||||
|  | import android.widget.ProgressBar; | ||||||
|  | import android.widget.Toast; | ||||||
|  |  | ||||||
|  | import org.schabi.newpipe.ErrorActivity; | ||||||
|  | import org.schabi.newpipe.R; | ||||||
|  | import org.schabi.newpipe.detail.VideoItemDetailActivity; | ||||||
|  | import org.schabi.newpipe.detail.VideoItemDetailFragment; | ||||||
|  | import org.schabi.newpipe.extractor.SearchResult; | ||||||
|  | import org.schabi.newpipe.extractor.ServiceList; | ||||||
|  | import org.schabi.newpipe.info_list.InfoListAdapter; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Created by Christian Schabesberger on 02.08.16. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | public class SearchInfoItemFragment extends Fragment { | ||||||
|  |  | ||||||
|  |     private static final String TAG = SearchInfoItemFragment.class.toString(); | ||||||
|  |  | ||||||
|  |     public class SearchQueryListener implements SearchView.OnQueryTextListener { | ||||||
|  |  | ||||||
|  |         @Override | ||||||
|  |         public boolean onQueryTextSubmit(String query) { | ||||||
|  |             Activity a = getActivity(); | ||||||
|  |             try { | ||||||
|  |                 searchQuery = query; | ||||||
|  |                 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, | ||||||
|  |                                     ServiceList.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(); | ||||||
|  |             } | ||||||
|  |             View bg = a.findViewById(R.id.mainBG); | ||||||
|  |             bg.setVisibility(View.GONE); | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         @Override | ||||||
|  |         public boolean onQueryTextChange(String newText) { | ||||||
|  |             if(!newText.isEmpty()) { | ||||||
|  |                 searchSuggestions(newText); | ||||||
|  |             } | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private int streamingServiceId = -1; | ||||||
|  |     private String searchQuery = ""; | ||||||
|  |     private boolean isLoading = false; | ||||||
|  |  | ||||||
|  |     private ProgressBar loadingIndicator = null; | ||||||
|  |     private SearchView searchView = null; | ||||||
|  |     private int pageNumber = 0; | ||||||
|  |     private SuggestionListAdapter suggestionListAdapter = null; | ||||||
|  |     private InfoListAdapter infoListAdapter = null; | ||||||
|  |     private LinearLayoutManager streamInfoListLayoutManager = null; | ||||||
|  |     private RecyclerView recyclerView = null; | ||||||
|  |  | ||||||
|  |     // savedInstanceBundle arguments | ||||||
|  |     private static final String QUERY = "query"; | ||||||
|  |     private static final String STREAMING_SERVICE = "streaming_service"; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Mandatory empty constructor for the fragment manager to instantiate the | ||||||
|  |      * fragment (e.g. upon screen orientation changes). | ||||||
|  |      */ | ||||||
|  |     public SearchInfoItemFragment() { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // TODO: Customize parameter initialization | ||||||
|  |     @SuppressWarnings("unused") | ||||||
|  |     public static SearchInfoItemFragment newInstance(int columnCount) { | ||||||
|  |         SearchInfoItemFragment fragment = new SearchInfoItemFragment(); | ||||||
|  |         return fragment; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onCreate(Bundle savedInstanceState) { | ||||||
|  |         super.onCreate(savedInstanceState); | ||||||
|  |  | ||||||
|  |         if(savedInstanceState != null) { | ||||||
|  |             searchQuery = savedInstanceState.getString(QUERY); | ||||||
|  |             streamingServiceId = savedInstanceState.getInt(STREAMING_SERVICE); | ||||||
|  |         } else { | ||||||
|  |             try { | ||||||
|  |                 streamingServiceId = ServiceList.getIdOfService("Youtube"); | ||||||
|  |             } catch(Exception e) { | ||||||
|  |                 e.printStackTrace(); | ||||||
|  |                 ErrorActivity.reportError(getActivity(), e, null, | ||||||
|  |                         getActivity().findViewById(android.R.id.content), | ||||||
|  |                         ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED, | ||||||
|  |                                 ServiceList.getNameOfService(streamingServiceId), | ||||||
|  |                                 "", R.string.general_error)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         SearchWorker sw = SearchWorker.getInstance(); | ||||||
|  |         sw.setSearchWorkerResultListner(new SearchWorker.SearchWorkerResultListner() { | ||||||
|  |             @Override | ||||||
|  |             public void onResult(SearchResult result) { | ||||||
|  |                 infoListAdapter.addStreamItemList(result.resultList); | ||||||
|  |                 isLoading = false; | ||||||
|  |                 loadingIndicator.setVisibility(View.GONE); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             @Override | ||||||
|  |             public void onNothingFound(int stringResource) { | ||||||
|  |                 //setListShown(true); | ||||||
|  |                 Toast.makeText(getActivity(), getString(stringResource), | ||||||
|  |                         Toast.LENGTH_SHORT).show(); | ||||||
|  |                 isLoading = false; | ||||||
|  |                 loadingIndicator.setVisibility(View.GONE); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             @Override | ||||||
|  |             public void onError(String message) { | ||||||
|  |                 //setListShown(true); | ||||||
|  |                 Toast.makeText(getActivity(), message, | ||||||
|  |                         Toast.LENGTH_LONG).show(); | ||||||
|  |                 isLoading = false; | ||||||
|  |                 loadingIndicator.setVisibility(View.GONE); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public View onCreateView(LayoutInflater inflater, ViewGroup container, | ||||||
|  |                              Bundle savedInstanceState) { | ||||||
|  |         View view = inflater.inflate(R.layout.fragment_searchinfoitem, container, false); | ||||||
|  |  | ||||||
|  |         Context context = view.getContext(); | ||||||
|  |         loadingIndicator = (ProgressBar) view.findViewById(R.id.progressBar); | ||||||
|  |         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.setOnItemSelectedListener(new InfoListAdapter.OnItemSelectedListener() { | ||||||
|  |             @Override | ||||||
|  |             public void selected(String url) { | ||||||
|  |                 Intent i = new Intent(getActivity(), VideoItemDetailActivity.class); | ||||||
|  |                 i.putExtra(VideoItemDetailFragment.STREAMING_SERVICE, streamingServiceId); | ||||||
|  |                 i.putExtra(VideoItemDetailFragment.VIDEO_URL, url); | ||||||
|  |                 getActivity().startActivity(i); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |         recyclerView.setAdapter(infoListAdapter); | ||||||
|  |  | ||||||
|  |         recyclerView.setOnScrollListener(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++; | ||||||
|  |                         search(searchQuery, pageNumber); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         return view; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onAttach(Context context) { | ||||||
|  |         super.onAttach(context); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onDetach() { | ||||||
|  |         super.onDetach(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { | ||||||
|  |         super.onCreateOptionsMenu(menu, inflater); | ||||||
|  |         inflater.inflate(R.menu.search_menu, menu); | ||||||
|  |  | ||||||
|  |         MenuItem searchItem = menu.findItem(R.id.action_search); | ||||||
|  |         searchView = (SearchView) searchItem.getActionView(); | ||||||
|  |         setupSearchView(searchView); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean onOptionsItemSelected(MenuItem item) { | ||||||
|  |         return super.onOptionsItemSelected(item); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void setupSearchView(SearchView searchView) { | ||||||
|  |         suggestionListAdapter = new SuggestionListAdapter(getActivity()); | ||||||
|  |         searchView.setSuggestionsAdapter(suggestionListAdapter); | ||||||
|  |         searchView.setOnSuggestionListener(new SearchSuggestionListener(searchView, suggestionListAdapter)); | ||||||
|  |         searchView.setOnQueryTextListener(new SearchQueryListener()); | ||||||
|  |         if(searchQuery != null && !searchQuery.isEmpty()) { | ||||||
|  |             searchView.setQuery(searchQuery, false); | ||||||
|  |             searchView.setIconifiedByDefault(false); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void search(String query) { | ||||||
|  |         infoListAdapter.clearSteamItemList(); | ||||||
|  |         pageNumber = 0; | ||||||
|  |         search(query, pageNumber); | ||||||
|  |         loadingIndicator.setVisibility(View.VISIBLE); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void search(String query, int page) { | ||||||
|  |         isLoading = true; | ||||||
|  |         SearchWorker sw = SearchWorker.getInstance(); | ||||||
|  |         sw.search(streamingServiceId, query, page, getActivity()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void searchSuggestions(String query) { | ||||||
|  |         SuggestionSearchRunnable suggestionSearchRunnable = | ||||||
|  |                 new SuggestionSearchRunnable(streamingServiceId, query, getActivity(), suggestionListAdapter); | ||||||
|  |         Thread suggestionThread = new Thread(suggestionSearchRunnable); | ||||||
|  |         suggestionThread.start(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,32 @@ | |||||||
|  | package org.schabi.newpipe.search_fragment; | ||||||
|  |  | ||||||
|  | import android.support.v7.widget.SearchView; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Created by the-scrabi on 02.08.16. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | public class SearchSuggestionListener implements SearchView.OnSuggestionListener{ | ||||||
|  |  | ||||||
|  |     private SearchView searchView; | ||||||
|  |     private SuggestionListAdapter adapter; | ||||||
|  |  | ||||||
|  |     public SearchSuggestionListener(SearchView searchView, SuggestionListAdapter adapter) { | ||||||
|  |         this.searchView = searchView; | ||||||
|  |         this.adapter = adapter; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean onSuggestionSelect(int position) { | ||||||
|  |         String suggestion = adapter.getSuggestion(position); | ||||||
|  |         searchView.setQuery(suggestion,true); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean onSuggestionClick(int position) { | ||||||
|  |         String suggestion = adapter.getSuggestion(position); | ||||||
|  |         searchView.setQuery(suggestion,true); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,174 @@ | |||||||
|  | package org.schabi.newpipe.search_fragment; | ||||||
|  |  | ||||||
|  | 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 android.widget.Toast; | ||||||
|  |  | ||||||
|  | import org.schabi.newpipe.Downloader; | ||||||
|  | import org.schabi.newpipe.ErrorActivity; | ||||||
|  | import org.schabi.newpipe.R; | ||||||
|  | import org.schabi.newpipe.extractor.ExtractionException; | ||||||
|  | import org.schabi.newpipe.extractor.SearchEngine; | ||||||
|  | import org.schabi.newpipe.extractor.SearchResult; | ||||||
|  | import org.schabi.newpipe.extractor.ServiceList; | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Created by the-scrabi on 02.08.16. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | public class SearchWorker { | ||||||
|  |     private static final String TAG = SearchWorker.class.toString(); | ||||||
|  |  | ||||||
|  |     public interface SearchWorkerResultListner { | ||||||
|  |         void onResult(SearchResult result); | ||||||
|  |         void onNothingFound(final int stringResource); | ||||||
|  |         void onError(String message); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     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) { | ||||||
|  |                 searchWorkerResultListner.onResult(result); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private class SearchRunnable implements Runnable { | ||||||
|  |         public static final String YOUTUBE = "Youtube"; | ||||||
|  |         private final String query; | ||||||
|  |         private final int page; | ||||||
|  |         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, Activity activity, int requestId) { | ||||||
|  |             this.serviceId = serviceId; | ||||||
|  |             this.query = query; | ||||||
|  |             this.page = page; | ||||||
|  |             this.a = activity; | ||||||
|  |         } | ||||||
|  |         void terminate() { | ||||||
|  |             runs = false; | ||||||
|  |         } | ||||||
|  |         @Override | ||||||
|  |         public void run() { | ||||||
|  |             SearchResult result = null; | ||||||
|  |             SearchEngine engine = null; | ||||||
|  |  | ||||||
|  |             try { | ||||||
|  |                 engine = ServiceList.getService(serviceId) | ||||||
|  |                         .getSearchEngineInstance(new Downloader()); | ||||||
|  |             } 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, new Downloader()); | ||||||
|  |  | ||||||
|  |                 if(runs) { | ||||||
|  |                     h.post(new ResultRunnable(result, requestId)); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 // look for errors during extraction | ||||||
|  |                 // soft errors: | ||||||
|  |                 if(result != null && | ||||||
|  |                         !result.errors.isEmpty()) { | ||||||
|  |                     Log.e(TAG, "OCCURRED ERRORS DURING SEARCH EXTRACTION:"); | ||||||
|  |                     for(Throwable e : result.errors) { | ||||||
|  |                         e.printStackTrace(); | ||||||
|  |                         Log.e(TAG, "------"); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     View rootView = a.findViewById(android.R.id.content); | ||||||
|  |                     ErrorActivity.reportError(h, a, result.errors, null, rootView, | ||||||
|  |                             ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED, | ||||||
|  |                         /* todo: this shoudl not be assigned static */  YOUTUBE, query, R.string.light_parsing_error)); | ||||||
|  |  | ||||||
|  |                 } | ||||||
|  |                 // hard errors: | ||||||
|  |             } catch(IOException e) { | ||||||
|  |                 h.post(new Runnable() { | ||||||
|  |                     @Override | ||||||
|  |                     public void run() { | ||||||
|  |                         searchWorkerResultListner.onNothingFound(R.string.network_error); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |                 e.printStackTrace(); | ||||||
|  |             } catch(final SearchEngine.NothingFoundException e) { | ||||||
|  |                 h.post(new Runnable() { | ||||||
|  |                     @Override | ||||||
|  |                     public void run() { | ||||||
|  |                         searchWorkerResultListner.onError(e.getMessage()); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |             } catch(ExtractionException e) { | ||||||
|  |                 ErrorActivity.reportError(h, a, e, null, null, | ||||||
|  |                         ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED, | ||||||
|  |                         /* todo: this shoudl not be assigned static */ | ||||||
|  |                                 YOUTUBE, 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 SearchWorkerResultListner searchWorkerResultListner = 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 setSearchWorkerResultListner(SearchWorkerResultListner listener) { | ||||||
|  |         searchWorkerResultListner = listener; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private SearchWorker() { | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void search(int serviceId, String query, int page, Activity a) { | ||||||
|  |         if(runnable != null) { | ||||||
|  |             terminate(); | ||||||
|  |         } | ||||||
|  |         runnable = new SearchRunnable(serviceId, query, page, a, requestId); | ||||||
|  |         Thread thread = new Thread(runnable); | ||||||
|  |         thread.start(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void terminate() { | ||||||
|  |         requestId++; | ||||||
|  |         runnable.terminate(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe; | package org.schabi.newpipe.search_fragment; | ||||||
| 
 | 
 | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.database.Cursor; | import android.database.Cursor; | ||||||
| @@ -9,27 +9,10 @@ import android.view.View; | |||||||
| import android.view.ViewGroup; | import android.view.ViewGroup; | ||||||
| import android.widget.TextView; | import android.widget.TextView; | ||||||
| 
 | 
 | ||||||
| import java.util.ArrayList; |  | ||||||
| import java.util.List; | import java.util.List; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Created by Madiyar on 23.02.2016. |  * Created by the-scrabi on 02.08.16. | ||||||
|  * |  | ||||||
|  * Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org> |  | ||||||
|  * SuggestionListAdapter.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 SuggestionListAdapter extends CursorAdapter { | public class SuggestionListAdapter extends CursorAdapter { | ||||||
| @@ -0,0 +1,90 @@ | |||||||
|  | package org.schabi.newpipe.search_fragment; | ||||||
|  |  | ||||||
|  | import android.app.Activity; | ||||||
|  | import android.content.Context; | ||||||
|  | import android.content.SharedPreferences; | ||||||
|  | import android.os.Handler; | ||||||
|  | import android.preference.PreferenceManager; | ||||||
|  | import android.widget.Toast; | ||||||
|  |  | ||||||
|  | import org.schabi.newpipe.Downloader; | ||||||
|  | import org.schabi.newpipe.ErrorActivity; | ||||||
|  | import org.schabi.newpipe.R; | ||||||
|  | import org.schabi.newpipe.extractor.ExtractionException; | ||||||
|  | import org.schabi.newpipe.extractor.SearchEngine; | ||||||
|  | import org.schabi.newpipe.extractor.ServiceList; | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Created by the-scrabi on 02.08.16. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | public class SuggestionSearchRunnable implements Runnable{ | ||||||
|  |  | ||||||
|  |     private class SuggestionResultRunnable implements Runnable{ | ||||||
|  |  | ||||||
|  |         private List<String> suggestions; | ||||||
|  |         private SuggestionListAdapter adapter; | ||||||
|  |  | ||||||
|  |         private SuggestionResultRunnable(List<String> suggestions, SuggestionListAdapter adapter) { | ||||||
|  |             this.suggestions = suggestions; | ||||||
|  |             this.adapter = adapter; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         @Override | ||||||
|  |         public void run() { | ||||||
|  |             adapter.updateAdapter(suggestions); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private final int serviceId; | ||||||
|  |     private final String query; | ||||||
|  |     final Handler h = new Handler(); | ||||||
|  |     private Activity a = null; | ||||||
|  |     private 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 { | ||||||
|  |             SearchEngine engine = | ||||||
|  |                     ServiceList.getService(serviceId).getSearchEngineInstance(new Downloader()); | ||||||
|  |             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 = engine.suggestionList(query,searchLanguage,new Downloader()); | ||||||
|  |             h.post(new SuggestionResultRunnable(suggestions, adapter)); | ||||||
|  |         } catch (ExtractionException e) { | ||||||
|  |             ErrorActivity.reportError(h, a, e, null, a.findViewById(android.R.id.content), | ||||||
|  |                     ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED, | ||||||
|  |                             ServiceList.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, | ||||||
|  |                             ServiceList.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(); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -18,7 +18,7 @@ | |||||||
|  * along with NewPipe.  If not, see <http://www.gnu.org/licenses/>. |  * along with NewPipe.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package org.schabi.newpipe; | package org.schabi.newpipe.settings; | ||||||
| 
 | 
 | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.content.SharedPreferences; | import android.content.SharedPreferences; | ||||||
| @@ -26,6 +26,8 @@ import android.os.Environment; | |||||||
| import android.preference.PreferenceManager; | import android.preference.PreferenceManager; | ||||||
| import android.support.annotation.NonNull; | import android.support.annotation.NonNull; | ||||||
| 
 | 
 | ||||||
|  | import org.schabi.newpipe.R; | ||||||
|  | 
 | ||||||
| import java.io.File; | import java.io.File; | ||||||
| 
 | 
 | ||||||
| import us.shandian.giga.util.Utility; | import us.shandian.giga.util.Utility; | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe; | package org.schabi.newpipe.settings; | ||||||
| 
 | 
 | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.content.Intent; | import android.content.Intent; | ||||||
| @@ -14,6 +14,8 @@ import android.view.MenuItem; | |||||||
| import android.view.View; | import android.view.View; | ||||||
| import android.view.ViewGroup; | import android.view.ViewGroup; | ||||||
| 
 | 
 | ||||||
|  | import org.schabi.newpipe.R; | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Created by Christian Schabesberger on 31.08.15. |  * Created by Christian Schabesberger on 31.08.15. | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe; | package org.schabi.newpipe.settings; | ||||||
| 
 | 
 | ||||||
| import android.app.Activity; | import android.app.Activity; | ||||||
| import android.content.ClipData; | import android.content.ClipData; | ||||||
| @@ -16,6 +16,9 @@ import android.preference.PreferenceScreen; | |||||||
| 
 | 
 | ||||||
| import com.nononsenseapps.filepicker.FilePickerActivity; | import com.nononsenseapps.filepicker.FilePickerActivity; | ||||||
| 
 | 
 | ||||||
|  | import org.schabi.newpipe.App; | ||||||
|  | import org.schabi.newpipe.R; | ||||||
|  | 
 | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| 
 | 
 | ||||||
| import info.guardianproject.netcipher.proxy.OrbotHelper; | import info.guardianproject.netcipher.proxy.OrbotHelper; | ||||||
| @@ -5,7 +5,7 @@ import android.util.Log; | |||||||
|  |  | ||||||
| import com.google.gson.Gson; | import com.google.gson.Gson; | ||||||
|  |  | ||||||
| import org.schabi.newpipe.NewPipeSettings; | import org.schabi.newpipe.settings.NewPipeSettings; | ||||||
|  |  | ||||||
| import java.io.File; | import java.io.File; | ||||||
| import java.io.RandomAccessFile; | import java.io.RandomAccessFile; | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ import android.os.Message; | |||||||
| import android.support.v4.app.NotificationCompat.Builder; | import android.support.v4.app.NotificationCompat.Builder; | ||||||
| import android.util.Log; | import android.util.Log; | ||||||
|  |  | ||||||
| import org.schabi.newpipe.NewPipeSettings; | import org.schabi.newpipe.settings.NewPipeSettings; | ||||||
| import org.schabi.newpipe.R; | import org.schabi.newpipe.R; | ||||||
| import us.shandian.giga.get.DownloadManager; | import us.shandian.giga.get.DownloadManager; | ||||||
| import us.shandian.giga.get.DownloadManagerImpl; | import us.shandian.giga.get.DownloadManagerImpl; | ||||||
|   | |||||||
| @@ -17,9 +17,8 @@ import java.io.IOException; | |||||||
| import java.security.MessageDigest; | import java.security.MessageDigest; | ||||||
| import java.security.NoSuchAlgorithmException; | import java.security.NoSuchAlgorithmException; | ||||||
|  |  | ||||||
| import org.schabi.newpipe.NewPipeSettings; | import org.schabi.newpipe.settings.NewPipeSettings; | ||||||
| import org.schabi.newpipe.R; | import org.schabi.newpipe.R; | ||||||
| import us.shandian.giga.get.DownloadMission; |  | ||||||
|  |  | ||||||
| import com.nononsenseapps.filepicker.FilePickerActivity; | import com.nononsenseapps.filepicker.FilePickerActivity; | ||||||
| import com.nononsenseapps.filepicker.AbstractFilePickerFragment; | import com.nononsenseapps.filepicker.AbstractFilePickerFragment; | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-hdpi/ic_rss_feed_white_24dp.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/src/main/res/drawable-hdpi/ic_rss_feed_white_24dp.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 417 B | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-mdpi/ic_rss_feed_white_24dp.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/src/main/res/drawable-mdpi/ic_rss_feed_white_24dp.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 286 B | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-xhdpi/ic_rss_feed_white_24dp.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/src/main/res/drawable-xhdpi/ic_rss_feed_white_24dp.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 528 B | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-xxhdpi/ic_rss_feed_white_24dp.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/src/main/res/drawable-xxhdpi/ic_rss_feed_white_24dp.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 761 B | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-xxxhdpi/ic_rss_feed_white_24dp.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/src/main/res/drawable-xxxhdpi/ic_rss_feed_white_24dp.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1012 B | 
							
								
								
									
										13
									
								
								app/src/main/res/drawable/white_circle.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								app/src/main/res/drawable/white_circle.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <shape | ||||||
|  |     xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|  |     android:shape="oval"> | ||||||
|  |  | ||||||
|  |     <solid | ||||||
|  |         android:color="@android:color/white"/> | ||||||
|  |  | ||||||
|  |     <size | ||||||
|  |         android:height="@dimen/channel_avatar_halo_size" | ||||||
|  |         android:width="@dimen/channel_avatar_halo_size"/> | ||||||
|  |  | ||||||
|  | </shape> | ||||||
| @@ -1,55 +0,0 @@ | |||||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     xmlns:tools="http://schemas.android.com/tools" |  | ||||||
|     android:layout_width="match_parent" |  | ||||||
|     android:layout_height="match_parent" |  | ||||||
|     android:baselineAligned="false" |  | ||||||
|     android:divider="?android:attr/dividerHorizontal" |  | ||||||
|     android:orientation="horizontal" |  | ||||||
|     android:showDividers="middle" |  | ||||||
|     tools:context=".VideoItemListActivity"> |  | ||||||
|  |  | ||||||
|     <!-- |  | ||||||
|     This layout is a two-pane layout for the VideoItems |  | ||||||
|     master/detail flow. |  | ||||||
|      |  | ||||||
|     --> |  | ||||||
|  |  | ||||||
|     <LinearLayout |  | ||||||
|         android:orientation="vertical" |  | ||||||
|         android:layout_width="0dp" |  | ||||||
|         android:layout_height="match_parent" |  | ||||||
|         android:layout_weight = "2"> |  | ||||||
|  |  | ||||||
|         <android.support.v7.widget.SearchView |  | ||||||
|             android:id="@+id/searchViewTablet" |  | ||||||
|             android:layout_width="match_parent" |  | ||||||
|             android:layout_height="wrap_content" |  | ||||||
|             android:iconifiedByDefault="false" |  | ||||||
|             android:focusable="false" |  | ||||||
|             tools:ignore="InconsistentLayout" /> |  | ||||||
|  |  | ||||||
|         <include layout="@layout/main_bg" /> |  | ||||||
|  |  | ||||||
|         <fragment android:id="@+id/videoitem_list" |  | ||||||
|             android:name="org.schabi.newpipe.VideoItemListFragment" |  | ||||||
|             android:layout_width="match_parent" |  | ||||||
|             android:layout_height="wrap_content" |  | ||||||
|             tools:layout="@android:layout/list_content" /> |  | ||||||
|  |  | ||||||
|     </LinearLayout> |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     <LinearLayout |  | ||||||
|         android:orientation="vertical" |  | ||||||
|         android:layout_height="match_parent" |  | ||||||
|         android:layout_width="0dp" |  | ||||||
|         android:layout_weight="4"> |  | ||||||
|  |  | ||||||
|         <FrameLayout android:id="@+id/videoitem_detail_container" |  | ||||||
|             android:layout_width="match_parent" |  | ||||||
|             android:layout_height="match_parent" |  | ||||||
|             tools:ignore="InconsistentLayout" /> |  | ||||||
|  |  | ||||||
|     </LinearLayout> |  | ||||||
|  |  | ||||||
| </LinearLayout> |  | ||||||
| @@ -3,15 +3,15 @@ | |||||||
| <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|     xmlns:tools="http://schemas.android.com/tools" |     xmlns:tools="http://schemas.android.com/tools" | ||||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" |     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||||
|     tools:context=".VideoItemDetailFragment" |     tools:context=".detail.VideoItemDetailFragment" | ||||||
|     android:textIsSelectable="true" |     android:textIsSelectable="true" | ||||||
|     style="?android:attr/textAppearanceLarge" |     style="?android:attr/textAppearanceLarge" | ||||||
|     android:layout_width="match_parent" |     android:layout_width="match_parent" | ||||||
|     android:layout_height="match_parent" |     android:layout_height="match_parent" | ||||||
|     android:id="@+id/videoitem_detail"> |     android:id="@+id/video_item_detail"> | ||||||
|  |  | ||||||
|     <com.nirhart.parallaxscroll.views.ParallaxScrollView |     <com.nirhart.parallaxscroll.views.ParallaxScrollView | ||||||
|         android:id="@+id/detailMainContent" |         android:id="@+id/detail_main_content" | ||||||
|         android:layout_width="match_parent" |         android:layout_width="match_parent" | ||||||
|         android:layout_height="match_parent" |         android:layout_height="match_parent" | ||||||
|         android:visibility="visible" |         android:visibility="visible" | ||||||
| @@ -23,12 +23,12 @@ | |||||||
|             android:layout_height="wrap_content"> |             android:layout_height="wrap_content"> | ||||||
|  |  | ||||||
|             <RelativeLayout |             <RelativeLayout | ||||||
|                 android:id="@+id/detailVideoThumbnailWindowLayout" |                 android:id="@+id/detail_stream_thumbnail_window_layout" | ||||||
|                 android:layout_width="match_parent" |                 android:layout_width="match_parent" | ||||||
|                 android:layout_height="wrap_content" |                 android:layout_height="wrap_content" | ||||||
|                 android:background="?attr/selectableItemBackground"> |                 android:background="?attr/selectableItemBackground"> | ||||||
|  |  | ||||||
|                 <ImageView android:id="@+id/detailThumbnailView" |                 <ImageView android:id="@+id/detail_thumbnail_view" | ||||||
|                     android:contentDescription="@string/detail_thumbnail_view_description" |                     android:contentDescription="@string/detail_thumbnail_view_description" | ||||||
|                     android:layout_width="match_parent" |                     android:layout_width="match_parent" | ||||||
|                     android:layout_height="wrap_content" |                     android:layout_height="wrap_content" | ||||||
| @@ -40,13 +40,13 @@ | |||||||
|                     android:background="@android:color/black" |                     android:background="@android:color/black" | ||||||
|                     android:src="@drawable/dummy_thumbnail_dark"/> |                     android:src="@drawable/dummy_thumbnail_dark"/> | ||||||
|  |  | ||||||
|                 <ProgressBar android:id="@+id/detailProgressBar" |                 <ProgressBar android:id="@+id/detail_progress_bar" | ||||||
|                     android:layout_width="wrap_content" |                     android:layout_width="wrap_content" | ||||||
|                     android:layout_height="wrap_content" |                     android:layout_height="wrap_content" | ||||||
|                     android:layout_centerInParent="true" |                     android:layout_centerInParent="true" | ||||||
|                     android:indeterminate="true"/> |                     android:indeterminate="true"/> | ||||||
|  |  | ||||||
|                 <ImageView android:id="@+id/playArrowView" |                 <ImageView android:id="@+id/play_arrow_view" | ||||||
|                     android:layout_width="wrap_content" |                     android:layout_width="wrap_content" | ||||||
|                     android:layout_height="wrap_content" |                     android:layout_height="wrap_content" | ||||||
|                     android:background="@android:color/transparent" |                     android:background="@android:color/transparent" | ||||||
| @@ -55,18 +55,17 @@ | |||||||
|                     android:visibility="invisible"/> |                     android:visibility="invisible"/> | ||||||
|  |  | ||||||
|                 <Button |                 <Button | ||||||
|                     android:id="@+id/detailVideoThumbnailWindowBackgroundButton" |                     android:id="@+id/detail_stream_thumbnail_window_background_button" | ||||||
|                     android:layout_width="match_parent" |                     android:layout_width="match_parent" | ||||||
|                     android:layout_height="match_parent" |                     android:layout_height="match_parent" | ||||||
|                     android:background="?attr/selectableItemBackground"/> |                     android:background="?attr/selectableItemBackground"/> | ||||||
|  |  | ||||||
|             </RelativeLayout> |             </RelativeLayout> | ||||||
|  |  | ||||||
|             <RelativeLayout android:id="@+id/detailTextContentLayout" |             <RelativeLayout android:id="@+id/detail_text_content_layout" | ||||||
|  |  | ||||||
|                 android:layout_width="match_parent" |                 android:layout_width="match_parent" | ||||||
|                 android:layout_height="match_parent" |                 android:layout_height="match_parent" | ||||||
|                 android:layout_below="@id/detailVideoThumbnailWindowLayout" |                 android:layout_below="@id/detail_stream_thumbnail_window_layout" | ||||||
|                 android:background="@color/light_background_color" |                 android:background="@color/light_background_color" | ||||||
|                 android:visibility="gone"> |                 android:visibility="gone"> | ||||||
|  |  | ||||||
| @@ -75,7 +74,7 @@ | |||||||
|                     android:layout_height="wrap_content" |                     android:layout_height="wrap_content" | ||||||
|                     android:id="@+id/detailTopView"> |                     android:id="@+id/detailTopView"> | ||||||
|  |  | ||||||
|                     <TextView android:id="@+id/detailVideoTitleView" |                     <TextView android:id="@+id/detail_video_title_view" | ||||||
|                         android:layout_width="0dp" |                         android:layout_width="0dp" | ||||||
|                         android:layout_weight=".7" |                         android:layout_weight=".7" | ||||||
|                         android:layout_height="wrap_content" |                         android:layout_height="wrap_content" | ||||||
| @@ -89,7 +88,7 @@ | |||||||
|                     <ImageView |                     <ImageView | ||||||
|                         android:layout_width="15dp" |                         android:layout_width="15dp" | ||||||
|                         android:layout_height="30dp" |                         android:layout_height="30dp" | ||||||
|                         android:id="@+id/toggleDescriptionView" |                         android:id="@+id/toggle_description_view" | ||||||
|                         android:src="@drawable/arrow_down" |                         android:src="@drawable/arrow_down" | ||||||
|                         android:layout_marginLeft="10dp" |                         android:layout_marginLeft="10dp" | ||||||
|                         android:layout_marginStart="10dp" |                         android:layout_marginStart="10dp" | ||||||
| @@ -99,7 +98,7 @@ | |||||||
|  |  | ||||||
|                 </LinearLayout> |                 </LinearLayout> | ||||||
|  |  | ||||||
|                 <TextView android:id="@+id/detailViewCountView" |                 <TextView android:id="@+id/detail_view_count_view" | ||||||
|                     android:layout_width="match_parent" |                     android:layout_width="match_parent" | ||||||
|                     android:layout_height="wrap_content" |                     android:layout_height="wrap_content" | ||||||
|                     android:textSize="@dimen/video_item_detail_views_text_size" |                     android:textSize="@dimen/video_item_detail_views_text_size" | ||||||
| @@ -115,14 +114,14 @@ | |||||||
|                 <RelativeLayout |                 <RelativeLayout | ||||||
|                     android:layout_width="match_parent" |                     android:layout_width="match_parent" | ||||||
|                     android:layout_height="wrap_content" |                     android:layout_height="wrap_content" | ||||||
|                     android:layout_below="@id/detailViewCountView" |                     android:layout_below="@id/detail_view_count_view" | ||||||
|                     android:id="@+id/detailExtraView" |                     android:id="@+id/detailExtraView" | ||||||
|                     android:layout_marginLeft="12dp" |                     android:layout_marginLeft="12dp" | ||||||
|                     android:layout_marginStart="12dp" |                     android:layout_marginStart="12dp" | ||||||
|                     android:layout_marginRight="12dp" |                     android:layout_marginRight="12dp" | ||||||
|                     android:layout_marginEnd="12dp" |                     android:layout_marginEnd="12dp" | ||||||
|                     android:visibility="gone"> |                     android:visibility="gone"> | ||||||
|                     <TextView android:id="@+id/detailUploadDateView" |                     <TextView android:id="@+id/detail_upload_date_view" | ||||||
|                         android:layout_width="wrap_content" |                         android:layout_width="wrap_content" | ||||||
|                         android:layout_height="wrap_content" |                         android:layout_height="wrap_content" | ||||||
|                         android:textSize="@dimen/video_item_detail_upload_date_text_size" |                         android:textSize="@dimen/video_item_detail_upload_date_text_size" | ||||||
| @@ -130,12 +129,12 @@ | |||||||
|                         android:text="Upload date" |                         android:text="Upload date" | ||||||
|                         android:layout_marginTop="3dp" /> |                         android:layout_marginTop="3dp" /> | ||||||
|  |  | ||||||
|                     <TextView android:id="@+id/detailDescriptionView" |                     <TextView android:id="@+id/detail_description_view" | ||||||
|                         android:layout_width="match_parent" |                         android:layout_width="match_parent" | ||||||
|                         android:layout_height="wrap_content" |                         android:layout_height="wrap_content" | ||||||
|                         android:textSize="@dimen/video_item_detail_description_text_size" |                         android:textSize="@dimen/video_item_detail_description_text_size" | ||||||
|                         android:textAppearance="?android:attr/textAppearanceMedium" |                         android:textAppearance="?android:attr/textAppearanceMedium" | ||||||
|                         android:layout_below="@id/detailUploadDateView" |                         android:layout_below="@id/detail_upload_date_view" | ||||||
|                         android:text="Description............." |                         android:text="Description............." | ||||||
|                         android:layout_marginTop="3dp" /> |                         android:layout_marginTop="3dp" /> | ||||||
|                 </RelativeLayout> |                 </RelativeLayout> | ||||||
| @@ -143,27 +142,27 @@ | |||||||
|                 <LinearLayout |                 <LinearLayout | ||||||
|                     android:layout_width="match_parent" |                     android:layout_width="match_parent" | ||||||
|                     android:layout_height="wrap_content" |                     android:layout_height="wrap_content" | ||||||
|                     android:id="@+id/linearLayout" |                     android:id="@+id/stream_info_layout" | ||||||
|                     android:layout_marginLeft="12dp" |                     android:layout_marginLeft="12dp" | ||||||
|                     android:layout_marginStart="12dp" |                     android:layout_marginStart="12dp" | ||||||
|                     android:layout_below="@+id/detailExtraView" |                     android:layout_below="@+id/detailExtraView" | ||||||
|                     android:layout_alignParentRight="true" |                     android:layout_alignParentRight="true" | ||||||
|                     android:layout_alignParentEnd="true" |                     android:layout_alignParentEnd="true" | ||||||
|                     android:layout_marginTop="5dp"> |                     android:layout_marginTop="5dp"> | ||||||
|                     <ImageView android:id="@+id/detailThumbsUpImgView" |                     <ImageView android:id="@+id/detail_thumbs_up_img_view" | ||||||
|                         android:contentDescription="@string/detail_likes_img_view_description" |                         android:contentDescription="@string/detail_likes_img_view_description" | ||||||
|                         android:layout_width="@dimen/video_item_detail_like_image_width" |                         android:layout_width="@dimen/video_item_detail_like_image_width" | ||||||
|                         android:layout_height="@dimen/video_item_detail_like_image_height" |                         android:layout_height="@dimen/video_item_detail_like_image_height" | ||||||
|                         android:src="@drawable/thumbs_up" /> |                         android:src="@drawable/thumbs_up" /> | ||||||
|  |  | ||||||
|                     <TextView android:id="@+id/detailThumbsUpCountView" |                     <TextView android:id="@+id/detail_thumbs_up_count_view" | ||||||
|                         android:layout_width="wrap_content" |                         android:layout_width="wrap_content" | ||||||
|                         android:layout_height="wrap_content" |                         android:layout_height="wrap_content" | ||||||
|                         android:textSize="@dimen/video_item_detail_likes_text_size" |                         android:textSize="@dimen/video_item_detail_likes_text_size" | ||||||
|                         android:textAppearance="?android:attr/textAppearanceMedium" |                         android:textAppearance="?android:attr/textAppearanceMedium" | ||||||
|                         android:text="200" /> |                         android:text="200" /> | ||||||
|  |  | ||||||
|                     <ImageView android:id="@+id/detailThumbsDownImgView" |                     <ImageView android:id="@+id/detail_thumbs_down_img_view" | ||||||
|                         android:contentDescription="@string/detail_dislikes_img_view_description" |                         android:contentDescription="@string/detail_dislikes_img_view_description" | ||||||
|                         android:layout_width="@dimen/video_item_detail_like_image_width" |                         android:layout_width="@dimen/video_item_detail_like_image_width" | ||||||
|                         android:layout_height="@dimen/video_item_detail_like_image_height" |                         android:layout_height="@dimen/video_item_detail_like_image_height" | ||||||
| @@ -171,7 +170,7 @@ | |||||||
|                         android:layout_marginLeft="10dp" |                         android:layout_marginLeft="10dp" | ||||||
|                         android:layout_marginStart="10dp"/> |                         android:layout_marginStart="10dp"/> | ||||||
|  |  | ||||||
|                     <TextView android:id="@+id/detailThumbsDownCountView" |                     <TextView android:id="@+id/detail_thumbs_down_count_view" | ||||||
|                         android:layout_width="wrap_content" |                         android:layout_width="wrap_content" | ||||||
|                         android:layout_height="wrap_content" |                         android:layout_height="wrap_content" | ||||||
|                         android:textSize="@dimen/video_item_detail_likes_text_size" |                         android:textSize="@dimen/video_item_detail_likes_text_size" | ||||||
| @@ -179,59 +178,72 @@ | |||||||
|                         android:text="100" /> |                         android:text="100" /> | ||||||
|                 </LinearLayout> |                 </LinearLayout> | ||||||
|  |  | ||||||
|                 <RelativeLayout |                 <FrameLayout | ||||||
|                     android:layout_width="match_parent" |                     android:layout_width="match_parent" | ||||||
|                     android:layout_height="wrap_content" |                     android:layout_height="wrap_content" | ||||||
|                     android:layout_below="@+id/linearLayout" |                     android:id="@+id/detail_uploader_frame" | ||||||
|                     android:id="@+id/detailUploaderWrapView" |                     android:layout_below="@id/stream_info_layout"> | ||||||
|                     android:layout_marginTop="12dp"> |                     <RelativeLayout | ||||||
|  |  | ||||||
|                     <View |  | ||||||
|                         android:background="#000" |  | ||||||
|                         android:layout_width="match_parent" |                         android:layout_width="match_parent" | ||||||
|                         android:layout_height="1px" /> |  | ||||||
|  |  | ||||||
|                     <de.hdodenhof.circleimageview.CircleImageView android:id="@+id/detailUploaderThumbnailView" |  | ||||||
|                         android:contentDescription="@string/detail_uploader_thumbnail_view_description" |  | ||||||
|                         android:layout_width="@dimen/video_item_detail_uploader_image_size" |  | ||||||
|                         android:layout_height="@dimen/video_item_detail_uploader_image_size" |  | ||||||
|                         android:src="@drawable/buddy" |  | ||||||
|                         android:layout_marginLeft="10dp" |  | ||||||
|                         android:layout_marginStart="10dp" |  | ||||||
|                         android:layout_alignParentLeft="true" |  | ||||||
|                         android:layout_alignParentStart="true" |  | ||||||
|                         android:layout_marginTop="5dp" |  | ||||||
|                         android:layout_marginBottom="5dp"/> |  | ||||||
|  |  | ||||||
|                     <TextView android:id="@+id/detailUploaderView" |  | ||||||
|                         android:layout_width="wrap_content" |  | ||||||
|                         android:layout_height="wrap_content" |                         android:layout_height="wrap_content" | ||||||
|                         android:textStyle="bold" |                         android:id="@+id/detail_uploader_layout" | ||||||
|                         android:textSize="@dimen/video_item_detail_uploader_text_size" |                         android:layout_marginTop="12dp"> | ||||||
|                         android:textAppearance="?android:attr/textAppearanceLarge" |  | ||||||
|                         android:text="Uploader" |  | ||||||
|                         android:layout_centerVertical="true" |  | ||||||
|                         android:layout_toRightOf="@+id/detailUploaderThumbnailView" |  | ||||||
|                         android:layout_toEndOf="@+id/detailUploaderThumbnailView" |  | ||||||
|                         android:layout_marginLeft="15dp" |  | ||||||
|                         android:layout_marginStart="28dp" /> |  | ||||||
|  |  | ||||||
|                     <View |                         <View | ||||||
|                         android:background="#000" |                             android:background="#000" | ||||||
|  |                             android:layout_width="match_parent" | ||||||
|  |                             android:layout_height="1px" /> | ||||||
|  |  | ||||||
|  |                         <de.hdodenhof.circleimageview.CircleImageView | ||||||
|  |                             android:id="@+id/detail_uploader_thumbnail_view" | ||||||
|  |                             android:contentDescription="@string/detail_uploader_thumbnail_view_description" | ||||||
|  |                             android:layout_width="@dimen/video_item_detail_uploader_image_size" | ||||||
|  |                             android:layout_height="@dimen/video_item_detail_uploader_image_size" | ||||||
|  |                             android:src="@drawable/buddy" | ||||||
|  |                             android:layout_marginLeft="10dp" | ||||||
|  |                             android:layout_marginStart="10dp" | ||||||
|  |                             android:layout_alignParentLeft="true" | ||||||
|  |                             android:layout_alignParentStart="true" | ||||||
|  |                             android:layout_marginTop="5dp" | ||||||
|  |                             android:layout_marginBottom="5dp"/> | ||||||
|  |  | ||||||
|  |                         <TextView android:id="@+id/detail_uploader_view" | ||||||
|  |                             android:layout_width="wrap_content" | ||||||
|  |                             android:layout_height="wrap_content" | ||||||
|  |                             android:textStyle="bold" | ||||||
|  |                             android:textSize="@dimen/video_item_detail_uploader_text_size" | ||||||
|  |                             android:textAppearance="?android:attr/textAppearanceLarge" | ||||||
|  |                             android:text="Uploader" | ||||||
|  |                             android:layout_centerVertical="true" | ||||||
|  |                             android:layout_toRightOf="@+id/detail_uploader_thumbnail_view" | ||||||
|  |                             android:layout_toEndOf="@+id/detail_uploader_thumbnail_view" | ||||||
|  |                             android:layout_marginLeft="15dp" | ||||||
|  |                             android:layout_marginStart="28dp" /> | ||||||
|  |  | ||||||
|  |                         <View | ||||||
|  |                             android:background="#000" | ||||||
|  |                             android:layout_width="match_parent" | ||||||
|  |                             android:layout_height="1px" | ||||||
|  |                             android:layout_below="@id/detail_uploader_thumbnail_view"/> | ||||||
|  |                     </RelativeLayout> | ||||||
|  |                     <Button | ||||||
|  |                         android:layout_marginTop="13dp" | ||||||
|  |                         android:id="@+id/channel_button" | ||||||
|                         android:layout_width="match_parent" |                         android:layout_width="match_parent" | ||||||
|                         android:layout_height="1px" |                         android:layout_height="match_parent" | ||||||
|                         android:layout_below="@id/detailUploaderThumbnailView"/> |                         android:background="?attr/selectableItemBackground"/> | ||||||
|                 </RelativeLayout> |                 </FrameLayout> | ||||||
|  |  | ||||||
|                 <RelativeLayout android:id="@+id/detailNextVideoRootLayout" |                 <RelativeLayout android:id="@+id/detail_next_stream_root_layout" | ||||||
|                     android:layout_width="match_parent" |                     android:layout_width="match_parent" | ||||||
|                     android:layout_height="match_parent" |                     android:layout_height="match_parent" | ||||||
|                     android:layout_gravity="center_horizontal|bottom" |                     android:layout_gravity="center_horizontal|bottom" | ||||||
|                     android:layout_below="@+id/detailUploaderWrapView" |                     android:layout_below="@+id/detail_uploader_frame" | ||||||
|                     android:layout_centerHorizontal="true" |                     android:layout_centerHorizontal="true" | ||||||
|                     android:layout_marginTop="10dp"> |                     android:layout_marginTop="10dp"> | ||||||
|  |  | ||||||
|                     <TextView android:id="@+id/detailNextVideoTitle" |                     <TextView | ||||||
|  |                         android:id="@+id/detail_next_stream_title" | ||||||
|                         android:layout_width="wrap_content" |                         android:layout_width="wrap_content" | ||||||
|                         android:layout_height="wrap_content" |                         android:layout_height="wrap_content" | ||||||
|                         android:layout_centerHorizontal="true" |                         android:layout_centerHorizontal="true" | ||||||
| @@ -240,38 +252,25 @@ | |||||||
|                         android:text="@string/next_video_title" |                         android:text="@string/next_video_title" | ||||||
|                         android:textAllCaps="true" /> |                         android:textAllCaps="true" /> | ||||||
|  |  | ||||||
|                     <RelativeLayout android:id="@+id/detailNextVidButtonAndContentLayout" |                     <android.support.v7.widget.RecyclerView | ||||||
|                         android:layout_width="match_parent" |                         android:layout_width="match_parent" | ||||||
|                         android:layout_height="match_parent" |                         android:layout_height="wrap_content" | ||||||
|                         android:layout_below="@id/detailNextVideoTitle"> |                         android:id="@+id/detail_next_stream_content"/> | ||||||
|                         <FrameLayout |                     <TextView android:id="@+id/detail_similar_title" | ||||||
|                             android:id="@+id/detailNextVideoFrame" |  | ||||||
|                             android:layout_width="match_parent" |  | ||||||
|                             android:layout_height="wrap_content"/> |  | ||||||
|                         <Button |  | ||||||
|                             android:id="@+id/detailNextVideoButton" |  | ||||||
|                             android:layout_width="match_parent" |  | ||||||
|                             android:layout_height="match_parent" |  | ||||||
|                             android:layout_alignTop="@id/detailNextVideoFrame" |  | ||||||
|                             android:layout_alignBottom="@id/detailNextVideoFrame" |  | ||||||
|                             android:background="?attr/selectableItemBackground"/> |  | ||||||
|                     </RelativeLayout> |  | ||||||
|                     <TextView android:id="@+id/detailSimilarTitle" |  | ||||||
|                         android:layout_width="wrap_content" |                         android:layout_width="wrap_content" | ||||||
|                         android:layout_height="wrap_content" |                         android:layout_height="wrap_content" | ||||||
|                         android:layout_centerHorizontal="true" |                         android:layout_centerHorizontal="true" | ||||||
|                         android:textSize="@dimen/video_item_detail_next_text_size" |                         android:textSize="@dimen/video_item_detail_next_text_size" | ||||||
|                         android:textAppearance="?android:attr/textAppearanceMedium" |                         android:textAppearance="?android:attr/textAppearanceMedium" | ||||||
|                         android:text="@string/similar_videos_btn_text" |                         android:text="@string/similar_videos_btn_text" | ||||||
|                         android:layout_below="@id/detailNextVidButtonAndContentLayout" |                         android:layout_below="@id/detail_next_stream_content" | ||||||
|                         android:textAllCaps="true" /> |                         android:textAllCaps="true" /> | ||||||
|                     <LinearLayout |                     <android.support.v7.widget.RecyclerView | ||||||
|                         android:id="@+id/similarVideosView" |                         android:id="@+id/similar_streams_view" | ||||||
|                         android:orientation="vertical" |                         android:orientation="vertical" | ||||||
|                         android:layout_width="match_parent" |                         android:layout_width="match_parent" | ||||||
|                         android:layout_height="match_parent" |                         android:layout_height="match_parent" | ||||||
|                         android:layout_below="@id/detailSimilarTitle"> |                         android:layout_below="@id/detail_similar_title"/> | ||||||
|                     </LinearLayout> |  | ||||||
|                 </RelativeLayout> |                 </RelativeLayout> | ||||||
|             </RelativeLayout> |             </RelativeLayout> | ||||||
|         </RelativeLayout> |         </RelativeLayout> | ||||||
|   | |||||||
							
								
								
									
										96
									
								
								app/src/main/res/layout/activity_channel.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								app/src/main/res/layout/activity_channel.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|  |     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||||
|  |     xmlns:tools="http://schemas.android.com/tools" | ||||||
|  |     android:layout_width="match_parent" | ||||||
|  |     android:layout_height="match_parent" | ||||||
|  |     android:fitsSystemWindows="true" | ||||||
|  |     android:id="@+id/rootView" | ||||||
|  |     tools:context="org.schabi.newpipe.ChannelActivity"> | ||||||
|  |  | ||||||
|  |     <android.support.design.widget.AppBarLayout | ||||||
|  |         android:id="@+id/channel_app_bar" | ||||||
|  |         android:layout_width="match_parent" | ||||||
|  |         android:layout_height="@dimen/app_bar_height" | ||||||
|  |         android:fitsSystemWindows="true" | ||||||
|  |         android:theme="@style/AppTheme.AppBarOverlay"> | ||||||
|  |  | ||||||
|  |         <android.support.design.widget.CollapsingToolbarLayout | ||||||
|  |             android:id="@+id/channel_toolbar_layout" | ||||||
|  |             android:layout_width="match_parent" | ||||||
|  |             android:layout_height="match_parent" | ||||||
|  |             android:fitsSystemWindows="true" | ||||||
|  |             app:contentScrim="@color/light_youtube_primary_color" | ||||||
|  |             app:statusBarScrim="@color/light_youtube_dark_color" | ||||||
|  |             app:layout_scrollFlags="scroll|exitUntilCollapsed"> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             <ImageView | ||||||
|  |                 android:id="@+id/channel_banner_image" | ||||||
|  |                 android:layout_width="match_parent" | ||||||
|  |                 android:layout_height="match_parent" | ||||||
|  |                 android:fitsSystemWindows="true" | ||||||
|  |                 android:scaleType="centerCrop" | ||||||
|  |                 android:background="@color/light_youtube_dark_color" | ||||||
|  |                 app:layout_collapseMode="parallax" /> | ||||||
|  |  | ||||||
|  |             <ImageView | ||||||
|  |                 android:id="@+id/channel_avatar_halo" | ||||||
|  |                 android:visibility="gone" | ||||||
|  |                 android:layout_width="wrap_content" | ||||||
|  |                 android:layout_height="wrap_content" | ||||||
|  |                 android:layout_marginLeft="28dp" | ||||||
|  |                 android:layout_marginStart="28dp" | ||||||
|  |                 android:layout_marginTop="38dp" | ||||||
|  |                 android:layout_alignParentLeft="true" | ||||||
|  |                 android:layout_alignParentStart="true" | ||||||
|  |                 android:src="@drawable/white_circle"/> | ||||||
|  |  | ||||||
|  |             <de.hdodenhof.circleimageview.CircleImageView | ||||||
|  |                 android:id="@+id/channel_avatar_view" | ||||||
|  |                 android:visibility="gone" | ||||||
|  |                 android:layout_width="@dimen/channel_avatar_size" | ||||||
|  |                 android:layout_height="@dimen/channel_avatar_size" | ||||||
|  |                 android:src="@drawable/buddy" | ||||||
|  |                 android:layout_marginLeft="30dp" | ||||||
|  |                 android:layout_marginStart="30dp" | ||||||
|  |                 android:layout_marginTop="40dp" | ||||||
|  |                 android:layout_alignParentLeft="true" | ||||||
|  |                 android:layout_alignParentStart="true"/> | ||||||
|  |  | ||||||
|  |             <android.support.v7.widget.Toolbar | ||||||
|  |                 android:id="@+id/cannel_toolbar" | ||||||
|  |                 android:layout_width="match_parent" | ||||||
|  |                 android:layout_height="?attr/actionBarSize" | ||||||
|  |                 app:layout_collapseMode="pin" | ||||||
|  |                 app:popupTheme="@style/AppTheme.PopupOverlay" /> | ||||||
|  |  | ||||||
|  |         </android.support.design.widget.CollapsingToolbarLayout> | ||||||
|  |     </android.support.design.widget.AppBarLayout> | ||||||
|  |  | ||||||
|  |     <android.support.design.widget.FloatingActionButton | ||||||
|  |         android:id="@+id/channel_rss_fab" | ||||||
|  |         android:layout_width="wrap_content" | ||||||
|  |         android:layout_height="wrap_content" | ||||||
|  |         android:layout_margin="@dimen/fab_margin" | ||||||
|  |         android:src="@drawable/ic_rss_feed_white_24dp" | ||||||
|  |         app:layout_anchor="@id/channel_app_bar" | ||||||
|  |         app:layout_anchorGravity="bottom|end" /> | ||||||
|  |  | ||||||
|  |     <RelativeLayout | ||||||
|  |         android:layout_width="match_parent" | ||||||
|  |         android:layout_height="match_parent" | ||||||
|  |         android:id="@+id/channel_loading"> | ||||||
|  |         <ProgressBar android:id="@+id/progressBar" | ||||||
|  |             android:layout_width="wrap_content" | ||||||
|  |             android:layout_height="wrap_content" | ||||||
|  |             android:layout_centerInParent="true" | ||||||
|  |             android:indeterminate="true"/> | ||||||
|  |     </RelativeLayout> | ||||||
|  |     <android.support.v7.widget.RecyclerView | ||||||
|  |         android:id="@+id/channel_streams_view" | ||||||
|  |         android:layout_width="match_parent" | ||||||
|  |         android:layout_height="match_parent" | ||||||
|  |         app:layout_behavior="@string/appbar_scrolling_view_behavior" | ||||||
|  |         android:scrollbars="vertical"/> | ||||||
|  | </android.support.design.widget.CoordinatorLayout> | ||||||
							
								
								
									
										17
									
								
								app/src/main/res/layout/activity_main.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								app/src/main/res/layout/activity_main.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|  |     xmlns:tools="http://schemas.android.com/tools" | ||||||
|  |     android:layout_width="match_parent" | ||||||
|  |     android:layout_height="match_parent" | ||||||
|  |     tools:context="org.schabi.newpipe.MainActivity" | ||||||
|  |     android:orientation="vertical"> | ||||||
|  |  | ||||||
|  |     <include layout="@layout/main_bg" /> | ||||||
|  |  | ||||||
|  |     <fragment | ||||||
|  |         android:id="@+id/search_fragment" | ||||||
|  |         android:name="org.schabi.newpipe.search_fragment.SearchInfoItemFragment" | ||||||
|  |         android:layout_width="match_parent" | ||||||
|  |         android:layout_height="match_parent" /> | ||||||
|  |  | ||||||
|  | </RelativeLayout> | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|     xmlns:tools="http://schemas.android.com/tools" android:id="@+id/videoitem_detail_container" |     xmlns:tools="http://schemas.android.com/tools" android:id="@+id/videoitem_detail_container" | ||||||
|     android:layout_width="match_parent" android:layout_height="match_parent" |     android:layout_width="match_parent" android:layout_height="match_parent" | ||||||
|     tools:context=".VideoItemDetailActivity" tools:ignore="MergeRootFrame" /> |     tools:context=".detail.VideoItemDetailActivity" tools:ignore="MergeRootFrame" /> | ||||||
|   | |||||||
| @@ -1,18 +0,0 @@ | |||||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|  |  | ||||||
|     android:layout_width="match_parent" |  | ||||||
|     android:layout_height="match_parent" |  | ||||||
|     android:orientation="vertical"> |  | ||||||
|  |  | ||||||
|     <include layout="@layout/main_bg" /> |  | ||||||
|  |  | ||||||
|     <fragment |  | ||||||
|         xmlns:tools="http://schemas.android.com/tools" |  | ||||||
|         android:id="@+id/videoitem_list" |  | ||||||
|         android:name="org.schabi.newpipe.VideoItemListFragment" |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:layout_height="match_parent" |  | ||||||
|         tools:context=".VideoItemListActivity" |  | ||||||
|         tools:layout="@android:layout/list_content" /> |  | ||||||
|  |  | ||||||
| </LinearLayout> |  | ||||||
							
								
								
									
										17
									
								
								app/src/main/res/layout/content_channel.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								app/src/main/res/layout/content_channel.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|  |     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||||
|  |     xmlns:tools="http://schemas.android.com/tools" | ||||||
|  |     android:layout_width="match_parent" | ||||||
|  |     android:layout_height="match_parent" | ||||||
|  |     app:layout_behavior="@string/appbar_scrolling_view_behavior" | ||||||
|  |     tools:context="org.schabi.newpipe.ChannelActivity" | ||||||
|  |     tools:showIn="@layout/activity_channel"> | ||||||
|  |  | ||||||
|  |     <TextView | ||||||
|  |         android:layout_width="wrap_content" | ||||||
|  |         android:layout_height="wrap_content" | ||||||
|  |         android:layout_margin="@dimen/text_margin" | ||||||
|  |         android:text="@string/large_text" /> | ||||||
|  |  | ||||||
|  | </android.support.v4.widget.NestedScrollView> | ||||||
							
								
								
									
										24
									
								
								app/src/main/res/layout/fragment_searchinfoitem.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								app/src/main/res/layout/fragment_searchinfoitem.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|  |     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||||
|  |     xmlns:tools="http://schemas.android.com/tools" | ||||||
|  |     android:layout_height="match_parent" | ||||||
|  |     android:layout_width="match_parent" | ||||||
|  |     android:name="org.schabi.newpipe.SearchInfoItemFragment" | ||||||
|  |     tools:context=".search_fragment.SearchInfoItemFragment"> | ||||||
|  |  | ||||||
|  |     <android.support.v7.widget.RecyclerView | ||||||
|  |         android:id="@+id/list" | ||||||
|  |         android:layout_width="match_parent" | ||||||
|  |         android:layout_height="match_parent" | ||||||
|  |         app:layoutManager="LinearLayoutManager" | ||||||
|  |         tools:listitem="@layout/video_item" | ||||||
|  |         android:scrollbars="vertical"/> | ||||||
|  |  | ||||||
|  |     <ProgressBar android:id="@+id/progressBar" | ||||||
|  |         android:layout_width="wrap_content" | ||||||
|  |         android:layout_height="wrap_content" | ||||||
|  |         android:layout_centerInParent="true" | ||||||
|  |         android:indeterminate="true" | ||||||
|  |         android:visibility="gone"/> | ||||||
|  | </RelativeLayout> | ||||||
| @@ -3,15 +3,15 @@ | |||||||
| <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|     xmlns:tools="http://schemas.android.com/tools" |     xmlns:tools="http://schemas.android.com/tools" | ||||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" |     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||||
|     tools:context=".VideoItemDetailFragment" |     tools:context=".detail.VideoItemDetailFragment" | ||||||
|     android:textIsSelectable="true" |     android:textIsSelectable="true" | ||||||
|     style="?android:attr/textAppearanceLarge" |     style="?android:attr/textAppearanceLarge" | ||||||
|     android:layout_width="match_parent" |     android:layout_width="match_parent" | ||||||
|     android:layout_height="match_parent" |     android:layout_height="match_parent" | ||||||
|     android:id="@+id/videoitem_detail"> |     android:id="@+id/video_item_detail"> | ||||||
|  |  | ||||||
|     <com.nirhart.parallaxscroll.views.ParallaxScrollView |     <com.nirhart.parallaxscroll.views.ParallaxScrollView | ||||||
|         android:id="@+id/detailMainContent" |         android:id="@+id/detail_main_content" | ||||||
|         android:layout_width="match_parent" |         android:layout_width="match_parent" | ||||||
|         android:layout_height="match_parent" |         android:layout_height="match_parent" | ||||||
|         android:visibility="visible" |         android:visibility="visible" | ||||||
| @@ -23,12 +23,12 @@ | |||||||
|             android:layout_height="wrap_content"> |             android:layout_height="wrap_content"> | ||||||
|  |  | ||||||
|             <RelativeLayout |             <RelativeLayout | ||||||
|                 android:id="@+id/detailVideoThumbnailWindowLayout" |                 android:id="@+id/detail_stream_thumbnail_window_layout" | ||||||
|                 android:layout_width="match_parent" |                 android:layout_width="match_parent" | ||||||
|                 android:layout_height="wrap_content" |                 android:layout_height="wrap_content" | ||||||
|                 android:background="?attr/selectableItemBackground"> |                 android:background="?attr/selectableItemBackground"> | ||||||
|  |  | ||||||
|                 <ImageView android:id="@+id/detailThumbnailView" |                 <ImageView android:id="@+id/detail_thumbnail_view" | ||||||
|                     android:contentDescription="@string/detail_thumbnail_view_description" |                     android:contentDescription="@string/detail_thumbnail_view_description" | ||||||
|                     android:layout_width="match_parent" |                     android:layout_width="match_parent" | ||||||
|                     android:layout_height="wrap_content" |                     android:layout_height="wrap_content" | ||||||
| @@ -40,14 +40,14 @@ | |||||||
|                     android:background="@android:color/black" |                     android:background="@android:color/black" | ||||||
|                     android:src="@drawable/dummy_thumbnail_dark"/> |                     android:src="@drawable/dummy_thumbnail_dark"/> | ||||||
|  |  | ||||||
|                 <ProgressBar android:id="@+id/detailProgressBar" |                 <ProgressBar android:id="@+id/detail_progress_bar" | ||||||
|                     android:layout_width="wrap_content" |                     android:layout_width="wrap_content" | ||||||
|                     android:layout_height="wrap_content" |                     android:layout_height="wrap_content" | ||||||
|                     android:layout_centerInParent="true" |                     android:layout_centerInParent="true" | ||||||
|                     android:indeterminate="true"/> |                     android:indeterminate="true"/> | ||||||
|  |  | ||||||
|                 <android.support.design.widget.FloatingActionButton |                 <android.support.design.widget.FloatingActionButton | ||||||
|                     android:id="@+id/playVideoButton" |                     android:id="@+id/play_video_button" | ||||||
|                     android:visibility="invisible" |                     android:visibility="invisible" | ||||||
|                     android:layout_width="wrap_content" |                     android:layout_width="wrap_content" | ||||||
|                     android:layout_height="wrap_content" |                     android:layout_height="wrap_content" | ||||||
| @@ -57,18 +57,17 @@ | |||||||
|                     android:layout_margin="@dimen/video_item_detail_play_fab_margin"/> |                     android:layout_margin="@dimen/video_item_detail_play_fab_margin"/> | ||||||
|  |  | ||||||
|                 <Button |                 <Button | ||||||
|                     android:id="@+id/detailVideoThumbnailWindowBackgroundButton" |                     android:id="@+id/detail_stream_thumbnail_window_background_button" | ||||||
|                     android:layout_width="match_parent" |                     android:layout_width="match_parent" | ||||||
|                     android:layout_height="match_parent" |                     android:layout_height="match_parent" | ||||||
|                     android:background="?attr/selectableItemBackground"/> |                     android:background="?attr/selectableItemBackground"/> | ||||||
|  |  | ||||||
|             </RelativeLayout> |             </RelativeLayout> | ||||||
|  |  | ||||||
|             <RelativeLayout android:id="@+id/detailTextContentLayout" |             <RelativeLayout android:id="@+id/detail_text_content_layout" | ||||||
|  |  | ||||||
|                 android:layout_width="match_parent" |                 android:layout_width="match_parent" | ||||||
|                 android:layout_height="match_parent" |                 android:layout_height="match_parent" | ||||||
|                 android:layout_below="@id/detailVideoThumbnailWindowLayout" |                 android:layout_below="@id/detail_stream_thumbnail_window_layout" | ||||||
|                 android:background="@color/light_background_color" |                 android:background="@color/light_background_color" | ||||||
|                 android:visibility="gone"> |                 android:visibility="gone"> | ||||||
|  |  | ||||||
| @@ -77,7 +76,7 @@ | |||||||
|                     android:layout_height="wrap_content" |                     android:layout_height="wrap_content" | ||||||
|                     android:id="@+id/detailTopView"> |                     android:id="@+id/detailTopView"> | ||||||
|  |  | ||||||
|                     <TextView android:id="@+id/detailVideoTitleView" |                     <TextView android:id="@+id/detail_video_title_view" | ||||||
|                         android:layout_width="0dp" |                         android:layout_width="0dp" | ||||||
|                         android:layout_weight=".7" |                         android:layout_weight=".7" | ||||||
|                         android:layout_height="wrap_content" |                         android:layout_height="wrap_content" | ||||||
| @@ -91,7 +90,7 @@ | |||||||
|                     <ImageView |                     <ImageView | ||||||
|                         android:layout_width="15dp" |                         android:layout_width="15dp" | ||||||
|                         android:layout_height="30dp" |                         android:layout_height="30dp" | ||||||
|                         android:id="@+id/toggleDescriptionView" |                         android:id="@+id/toggle_description_view" | ||||||
|                         android:src="@drawable/arrow_down" |                         android:src="@drawable/arrow_down" | ||||||
|                         android:layout_marginLeft="10dp" |                         android:layout_marginLeft="10dp" | ||||||
|                         android:layout_marginStart="10dp" |                         android:layout_marginStart="10dp" | ||||||
| @@ -101,7 +100,7 @@ | |||||||
|  |  | ||||||
|                 </LinearLayout> |                 </LinearLayout> | ||||||
|  |  | ||||||
|                 <TextView android:id="@+id/detailViewCountView" |                 <TextView android:id="@+id/detail_view_count_view" | ||||||
|                     android:layout_width="match_parent" |                     android:layout_width="match_parent" | ||||||
|                     android:layout_height="wrap_content" |                     android:layout_height="wrap_content" | ||||||
|                     android:textSize="@dimen/video_item_detail_views_text_size" |                     android:textSize="@dimen/video_item_detail_views_text_size" | ||||||
| @@ -117,14 +116,14 @@ | |||||||
|                 <RelativeLayout |                 <RelativeLayout | ||||||
|                     android:layout_width="match_parent" |                     android:layout_width="match_parent" | ||||||
|                     android:layout_height="wrap_content" |                     android:layout_height="wrap_content" | ||||||
|                     android:layout_below="@id/detailViewCountView" |                     android:layout_below="@id/detail_view_count_view" | ||||||
|                     android:id="@+id/detailExtraView" |                     android:id="@+id/detailExtraView" | ||||||
|                     android:layout_marginLeft="12dp" |                     android:layout_marginLeft="12dp" | ||||||
|                     android:layout_marginStart="12dp" |                     android:layout_marginStart="12dp" | ||||||
|                     android:layout_marginRight="12dp" |                     android:layout_marginRight="12dp" | ||||||
|                     android:layout_marginEnd="12dp" |                     android:layout_marginEnd="12dp" | ||||||
|                     android:visibility="gone"> |                     android:visibility="gone"> | ||||||
|                     <TextView android:id="@+id/detailUploadDateView" |                     <TextView android:id="@+id/detail_upload_date_view" | ||||||
|                         android:layout_width="wrap_content" |                         android:layout_width="wrap_content" | ||||||
|                         android:layout_height="wrap_content" |                         android:layout_height="wrap_content" | ||||||
|                         android:textSize="@dimen/video_item_detail_upload_date_text_size" |                         android:textSize="@dimen/video_item_detail_upload_date_text_size" | ||||||
| @@ -132,12 +131,12 @@ | |||||||
|                         android:text="Upload date" |                         android:text="Upload date" | ||||||
|                         android:layout_marginTop="3dp" /> |                         android:layout_marginTop="3dp" /> | ||||||
|  |  | ||||||
|                     <TextView android:id="@+id/detailDescriptionView" |                     <TextView android:id="@+id/detail_description_view" | ||||||
|                         android:layout_width="match_parent" |                         android:layout_width="match_parent" | ||||||
|                         android:layout_height="wrap_content" |                         android:layout_height="wrap_content" | ||||||
|                         android:textSize="@dimen/video_item_detail_description_text_size" |                         android:textSize="@dimen/video_item_detail_description_text_size" | ||||||
|                         android:textAppearance="?android:attr/textAppearanceMedium" |                         android:textAppearance="?android:attr/textAppearanceMedium" | ||||||
|                         android:layout_below="@id/detailUploadDateView" |                         android:layout_below="@id/detail_upload_date_view" | ||||||
|                         android:text="Description............." |                         android:text="Description............." | ||||||
|                         android:layout_marginTop="3dp" /> |                         android:layout_marginTop="3dp" /> | ||||||
|                 </RelativeLayout> |                 </RelativeLayout> | ||||||
| @@ -145,27 +144,27 @@ | |||||||
|                 <LinearLayout |                 <LinearLayout | ||||||
|                     android:layout_width="match_parent" |                     android:layout_width="match_parent" | ||||||
|                     android:layout_height="wrap_content" |                     android:layout_height="wrap_content" | ||||||
|                     android:id="@+id/linearLayout" |                     android:id="@+id/stream_info_layout" | ||||||
|                     android:layout_marginLeft="12dp" |                     android:layout_marginLeft="12dp" | ||||||
|                     android:layout_marginStart="12dp" |                     android:layout_marginStart="12dp" | ||||||
|                     android:layout_below="@+id/detailExtraView" |                     android:layout_below="@+id/detailExtraView" | ||||||
|                     android:layout_alignParentRight="true" |                     android:layout_alignParentRight="true" | ||||||
|                     android:layout_alignParentEnd="true" |                     android:layout_alignParentEnd="true" | ||||||
|                     android:layout_marginTop="5dp"> |                     android:layout_marginTop="5dp"> | ||||||
|                     <ImageView android:id="@+id/detailThumbsUpImgView" |                     <ImageView android:id="@+id/detail_thumbs_up_img_view" | ||||||
|                         android:contentDescription="@string/detail_likes_img_view_description" |                         android:contentDescription="@string/detail_likes_img_view_description" | ||||||
|                         android:layout_width="@dimen/video_item_detail_like_image_width" |                         android:layout_width="@dimen/video_item_detail_like_image_width" | ||||||
|                         android:layout_height="@dimen/video_item_detail_like_image_height" |                         android:layout_height="@dimen/video_item_detail_like_image_height" | ||||||
|                         android:src="@drawable/thumbs_up" /> |                         android:src="@drawable/thumbs_up" /> | ||||||
|  |  | ||||||
|                     <TextView android:id="@+id/detailThumbsUpCountView" |                     <TextView android:id="@+id/detail_thumbs_up_count_view" | ||||||
|                         android:layout_width="wrap_content" |                         android:layout_width="wrap_content" | ||||||
|                         android:layout_height="wrap_content" |                         android:layout_height="wrap_content" | ||||||
|                         android:textSize="@dimen/video_item_detail_likes_text_size" |                         android:textSize="@dimen/video_item_detail_likes_text_size" | ||||||
|                         android:textAppearance="?android:attr/textAppearanceMedium" |                         android:textAppearance="?android:attr/textAppearanceMedium" | ||||||
|                         android:text="200" /> |                         android:text="200" /> | ||||||
|  |  | ||||||
|                     <ImageView android:id="@+id/detailThumbsDownImgView" |                     <ImageView android:id="@+id/detail_thumbs_down_img_view" | ||||||
|                         android:contentDescription="@string/detail_dislikes_img_view_description" |                         android:contentDescription="@string/detail_dislikes_img_view_description" | ||||||
|                         android:layout_width="@dimen/video_item_detail_like_image_width" |                         android:layout_width="@dimen/video_item_detail_like_image_width" | ||||||
|                         android:layout_height="@dimen/video_item_detail_like_image_height" |                         android:layout_height="@dimen/video_item_detail_like_image_height" | ||||||
| @@ -173,7 +172,7 @@ | |||||||
|                         android:layout_marginLeft="10dp" |                         android:layout_marginLeft="10dp" | ||||||
|                         android:layout_marginStart="10dp"/> |                         android:layout_marginStart="10dp"/> | ||||||
|  |  | ||||||
|                     <TextView android:id="@+id/detailThumbsDownCountView" |                     <TextView android:id="@+id/detail_thumbs_down_count_view" | ||||||
|                         android:layout_width="wrap_content" |                         android:layout_width="wrap_content" | ||||||
|                         android:layout_height="wrap_content" |                         android:layout_height="wrap_content" | ||||||
|                         android:textSize="@dimen/video_item_detail_likes_text_size" |                         android:textSize="@dimen/video_item_detail_likes_text_size" | ||||||
| @@ -181,59 +180,72 @@ | |||||||
|                         android:text="100" /> |                         android:text="100" /> | ||||||
|                 </LinearLayout> |                 </LinearLayout> | ||||||
|  |  | ||||||
|                 <RelativeLayout |                 <FrameLayout | ||||||
|                     android:layout_width="match_parent" |                     android:layout_width="match_parent" | ||||||
|                     android:layout_height="wrap_content" |                     android:layout_height="wrap_content" | ||||||
|                     android:layout_below="@+id/linearLayout" |                     android:id="@+id/detail_uploader_frame" | ||||||
|                     android:id="@+id/detailUploaderWrapView" |                     android:layout_below="@id/stream_info_layout"> | ||||||
|                     android:layout_marginTop="12dp"> |                     <RelativeLayout | ||||||
|  |  | ||||||
|                     <View |  | ||||||
|                         android:background="#000" |  | ||||||
|                         android:layout_width="match_parent" |                         android:layout_width="match_parent" | ||||||
|                         android:layout_height="1px" /> |  | ||||||
|  |  | ||||||
|                     <de.hdodenhof.circleimageview.CircleImageView android:id="@+id/detailUploaderThumbnailView" |  | ||||||
|                         android:contentDescription="@string/detail_uploader_thumbnail_view_description" |  | ||||||
|                         android:layout_width="@dimen/video_item_detail_uploader_image_size" |  | ||||||
|                         android:layout_height="@dimen/video_item_detail_uploader_image_size" |  | ||||||
|                         android:src="@drawable/buddy" |  | ||||||
|                         android:layout_marginLeft="10dp" |  | ||||||
|                         android:layout_marginStart="10dp" |  | ||||||
|                         android:layout_alignParentLeft="true" |  | ||||||
|                         android:layout_alignParentStart="true" |  | ||||||
|                         android:layout_marginTop="5dp" |  | ||||||
|                         android:layout_marginBottom="5dp"/> |  | ||||||
|  |  | ||||||
|                     <TextView android:id="@+id/detailUploaderView" |  | ||||||
|                         android:layout_width="wrap_content" |  | ||||||
|                         android:layout_height="wrap_content" |                         android:layout_height="wrap_content" | ||||||
|                         android:textStyle="bold" |                         android:id="@+id/detail_uploader_layout" | ||||||
|                         android:textSize="@dimen/video_item_detail_uploader_text_size" |                         android:layout_marginTop="12dp"> | ||||||
|                         android:textAppearance="?android:attr/textAppearanceLarge" |  | ||||||
|                         android:text="Uploader" |  | ||||||
|                         android:layout_centerVertical="true" |  | ||||||
|                         android:layout_toRightOf="@+id/detailUploaderThumbnailView" |  | ||||||
|                         android:layout_toEndOf="@+id/detailUploaderThumbnailView" |  | ||||||
|                         android:layout_marginLeft="15dp" |  | ||||||
|                         android:layout_marginStart="28dp" /> |  | ||||||
|  |  | ||||||
|                     <View |                         <View | ||||||
|                         android:background="#000" |                             android:background="#000" | ||||||
|  |                             android:layout_width="match_parent" | ||||||
|  |                             android:layout_height="1px" /> | ||||||
|  |  | ||||||
|  |                         <de.hdodenhof.circleimageview.CircleImageView | ||||||
|  |                             android:id="@+id/detail_uploader_thumbnail_view" | ||||||
|  |                             android:contentDescription="@string/detail_uploader_thumbnail_view_description" | ||||||
|  |                             android:layout_width="@dimen/video_item_detail_uploader_image_size" | ||||||
|  |                             android:layout_height="@dimen/video_item_detail_uploader_image_size" | ||||||
|  |                             android:src="@drawable/buddy" | ||||||
|  |                             android:layout_marginLeft="10dp" | ||||||
|  |                             android:layout_marginStart="10dp" | ||||||
|  |                             android:layout_alignParentLeft="true" | ||||||
|  |                             android:layout_alignParentStart="true" | ||||||
|  |                             android:layout_marginTop="5dp" | ||||||
|  |                             android:layout_marginBottom="5dp"/> | ||||||
|  |  | ||||||
|  |                         <TextView android:id="@+id/detail_uploader_view" | ||||||
|  |                             android:layout_width="wrap_content" | ||||||
|  |                             android:layout_height="wrap_content" | ||||||
|  |                             android:textStyle="bold" | ||||||
|  |                             android:textSize="@dimen/video_item_detail_uploader_text_size" | ||||||
|  |                             android:textAppearance="?android:attr/textAppearanceLarge" | ||||||
|  |                             android:text="Uploader" | ||||||
|  |                             android:layout_centerVertical="true" | ||||||
|  |                             android:layout_toRightOf="@+id/detail_uploader_thumbnail_view" | ||||||
|  |                             android:layout_toEndOf="@+id/detail_uploader_thumbnail_view" | ||||||
|  |                             android:layout_marginLeft="15dp" | ||||||
|  |                             android:layout_marginStart="28dp" /> | ||||||
|  |  | ||||||
|  |                         <View | ||||||
|  |                             android:background="#000" | ||||||
|  |                             android:layout_width="match_parent" | ||||||
|  |                             android:layout_height="1px" | ||||||
|  |                             android:layout_below="@id/detail_uploader_thumbnail_view"/> | ||||||
|  |                     </RelativeLayout> | ||||||
|  |                     <Button | ||||||
|  |                         android:layout_marginTop="13dp" | ||||||
|  |                         android:id="@+id/channel_button" | ||||||
|                         android:layout_width="match_parent" |                         android:layout_width="match_parent" | ||||||
|                         android:layout_height="1px" |                         android:layout_height="match_parent" | ||||||
|                         android:layout_below="@id/detailUploaderThumbnailView"/> |                         android:background="?attr/selectableItemBackground"/> | ||||||
|                 </RelativeLayout> |                 </FrameLayout> | ||||||
|  |  | ||||||
|                 <RelativeLayout android:id="@+id/detailNextVideoRootLayout" |                 <RelativeLayout android:id="@+id/detail_next_stream_root_layout" | ||||||
|                     android:layout_width="match_parent" |                     android:layout_width="match_parent" | ||||||
|                     android:layout_height="match_parent" |                     android:layout_height="match_parent" | ||||||
|                     android:layout_gravity="center_horizontal|bottom" |                     android:layout_gravity="center_horizontal|bottom" | ||||||
|                     android:layout_below="@+id/detailUploaderWrapView" |                     android:layout_below="@+id/detail_uploader_frame" | ||||||
|                     android:layout_centerHorizontal="true" |                     android:layout_centerHorizontal="true" | ||||||
|                     android:layout_marginTop="10dp"> |                     android:layout_marginTop="10dp"> | ||||||
|  |  | ||||||
|                     <TextView android:id="@+id/detailNextVideoTitle" |                     <TextView | ||||||
|  |                         android:id="@+id/detail_next_stream_title" | ||||||
|                         android:layout_width="wrap_content" |                         android:layout_width="wrap_content" | ||||||
|                         android:layout_height="wrap_content" |                         android:layout_height="wrap_content" | ||||||
|                         android:layout_centerHorizontal="true" |                         android:layout_centerHorizontal="true" | ||||||
| @@ -242,38 +254,25 @@ | |||||||
|                         android:text="@string/next_video_title" |                         android:text="@string/next_video_title" | ||||||
|                         android:textAllCaps="true" /> |                         android:textAllCaps="true" /> | ||||||
|  |  | ||||||
|                     <RelativeLayout android:id="@+id/detailNextVidButtonAndContentLayout" |                     <android.support.v7.widget.RecyclerView | ||||||
|                         android:layout_width="match_parent" |                         android:layout_width="match_parent" | ||||||
|                         android:layout_height="match_parent" |                         android:layout_height="wrap_content" | ||||||
|                         android:layout_below="@id/detailNextVideoTitle"> |                         android:id="@+id/detail_next_stream_content"/> | ||||||
|                         <FrameLayout |                     <TextView android:id="@+id/detail_similar_title" | ||||||
|                             android:id="@+id/detailNextVideoFrame" |  | ||||||
|                             android:layout_width="match_parent" |  | ||||||
|                             android:layout_height="wrap_content"/> |  | ||||||
|                         <Button |  | ||||||
|                             android:id="@+id/detailNextVideoButton" |  | ||||||
|                             android:layout_width="match_parent" |  | ||||||
|                             android:layout_height="match_parent" |  | ||||||
|                             android:layout_alignTop="@id/detailNextVideoFrame" |  | ||||||
|                             android:layout_alignBottom="@id/detailNextVideoFrame" |  | ||||||
|                             android:background="?attr/selectableItemBackground"/> |  | ||||||
|                     </RelativeLayout> |  | ||||||
|                     <TextView android:id="@+id/detailSimilarTitle" |  | ||||||
|                         android:layout_width="wrap_content" |                         android:layout_width="wrap_content" | ||||||
|                         android:layout_height="wrap_content" |                         android:layout_height="wrap_content" | ||||||
|                         android:layout_centerHorizontal="true" |                         android:layout_centerHorizontal="true" | ||||||
|                         android:textSize="@dimen/video_item_detail_next_text_size" |                         android:textSize="@dimen/video_item_detail_next_text_size" | ||||||
|                         android:textAppearance="?android:attr/textAppearanceMedium" |                         android:textAppearance="?android:attr/textAppearanceMedium" | ||||||
|                         android:text="@string/similar_videos_btn_text" |                         android:text="@string/similar_videos_btn_text" | ||||||
|                         android:layout_below="@id/detailNextVidButtonAndContentLayout" |                         android:layout_below="@id/detail_next_stream_content" | ||||||
|                         android:textAllCaps="true" /> |                         android:textAllCaps="true" /> | ||||||
|                     <LinearLayout |                     <android.support.v7.widget.RecyclerView | ||||||
|  |                         android:id="@+id/similar_streams_view" | ||||||
|                         android:orientation="vertical" |                         android:orientation="vertical" | ||||||
|                         android:layout_width="match_parent" |                         android:layout_width="match_parent" | ||||||
|                         android:layout_height="match_parent" |                         android:layout_height="match_parent" | ||||||
|                         android:id="@+id/similarVideosView" |                         android:layout_below="@id/detail_similar_title"/> | ||||||
|                         android:layout_below="@id/detailSimilarTitle"> |  | ||||||
|                     </LinearLayout> |  | ||||||
|                 </RelativeLayout> |                 </RelativeLayout> | ||||||
|             </RelativeLayout> |             </RelativeLayout> | ||||||
|         </RelativeLayout> |         </RelativeLayout> | ||||||
|   | |||||||
| @@ -4,8 +4,7 @@ | |||||||
|     android:orientation="vertical" android:layout_width="match_parent" |     android:orientation="vertical" android:layout_width="match_parent" | ||||||
|     android:layout_height="match_parent" |     android:layout_height="match_parent" | ||||||
|     android:id="@+id/mainBG" |     android:id="@+id/mainBG" | ||||||
|     tools:context=".VideoItemDetailActivity" |     tools:context=".detail.VideoItemDetailActivity"> | ||||||
|     tools:showIn="@layout/activity_videoitem_list"> |  | ||||||
|  |  | ||||||
|     <TextView |     <TextView | ||||||
|         android:layout_width="wrap_content" |         android:layout_width="wrap_content" | ||||||
|   | |||||||
| @@ -1,15 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     android:layout_width="match_parent" |  | ||||||
|     android:layout_height="match_parent" |  | ||||||
|     android:gravity="center_horizontal" |  | ||||||
|     android:orientation="vertical"> |  | ||||||
|  |  | ||||||
|     <ProgressBar |  | ||||||
|         android:id="@+id/paginate_progress_bar" |  | ||||||
|         android:layout_width="wrap_content" |  | ||||||
|         android:layout_height="wrap_content" |  | ||||||
|         android:gravity="center" |  | ||||||
|         android:paddingBottom="10dp"/> |  | ||||||
|  |  | ||||||
| </LinearLayout> |  | ||||||
| @@ -1,8 +1,14 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> | ||||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|  | <FrameLayout | ||||||
|  |     android:id="@+id/item_main_layout" | ||||||
|  |     xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|     xmlns:tools="http://schemas.android.com/tools" |     xmlns:tools="http://schemas.android.com/tools" | ||||||
|     android:layout_width="match_parent" |     android:layout_width="match_parent" | ||||||
|     android:layout_height="wrap_content" > |     android:layout_height="wrap_content"> | ||||||
|  |     <LinearLayout | ||||||
|  |         android:layout_width="match_parent" | ||||||
|  |         android:layout_height="wrap_content" > | ||||||
|  |  | ||||||
|         <RelativeLayout |         <RelativeLayout | ||||||
|             xmlns:android="http://schemas.android.com/apk/res/android" |             xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
| @@ -92,4 +98,11 @@ | |||||||
|             </LinearLayout> |             </LinearLayout> | ||||||
|  |  | ||||||
|         </RelativeLayout> |         </RelativeLayout> | ||||||
| </LinearLayout> |     </LinearLayout> | ||||||
|  |  | ||||||
|  |     <Button | ||||||
|  |         android:id="@+id/item_button" | ||||||
|  |         android:layout_width="match_parent" | ||||||
|  |         android:layout_height="match_parent" | ||||||
|  |         android:background="?attr/selectableItemBackground"/> | ||||||
|  | </FrameLayout> | ||||||
							
								
								
									
										11
									
								
								app/src/main/res/menu/main_menu.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								app/src/main/res/menu/main_menu.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <menu xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|  |     xmlns:app="http://schemas.android.com/apk/res-auto"> | ||||||
|  |     <item android:id="@+id/action_settings" | ||||||
|  |         app:showAsAction="never" | ||||||
|  |         android:title="@string/settings"/> | ||||||
|  |  | ||||||
|  |     <item android:id="@+id/action_show_downloads" | ||||||
|  |         app:showAsAction="never" | ||||||
|  |         android:title="@string/downloads" /> | ||||||
|  | </menu> | ||||||
							
								
								
									
										10
									
								
								app/src/main/res/menu/menu_channel.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/src/main/res/menu/menu_channel.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | <menu xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|  |     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||||
|  |     xmlns:tools="http://schemas.android.com/tools" | ||||||
|  |     tools:context="org.schabi.newpipe.ChannelActivity"> | ||||||
|  |     <item | ||||||
|  |         android:id="@+id/action_settings" | ||||||
|  |         android:orderInCategory="100" | ||||||
|  |         android:title="@string/action_settings" | ||||||
|  |         app:showAsAction="never" /> | ||||||
|  | </menu> | ||||||
							
								
								
									
										9
									
								
								app/src/main/res/menu/search_menu.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								app/src/main/res/menu/search_menu.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <menu xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|  |     xmlns:app="http://schemas.android.com/apk/res-auto"> | ||||||
|  |     <item android:id="@+id/action_search" | ||||||
|  |         android:icon="@android:drawable/ic_menu_search" | ||||||
|  |         app:showAsAction="ifRoom" | ||||||
|  |         android:title="@string/search" | ||||||
|  |         app:actionViewClass="android.support.v7.widget.SearchView" /> | ||||||
|  | </menu> | ||||||
| @@ -10,7 +10,7 @@ | |||||||
|         <item name="android:windowBackground">@color/light_background_color</item> |         <item name="android:windowBackground">@color/light_background_color</item> | ||||||
|     </style> |     </style> | ||||||
|  |  | ||||||
|     <style name="NewPipeActionbarTheme" parent="Widget.AppCompat.Light.ActionBar.Solid" > |     <style name="NewPipeActionbarTheme" parent="Widget.AppCompat.Light.ActionBar.Solid"> | ||||||
|         <item name="android:displayOptions">showHome</item> |         <item name="android:displayOptions">showHome</item> | ||||||
|         <item name="displayOptions">showHome</item> |         <item name="displayOptions">showHome</item> | ||||||
|         <item name="android:background">@color/light_youtube_primary_color</item> |         <item name="android:background">@color/light_youtube_primary_color</item> | ||||||
| @@ -28,11 +28,18 @@ | |||||||
|         <item name="android:windowBackground">@android:color/black</item> |         <item name="android:windowBackground">@android:color/black</item> | ||||||
|     </style> |     </style> | ||||||
|  |  | ||||||
|     <style name="VideoPlayerActionBarTheme" parent="Widget.AppCompat.Light.ActionBar.Solid.Inverse" > |     <style name="VideoPlayerActionBarTheme" parent="Widget.AppCompat.Light.ActionBar.Solid.Inverse"> | ||||||
|         <item name="android:displayOptions">showHome</item> |         <item name="android:displayOptions">showHome</item> | ||||||
|         <item name="displayOptions">showHome</item> |         <item name="displayOptions">showHome</item> | ||||||
|         <item name="android:background">@color/video_overlay_color</item> |         <item name="android:background">@color/video_overlay_color</item> | ||||||
|         <item name="background">@color/video_overlay_color</item> |         <item name="background">@color/video_overlay_color</item> | ||||||
|     </style> |     </style> | ||||||
|  |  | ||||||
|  |     <style name="AppTheme.NoActionBar"> | ||||||
|  |         <item name="windowActionBar">false</item> | ||||||
|  |         <item name="windowNoTitle">true</item> | ||||||
|  |         <item name="android:windowDrawsSystemBarBackgrounds">true</item> | ||||||
|  |         <item name="android:statusBarColor">@android:color/transparent</item> | ||||||
|  |     </style> | ||||||
|  |  | ||||||
| </resources> | </resources> | ||||||
| @@ -31,11 +31,16 @@ | |||||||
|     <dimen name="video_item_detail_uploader_image_size">50dp</dimen> |     <dimen name="video_item_detail_uploader_image_size">50dp</dimen> | ||||||
|     <dimen name="video_item_detail_like_image_height">18sp</dimen> |     <dimen name="video_item_detail_like_image_height">18sp</dimen> | ||||||
|     <dimen name="video_item_detail_like_image_width">18sp</dimen> |     <dimen name="video_item_detail_like_image_width">18sp</dimen> | ||||||
|  |     <dimen name="channel_avatar_size">70dp</dimen> | ||||||
|  |     <dimen name="channel_avatar_halo_size">74dp</dimen> | ||||||
|     <!-- Paddings & Margins --> |     <!-- Paddings & Margins --> | ||||||
|     <dimen name="video_item_detail_like_margin">6sp</dimen> |     <dimen name="video_item_detail_like_margin">6sp</dimen> | ||||||
|     <dimen name="video_item_detail_play_fab_margin">20dp</dimen> |     <dimen name="video_item_detail_play_fab_margin">20dp</dimen> | ||||||
|     <!-- Default screen margins, per the Android Design guidelines. --> |     <!-- Default screen margins, per the Android Design guidelines. --> | ||||||
|     <dimen name="activity_horizontal_margin">16dp</dimen> |     <dimen name="activity_horizontal_margin">16dp</dimen> | ||||||
|     <dimen name="activity_vertical_margin">16dp</dimen> |     <dimen name="activity_vertical_margin">16dp</dimen> | ||||||
|  |     <dimen name="app_bar_height">180dp</dimen> | ||||||
|  |     <dimen name="fab_margin">16dp</dimen> | ||||||
|  |     <dimen name="text_margin">16dp</dimen> | ||||||
|  |  | ||||||
| </resources> | </resources> | ||||||
|   | |||||||
| @@ -96,6 +96,7 @@ | |||||||
|     <string name="could_not_setup_download_menu">Could not setup download menu.</string> |     <string name="could_not_setup_download_menu">Could not setup download menu.</string> | ||||||
|     <string name="live_streams_not_supported">This is a LIVE STREAM. These are not yet supported.</string> |     <string name="live_streams_not_supported">This is a LIVE STREAM. These are not yet supported.</string> | ||||||
|     <string name="could_not_get_stream">Could not get any stream.</string> |     <string name="could_not_get_stream">Could not get any stream.</string> | ||||||
|  |     <string name="could_not_load_image">Could not load Image</string> | ||||||
|     <!-- error activity --> |     <!-- error activity --> | ||||||
|     <string name="sorry_string">Sorry that should not happen.</string> |     <string name="sorry_string">Sorry that should not happen.</string> | ||||||
|     <string name="guru_meditation" translatable="false">Guru Meditation.</string> |     <string name="guru_meditation" translatable="false">Guru Meditation.</string> | ||||||
| @@ -125,7 +126,7 @@ | |||||||
|     <string name="err_dir_create">Cannot create download directory \'%1$s\'</string> |     <string name="err_dir_create">Cannot create download directory \'%1$s\'</string> | ||||||
|     <string name="info_dir_created">Created download directory \'%1$s\'</string> |     <string name="info_dir_created">Created download directory \'%1$s\'</string> | ||||||
|  |  | ||||||
| 	<string name="enable_background_audio">Play in background</string> |     <string name="enable_background_audio">Play in background</string> | ||||||
|     <string name="video">Video</string> |     <string name="video">Video</string> | ||||||
|     <string name="audio">Audio</string> |     <string name="audio">Audio</string> | ||||||
|     <string name="text">Text</string> |     <string name="text">Text</string> | ||||||
| @@ -176,6 +177,97 @@ | |||||||
|     <!-- Checksum types --> |     <!-- Checksum types --> | ||||||
|     <string name="md5" translatable="false">MD5</string> |     <string name="md5" translatable="false">MD5</string> | ||||||
|     <string name="sha1" translatable="false">SHA1</string> |     <string name="sha1" translatable="false">SHA1</string> | ||||||
|  |     <string name="title_activity_channel">ChannelActivity</string> | ||||||
|  |     <string name="large_text"> | ||||||
|  |         "Material is the metaphor.\n\n" | ||||||
|  |  | ||||||
|  |         "A material metaphor is the unifying theory of a rationalized space and a system of motion." | ||||||
|  |         "The material is grounded in tactile reality, inspired by the study of paper and ink, yet " | ||||||
|  |         "technologically advanced and open to imagination and magic.\n" | ||||||
|  |         "Surfaces and edges of the material provide visual cues that are grounded in reality. The " | ||||||
|  |         "use of familiar tactile attributes helps users quickly understand affordances. Yet the " | ||||||
|  |         "flexibility of the material creates new affordances that supercede those in the physical " | ||||||
|  |         "world, without breaking the rules of physics.\n" | ||||||
|  |         "The fundamentals of light, surface, and movement are key to conveying how objects move, " | ||||||
|  |         "interact, and exist in space and in relation to each other. Realistic lighting shows " | ||||||
|  |         "seams, divides space, and indicates moving parts.\n\n" | ||||||
|  |  | ||||||
|  |         "Bold, graphic, intentional.\n\n" | ||||||
|  |  | ||||||
|  |         "The foundational elements of print based design typography, grids, space, scale, color, " | ||||||
|  |         "and use of imagery guide visual treatments. These elements do far more than please the " | ||||||
|  |         "eye. They create hierarchy, meaning, and focus. Deliberate color choices, edge to edge " | ||||||
|  |         "imagery, large scale typography, and intentional white space create a bold and graphic " | ||||||
|  |         "interface that immerse the user in the experience.\n" | ||||||
|  |         "An emphasis on user actions makes core functionality immediately apparent and provides " | ||||||
|  |         "waypoints for the user.\n\n" | ||||||
|  |  | ||||||
|  |         "Motion provides meaning.\n\n" | ||||||
|  |  | ||||||
|  |         "Motion respects and reinforces the user as the prime mover. Primary user actions are " | ||||||
|  |         "inflection points that initiate motion, transforming the whole design.\n" | ||||||
|  |         "All action takes place in a single environment. Objects are presented to the user without " | ||||||
|  |         "breaking the continuity of experience even as they transform and reorganize.\n" | ||||||
|  |         "Motion is meaningful and appropriate, serving to focus attention and maintain continuity. " | ||||||
|  |         "Feedback is subtle yet clear. Transitions are efficient yet coherent.\n\n" | ||||||
|  |  | ||||||
|  |         "3D world.\n\n" | ||||||
|  |  | ||||||
|  |         "The material environment is a 3D space, which means all objects have x, y, and z " | ||||||
|  |         "dimensions. The z-axis is perpendicularly aligned to the plane of the display, with the " | ||||||
|  |         "positive z-axis extending towards the viewer. Every sheet of material occupies a single " | ||||||
|  |         "position along the z-axis and has a standard 1dp thickness.\n" | ||||||
|  |         "On the web, the z-axis is used for layering and not for perspective. The 3D world is " | ||||||
|  |         "emulated by manipulating the y-axis.\n\n" | ||||||
|  |  | ||||||
|  |         "Light and shadow.\n\n" | ||||||
|  |  | ||||||
|  |         "Within the material environment, virtual lights illuminate the scene. Key lights create " | ||||||
|  |         "directional shadows, while ambient light creates soft shadows from all angles.\n" | ||||||
|  |         "Shadows in the material environment are cast by these two light sources. In Android " | ||||||
|  |         "development, shadows occur when light sources are blocked by sheets of material at " | ||||||
|  |         "various positions along the z-axis. On the web, shadows are depicted by manipulating the " | ||||||
|  |         "y-axis only. The following example shows the card with a height of 6dp.\n\n" | ||||||
|  |  | ||||||
|  |         "Resting elevation.\n\n" | ||||||
|  |  | ||||||
|  |         "All material objects, regardless of size, have a resting elevation, or default elevation " | ||||||
|  |         "that does not change. If an object changes elevation, it should return to its resting " | ||||||
|  |         "elevation as soon as possible.\n\n" | ||||||
|  |  | ||||||
|  |         "Component elevations.\n\n" | ||||||
|  |  | ||||||
|  |         "The resting elevation for a component type is consistent across apps (e.g., FAB elevation " | ||||||
|  |         "does not vary from 6dp in one app to 16dp in another app).\n" | ||||||
|  |         "Components may have different resting elevations across platforms, depending on the depth " | ||||||
|  |         "of the environment (e.g., TV has a greater depth than mobile or desktop).\n\n" | ||||||
|  |  | ||||||
|  |         "Responsive elevation and dynamic elevation offsets.\n\n" | ||||||
|  |  | ||||||
|  |         "Some component types have responsive elevation, meaning they change elevation in response " | ||||||
|  |         "to user input (e.g., normal, focused, and pressed) or system events. These elevation " | ||||||
|  |         "changes are consistently implemented using dynamic elevation offsets.\n" | ||||||
|  |         "Dynamic elevation offsets are the goal elevation that a component moves towards, relative " | ||||||
|  |         "to the component’s resting state. They ensure that elevation changes are consistent " | ||||||
|  |         "across actions and component types. For example, all components that lift on press have " | ||||||
|  |         "the same elevation change relative to their resting elevation.\n" | ||||||
|  |         "Once the input event is completed or cancelled, the component will return to its resting " | ||||||
|  |         "elevation.\n\n" | ||||||
|  |  | ||||||
|  |         "Avoiding elevation interference.\n\n" | ||||||
|  |  | ||||||
|  |         "Components with responsive elevations may encounter other components as they move between " | ||||||
|  |         "their resting elevations and dynamic elevation offsets. Because material cannot pass " | ||||||
|  |         "through other material, components avoid interfering with one another any number of ways, " | ||||||
|  |         "whether on a per component basis or using the entire app layout.\n" | ||||||
|  |         "On a component level, components can move or be removed before they cause interference. " | ||||||
|  |         "For example, a floating action button (FAB) can disappear or move off screen before a " | ||||||
|  |         "user picks up a card, or it can move if a snackbar appears.\n" | ||||||
|  |         "On the layout level, design your app layout to minimize opportunities for interference. " | ||||||
|  |         "For example, position the FAB to one side of stream of a cards so the FAB won’t interfere " | ||||||
|  |         "when a user tries to pick up one of cards.\n\n" | ||||||
|  |     </string> | ||||||
|  |     <string name="action_settings">Settings</string> | ||||||
|  |  | ||||||
|     <!-- End of GigaGet's Strings --> |     <!-- End of GigaGet's Strings --> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,18 +1,17 @@ | |||||||
| <resources> | <resources> | ||||||
|  |  | ||||||
|   <style name="RootTheme" parent="android:Theme.Holo"> |     <style name="RootTheme" parent="android:Theme.Holo"></style> | ||||||
|   </style> |  | ||||||
|  |  | ||||||
|   <style name="PlayerTheme" parent="@style/RootTheme"> |     <style name="PlayerTheme" parent="@style/RootTheme"> | ||||||
|     <item name="android:windowNoTitle">true</item> |         <item name="android:windowNoTitle">true</item> | ||||||
|     <item name="android:windowBackground">@android:color/black</item> |         <item name="android:windowBackground">@android:color/black</item> | ||||||
|   </style> |     </style> | ||||||
|  |  | ||||||
|   <style name="ExoPlayerButton"> |     <style name="ExoPlayerButton"> | ||||||
|     <item name="android:layout_width">wrap_content</item> |         <item name="android:layout_width">wrap_content</item> | ||||||
|     <item name="android:layout_height">wrap_content</item> |         <item name="android:layout_height">wrap_content</item> | ||||||
|     <item name="android:minWidth">40dp</item> |         <item name="android:minWidth">40dp</item> | ||||||
|   </style> |     </style> | ||||||
|  |  | ||||||
|     <!-- Base application theme. --> |     <!-- Base application theme. --> | ||||||
|     <style name="AppTheme" parent="Theme.AppCompat.Light"> |     <style name="AppTheme" parent="Theme.AppCompat.Light"> | ||||||
| @@ -24,7 +23,7 @@ | |||||||
|         <item name="android:windowBackground">@color/light_background_color</item> |         <item name="android:windowBackground">@color/light_background_color</item> | ||||||
|     </style> |     </style> | ||||||
|  |  | ||||||
|     <style name="NewPipeActionbarTheme" parent="Widget.AppCompat.Light.ActionBar.Solid" > |     <style name="NewPipeActionbarTheme" parent="Widget.AppCompat.Light.ActionBar.Solid"> | ||||||
|         <item name="android:displayOptions">showHome</item> |         <item name="android:displayOptions">showHome</item> | ||||||
|         <item name="displayOptions">showHome</item> |         <item name="displayOptions">showHome</item> | ||||||
|         <item name="android:background">@color/light_youtube_primary_color</item> |         <item name="android:background">@color/light_youtube_primary_color</item> | ||||||
| @@ -41,7 +40,7 @@ | |||||||
|         <item name="android:windowBackground">@android:color/black</item> |         <item name="android:windowBackground">@android:color/black</item> | ||||||
|     </style> |     </style> | ||||||
|  |  | ||||||
|     <style name="VideoPlayerActionBarTheme" parent="Widget.AppCompat.Light.ActionBar.Solid.Inverse" > |     <style name="VideoPlayerActionBarTheme" parent="Widget.AppCompat.Light.ActionBar.Solid.Inverse"> | ||||||
|         <item name="android:displayOptions">showHome</item> |         <item name="android:displayOptions">showHome</item> | ||||||
|         <item name="displayOptions">showHome</item> |         <item name="displayOptions">showHome</item> | ||||||
|         <item name="android:background">@color/video_overlay_color</item> |         <item name="android:background">@color/video_overlay_color</item> | ||||||
| @@ -72,4 +71,13 @@ | |||||||
|         <item name="colorAccent">@color/light_youtube_accent_color</item> |         <item name="colorAccent">@color/light_youtube_accent_color</item> | ||||||
|     </style> |     </style> | ||||||
|  |  | ||||||
|  |     <style name="AppTheme.NoActionBar"> | ||||||
|  |         <item name="windowActionBar">false</item> | ||||||
|  |         <item name="windowNoTitle">true</item> | ||||||
|  |     </style> | ||||||
|  |  | ||||||
|  |     <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" /> | ||||||
|  |  | ||||||
|  |     <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" /> | ||||||
|  |  | ||||||
| </resources> | </resources> | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ buildscript { | |||||||
|         jcenter() |         jcenter() | ||||||
|     } |     } | ||||||
|     dependencies { |     dependencies { | ||||||
|         classpath 'com.android.tools.build:gradle:2.1.2' |         classpath 'com.android.tools.build:gradle:2.1.3' | ||||||
|  |  | ||||||
|         // NOTE: Do not place your application dependencies here; they belong |         // NOTE: Do not place your application dependencies here; they belong | ||||||
|         // in the individual module build.gradle files |         // in the individual module build.gradle files | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,6 @@ | |||||||
| #Mon Aug 10 20:55:41 CEST 2015 | #Sat Aug 20 00:40:54 CEST 2016 | ||||||
| distributionBase=GRADLE_USER_HOME | distributionBase=GRADLE_USER_HOME | ||||||
| distributionPath=wrapper/dists | distributionPath=wrapper/dists | ||||||
| zipStoreBase=GRADLE_USER_HOME | zipStoreBase=GRADLE_USER_HOME | ||||||
| zipStorePath=wrapper/dists | zipStorePath=wrapper/dists | ||||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Christian Schabesberger
					Christian Schabesberger