Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f67158a2a7 | ||
![]() |
c22c2009d4 | ||
![]() |
ab4d626ea9 | ||
![]() |
96709d22e9 | ||
![]() |
f0bd171eee | ||
![]() |
c4191077f3 | ||
![]() |
321d090052 | ||
![]() |
32dcb4d281 | ||
![]() |
080159849e | ||
![]() |
d9e690f62c | ||
![]() |
8c0156dea3 | ||
![]() |
038c59ce66 | ||
![]() |
72e08c0447 | ||
![]() |
173eaa8cf8 | ||
![]() |
a04cd24e5e | ||
![]() |
0e11404b3b | ||
![]() |
342807e26a | ||
![]() |
c068f08ff8 |
46
README.md
@@ -1,7 +1,45 @@
|
||||
NewPipe
|
||||
-------
|
||||
# NewPipe
|
||||
|
||||
[](https://hosted.weblate.org/engage/NewPipe/)
|
||||
|
||||
NewPipe is a lightweight youtube frontend for android. It's supposed to be used without the youtube-api and without any google play services. NewPipe only parses the youtube website in order to gain the information it needs.
|
||||
[](http://dasochan.nl/newpipe/)
|
||||
NewPipe: A free lightweight Youtube fronted for Android.
|
||||
|
||||
This a very early version of the app, so not all functionality is implemented, and there may still be a lot of bugs. But all in all it's doing what it is supposed to do. It makes it possible to watch youtube videos. So don't be cruel to this app. It will improve...
|
||||
[](https://f-droid.org/repository/browse/?fdfilter=newpipe&fdid=org.schabi.newpipe)
|
||||
|
||||
## Description
|
||||
|
||||
NewPipe does not use any Google framework libraries, or the Youtube api. It only parses the website in order to gain the information it needs. Therefore this app can be used on devices without g-services installed. Also NewPipe does not store data on the Youtube website (no login), and it's free software.
|
||||
|
||||
## Features
|
||||
|
||||
* Search videos
|
||||
* Display general information about a video
|
||||
* Watch Youtube videos
|
||||
* Listen to Youtube videos (audio only streaming)
|
||||
* Select the streaming player to watch the video with
|
||||
* Download videos (working, but it could be better)
|
||||
* Download audio only (working but, but it could be better)
|
||||
* Open a video in Kodi
|
||||
|
||||
## Coming Features
|
||||
|
||||
* Shows Next/Related videos
|
||||
* Improved Downloading
|
||||
* Bookmarks
|
||||
* View history
|
||||
* Search history
|
||||
* Search channels
|
||||
* Display general information about channels
|
||||
* Subscribe to channels
|
||||
* Watch videos from a channel
|
||||
* Search/Watch Playlists
|
||||
* ... and many more
|
||||
|
||||
### Multi service support
|
||||
Generally NewPipe is designed to not only support YouTube, but many more streaming services. How ever, right now NewPipe is not stable enough to support more than only youtube. But if all works as plant, NewPipe will get such support by the version 2.0.
|
||||
|
||||
# Help is always welcome !!!
|
||||
Whether its about ideas, translation, design changes, code cleaning, or real heavy code changes. Help is always welcome.
|
||||
|
||||
The more is done the better it gets!
|
||||
|
16
app/app.iml
@@ -71,9 +71,10 @@
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/23.0.1/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/design/23.0.1/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/23.0.1/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/23.1.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/design/23.1.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/recyclerview-v7/23.1.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/23.1.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
|
||||
@@ -91,11 +92,12 @@
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Android API 23 Platform" jdkType="Android SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" exported="" name="recyclerview-v7-23.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="jsoup-1.8.3" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-v4-23.0.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-v4-23.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="rhino-1.7.7" level="project" />
|
||||
<orderEntry type="library" exported="" name="design-23.0.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="appcompat-v7-23.0.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-annotations-23.0.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="design-23.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="appcompat-v7-23.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-annotations-23.1.0" level="project" />
|
||||
</component>
|
||||
</module>
|
@@ -8,8 +8,8 @@ android {
|
||||
applicationId "org.schabi.newpipe"
|
||||
minSdkVersion 15
|
||||
targetSdkVersion 23
|
||||
versionCode 4
|
||||
versionName "0.4.1"
|
||||
versionCode 5
|
||||
versionName "0.5.0"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
@@ -21,9 +21,9 @@ android {
|
||||
|
||||
dependencies {
|
||||
compile fileTree(include: ['*.jar'], dir: 'libs')
|
||||
compile 'com.android.support:appcompat-v7:23.0.1'
|
||||
compile 'com.android.support:support-v4:23.0.1'
|
||||
compile 'com.android.support:appcompat-v7:23.1.0'
|
||||
compile 'com.android.support:support-v4:23.1.0'
|
||||
compile 'org.jsoup:jsoup:1.8.3'
|
||||
compile 'org.mozilla:rhino:1.7.7'
|
||||
compile 'com.android.support:design:23.0.1'
|
||||
compile 'com.android.support:design:23.1.0'
|
||||
}
|
||||
|
@@ -34,27 +34,27 @@
|
||||
<data
|
||||
android:host="youtube.com"
|
||||
android:scheme="http"
|
||||
android:pathPrefix="/watch"/>
|
||||
android:pathPattern="/?*#*/*watch"/>
|
||||
<data
|
||||
android:host="youtube.com"
|
||||
android:scheme="https"
|
||||
android:pathPrefix="/watch"/>
|
||||
android:pathPattern="/?*#*/*watch"/>
|
||||
<data
|
||||
android:host="www.youtube.com"
|
||||
android:scheme="http"
|
||||
android:pathPrefix="/watch"/>
|
||||
android:pathPattern="/?*#*/*watch"/>
|
||||
<data
|
||||
android:host="www.youtube.com"
|
||||
android:scheme="https"
|
||||
android:pathPrefix="/watch"/>
|
||||
android:pathPattern="/?*#*/*watch"/>
|
||||
<data
|
||||
android:host="m.youtube.com"
|
||||
android:scheme="http"
|
||||
android:pathPrefix="/watch"/>
|
||||
android:pathPattern="/?*#*/*watch"/>
|
||||
<data
|
||||
android:host="m.youtube.com"
|
||||
android:scheme="https"
|
||||
android:pathPrefix="/watch"/>
|
||||
android:pathPattern="/?*#*/*watch"/>
|
||||
<data
|
||||
android:host="youtu.be"
|
||||
android:scheme="https"
|
||||
|
@@ -1,7 +1,5 @@
|
||||
package org.schabi.newpipe;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 10.08.15.
|
||||
*
|
||||
|
@@ -5,7 +5,6 @@ import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.media.MediaPlayer;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
@@ -19,15 +18,11 @@ import android.view.Display;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.Surface;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.MediaController;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.VideoView;
|
||||
|
||||
/**
|
||||
@@ -84,7 +79,7 @@ public class PlayVideoActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_play_video);
|
||||
|
||||
isLandscape = checkIfLandscape();
|
||||
hasSoftKeys = checkIfhasSoftKeys();
|
||||
hasSoftKeys = checkIfHasSoftKeys();
|
||||
|
||||
actionBar = getSupportActionBar();
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
@@ -138,9 +133,11 @@ public class PlayVideoActivity extends AppCompatActivity {
|
||||
}
|
||||
});
|
||||
|
||||
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
||||
if (android.os.Build.VERSION.SDK_INT >= 17) {
|
||||
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
||||
}
|
||||
|
||||
prefs = getPreferences(Context.MODE_PRIVATE);
|
||||
if(prefs.getBoolean(PREF_IS_LANDSCAPE, false) && !isLandscape) {
|
||||
@@ -203,10 +200,10 @@ public class PlayVideoActivity extends AppCompatActivity {
|
||||
|
||||
if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
isLandscape = true;
|
||||
adjustMediaControllMetrics();
|
||||
adjustMediaControlMetrics();
|
||||
} else if (config.orientation == Configuration.ORIENTATION_PORTRAIT){
|
||||
isLandscape = false;
|
||||
adjustMediaControllMetrics();
|
||||
adjustMediaControlMetrics();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,7 +226,7 @@ public class PlayVideoActivity extends AppCompatActivity {
|
||||
uiIsHidden = false;
|
||||
mediaController.show(100000);
|
||||
actionBar.show();
|
||||
adjustMediaControllMetrics();
|
||||
adjustMediaControlMetrics();
|
||||
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
Handler handler = new Handler();
|
||||
handler.postDelayed(new Runnable() {
|
||||
@@ -250,16 +247,18 @@ public class PlayVideoActivity extends AppCompatActivity {
|
||||
uiIsHidden = true;
|
||||
actionBar.hide();
|
||||
mediaController.hide();
|
||||
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
||||
if (android.os.Build.VERSION.SDK_INT >= 17) {
|
||||
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
||||
}
|
||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
}
|
||||
|
||||
private void adjustMediaControllMetrics() {
|
||||
private void adjustMediaControlMetrics() {
|
||||
MediaController.LayoutParams mediaControllerLayout
|
||||
= new MediaController.LayoutParams(MediaController.LayoutParams.MATCH_PARENT,
|
||||
MediaController.LayoutParams.WRAP_CONTENT);
|
||||
@@ -274,7 +273,7 @@ public class PlayVideoActivity extends AppCompatActivity {
|
||||
mediaController.setLayoutParams(mediaControllerLayout);
|
||||
}
|
||||
|
||||
private boolean checkIfhasSoftKeys(){
|
||||
private boolean checkIfHasSoftKeys(){
|
||||
if(Build.VERSION.SDK_INT >= 17) {
|
||||
return getNavigationBarHeight() != 0 || getNavigationBarWidth() != 0;
|
||||
} else {
|
||||
|
@@ -1,7 +1,5 @@
|
||||
package org.schabi.newpipe;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
|
@@ -110,13 +110,13 @@ public class VideoInfo {
|
||||
}
|
||||
|
||||
public static class AudioStream {
|
||||
public AudioStream(String url, int format, int bandWidth, int samplingRate) {
|
||||
public AudioStream(String url, int format, int bandwidth, int samplingRate) {
|
||||
this.url = url; this.format = format;
|
||||
this.bandWidth = bandWidth; this.samplingRate = samplingRate;
|
||||
this.bandwidth = bandwidth; this.samplingRate = samplingRate;
|
||||
}
|
||||
public String url = "";
|
||||
public int format = -1;
|
||||
public int bandWidth = -1;
|
||||
public int bandwidth = -1;
|
||||
public int samplingRate = -1;
|
||||
|
||||
}
|
||||
|
@@ -31,4 +31,5 @@ public class VideoInfoItem {
|
||||
public Bitmap thumbnail = null;
|
||||
public String webpage_url = "";
|
||||
public String upload_date = "";
|
||||
public String view_count = "";
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
package org.schabi.newpipe;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 24.10.15.
|
||||
*
|
||||
* 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 VideoInfoItemViewCreator {
|
||||
private static final String TAG = VideoInfoItemViewCreator.class.toString();
|
||||
|
||||
LayoutInflater inflater;
|
||||
|
||||
public VideoInfoItemViewCreator(LayoutInflater inflater) {
|
||||
this.inflater = inflater;
|
||||
}
|
||||
|
||||
public View getViewByVideoInfoItem(View convertView, ViewGroup parent, VideoInfoItem info) {
|
||||
ViewHolder holder;
|
||||
if(convertView == null) {
|
||||
convertView = inflater.inflate(R.layout.video_item, parent, false);
|
||||
holder = new ViewHolder();
|
||||
holder.itemThumbnailView = (ImageView) convertView.findViewById(R.id.itemThumbnailView);
|
||||
holder.itemVideoTitleView = (TextView) convertView.findViewById(R.id.itemVideoTitleView);
|
||||
holder.itemUploaderView = (TextView) convertView.findViewById(R.id.itemUploaderView);
|
||||
holder.itemDurationView = (TextView) convertView.findViewById(R.id.itemDurationView);
|
||||
holder.itemUploadDateView = (TextView) convertView.findViewById(R.id.itemUploadDateView);
|
||||
convertView.setTag(holder);
|
||||
} else {
|
||||
holder = (ViewHolder) convertView.getTag();
|
||||
}
|
||||
|
||||
if(info.thumbnail == null) {
|
||||
holder.itemThumbnailView.setImageResource(R.drawable.dummi_thumbnail);
|
||||
} else {
|
||||
holder.itemThumbnailView.setImageBitmap(info.thumbnail);
|
||||
}
|
||||
holder.itemVideoTitleView.setText(info.title);
|
||||
holder.itemUploaderView.setText(info.uploader);
|
||||
holder.itemDurationView.setText(info.duration);
|
||||
if(!info.upload_date.isEmpty()) {
|
||||
holder.itemUploadDateView.setText(info.upload_date);
|
||||
} else {
|
||||
//tewak if nececeary: This is a hack preventing to have a white space in the layout :P
|
||||
holder.itemUploadDateView.setText(info.view_count);
|
||||
}
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
private class ViewHolder {
|
||||
public ImageView itemThumbnailView;
|
||||
public TextView itemVideoTitleView, itemUploaderView, itemDurationView, itemUploadDateView;
|
||||
}
|
||||
|
||||
}
|
@@ -1,22 +1,14 @@
|
||||
package org.schabi.newpipe;
|
||||
|
||||
import android.content.ContentProviderOperation;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.app.NavUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import org.schabi.newpipe.youtube.YoutubeExtractor;
|
||||
import android.widget.Toast;
|
||||
|
||||
|
||||
/**
|
||||
@@ -41,9 +33,11 @@ public class VideoItemDetailActivity extends AppCompatActivity {
|
||||
|
||||
private static final String TAG = VideoItemDetailActivity.class.toString();
|
||||
|
||||
VideoItemDetailFragment fragment;
|
||||
|
||||
private String videoUrl;
|
||||
private int currentStreamingService = -1;
|
||||
private boolean isLandscape;
|
||||
private Menu menu = null;
|
||||
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -51,7 +45,6 @@ public class VideoItemDetailActivity extends AppCompatActivity {
|
||||
|
||||
// Show the Up button in the action bar.
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
ActionBarHandler.getHandler().setupNavMenu(this);
|
||||
|
||||
// savedInstanceState is non-null when there is fragment state
|
||||
// saved from previous configurations of this activity
|
||||
@@ -83,6 +76,10 @@ public class VideoItemDetailActivity extends AppCompatActivity {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(extractor == null) {
|
||||
Toast.makeText(this, R.string.urlNotSupportedText, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
}
|
||||
arguments.putString(VideoItemDetailFragment.VIDEO_URL,
|
||||
extractor.getVideoUrl(extractor.getVideoId(videoUrl)));
|
||||
arguments.putBoolean(VideoItemDetailFragment.AUTO_PLAY,
|
||||
@@ -95,14 +92,27 @@ public class VideoItemDetailActivity extends AppCompatActivity {
|
||||
arguments.putInt(VideoItemDetailFragment.STREAMING_SERVICE, currentStreamingService);
|
||||
arguments.putBoolean(VideoItemDetailFragment.AUTO_PLAY, false);
|
||||
}
|
||||
// Create the detail fragment and add it to the activity
|
||||
// using a fragment transaction.
|
||||
VideoItemDetailFragment fragment = new VideoItemDetailFragment();
|
||||
fragment.setArguments(arguments);
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.add(R.id.videoitem_detail_container, fragment)
|
||||
.commit();
|
||||
|
||||
} else {
|
||||
videoUrl = savedInstanceState.getString(VideoItemDetailFragment.VIDEO_URL);
|
||||
currentStreamingService = savedInstanceState.getInt(VideoItemDetailFragment.STREAMING_SERVICE);
|
||||
arguments = savedInstanceState;
|
||||
}
|
||||
|
||||
// Create the detail fragment and add it to the activity
|
||||
// using a fragment transaction.
|
||||
fragment = new VideoItemDetailFragment();
|
||||
fragment.setArguments(arguments);
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.add(R.id.videoitem_detail_container, fragment)
|
||||
.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
outState.putString(VideoItemDetailFragment.VIDEO_URL, videoUrl);
|
||||
outState.putInt(VideoItemDetailFragment.STREAMING_SERVICE, currentStreamingService);
|
||||
outState.putBoolean(VideoItemDetailFragment.AUTO_PLAY, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -121,17 +131,15 @@ public class VideoItemDetailActivity extends AppCompatActivity {
|
||||
NavUtils.navigateUpTo(this, intent);
|
||||
return true;
|
||||
} else {
|
||||
ActionBarHandler.getHandler().onItemSelected(item, this);
|
||||
return fragment.onOptionsItemSelected(item) ||
|
||||
super.onOptionsItemSelected(item);
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreatePanelMenu(int featured, Menu menu) {
|
||||
super.onCreatePanelMenu(featured, menu);
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
ActionBarHandler.getHandler().setupMenu(menu, inflater, this);
|
||||
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
super.onCreateOptionsMenu(menu);
|
||||
fragment.onCreateOptionsMenu(menu, getMenuInflater());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@@ -2,21 +2,14 @@ package org.schabi.newpipe;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.support.v7.widget.SearchView;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.support.v7.widget.SearchView;
|
||||
import android.widget.ImageView;
|
||||
|
||||
/**
|
||||
@@ -48,6 +41,8 @@ public class VideoItemListActivity extends AppCompatActivity
|
||||
private String searchQuery = "";
|
||||
|
||||
private VideoItemListFragment listFragment;
|
||||
private VideoItemDetailFragment videoFragment = null;
|
||||
Menu menu = null;
|
||||
|
||||
public class SearchVideoQueryListener implements SearchView.OnQueryTextListener {
|
||||
|
||||
@@ -68,7 +63,6 @@ public class VideoItemListActivity extends AppCompatActivity
|
||||
// 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();
|
||||
hideWatermark();
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -82,13 +76,6 @@ public class VideoItemListActivity extends AppCompatActivity
|
||||
|
||||
}
|
||||
|
||||
private void hideWatermark() {
|
||||
ImageView waterMark = (ImageView) findViewById(R.id.list_view_watermark);
|
||||
if(waterMark != null) {
|
||||
waterMark.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the activity is in two-pane mode, i.e. running on a tablet
|
||||
* device.
|
||||
@@ -114,7 +101,6 @@ public class VideoItemListActivity extends AppCompatActivity
|
||||
searchQuery = savedInstanceState.getString(QUERY);
|
||||
currentStreamingServiceId = savedInstanceState.getInt(STREAMING_SERVICE);
|
||||
if(!searchQuery.isEmpty()) {
|
||||
hideWatermark();
|
||||
listFragment.search(searchQuery);
|
||||
}
|
||||
}
|
||||
@@ -139,9 +125,6 @@ public class VideoItemListActivity extends AppCompatActivity
|
||||
searchView.setIconifiedByDefault(false);
|
||||
searchView.setIconified(false);
|
||||
searchView.setOnQueryTextListener(new SearchVideoQueryListener());
|
||||
|
||||
ActionBarHandler.getHandler().setupNavMenu(this);
|
||||
|
||||
}
|
||||
|
||||
SettingsActivity.initSettings(this);
|
||||
@@ -167,10 +150,17 @@ public class VideoItemListActivity extends AppCompatActivity
|
||||
arguments.putString(VideoItemDetailFragment.ARG_ITEM_ID, id);
|
||||
arguments.putString(VideoItemDetailFragment.VIDEO_URL, webpage_url);
|
||||
arguments.putInt(VideoItemDetailFragment.STREAMING_SERVICE, currentStreamingServiceId);
|
||||
VideoItemDetailFragment fragment = new VideoItemDetailFragment();
|
||||
fragment.setArguments(arguments);
|
||||
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, fragment)
|
||||
.replace(R.id.videoitem_detail_container, videoFragment)
|
||||
.commit();
|
||||
} else {
|
||||
// In single-pane mode, simply start the detail activity
|
||||
@@ -184,10 +174,11 @@ public class VideoItemListActivity extends AppCompatActivity
|
||||
}
|
||||
|
||||
|
||||
public boolean onCreatePanelMenu(int featured, Menu menu) {
|
||||
super.onCreatePanelMenu(featured, menu);
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
super.onCreateOptionsMenu(menu);
|
||||
this.menu = menu;
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
if(findViewById(R.id.videoitem_detail_container) == null) {
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.videoitem_list, menu);
|
||||
MenuItem searchItem = menu.findItem(R.id.action_search);
|
||||
SearchView searchView = (SearchView) searchItem.getActionView();
|
||||
@@ -195,9 +186,10 @@ public class VideoItemListActivity extends AppCompatActivity
|
||||
searchView.setOnQueryTextListener(
|
||||
new SearchVideoQueryListener());
|
||||
|
||||
} else if (videoFragment != null){
|
||||
videoFragment.onCreateOptionsMenu(menu, inflater);
|
||||
} else {
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
ActionBarHandler.getHandler().setupMenu(menu, inflater, this);
|
||||
inflater.inflate(R.menu.videoitem_two_pannel, menu);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -210,8 +202,8 @@ public class VideoItemListActivity extends AppCompatActivity
|
||||
Intent intent = new Intent(this, SettingsActivity.class);
|
||||
startActivity(intent);
|
||||
} else {
|
||||
ActionBarHandler.getHandler().onItemSelected(item, this);
|
||||
return super.onOptionsItemSelected(item);
|
||||
return videoFragment.onOptionsItemSelected(item) ||
|
||||
super.onOptionsItemSelected(item);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@@ -13,7 +13,6 @@ import android.widget.ListView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
|
||||
@@ -54,14 +53,14 @@ public class VideoItemListFragment extends ListFragment {
|
||||
|
||||
private class ResultRunnable implements Runnable {
|
||||
private SearchEngine.Result result;
|
||||
private int reuqestId;
|
||||
private int requestId;
|
||||
public ResultRunnable(SearchEngine.Result result, int requestId) {
|
||||
this.result = result;
|
||||
this.reuqestId = requestId;
|
||||
this.requestId = requestId;
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
updateListOnResult(result, reuqestId);
|
||||
updateListOnResult(result, requestId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +212,7 @@ public class VideoItemListFragment extends ListFragment {
|
||||
loadThumbsThread = new Thread(loadThumbsRunnable);
|
||||
loadThumbsThread.start();
|
||||
} catch(java.lang.IllegalStateException e) {
|
||||
Log.w(TAG, "Trying to set value while activity is not existing anymore.");
|
||||
Log.w(TAG, "Trying to set value while activity doesn't exist anymore.");
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -230,7 +229,7 @@ public class VideoItemListFragment extends ListFragment {
|
||||
}
|
||||
if(searchThread != null) {
|
||||
searchRunnable.terminate();
|
||||
// No need to join, since we don't realy terminate the thread. We just demand
|
||||
// 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.
|
||||
}
|
||||
}
|
||||
|
@@ -6,14 +6,12 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* Created by the-scrabi on 11.08.15.
|
||||
* Created by Christian Schabesberger on 11.08.15.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
||||
* VideoListAdapter.java is part of NewPipe.
|
||||
@@ -33,18 +31,20 @@ import java.util.Vector;
|
||||
*/
|
||||
|
||||
public class VideoListAdapter extends BaseAdapter {
|
||||
|
||||
private static final String TAG = VideoListAdapter.class.toString();
|
||||
private LayoutInflater inflater;
|
||||
|
||||
private Context context;
|
||||
private VideoInfoItemViewCreator viewCreator;
|
||||
private Vector<VideoInfoItem> videoList = new Vector<>();
|
||||
private Vector<Boolean> downloadedThumbnailList = new Vector<>();
|
||||
VideoItemListFragment videoListFragment;
|
||||
ListView listView;
|
||||
|
||||
public VideoListAdapter(Context context, VideoItemListFragment videoListFragment) {
|
||||
inflater = LayoutInflater.from(context);
|
||||
viewCreator = new VideoInfoItemViewCreator(LayoutInflater.from(context));
|
||||
this.videoListFragment = videoListFragment;
|
||||
this.listView = videoListFragment.getListView();
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public void addVideoList(Vector<VideoInfoItem> videos) {
|
||||
@@ -96,30 +96,7 @@ public class VideoListAdapter extends BaseAdapter {
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
ViewHolder holder;
|
||||
if(convertView == null) {
|
||||
convertView = inflater.inflate(R.layout.video_item, parent, false);
|
||||
holder = new ViewHolder();
|
||||
holder.itemThumbnailView = (ImageView) convertView.findViewById(R.id.itemThumbnailView);
|
||||
holder.itemVideoTitleView = (TextView) convertView.findViewById(R.id.itemVideoTitleView);
|
||||
holder.itemUploaderView = (TextView) convertView.findViewById(R.id.itemUploaderView);
|
||||
holder.itemDurationView = (TextView) convertView.findViewById(R.id.itemDurationView);
|
||||
holder.itemUploadDateView = (TextView) convertView.findViewById(R.id.itemUploadDateView);
|
||||
convertView.setTag(holder);
|
||||
} else {
|
||||
holder = (ViewHolder) convertView.getTag();
|
||||
}
|
||||
|
||||
final Context context = parent.getContext();
|
||||
if(videoList.get(position).thumbnail == null) {
|
||||
holder.itemThumbnailView.setImageResource(R.drawable.dummi_thumbnail);
|
||||
} else {
|
||||
holder.itemThumbnailView.setImageBitmap(videoList.get(position).thumbnail);
|
||||
}
|
||||
holder.itemVideoTitleView.setText(videoList.get(position).title);
|
||||
holder.itemUploaderView.setText(videoList.get(position).uploader);
|
||||
holder.itemDurationView.setText(videoList.get(position).duration);
|
||||
holder.itemUploadDateView.setText(videoList.get(position).upload_date);
|
||||
convertView = viewCreator.getViewByVideoInfoItem(convertView, parent, videoList.get(position));
|
||||
|
||||
if(listView.isItemChecked(position)) {
|
||||
convertView.setBackgroundColor(context.getResources().getColor(R.color.primaryColorYoutube));
|
||||
@@ -129,9 +106,4 @@ public class VideoListAdapter extends BaseAdapter {
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
private class ViewHolder {
|
||||
public ImageView itemThumbnailView;
|
||||
public TextView itemVideoTitleView, itemUploaderView, itemDurationView, itemUploadDateView;
|
||||
}
|
||||
}
|
@@ -1,30 +1,29 @@
|
||||
package org.schabi.newpipe.youtube;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.schabi.newpipe.Downloader;
|
||||
import org.schabi.newpipe.Extractor;
|
||||
import org.schabi.newpipe.VideoInfo;
|
||||
|
||||
import android.util.Log;
|
||||
import android.util.Xml;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.parser.Parser;
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.Function;
|
||||
import org.mozilla.javascript.ScriptableObject;
|
||||
import org.schabi.newpipe.Downloader;
|
||||
import org.schabi.newpipe.Extractor;
|
||||
import org.schabi.newpipe.VideoInfo;
|
||||
import org.schabi.newpipe.VideoInfoItem;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Vector;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.Matcher;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.parser.Parser;
|
||||
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.Function;
|
||||
import org.mozilla.javascript.ScriptableObject;
|
||||
import org.schabi.newpipe.VideoInfoItem;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 06.08.15.
|
||||
@@ -48,8 +47,6 @@ import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
public class YoutubeExtractor implements Extractor {
|
||||
|
||||
|
||||
|
||||
private static final String TAG = YoutubeExtractor.class.toString();
|
||||
|
||||
// These lists only contain itag formats that are supported by the common Android Video player.
|
||||
@@ -93,7 +90,7 @@ public class YoutubeExtractor implements Extractor {
|
||||
}
|
||||
}
|
||||
|
||||
private String decryptoinCode = "";
|
||||
private String decryptionCode = "";
|
||||
private static final String DECRYPTION_FUNC_NAME="decrypt";
|
||||
|
||||
@Override
|
||||
@@ -101,7 +98,12 @@ public class YoutubeExtractor implements Extractor {
|
||||
try {
|
||||
URI uri = new URI(videoUrl);
|
||||
if(uri.getHost().contains("youtube")) {
|
||||
String query = uri.getQuery();
|
||||
String query = uri.getFragment();
|
||||
if(query == null) {
|
||||
query = uri.getQuery();
|
||||
} else {
|
||||
query = query.replace("/watch?", "");
|
||||
}
|
||||
String queryElements[] = query.split("&");
|
||||
Map<String, String> queryArguments = new HashMap<>();
|
||||
for (String e : queryElements) {
|
||||
@@ -156,7 +158,7 @@ public class YoutubeExtractor implements Extractor {
|
||||
//-------------------------------------
|
||||
JSONObject playerArgs = null;
|
||||
JSONObject ytAssets = null;
|
||||
String dashManifest = "";
|
||||
String dashManifest;
|
||||
{
|
||||
Pattern p = Pattern.compile("ytplayer.config\\s*=\\s*(\\{.*?\\});");
|
||||
Matcher m = p.matcher(site);
|
||||
@@ -183,18 +185,23 @@ public class YoutubeExtractor implements Extractor {
|
||||
videoInfo.thumbnail_url = playerArgs.getString("thumbnail_url");
|
||||
videoInfo.duration = playerArgs.getInt("length_seconds");
|
||||
videoInfo.average_rating = playerArgs.getString("avg_rating");
|
||||
// View Count will be extracted from html
|
||||
dashManifest = playerArgs.getString("dashmpd");
|
||||
String playerUrl = ytAssets.getString("js");
|
||||
if(playerUrl.startsWith("//")) {
|
||||
playerUrl = "https:" + playerUrl;
|
||||
}
|
||||
if(decryptoinCode.isEmpty()) {
|
||||
decryptoinCode = loadDecryptioinCode(playerUrl);
|
||||
if(decryptionCode.isEmpty()) {
|
||||
decryptionCode = loadDecryptionCode(playerUrl);
|
||||
}
|
||||
|
||||
// extract audio
|
||||
videoInfo.audioStreams = parseDashManifest(dashManifest, decryptoinCode);
|
||||
try {
|
||||
dashManifest = playerArgs.getString("dashmpd");
|
||||
videoInfo.audioStreams = parseDashManifest(dashManifest, decryptionCode);
|
||||
} catch (Exception e) {
|
||||
//todo: check if the following statement is true
|
||||
Log.e(TAG, "Dash manifest seems not to bee available.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
//------------------------------------
|
||||
// extract video stream url
|
||||
@@ -213,10 +220,10 @@ public class YoutubeExtractor implements Extractor {
|
||||
|
||||
// if video has a signature: decrypt it and add it to the url
|
||||
if(tags.get("s") != null) {
|
||||
if(decryptoinCode.isEmpty()) {
|
||||
decryptoinCode = loadDecryptioinCode(playerUrl);
|
||||
if(decryptionCode.isEmpty()) {
|
||||
decryptionCode = loadDecryptionCode(playerUrl);
|
||||
}
|
||||
streamUrl = streamUrl + "&signature=" + decryptSignature(tags.get("s"), decryptoinCode);
|
||||
streamUrl = streamUrl + "&signature=" + decryptSignature(tags.get("s"), decryptionCode);
|
||||
}
|
||||
|
||||
if(resolveFormat(itag) != -1) {
|
||||
@@ -236,7 +243,7 @@ public class YoutubeExtractor implements Extractor {
|
||||
}
|
||||
|
||||
//-------------------------------
|
||||
// extrating from html page
|
||||
// extracting from html page
|
||||
//-------------------------------
|
||||
|
||||
|
||||
@@ -297,32 +304,28 @@ public class YoutubeExtractor implements Extractor {
|
||||
// view count
|
||||
videoInfo.view_count = doc.select("div[class=\"watch-view-count\"]").first().text();
|
||||
|
||||
/* todo finish this code
|
||||
|
||||
// next video
|
||||
videoInfo.nextVideo = extractVideoInfoItem(doc.select("div[class=\"watch-sidebar-section\"]").first()
|
||||
.select("li").first());
|
||||
|
||||
int i = 0;
|
||||
// related videos
|
||||
videoInfo.relatedVideos = new Vector<>();
|
||||
for(Element li : doc.select("ul[id=\"watch-related\"]").first().children()) {
|
||||
// first check if we have a playlist. If so leave them out
|
||||
if(li.select("a[class*=\"content-link\"]").first() != null) {
|
||||
//videoInfo.relatedVideos.add(extractVideoInfoItem(li));
|
||||
//i++;
|
||||
//Log.d(TAG, Integer.toString(i));
|
||||
videoInfo.relatedVideos.add(extractVideoInfoItem(li));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
return videoInfo;
|
||||
}
|
||||
|
||||
private VideoInfo.AudioStream[] parseDashManifest(String dashManifest, String decryptoinCode) {
|
||||
if(!dashManifest.contains("/signature/")) {
|
||||
String encryptedSig = "";
|
||||
String decryptedSig = "";
|
||||
String decryptedSig;
|
||||
try {
|
||||
Pattern p = Pattern.compile("/s/([a-fA-F0-9\\.]+)");
|
||||
Matcher m = p.matcher(dashManifest);
|
||||
@@ -406,11 +409,10 @@ public class YoutubeExtractor implements Extractor {
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
info.title = li.select("span[class=\"title\"]").first()
|
||||
.text();
|
||||
|
||||
info.title = li.select("span[class=\"title\"]").first().text();
|
||||
info.view_count = li.select("span[class*=\"view-count\"]").first().text();
|
||||
info.uploader = li.select("span[class=\"g-hovercard\"]").first().text();
|
||||
|
||||
info.duration = li.select("span[class=\"video-time\"]").first().text();
|
||||
|
||||
Element img = li.select("img").first();
|
||||
@@ -421,7 +423,9 @@ public class YoutubeExtractor implements Extractor {
|
||||
if(info.thumbnail_url.contains(".gif")) {
|
||||
info.thumbnail_url = img.attr("data-thumb");
|
||||
}
|
||||
|
||||
if(info.thumbnail_url.startsWith("//")) {
|
||||
info.thumbnail_url = "https:" + info.thumbnail_url;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
@@ -438,7 +442,7 @@ public class YoutubeExtractor implements Extractor {
|
||||
return retval;
|
||||
}
|
||||
|
||||
private String loadDecryptioinCode(String playerUrl) {
|
||||
private String loadDecryptionCode(String playerUrl) {
|
||||
String playerCode = Downloader.download(playerUrl);
|
||||
String decryptionFuncName = "";
|
||||
String decryptionFunc = "";
|
||||
|
@@ -1,18 +1,17 @@
|
||||
package org.schabi.newpipe.youtube;
|
||||
|
||||
import org.schabi.newpipe.Downloader;
|
||||
import org.schabi.newpipe.SearchEngine;
|
||||
import org.schabi.newpipe.VideoInfoItem;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.schabi.newpipe.Downloader;
|
||||
import org.schabi.newpipe.SearchEngine;
|
||||
import org.schabi.newpipe.VideoInfoItem;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 09.08.15.
|
||||
|
@@ -42,12 +42,6 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="4">
|
||||
|
||||
<ImageView android:id="@+id/list_view_watermark"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="10dp"
|
||||
android:src="@drawable/new_pipe_watermark"/>
|
||||
|
||||
<FrameLayout android:id="@+id/videoitem_detail_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
208
app/src/main/res/layout-sw600dp/fragment_videoitem_detail.xml
Normal file
@@ -3,12 +3,6 @@
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView android:id="@+id/list_view_watermark"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="10dp"
|
||||
android:src="@drawable/new_pipe_watermark"/>
|
||||
|
||||
<fragment
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/videoitem_list"
|
||||
|
@@ -7,8 +7,9 @@
|
||||
android:padding="6dp">
|
||||
|
||||
<ImageView android:id="@+id/itemThumbnailView"
|
||||
android:layout_width="125dp"
|
||||
android:layout_width="142dp"
|
||||
android:layout_height="80dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:src="@drawable/dummi_thumbnail"/>
|
||||
|
9
app/src/main/res/menu/videoitem_two_pannel.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_settings"
|
||||
app:showAsAction="never"
|
||||
android:title="@string/settings"/>
|
||||
|
||||
</menu>
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 7.1 KiB |
@@ -3,7 +3,7 @@
|
||||
<string name="app_name">NewPipe</string>
|
||||
<string name="title_videoitem_detail">NewPipe</string>
|
||||
<string name="nothingFound">Nichts gefunden</string>
|
||||
<string name="viewSufix">views</string>
|
||||
<string name="viewSufix">Aufrufe</string>
|
||||
<string name="uploadDatePrefix">Hochgeladen am: </string>
|
||||
<string name="noPlayerFound">Keinen Streamplayer gefunden. Vielleicht möchtest du einen installieren.</string>
|
||||
<string name="installStreamPlayer">Jetzt installieren</string>
|
||||
@@ -45,4 +45,7 @@
|
||||
<item>Video</item>
|
||||
<item>Audio</item>
|
||||
</string-array>
|
||||
<string name="nextVideoTitle">Nächstes Video</string>
|
||||
<string name="showNextVideoTitle">Zeige \"Nächstes Video\" Auswahl.</string>
|
||||
<string name="urlNotSupportedText">Url wird nicht unterstützt.</string>
|
||||
</resources>
|
||||
|
@@ -29,4 +29,20 @@
|
||||
<string name="autoPlayThroughIntentTitle">Аутопуштање преко Интента</string>
|
||||
<string name="autoPlayThroughIntentSummary">Аутоматски почиње пушта видео по позиву из друге апликације.</string>
|
||||
<string name="defaultResolutionPreferenceTitle">Подразумевана резолуција</string>
|
||||
</resources>
|
||||
<string name="playWithKodiTitle">Пусти помоћу Кодија</string>
|
||||
<string name="koreNotFound">Апликација Кор није нађена. Кор (Kore) је потребан да бисте пуштали видее у Коди медија центру.</string>
|
||||
<string name="installeKore">Инсталирај Кор</string>
|
||||
<string name="fdroidKoreUrl">https://f-droid.org/repository/browse/?fdfilter=Kore&fdid=org.xbmc.kore</string>
|
||||
<string name="showPlayWithKodiTitle">Прикажи „Пусти помоћу Кодија“</string>
|
||||
<string name="showPlayWithKodiSummary">Приказ опције за пуштање видеа у Коди медија центру.</string>
|
||||
<string name="leftPlayButtonTitle">Прикажи дугме за пуштање на левој страни.</string>
|
||||
<string name="playAudio">Аудио</string>
|
||||
<string name="defaultAudioFormatTitle">Подразумевани формат звука</string>
|
||||
<string name="webMAudioDescription">WebM - слободни формат</string>
|
||||
<string name="m4aAudioDescription">m4a - бољи квалитет</string>
|
||||
<string name="downloadDialogTitle">Преузми</string>
|
||||
<string-array name="downloadOptions">
|
||||
<item>Видео</item>
|
||||
<item>Аудио</item>
|
||||
</string-array>
|
||||
</resources>
|
@@ -23,4 +23,5 @@
|
||||
<item>m4a</item>
|
||||
</string-array>
|
||||
<string name="defaultAudioFormat">m4a</string>
|
||||
<string name="showNextVideo">show_next_video</string>
|
||||
</resources>
|