diff --git a/README.md b/README.md
index c852401c3..557c5d9be 100644
--- a/README.md
+++ b/README.md
@@ -24,6 +24,11 @@ Project status:
[](screenshots/screenshot_3.png)
[](screenshots/screenshot_4.png)
[](screenshots/screenshot_5.png)
+[](screenshots/screenshot_6.png)
+[](screenshots/screenshot_7.png)
+[](screenshots/screenshot_8.png)
+[](screenshots/screenshot_9.png)
+
## Description
diff --git a/app/build.gradle b/app/build.gradle
index 79efd38ef..c5106adbf 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -8,8 +8,8 @@ android {
applicationId "org.schabi.newpipe"
minSdkVersion 15
targetSdkVersion 25
- versionCode 31
- versionName "0.9.4"
+ versionCode 32
+ versionName "0.9.5"
}
buildTypes {
release {
diff --git a/app/src/main/java/org/schabi/newpipe/ThemableActivity.java b/app/src/main/java/org/schabi/newpipe/ThemableActivity.java
deleted file mode 100644
index 8b0d126be..000000000
--- a/app/src/main/java/org/schabi/newpipe/ThemableActivity.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.schabi.newpipe;
-
-import android.os.Bundle;
-import android.preference.PreferenceManager;
-import android.support.v7.app.AppCompatActivity;
-
-public class ThemableActivity extends AppCompatActivity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (PreferenceManager.getDefaultSharedPreferences(this)
- .getString("theme", getResources().getString(R.string.light_theme_title)).
- equals(getResources().getString(R.string.dark_theme_title))) {
- setTheme(R.style.DarkTheme);
- }
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- if (PreferenceManager.getDefaultSharedPreferences(this)
- .getString("theme", getResources().getString(R.string.light_theme_title)).
- equals(getResources().getString(R.string.dark_theme_title))) {
- setTheme(R.style.DarkTheme);
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/BaseFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/BaseFragment.java
index d51267104..5337912b8 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/BaseFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/BaseFragment.java
@@ -1,7 +1,5 @@
package org.schabi.newpipe.fragments;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
@@ -28,6 +26,8 @@ import org.schabi.newpipe.R;
import java.util.concurrent.atomic.AtomicBoolean;
+import static org.schabi.newpipe.util.AnimationUtils.animateView;
+
public abstract class BaseFragment extends Fragment {
protected final String TAG = "BaseFragment@" + Integer.toHexString(hashCode());
protected static final boolean DEBUG = MainActivity.DEBUG;
@@ -130,70 +130,6 @@ public abstract class BaseFragment extends Fragment {
// Utils
//////////////////////////////////////////////////////////////////////////*/
- public void animateView(final View view, final boolean enterOrExit, long duration) {
- animateView(view, enterOrExit, duration, 0, null);
- }
-
- public void animateView(final View view, final boolean enterOrExit, long duration, final Runnable execOnEnd) {
- animateView(view, enterOrExit, duration, 0, execOnEnd);
- }
-
- public void animateView(final View view, final boolean enterOrExit, long duration, long delay) {
- animateView(view, enterOrExit, duration, delay, null);
- }
-
- /**
- * Animate the view
- *
- * @param view view that will be animated
- * @param enterOrExit true to enter, false to exit
- * @param duration how long the animation will take, in milliseconds
- * @param delay how long the animation will take to start, in milliseconds
- * @param execOnEnd runnable that will be executed when the animation ends
- */
- public void animateView(final View view, final boolean enterOrExit, long duration, long delay, final Runnable execOnEnd) {
- if (DEBUG) Log.d(TAG, "animateView() called with: view = [" + view + "], enterOrExit = [" + enterOrExit + "], duration = [" + duration + "], execOnEnd = [" + execOnEnd + "]");
- if (view == null) return;
-
- if (view.getVisibility() == View.VISIBLE && enterOrExit) {
- view.animate().setListener(null).cancel();
- view.setVisibility(View.VISIBLE);
- view.setAlpha(1f);
- if (execOnEnd != null) execOnEnd.run();
- return;
- } else if ((view.getVisibility() == View.GONE || view.getVisibility() == View.INVISIBLE) && !enterOrExit) {
- view.animate().setListener(null).cancel();
- view.setVisibility(View.GONE);
- view.setAlpha(0f);
- if (execOnEnd != null) execOnEnd.run();
- return;
- }
-
- view.animate().setListener(null).cancel();
- view.setVisibility(View.VISIBLE);
-
- if (enterOrExit) {
- view.animate().alpha(1f).setDuration(duration).setStartDelay(delay)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (execOnEnd != null) execOnEnd.run();
- }
- }).start();
- } else {
- view.animate().alpha(0f)
- .setDuration(duration).setStartDelay(delay)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- view.setVisibility(View.GONE);
- if (execOnEnd != null) execOnEnd.run();
- }
- })
- .start();
- }
- }
-
protected void setErrorMessage(String message, boolean showRetryButton) {
if (errorTextView == null || activity == null) return;
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/channel/ChannelFragment.java
index fa02dbfdb..032212ed6 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/channel/ChannelFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/channel/ChannelFragment.java
@@ -36,6 +36,8 @@ import java.io.Serializable;
import java.text.NumberFormat;
import java.util.ArrayList;
+import static org.schabi.newpipe.util.AnimationUtils.animateView;
+
public class ChannelFragment extends BaseFragment implements ChannelExtractorWorker.OnChannelInfoReceive {
private final String TAG = "ChannelFragment@" + Integer.toHexString(hashCode());
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
index 7ad62d99f..65ddc36ed 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
@@ -19,6 +19,7 @@ import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.util.Log;
import android.util.TypedValue;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -39,7 +40,6 @@ import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
import org.schabi.newpipe.ImageErrorLoadingListener;
-import org.schabi.newpipe.Localization;
import org.schabi.newpipe.R;
import org.schabi.newpipe.ReCaptchaActivity;
import org.schabi.newpipe.download.DownloadDialog;
@@ -55,6 +55,8 @@ import org.schabi.newpipe.player.MainVideoPlayer;
import org.schabi.newpipe.player.PlayVideoActivity;
import org.schabi.newpipe.player.PopupVideoPlayer;
import org.schabi.newpipe.report.ErrorActivity;
+import org.schabi.newpipe.util.Constants;
+import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.Utils;
@@ -64,6 +66,8 @@ import java.io.Serializable;
import java.util.ArrayList;
import java.util.Stack;
+import static org.schabi.newpipe.util.AnimationUtils.animateView;
+
public class VideoDetailFragment extends BaseFragment implements StreamExtractorWorker.OnStreamInfoReceivedListener, SharedPreferences.OnSharedPreferenceChangeListener, View.OnClickListener {
private final String TAG = "VideoDetailFragment@" + Integer.toHexString(hashCode());
@@ -71,9 +75,6 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
private static final int INITIAL_RELATED_VIDEOS = 8;
private static final String KORE_PACKET = "org.xbmc.kore";
- private static final String SERVICE_ID_KEY = "service_id_key";
- private static final String VIDEO_URL_KEY = "video_url_key";
- private static final String VIDEO_TITLE_KEY = "video_title_key";
private static final String STACK_KEY = "stack_key";
private static final String INFO_KEY = "info_key";
private static final String WAS_RELATED_EXPANDED_KEY = "was_related_expanded_key";
@@ -170,9 +171,9 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
if (savedInstanceState != null) {
- videoTitle = savedInstanceState.getString(VIDEO_TITLE_KEY);
- videoUrl = savedInstanceState.getString(VIDEO_URL_KEY);
- serviceId = savedInstanceState.getInt(SERVICE_ID_KEY, 0);
+ videoTitle = savedInstanceState.getString(Constants.KEY_TITLE);
+ videoUrl = savedInstanceState.getString(Constants.KEY_URL);
+ serviceId = savedInstanceState.getInt(Constants.KEY_SERVICE_ID, 0);
wasRelatedStreamsExpanded = savedInstanceState.getBoolean(WAS_RELATED_EXPANDED_KEY, false);
Serializable serializable = savedInstanceState.getSerializable(STACK_KEY);
if (serializable instanceof Stack) {
@@ -289,9 +290,9 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
@Override
public void onSaveInstanceState(Bundle outState) {
if (DEBUG) Log.d(TAG, "onSaveInstanceState() called with: outState = [" + outState + "]");
- outState.putString(VIDEO_URL_KEY, videoUrl);
- outState.putString(VIDEO_TITLE_KEY, videoTitle);
- outState.putInt(SERVICE_ID_KEY, serviceId);
+ outState.putString(Constants.KEY_URL, videoUrl);
+ outState.putString(Constants.KEY_TITLE, videoTitle);
+ outState.putInt(Constants.KEY_SERVICE_ID, serviceId);
outState.putSerializable(STACK_KEY, stack);
int nextCount = currentStreamInfo != null && currentStreamInfo.next_video != null ? 2 : 0;
@@ -420,7 +421,10 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
if (isLoading.get()) return;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !PermissionHelper.checkSystemAlertWindowPermission(activity)) {
- Toast.makeText(activity, R.string.msg_popup_permission, Toast.LENGTH_LONG).show();
+ Toast toast = Toast.makeText(activity, R.string.msg_popup_permission, Toast.LENGTH_LONG);
+ TextView messageView = (TextView) toast.getView().findViewById(android.R.id.message);
+ if (messageView != null) messageView.setGravity(Gravity.CENTER);
+ toast.show();
return;
}
@@ -800,7 +804,7 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
* If it is, check if the cache contains the info already.
* If the cache doesn't have the info, load from the network.
*
- * @param info info to prepare and load, can be null
+ * @param info info to prepare and load, can be null
* @param scrollToTop whether or not scroll the scrollView to y = 0
*/
public void prepareAndLoad(StreamInfo info, boolean scrollToTop) {
@@ -844,7 +848,7 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
else parallaxScrollRootView.scrollTo(0, 0);
}
- animateView(contentRootLayoutHiding, false, greaterThanThreshold ? 250 : 0, new Runnable() {
+ animateView(contentRootLayoutHiding, false, greaterThanThreshold ? 250 : 0, 0, new Runnable() {
@Override
public void run() {
handleStreamInfo(infoFinal, false);
@@ -1052,7 +1056,7 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
private void setErrorImage(final int imageResource) {
if (thumbnailImageView == null || activity == null) return;
thumbnailImageView.setImageDrawable(ContextCompat.getDrawable(activity, imageResource));
- animateView(thumbnailImageView, false, 0, new Runnable() {
+ animateView(thumbnailImageView, false, 0, 0, new Runnable() {
@Override
public void run() {
animateView(thumbnailImageView, true, 500);
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/search/SearchFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/search/SearchFragment.java
index 8071f4a36..11707c38d 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/search/SearchFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/search/SearchFragment.java
@@ -37,6 +37,7 @@ import org.schabi.newpipe.extractor.search.SearchResult;
import org.schabi.newpipe.fragments.BaseFragment;
import org.schabi.newpipe.info_list.InfoItemBuilder;
import org.schabi.newpipe.info_list.InfoListAdapter;
+import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.workers.SearchWorker;
import org.schabi.newpipe.workers.SuggestionWorker;
@@ -45,12 +46,13 @@ import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
+import static org.schabi.newpipe.util.AnimationUtils.animateView;
+
public class SearchFragment extends BaseFragment implements SuggestionWorker.OnSuggestionResult, SearchWorker.OnSearchResult {
private final String TAG = "SearchFragment@" + Integer.toHexString(hashCode());
// savedInstanceBundle arguments
private static final String QUERY_KEY = "query_key";
private static final String PAGE_NUMBER_KEY = "page_number_key";
- private static final String SERVICE_KEY = "service_key";
private static final String INFO_LIST_KEY = "info_list_key";
private static final String WAS_LOADING_KEY = "was_loading_key";
private static final String ERROR_KEY = "error_key";
@@ -101,7 +103,7 @@ public class SearchFragment extends BaseFragment implements SuggestionWorker.OnS
setHasOptionsMenu(true);
if (savedInstanceState != null) {
searchQuery = savedInstanceState.getString(QUERY_KEY);
- serviceId = savedInstanceState.getInt(SERVICE_KEY, 0);
+ serviceId = savedInstanceState.getInt(Constants.KEY_SERVICE_ID, 0);
pageNumber = savedInstanceState.getInt(PAGE_NUMBER_KEY, 0);
wasLoading.set(savedInstanceState.getBoolean(WAS_LOADING_KEY, false));
filterItemCheckedId = savedInstanceState.getInt(FILTER_CHECKED_ID_KEY, 0);
@@ -171,7 +173,7 @@ public class SearchFragment extends BaseFragment implements SuggestionWorker.OnS
String query = searchEditText != null && !TextUtils.isEmpty(searchEditText.getText().toString())
? searchEditText.getText().toString() : searchQuery;
outState.putString(QUERY_KEY, query);
- outState.putInt(SERVICE_KEY, serviceId);
+ outState.putInt(Constants.KEY_SERVICE_ID, serviceId);
outState.putInt(PAGE_NUMBER_KEY, pageNumber);
outState.putSerializable(INFO_LIST_KEY, ((ArrayList) infoListAdapter.getItemsList()));
outState.putBoolean(WAS_LOADING_KEY, curSearchWorker != null && curSearchWorker.isRunning());
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/search/SearchSuggestionListener.java b/app/src/main/java/org/schabi/newpipe/fragments/search/SearchSuggestionListener.java
deleted file mode 100644
index 9e2a74dad..000000000
--- a/app/src/main/java/org/schabi/newpipe/fragments/search/SearchSuggestionListener.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package org.schabi.newpipe.fragments.search;
-
-import android.support.v7.widget.SearchView;
-
-/**
- * Created by Christian Schabesberger on 02.08.16.
- *
- * Copyright (C) Christian Schabesberger 2016
- * SearchSuggestionListener.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 .
- */
-
-
-public class SearchSuggestionListener implements SearchView.OnSuggestionListener{
-
- private final SearchView searchView;
- private final 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;
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java
index ce3525f17..45221325b 100644
--- a/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java
@@ -21,10 +21,13 @@ import android.widget.TextView;
import android.widget.Toast;
import org.schabi.newpipe.R;
+import org.schabi.newpipe.util.AnimationUtils;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.ThemeHelper;
+import static org.schabi.newpipe.util.AnimationUtils.animateView;
+
/**
* Activity Player implementing VideoPlayer
*
@@ -37,14 +40,7 @@ public class MainVideoPlayer extends Activity {
private AudioManager audioManager;
private GestureDetector gestureDetector;
- private final Runnable hideUiRunnable = new Runnable() {
- @Override
- public void run() {
- hideSystemUi();
- }
- };
private boolean activityPaused;
-
private VideoPlayerImpl playerImpl;
/*//////////////////////////////////////////////////////////////////////////
@@ -265,14 +261,15 @@ public class MainVideoPlayer extends Activity {
else if (v.getId() == screenRotationButton.getId()) onScreenRotationClicked();
if (getCurrentState() != STATE_COMPLETED) {
+ getControlsVisibilityHandler().removeCallbacksAndMessages(null);
animateView(playerImpl.getControlsRoot(), true, 300, 0, new Runnable() {
@Override
public void run() {
if (getCurrentState() == STATE_PLAYING && !playerImpl.isQualityMenuVisible()) {
- animateView(playerImpl.getControlsRoot(), false, 300, DEFAULT_CONTROLS_HIDE_TIME, true);
+ hideControls(300, DEFAULT_CONTROLS_HIDE_TIME);
}
}
- }, false);
+ });
}
}
@@ -285,14 +282,14 @@ public class MainVideoPlayer extends Activity {
public void onStopTrackingTouch(SeekBar seekBar) {
super.onStopTrackingTouch(seekBar);
if (playerImpl.wasPlaying()) {
- animateView(playerImpl.getControlsRoot(), false, 100, 0);
+ hideControls(100, 0);
}
}
@Override
public void onDismiss(PopupMenu menu) {
super.onDismiss(menu);
- if (isPlaying()) animateView(getControlsRoot(), false, 500, 0);
+ if (isPlaying()) hideControls(300, 0);
}
@Override
@@ -310,26 +307,23 @@ public class MainVideoPlayer extends Activity {
public void onLoading() {
super.onLoading();
playPauseButton.setImageResource(R.drawable.ic_pause_white);
- animateView(playPauseButton, false, 100, 0);
+ animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 100);
}
@Override
public void onBuffering() {
super.onBuffering();
- animateView(playPauseButton, false, 100, 0);
+ animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 100);
}
@Override
public void onPlaying() {
super.onPlaying();
- //playPauseButton.setImageResource(R.drawable.ic_pause_white);
- //animateView(playPauseButton, true, 500, 0);
-
- animateView(playPauseButton, false, 80, 0, new Runnable() {
+ animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 80, 0, new Runnable() {
@Override
public void run() {
playPauseButton.setImageResource(R.drawable.ic_pause_white);
- animateView(playPauseButton, true, 200, 0);
+ animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, true, 200);
}
});
@@ -339,14 +333,11 @@ public class MainVideoPlayer extends Activity {
@Override
public void onPaused() {
super.onPaused();
- //playPauseButton.setImageResource(R.drawable.ic_play_arrow_white);
- //animateView(playPauseButton, true, 100, 0);
-
- animateView(playPauseButton, false, 80, 0, new Runnable() {
+ animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 80, 0, new Runnable() {
@Override
public void run() {
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white);
- animateView(playPauseButton, true, 200, 0);
+ animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, true, 200);
}
});
@@ -356,7 +347,7 @@ public class MainVideoPlayer extends Activity {
@Override
public void onPausedSeek() {
super.onPausedSeek();
- animateView(playPauseButton, false, 100, 0);
+ animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 100);
}
@@ -366,11 +357,11 @@ public class MainVideoPlayer extends Activity {
playPauseButton.setImageResource(R.drawable.ic_pause_white);
} else {
showSystemUi();
- animateView(playPauseButton, false, 0, 0, new Runnable() {
+ animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 0, 0, new Runnable() {
@Override
public void run() {
playPauseButton.setImageResource(R.drawable.ic_replay_white);
- animateView(playPauseButton, true, 300, 0);
+ animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, true, 300);
}
});
}
@@ -382,19 +373,20 @@ public class MainVideoPlayer extends Activity {
//////////////////////////////////////////////////////////////////////////*/
@Override
- public void animateView(View view, boolean enterOrExit, long duration, long delay, final Runnable execOnEnd, boolean hideUi) {
- //if (execOnEnd == null) playerImpl.setDefaultAnimationEnd(hideUiRunnable);
-
- if (hideUi && execOnEnd != null) {
- Runnable combinedRunnable = new Runnable() {
- @Override
- public void run() {
- execOnEnd.run();
- hideUiRunnable.run();
- }
- };
- super.animateView(view, enterOrExit, duration, delay, combinedRunnable, true);
- } else super.animateView(view, enterOrExit, duration, delay, hideUi ? hideUiRunnable : execOnEnd, hideUi);
+ public void hideControls(final long duration, long delay) {
+ if (DEBUG) Log.d(TAG, "hideControls() called with: delay = [" + delay + "]");
+ getControlsVisibilityHandler().removeCallbacksAndMessages(null);
+ getControlsVisibilityHandler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ animateView(getControlsRoot(), false, duration, 0, new Runnable() {
+ @Override
+ public void run() {
+ hideSystemUi();
+ }
+ });
+ }
+ }, delay);
}
///////////////////////////////////////////////////////////////////////////
@@ -443,14 +435,9 @@ public class MainVideoPlayer extends Activity {
if (DEBUG) Log.d(TAG, "onSingleTapConfirmed() called with: e = [" + e + "]");
if (playerImpl.getCurrentState() != BasePlayer.STATE_PLAYING) return true;
- if (playerImpl.isControlsVisible()) playerImpl.animateView(playerImpl.getControlsRoot(), false, 150, 0, true);
+ if (playerImpl.isControlsVisible()) playerImpl.hideControls(150, 0);
else {
- playerImpl.animateView(playerImpl.getControlsRoot(), true, 500, 0, new Runnable() {
- @Override
- public void run() {
- playerImpl.animateView(playerImpl.getControlsRoot(), false, 300, VideoPlayer.DEFAULT_CONTROLS_HIDE_TIME, true);
- }
- });
+ playerImpl.showControlsThenHide();
showSystemUi();
}
return true;
@@ -500,7 +487,7 @@ public class MainVideoPlayer extends Activity {
if (DEBUG) Log.d(TAG, "onScroll().volumeControl, currentVolume = " + currentVolume);
playerImpl.getVolumeTextView().setText(volumeUnicode + " " + Math.round((((float) currentVolume) / maxVolume) * 100) + "%");
- if (playerImpl.getVolumeTextView().getVisibility() != View.VISIBLE) playerImpl.animateView(playerImpl.getVolumeTextView(), true, 200, 0);
+ if (playerImpl.getVolumeTextView().getVisibility() != View.VISIBLE) animateView(playerImpl.getVolumeTextView(), true, 200);
if (playerImpl.getBrightnessTextView().getVisibility() == View.VISIBLE) playerImpl.getBrightnessTextView().setVisibility(View.GONE);
} else {
WindowManager.LayoutParams lp = getWindow().getAttributes();
@@ -515,7 +502,7 @@ public class MainVideoPlayer extends Activity {
playerImpl.getBrightnessTextView().setText(brightnessUnicode + " " + (brightnessNormalized == 1 ? 0 : brightnessNormalized) + "%");
- if (playerImpl.getBrightnessTextView().getVisibility() != View.VISIBLE) playerImpl.animateView(playerImpl.getBrightnessTextView(), true, 200, 0);
+ if (playerImpl.getBrightnessTextView().getVisibility() != View.VISIBLE) animateView(playerImpl.getBrightnessTextView(), true, 200);
if (playerImpl.getVolumeTextView().getVisibility() == View.VISIBLE) playerImpl.getVolumeTextView().setVisibility(View.GONE);
}
return true;
@@ -527,11 +514,11 @@ public class MainVideoPlayer extends Activity {
eventsNum = 0;
/* if (playerImpl.getVolumeTextView().getVisibility() == View.VISIBLE) playerImpl.getVolumeTextView().setVisibility(View.GONE);
if (playerImpl.getBrightnessTextView().getVisibility() == View.VISIBLE) playerImpl.getBrightnessTextView().setVisibility(View.GONE);*/
- if (playerImpl.getVolumeTextView().getVisibility() == View.VISIBLE) playerImpl.animateView(playerImpl.getVolumeTextView(), false, 200, 200);
- if (playerImpl.getBrightnessTextView().getVisibility() == View.VISIBLE) playerImpl.animateView(playerImpl.getBrightnessTextView(), false, 200, 200);
+ if (playerImpl.getVolumeTextView().getVisibility() == View.VISIBLE) animateView(playerImpl.getVolumeTextView(), false, 200, 200);
+ if (playerImpl.getBrightnessTextView().getVisibility() == View.VISIBLE) animateView(playerImpl.getBrightnessTextView(), false, 200, 200);
if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == BasePlayer.STATE_PLAYING) {
- playerImpl.animateView(playerImpl.getControlsRoot(), false, 300, VideoPlayer.DEFAULT_CONTROLS_HIDE_TIME, true);
+ playerImpl.hideControls(300, VideoPlayer.DEFAULT_CONTROLS_HIDE_TIME);
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java
index ded6f34f9..0bc91509b 100644
--- a/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java
@@ -7,13 +7,14 @@ import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.SharedPreferences;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.PixelFormat;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
+import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -24,6 +25,8 @@ import android.view.View;
import android.view.WindowManager;
import android.widget.PopupMenu;
import android.widget.RemoteViews;
+import android.widget.SeekBar;
+import android.widget.TextView;
import android.widget.Toast;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
@@ -43,6 +46,8 @@ import org.schabi.newpipe.util.ThemeHelper;
import org.schabi.newpipe.util.Utils;
import org.schabi.newpipe.workers.StreamExtractorWorker;
+import static org.schabi.newpipe.util.AnimationUtils.animateView;
+
/**
* Service Popup Player implementing VideoPlayer
*
@@ -58,14 +63,19 @@ public class PopupVideoPlayer extends Service {
public static final String ACTION_OPEN_DETAIL = "org.schabi.newpipe.player.PopupVideoPlayer.OPEN_DETAIL";
public static final String ACTION_REPEAT = "org.schabi.newpipe.player.PopupVideoPlayer.REPEAT";
+ private static final String POPUP_SAVED_WIDTH = "popup_saved_width";
+ private static final String POPUP_SAVED_X = "popup_saved_x";
+ private static final String POPUP_SAVED_Y = "popup_saved_y";
+
private WindowManager windowManager;
private WindowManager.LayoutParams windowLayoutParams;
private GestureDetector gestureDetector;
private float screenWidth, screenHeight;
private float popupWidth, popupHeight;
- private float currentPopupHeight = 110.0f * Resources.getSystem().getDisplayMetrics().density;
- //private float minimumHeight = 100; // TODO: Use it when implementing the resize of the popup
+
+ private float minimumWidth, minimumHeight;
+ private float maximumWidth, maximumHeight;
private final String setAlphaMethodName = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) ? "setImageAlpha" : "setAlpha";
private NotificationManager notificationManager;
@@ -114,6 +124,8 @@ public class PopupVideoPlayer extends Service {
@Override
public void onConfigurationChanged(Configuration newConfig) {
updateScreenSize();
+ updatePopupSize(windowLayoutParams.width, -1);
+ checkPositionBounds();
}
@Override
@@ -129,6 +141,8 @@ public class PopupVideoPlayer extends Service {
currentExtractorWorker.cancel();
currentExtractorWorker = null;
}
+
+ savePositionAndSize();
}
@Override
@@ -144,21 +158,33 @@ public class PopupVideoPlayer extends Service {
private void initPopup() {
if (DEBUG) Log.d(TAG, "initPopup() called");
View rootView = View.inflate(this, R.layout.player_popup, null);
-
playerImpl.setup(rootView);
updateScreenSize();
+
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+ boolean popupRememberSizeAndPos = sharedPreferences.getBoolean(getString(R.string.popup_remember_size_pos_key), true);
+
+ float defaultSize = getResources().getDimension(R.dimen.popup_default_width);
+ popupWidth = popupRememberSizeAndPos ? sharedPreferences.getFloat(POPUP_SAVED_WIDTH, defaultSize) : defaultSize;
+
windowLayoutParams = new WindowManager.LayoutParams(
- (int) getMinimumVideoWidth(currentPopupHeight), (int) currentPopupHeight,
+ (int) popupWidth, (int) getMinimumVideoHeight(popupWidth),
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
-
windowLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
+ int centerX = (int) (screenWidth / 2f - popupWidth / 2f);
+ int centerY = (int) (screenHeight / 2f - popupHeight / 2f);
+ windowLayoutParams.x = popupRememberSizeAndPos ? sharedPreferences.getInt(POPUP_SAVED_X, centerX) : centerX;
+ windowLayoutParams.y = popupRememberSizeAndPos ? sharedPreferences.getInt(POPUP_SAVED_Y, centerY) : centerY;
+
+ checkPositionBounds();
+
MySimpleOnGestureListener listener = new MySimpleOnGestureListener();
gestureDetector = new GestureDetector(this, listener);
- gestureDetector.setIsLongpressEnabled(false);
+ //gestureDetector.setIsLongpressEnabled(false);
rootView.setOnTouchListener(listener);
playerImpl.getLoadingPanel().setMinimumWidth(windowLayoutParams.width);
playerImpl.getLoadingPanel().setMinimumHeight(windowLayoutParams.height);
@@ -219,13 +245,13 @@ public class PopupVideoPlayer extends Service {
notificationManager.notify(NOTIFICATION_ID, notBuilder.build());
}
-
/*//////////////////////////////////////////////////////////////////////////
// Misc
//////////////////////////////////////////////////////////////////////////*/
public void onVideoClose() {
if (DEBUG) Log.d(TAG, "onVideoClose() called");
+ savePositionAndSize();
stopSelf();
}
@@ -245,10 +271,23 @@ public class PopupVideoPlayer extends Service {
// Utils
//////////////////////////////////////////////////////////////////////////*/
- private float getMinimumVideoWidth(float height) {
- float width = height * (16.0f / 9.0f); // Respect the 16:9 ratio that most videos have
- if (DEBUG) Log.d(TAG, "getMinimumVideoWidth() called with: height = [" + height + "], returned: " + width);
- return width;
+ private void checkPositionBounds() {
+ if (windowLayoutParams.x > screenWidth - windowLayoutParams.width) windowLayoutParams.x = (int) (screenWidth - windowLayoutParams.width);
+ if (windowLayoutParams.x < 0) windowLayoutParams.x = 0;
+ if (windowLayoutParams.y > screenHeight - windowLayoutParams.height) windowLayoutParams.y = (int) (screenHeight - windowLayoutParams.height);
+ if (windowLayoutParams.y < 0) windowLayoutParams.y = 0;
+ }
+
+ private void savePositionAndSize() {
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(PopupVideoPlayer.this);
+ sharedPreferences.edit().putInt(POPUP_SAVED_X, windowLayoutParams.x).apply();
+ sharedPreferences.edit().putInt(POPUP_SAVED_Y, windowLayoutParams.y).apply();
+ sharedPreferences.edit().putFloat(POPUP_SAVED_WIDTH, windowLayoutParams.width).apply();
+ }
+
+ private float getMinimumVideoHeight(float width) {
+ //if (DEBUG) Log.d(TAG, "getMinimumVideoHeight() called with: width = [" + width + "], returned: " + height);
+ return width / (16.0f / 9.0f); // Respect the 16:9 ratio that most videos have
}
private void updateScreenSize() {
@@ -258,11 +297,39 @@ public class PopupVideoPlayer extends Service {
screenWidth = metrics.widthPixels;
screenHeight = metrics.heightPixels;
if (DEBUG) Log.d(TAG, "updateScreenSize() called > screenWidth = " + screenWidth + ", screenHeight = " + screenHeight);
+
+ popupWidth = getResources().getDimension(R.dimen.popup_default_width);
+ popupHeight = getMinimumVideoHeight(popupWidth);
+
+ minimumWidth = getResources().getDimension(R.dimen.popup_minimum_width);
+ minimumHeight = getMinimumVideoHeight(minimumWidth);
+
+ maximumWidth = screenWidth;
+ maximumHeight = screenHeight;
+ }
+
+ private void updatePopupSize(int width, int height) {
+ //if (DEBUG) Log.d(TAG, "updatePopupSize() called with: width = [" + width + "], height = [" + height + "]");
+
+ width = (int) (width > maximumWidth ? maximumWidth : width < minimumWidth ? minimumWidth : width);
+
+ if (height == -1) height = (int) getMinimumVideoHeight(width);
+ else height = (int) (height > maximumHeight ? maximumHeight : height < minimumHeight ? minimumHeight : height);
+
+ windowLayoutParams.width = width;
+ windowLayoutParams.height = height;
+ popupWidth = width;
+ popupHeight = height;
+
+ if (DEBUG) Log.d(TAG, "updatePopupSize() updated values: width = [" + width + "], height = [" + height + "]");
+ windowManager.updateViewLayout(playerImpl.getRootView(), windowLayoutParams);
}
///////////////////////////////////////////////////////////////////////////
private class VideoPlayerImpl extends VideoPlayer {
+ private TextView resizingIndicator;
+
VideoPlayerImpl() {
super("VideoPlayerImpl" + PopupVideoPlayer.TAG, PopupVideoPlayer.this);
}
@@ -271,13 +338,20 @@ public class PopupVideoPlayer extends Service {
public void playUrl(String url, String format, boolean autoPlay) {
super.playUrl(url, format, autoPlay);
- windowLayoutParams.width = (int) getMinimumVideoWidth(currentPopupHeight);
+ windowLayoutParams.width = (int) popupWidth;
+ windowLayoutParams.height = (int) getMinimumVideoHeight(popupWidth);
windowManager.updateViewLayout(getRootView(), windowLayoutParams);
notBuilder = createNotification();
startForeground(NOTIFICATION_ID, notBuilder.build());
}
+ @Override
+ public void initViews(View rootView) {
+ super.initViews(rootView);
+ resizingIndicator = (TextView) rootView.findViewById(R.id.resizing_indicator);
+ }
+
@Override
public void destroy() {
super.destroy();
@@ -337,7 +411,7 @@ public class PopupVideoPlayer extends Service {
@Override
public void onDismiss(PopupMenu menu) {
super.onDismiss(menu);
- if (isPlaying()) animateView(getControlsRoot(), false, 500, 0);
+ if (isPlaying()) hideControls(500, 0);
}
@Override
@@ -347,7 +421,14 @@ public class PopupVideoPlayer extends Service {
stopSelf();
}
- /*//////////////////////////////////////////////////////////////////////////
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ super.onStopTrackingTouch(seekBar);
+ if (playerImpl.wasPlaying()) {
+ hideControls(100, 0);
+ }
+ }
+/*//////////////////////////////////////////////////////////////////////////
// Broadcast Receiver
//////////////////////////////////////////////////////////////////////////*/
@@ -422,12 +503,21 @@ public class PopupVideoPlayer extends Service {
showAndAnimateControl(R.drawable.ic_replay_white, false);
}
+
+ @SuppressWarnings("WeakerAccess")
+ public TextView getResizingIndicator() {
+ return resizingIndicator;
+ }
}
private class MySimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener {
private int initialPopupX, initialPopupY;
private boolean isMoving;
+ private int onDownPopupWidth = 0;
+ private boolean isResizing;
+ private boolean isResizingRightSide;
+
@Override
public boolean onDoubleTap(MotionEvent e) {
if (DEBUG) Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY());
@@ -450,15 +540,34 @@ public class PopupVideoPlayer extends Service {
if (DEBUG) Log.d(TAG, "onDown() called with: e = [" + e + "]");
initialPopupX = windowLayoutParams.x;
initialPopupY = windowLayoutParams.y;
- popupWidth = playerImpl.getRootView().getWidth();
- popupHeight = playerImpl.getRootView().getHeight();
+ popupWidth = windowLayoutParams.width;
+ popupHeight = windowLayoutParams.height;
+ onDownPopupWidth = windowLayoutParams.width;
return false;
}
+ @Override
+ public void onLongPress(MotionEvent e) {
+ if (DEBUG) Log.d(TAG, "onLongPress() called with: e = [" + e + "]");
+ playerImpl.showAndAnimateControl(-1, true);
+ playerImpl.getLoadingPanel().setVisibility(View.GONE);
+
+ playerImpl.hideControls(0, 0);
+ animateView(playerImpl.getCurrentDisplaySeek(), false, 0, 0);
+ animateView(playerImpl.getResizingIndicator(), true, 200, 0);
+
+ isResizing = true;
+ isResizingRightSide = e.getRawX() > windowLayoutParams.x + (windowLayoutParams.width / 2f);
+ }
+
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
- if (!isMoving || playerImpl.getControlsRoot().getAlpha() != 1f) playerImpl.animateView(playerImpl.getControlsRoot(), true, 30, 0);
+ if (isResizing) return false;
+
+ if (playerImpl.getCurrentState() != BasePlayer.STATE_BUFFERING
+ && (!isMoving || playerImpl.getControlsRoot().getAlpha() != 1f)) playerImpl.showControls(0);
isMoving = true;
+
float diffX = (int) (e2.getRawX() - e1.getRawX()), posX = (int) (initialPopupX + diffX);
float diffY = (int) (e2.getRawY() - e1.getRawY()), posY = (int) (initialPopupY + diffY);
@@ -477,7 +586,7 @@ public class PopupVideoPlayer extends Service {
", e2.getRaw = [" + e2.getRawX() + ", " + e2.getRawY() + "]" +
", distanceXy = [" + distanceX + ", " + distanceY + "]" +
", posXy = [" + posX + ", " + posY + "]" +
- ", popupWh rootView.get wh = [" + popupWidth + " x " + popupHeight + "]");
+ ", popupWh = [" + popupWidth + " x " + popupHeight + "]");
windowManager.updateViewLayout(playerImpl.getRootView(), windowLayoutParams);
return true;
}
@@ -485,16 +594,38 @@ public class PopupVideoPlayer extends Service {
private void onScrollEnd() {
if (DEBUG) Log.d(TAG, "onScrollEnd() called");
if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == BasePlayer.STATE_PLAYING) {
- playerImpl.animateView(playerImpl.getControlsRoot(), false, 300, VideoPlayer.DEFAULT_CONTROLS_HIDE_TIME);
+ playerImpl.hideControls(300, VideoPlayer.DEFAULT_CONTROLS_HIDE_TIME);
}
}
@Override
public boolean onTouch(View v, MotionEvent event) {
gestureDetector.onTouchEvent(event);
- if (event.getAction() == MotionEvent.ACTION_UP && isMoving) {
- isMoving = false;
- onScrollEnd();
+ if (event.getAction() == MotionEvent.ACTION_MOVE && isResizing && !isMoving) {
+ //if (DEBUG) Log.d(TAG, "onTouch() ACTION_MOVE > v = [" + v + "], e1.getRaw = [" + event.getRawX() + ", " + event.getRawY() + "]");
+ int width;
+ if (isResizingRightSide) width = (int) event.getRawX() - windowLayoutParams.x;
+ else {
+ width = (int) (windowLayoutParams.width + (windowLayoutParams.x - event.getRawX()));
+ if (width > minimumWidth) windowLayoutParams.x = initialPopupX - (width - onDownPopupWidth);
+ }
+ if (width <= maximumWidth && width >= minimumWidth) updatePopupSize(width, -1);
+ return true;
+ }
+
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ if (DEBUG) Log.d(TAG, "onTouch() ACTION_UP > v = [" + v + "], e1.getRaw = [" + event.getRawX() + ", " + event.getRawY() + "]");
+ if (isMoving) {
+ isMoving = false;
+ onScrollEnd();
+ }
+
+ if (isResizing) {
+ isResizing = false;
+ animateView(playerImpl.getResizingIndicator(), false, 100, 0);
+ playerImpl.changeState(playerImpl.getCurrentState());
+ }
+ savePositionAndSize();
}
return true;
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java
index d64e60747..84f2192f9 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java
@@ -12,6 +12,7 @@ import android.graphics.Color;
import android.graphics.PorterDuff;
import android.net.Uri;
import android.os.Build;
+import android.os.Handler;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.view.Menu;
@@ -36,12 +37,15 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.stream_info.AudioStream;
import org.schabi.newpipe.extractor.stream_info.VideoStream;
+import org.schabi.newpipe.util.AnimationUtils;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
+import static org.schabi.newpipe.util.AnimationUtils.animateView;
+
/**
* Base for video players
*
@@ -101,6 +105,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
private ImageButton fullScreenButton;
private ValueAnimator controlViewAnimator;
+ private Handler controlsVisibilityHandler = new Handler();
private boolean isQualityPopupMenuVisible = false;
private boolean qualityChanged = false;
@@ -235,6 +240,9 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
if (!isProgressLoopRunning.get()) startProgressLoop();
+ controlsVisibilityHandler.removeCallbacksAndMessages(null);
+ animateView(controlsRoot, false, 300);
+
showAndAnimateControl(-1, true);
playbackSeekBar.setEnabled(true);
playbackSeekBar.setProgress(0);
@@ -242,10 +250,10 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
// Bug on lower api, disabling and enabling the seekBar resets the thumb color -.-, so sets the color again
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) playbackSeekBar.getThumb().setColorFilter(Color.RED, PorterDuff.Mode.SRC_IN);
- animateView(endScreen, false, 0, 0);
+ animateView(endScreen, false, 0);
loadingPanel.setBackgroundColor(Color.BLACK);
- animateView(loadingPanel, true, 0, 0);
- animateView(surfaceForeground, true, 100, 0);
+ animateView(loadingPanel, true, 0);
+ animateView(surfaceForeground, true, 100);
}
@Override
@@ -254,26 +262,21 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
if (!isProgressLoopRunning.get()) startProgressLoop();
showAndAnimateControl(-1, true);
loadingPanel.setVisibility(View.GONE);
- animateView(controlsRoot, true, 500, 0, new Runnable() {
- @Override
- public void run() {
- animateView(controlsRoot, false, 500, DEFAULT_CONTROLS_HIDE_TIME, true);
- }
- });
- animateView(currentDisplaySeek, false, 200, 0);
+ showControlsThenHide();
+ animateView(currentDisplaySeek, AnimationUtils.Type.SCALE_AND_ALPHA, false, 200);
}
@Override
public void onBuffering() {
if (DEBUG) Log.d(TAG, "onBuffering() called");
loadingPanel.setBackgroundColor(Color.TRANSPARENT);
- animateView(loadingPanel, true, 500, 0);
+ animateView(loadingPanel, true, 500);
}
@Override
public void onPaused() {
if (DEBUG) Log.d(TAG, "onPaused() called");
- animateView(controlsRoot, true, 500, 100);
+ showControls(400);
loadingPanel.setVisibility(View.GONE);
}
@@ -289,9 +292,9 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
if (isProgressLoopRunning.get()) stopProgressLoop();
- animateView(controlsRoot, true, 500, 0);
- animateView(endScreen, true, 800, 0);
- animateView(currentDisplaySeek, false, 200, 0);
+ showControls(500);
+ animateView(endScreen, true, 800);
+ animateView(currentDisplaySeek, AnimationUtils.Type.SCALE_AND_ALPHA, false, 200);
loadingPanel.setVisibility(View.GONE);
playbackSeekBar.setMax((int) simpleExoPlayer.getDuration());
@@ -302,7 +305,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
// Bug on lower api, disabling and enabling the seekBar resets the thumb color -.-, so sets the color again
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) playbackSeekBar.getThumb().setColorFilter(Color.RED, PorterDuff.Mode.SRC_IN);
- animateView(surfaceForeground, true, 100, 0);
+ animateView(surfaceForeground, true, 100);
if (currentRepeatMode == RepeatMode.REPEAT_ONE) {
changeState(STATE_LOADING);
@@ -324,7 +327,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
@Override
public void onRenderedFirstFrame() {
- animateView(surfaceForeground, false, 100, 0);
+ animateView(surfaceForeground, false, 100);
}
/*//////////////////////////////////////////////////////////////////////////
@@ -443,7 +446,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
if (DEBUG) Log.d(TAG, "onQualitySelectorClicked() called");
qualityPopupMenu.show();
isQualityPopupMenuVisible = true;
- animateView(getControlsRoot(), true, 300, 0);
+ showControls(300);
VideoStream videoStream = getSelectedVideoStream();
qualityTextView.setText(MediaFormat.getNameById(videoStream.format) + " " + videoStream.resolution);
@@ -469,8 +472,8 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
wasPlaying = isPlaying();
if (isPlaying()) simpleExoPlayer.setPlayWhenReady(false);
- animateView(controlsRoot, true, 0, 0);
- animateView(currentDisplaySeek, true, 300, 0);
+ showControls(0);
+ animateView(currentDisplaySeek, AnimationUtils.Type.SCALE_AND_ALPHA, true, 300);
}
@Override
@@ -481,7 +484,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
if (wasPlaying || simpleExoPlayer.getDuration() == seekBar.getProgress()) simpleExoPlayer.setPlayWhenReady(true);
playbackCurrentTime.setText(getTimeString(seekBar.getProgress()));
- animateView(currentDisplaySeek, false, 200, 0);
+ animateView(currentDisplaySeek, AnimationUtils.Type.SCALE_AND_ALPHA, false, 200);
if (getCurrentState() == STATE_PAUSED_SEEK) changeState(STATE_BUFFERING);
if (!isProgressLoopRunning.get()) startProgressLoop();
@@ -550,107 +553,37 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
controlViewAnimator.start();
}
- public void animateView(View view, boolean enterOrExit, long duration, long delay) {
- animateView(view, enterOrExit, duration, delay, null, false);
- }
-
- public void animateView(View view, boolean enterOrExit, long duration, long delay, boolean hideUi) {
- animateView(view, enterOrExit, duration, delay, null, hideUi);
- }
-
- public void animateView(final View view, final boolean enterOrExit, long duration, long delay, final Runnable execOnEnd) {
- animateView(view, enterOrExit, duration, delay, execOnEnd, false);
- }
-
- /**
- * Animate the view
- *
- * @param view view that will be animated
- * @param enterOrExit true to enter, false to exit
- * @param duration how long the animation will take, in milliseconds
- * @param delay how long the animation will wait to start, in milliseconds
- * @param execOnEnd runnable that will be executed when the animation ends
- * @param hideUi need to hide ui when animation ends,
- * just a helper for classes extending this
- */
- public void animateView(final View view, final boolean enterOrExit, long duration, long delay, final Runnable execOnEnd, boolean hideUi) {
- if (DEBUG) {
- Log.d(TAG, "animateView() called with: view = [" + view + "], enterOrExit = [" + enterOrExit + "], duration = [" + duration + "], delay = [" + delay + "], execOnEnd = [" + execOnEnd + "]");
- }
- if (view.getVisibility() == View.VISIBLE && enterOrExit) {
- if (DEBUG) Log.d(TAG, "animateView() view was already visible > view = [" + view + "]");
- view.animate().setListener(null).cancel();
- view.setVisibility(View.VISIBLE);
- view.setAlpha(1f);
- if (execOnEnd != null) execOnEnd.run();
- return;
- } else if ((view.getVisibility() == View.GONE || view.getVisibility() == View.INVISIBLE) && !enterOrExit) {
- if (DEBUG) Log.d(TAG, "animateView() view was already gone > view = [" + view + "]");
- view.animate().setListener(null).cancel();
- view.setVisibility(View.GONE);
- view.setAlpha(0f);
- if (execOnEnd != null) execOnEnd.run();
- return;
- }
-
- view.animate().setListener(null).cancel();
- view.setVisibility(View.VISIBLE);
-
- if (view == controlsRoot) {
- if (enterOrExit) {
- view.animate().alpha(1f).setDuration(duration).setStartDelay(delay)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (execOnEnd != null) execOnEnd.run();
- }
- }).start();
- } else {
- view.animate().alpha(0f)
- .setDuration(duration).setStartDelay(delay)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- view.setVisibility(View.GONE);
- if (execOnEnd != null) execOnEnd.run();
- }
- })
- .start();
- }
- return;
- }
-
- if (enterOrExit) {
- view.setAlpha(0f);
- view.setScaleX(.8f);
- view.setScaleY(.8f);
- view.animate().alpha(1f).scaleX(1f).scaleY(1f).setDuration(duration).setStartDelay(delay)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (execOnEnd != null) execOnEnd.run();
- }
- }).start();
- } else {
- view.setAlpha(1f);
- view.setScaleX(1f);
- view.setScaleY(1f);
- view.animate().alpha(0f).scaleX(.8f).scaleY(.8f).setDuration(duration).setStartDelay(delay)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- view.setVisibility(View.GONE);
- if (execOnEnd != null) execOnEnd.run();
- }
- })
- .start();
- }
- }
-
public boolean isQualityMenuVisible() {
return isQualityPopupMenuVisible;
}
+ public void showControlsThenHide() {
+ if (DEBUG) Log.d(TAG, "showControlsThenHide() called");
+ animateView(controlsRoot, true, 300, 0, new Runnable() {
+ @Override
+ public void run() {
+ hideControls(300, DEFAULT_CONTROLS_HIDE_TIME);
+ }
+ });
+ }
+
+ public void showControls(long duration) {
+ if (DEBUG) Log.d(TAG, "showControls() called");
+ controlsVisibilityHandler.removeCallbacksAndMessages(null);
+ animateView(controlsRoot, true, duration);
+ }
+
+ public void hideControls(final long duration, long delay) {
+ if (DEBUG) Log.d(TAG, "hideControls() called with: delay = [" + delay + "]");
+ controlsVisibilityHandler.removeCallbacksAndMessages(null);
+ controlsVisibilityHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ animateView(controlsRoot, false, duration);
+ }
+ }, delay);
+ }
+
/*//////////////////////////////////////////////////////////////////////////
// Getters and Setters
//////////////////////////////////////////////////////////////////////////*/
@@ -711,6 +644,10 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
this.startedFromNewPipe = startedFromNewPipe;
}
+ public Handler getControlsVisibilityHandler() {
+ return controlsVisibilityHandler;
+ }
+
public View getRootView() {
return rootView;
}
diff --git a/app/src/main/java/org/schabi/newpipe/util/AnimationUtils.java b/app/src/main/java/org/schabi/newpipe/util/AnimationUtils.java
new file mode 100644
index 000000000..c37eaa560
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/util/AnimationUtils.java
@@ -0,0 +1,126 @@
+package org.schabi.newpipe.util;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.util.Log;
+import android.view.View;
+
+import org.schabi.newpipe.player.BasePlayer;
+
+public class AnimationUtils {
+ private static final String TAG = "AnimationUtils";
+ private static final boolean DEBUG = BasePlayer.DEBUG;
+
+ public enum Type {
+ ALPHA, SCALE_AND_ALPHA
+ }
+
+ public static void animateView(View view, boolean enterOrExit, long duration) {
+ animateView(view, Type.ALPHA, enterOrExit, duration, 0, null);
+ }
+
+ public static void animateView(View view, boolean enterOrExit, long duration, long delay) {
+ animateView(view, Type.ALPHA, enterOrExit, duration, delay, null);
+ }
+
+ public static void animateView(View view, boolean enterOrExit, long duration, long delay, Runnable execOnEnd) {
+ animateView(view, Type.ALPHA, enterOrExit, duration, delay, execOnEnd);
+ }
+
+ public static void animateView(View view, Type animationType, boolean enterOrExit, long duration) {
+ animateView(view, animationType, enterOrExit, duration, 0, null);
+ }
+
+ public static void animateView(View view, Type animationType, boolean enterOrExit, long duration, long delay) {
+ animateView(view, animationType, enterOrExit, duration, delay, null);
+ }
+
+ /**
+ * Animate the view
+ *
+ * @param view view that will be animated
+ * @param animationType {@link Type} of the animation
+ * @param enterOrExit true to enter, false to exit
+ * @param duration how long the animation will take, in milliseconds
+ * @param delay how long the animation will wait to start, in milliseconds
+ * @param execOnEnd runnable that will be executed when the animation ends
+ */
+ public static void animateView(final View view, Type animationType, boolean enterOrExit, long duration, long delay, Runnable execOnEnd) {
+ if (DEBUG) {
+ Log.d(TAG, "animateView() called with: view = [" + view + "], animationType = [" + animationType + "], enterOrExit = [" + enterOrExit + "], duration = [" + duration + "], delay = [" + delay + "], execOnEnd = [" + execOnEnd + "]");
+ }
+
+ if (view.getVisibility() == View.VISIBLE && enterOrExit) {
+ if (DEBUG) Log.d(TAG, "animateView() view was already visible > view = [" + view + "]");
+ view.animate().setListener(null).cancel();
+ view.setVisibility(View.VISIBLE);
+ view.setAlpha(1f);
+ if (execOnEnd != null) execOnEnd.run();
+ return;
+ } else if ((view.getVisibility() == View.GONE || view.getVisibility() == View.INVISIBLE) && !enterOrExit) {
+ if (DEBUG) Log.d(TAG, "animateView() view was already gone > view = [" + view + "]");
+ view.animate().setListener(null).cancel();
+ view.setVisibility(View.GONE);
+ view.setAlpha(0f);
+ if (execOnEnd != null) execOnEnd.run();
+ return;
+ }
+
+ view.animate().setListener(null).cancel();
+ view.setVisibility(View.VISIBLE);
+
+ switch (animationType) {
+ case ALPHA:
+ animateAlpha(view, enterOrExit, duration, delay, execOnEnd);
+ break;
+ case SCALE_AND_ALPHA:
+ animateScaleAndAlpha(view, enterOrExit, duration, delay, execOnEnd);
+ break;
+ }
+ }
+
+ private static void animateScaleAndAlpha(final View view, boolean enterOrExit, long duration, long delay, final Runnable execOnEnd) {
+ if (enterOrExit) {
+ view.setAlpha(0f);
+ view.setScaleX(.8f);
+ view.setScaleY(.8f);
+ view.animate().alpha(1f).scaleX(1f).scaleY(1f).setDuration(duration).setStartDelay(delay).setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (execOnEnd != null) execOnEnd.run();
+ }
+ }).start();
+ } else {
+ view.setAlpha(1f);
+ view.setScaleX(1f);
+ view.setScaleY(1f);
+ view.animate().alpha(0f).scaleX(.8f).scaleY(.8f).setDuration(duration).setStartDelay(delay).setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.setVisibility(View.GONE);
+ if (execOnEnd != null) execOnEnd.run();
+ }
+ }).start();
+ }
+ }
+
+
+ private static void animateAlpha(final View view, boolean enterOrExit, long duration, long delay, final Runnable execOnEnd) {
+ if (enterOrExit) {
+ view.animate().alpha(1f).setDuration(duration).setStartDelay(delay).setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (execOnEnd != null) execOnEnd.run();
+ }
+ }).start();
+ } else {
+ view.animate().alpha(0f).setDuration(duration).setStartDelay(delay).setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.setVisibility(View.GONE);
+ if (execOnEnd != null) execOnEnd.run();
+ }
+ }).start();
+ }
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/Localization.java b/app/src/main/java/org/schabi/newpipe/util/Localization.java
similarity index 98%
rename from app/src/main/java/org/schabi/newpipe/Localization.java
rename to app/src/main/java/org/schabi/newpipe/util/Localization.java
index 2fa461129..f1eb50c6a 100644
--- a/app/src/main/java/org/schabi/newpipe/Localization.java
+++ b/app/src/main/java/org/schabi/newpipe/util/Localization.java
@@ -1,10 +1,12 @@
-package org.schabi.newpipe;
+package org.schabi.newpipe.util;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.preference.PreferenceManager;
+import org.schabi.newpipe.R;
+
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.ParseException;
diff --git a/app/src/main/res/layout/player_popup.xml b/app/src/main/res/layout/player_popup.xml
index a3be1511c..9ff3db76a 100644
--- a/app/src/main/res/layout/player_popup.xml
+++ b/app/src/main/res/layout/player_popup.xml
@@ -5,7 +5,9 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
- android:gravity="center">
+ android:gravity="center"
+ tools:layout_width="@dimen/popup_default_width"
+ tools:layout_height="101.25dp">
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-bn-rBD/strings.xml b/app/src/main/res/values-bn-rBD/strings.xml
new file mode 100644
index 000000000..587b12ce4
--- /dev/null
+++ b/app/src/main/res/values-bn-rBD/strings.xml
@@ -0,0 +1,227 @@
+
+
+
+ শুরু করতে অনুসন্ধান এ আলতো চাপ
+ NewPipe ব্যাকগ্রাউন্ড প্লেয়ার
+
+ %1$s বার দেখা হয়েছে
+ প্রকাশকাল %1$s
+ কোন স্ট্রিম প্লেয়ার পাওয়া যায়নি। তুমি কি VLC ইনস্টল করতে চাও?
+ ইনস্টল
+ বাদ দাও
+
+ ব্রাউজারে খোলো
+ পপআপ মোডে খোলো
+ শেয়ার
+ লোড হচ্ছে
+ ডাউনলোড
+ খোঁজ
+ সেটিং
+ তুমি কি বলতে চাচ্ছ %1$s ?
+ "পেজ খোঁজ : "
+ শেয়ার কর
+ ব্রাউজার পছন্দ কর
+ রোটেশন
+ সেটিং
+ বাহ্যিক ভিডিও প্লেয়ার ব্যবহার করো
+ বাহ্যিক অডিও প্লেয়ার ব্যবহার করো
+ NewPipe পপআপ মোড
+
+ ব্যাকগ্রাউন্ড
+ পপআপ
+
+ Video download path
+ ডাউনলোড করা ভিডিও সঞ্চয় পাথ করার পাথ
+ ভিডিওগুলির জন্য ডাউনলোডের পাথ প্রবেশ করাও
+
+ অডিও ডাউনলোড পাথ
+ ডাউনলোড করা অডিও সঞ্চয় পাথ করার পাথ
+ অডিও ফাইলগুলির জন্য ডাউনলোডের পাথ প্রবেশ করাও
+
+ স্বয়ংক্রিয়ভাবে প্লে করুন যখন অন্য অ্যাপ্লিকেশন থেকে চালু করা হয়
+ স্বয়ংক্রিয়ভাবে একটি ভিডিও প্লে করো যখন NewPipe অন্য অ্যাপ্লিকেশন থেকে চালু করা হয়।
+ ডিফল্ট রেজল্যুশন
+ ডিফল্ট পপআপ রেজল্যুশন
+ উচ্চ রেজুলেশন দেখাও
+ শুধুমাত্র কিছু ডিভাইস 2k / 4k ভিডিও চালানোয় সমর্থন
+ Kodi এর মাধ্যমে চালাও
+ Kore অ্যাপ্লিকেশন খুঁজে পাওয়া যায়নি। Kore ইনস্টল করবে?
+
+ দেখাও \"Kodi এর মাধ্যমে চালাও \" বিকল্প
+ Kodi মিডিয়া সেন্টারে এর মাধ্যমে একটি ভিডিও প্লে করার জন্য একটি বিকল্প প্রদর্শন কর
+ অডিও
+ ডিফল্ট অডিও ফরম্যাট
+ পছন্দের ভিডিও ফরম্যাট
+ WebM — বিনামূল্য/স্বাধীন ফরম্যাট
+ m4a — ভালো মানের
+ থিম
+ উজ্জ্বল
+ অন্ধকার
+ কালো
+ পপআপ আকার এবং অবস্থান মনে রাখো
+ শেষ আকার এবং পপআপ সেট অবস্থান মনে রাখো
+
+ ডাউনলোড
+
+ - ভিডিও
+ - অডিও
+
+
+
+ - ভিডিও
+
+
+
+ - অডিও
+
+
+
+
+ পরবর্তী ভিডিওগুলি
+ পরবর্তী এবং অনুরূপ ভিডিওগুলি দেখাও
+ URL সমর্থিত নয়
+ অনুরূপ ভিডিওগুলি
+ Preferred content language
+ ভিডিও এবং অডিও
+ পপআপ
+ এপিয়ারেন্স
+ অন্যান্য
+
+ ব্যাকগ্রাউন্ডে বাজাও
+ পপআপ মোডে চালাও
+
+ চালাও
+ কনটেন্ট
+ বয়স সীমাবদ্ধ কনটেন্ট দেখাও
+ ভিডিওটিকে বয়স সীমিত করা হয়েছে। প্রথমে সেটিংসে বয়স সীমাবদ্ধ ভিডিওগুলি সক্ষম করুন।
+ লাইভ
+ ডাউনলোড
+ ডাউনলোড
+ সেটিং
+ ত্রুটির তথ্য প্রতিবেদন
+ সবগুলি
+ চ্যানেল
+ হ্যা
+ পরবর্তী সময়ে
+ নিস্ক্রীয়
+ Filter
+ রিফ্রেশ
+ পরিষ্কার
+ আকার পরিবর্তন
+
+
+ ত্রুটি
+ নেটওয়ার্ক ত্রুটি
+ সব থাম্বনেইল লোড করা যায়নি
+ ভিডিও URL স্বাক্ষর ডিক্রিপ্ট করা যায়নি।
+ ওয়েবসাইট বিশ্লেষন করা যায়নি।
+ ওয়েবসাইট সম্পুর্নভাবে বিশ্লেষন করা যায়নি।
+ কনটেন্ট উপলব্ধ নয়
+ GEMA কর্তৃক ব্লক করা হয়েছে
+ ডাউনলোড মেনু সেটআপ করা যায়নি
+ এটি একটি লাইভ স্ট্রিম। যা এখনও সমর্থিত নয়।
+ কোনও স্ট্রিম পাওয়া যায়নি।
+ চিত্র লোড করা যায়নি
+ অ্যাপ / UI ক্র্যাশ করেছে
+
+ দুঃখিত এটা ঘটা উচিত ছিল না
+
+ মেলের মাধ্যমে ত্রুটি প্রতিবেদন করুন
+ দুঃখিত কিছু ত্রুটি ঘটেছে
+ প্রতিবেদন
+ তথ্য:
+ কি হয়েছিল:
+
+ অনুসন্ধান:
+ অনুরোধ করা স্ট্রিম:
+ তোমার মন্তব্য (ইংরেজিতে):
+ বর্ণনা:
+
+
+
+ ভিডিও প্রাকদর্শন থাম্বনেইল
+ ভিডিও প্রাকদর্শন থাম্বনেইল
+ আপলোডারের ইউজারপিক থাম্বনেইল
+ পছন্দ
+ অপছন্দ
+ টর ব্যবহার করো
+ (পরীক্ষামূলক) গোপনীয়তা বর্ধিত করতে টর এর মাধ্যমে ডাউনলোড ট্রাফিক জোরপুর্বক পাঠাও (ভিডিওগুলি স্ট্রিমিং সমর্থিত নয়)
+ একটি ত্রুটি রিপোর্ট করো
+ ব্যবহারকারীর প্রতিবেদন
+
+ \'%1$s\' ডাউনলোড ডিরেক্টরি তৈরি করতে পারছে না
+ \'%1$s\' ডাউনলোড ডিরেক্টরি তৈরি করা হয়েছে
+
+ ব্যাকগ্রাউন্ড এ চালাও
+ ভিডিও
+ অডিও
+ টেক্সট
+ লগিং
+ সাধারণ
+ বাগাড়ম্বরপূর্ণ
+ পুনরায় চেষ্টা করো
+ [বন্ধ]
+ সুরক্ষিত কনটেন্ট 18 বছরের নিচে API মাত্রাগুলি সমর্থিত নয়
+ এই ডিভাইসটি প্রয়োজনীয় DRM স্কিমের সমর্থন করে না
+ একটি অজানা DRM ত্রূটি ঘটেছে
+ এই ডিভাইসটি %1$s এর জন্য একটি ডিকোডার প্রদান করে না
+ এই ডিভাইসটি %1$s এর জন্য একটি নিরাপদ ডিকোডার প্রদান করে না
+ ডিভাইস ডিকোডার জিজ্ঞাসা করতে অক্ষমs
+ ডিকোডার চালু করতে অক্ষম %1$s
+ স্টোরেজ অ্যাক্সেস করার অনুমতি অস্বীকার করা হয়েছে
+ পুরানো প্লেয়ার ব্যবহার করো
+ মিডিয়াফ্রেমওয়ার্ক প্লেয়ারের পুরানো বিল্ড।
+ ভিডিওগুলি
+ গ্রাহক
+ গ্রাহকরা
+ সাবস্ক্রাইব
+ প্রদর্শন
+ K
+ M
+ B
+
+
+ শুরু
+ বিরতি
+ প্রদর্শন
+ ডিলেট
+ চেকসাম
+
+
+ নতুন মিশন
+ ঠিক আছে
+ তালিকা এবং গ্রিডের মধ্যে পরিবর্তন করো
+
+
+
+ ডাউনলোড URL
+ ফাইলের নাম
+ থ্রেড
+ ফাইলের নাম আনো
+ ভুল বার্তা
+ সার্ভার অসমর্থিত
+ ফাইল ইতিমধ্যে বিদ্যমান
+ বিকৃত URL অথবা ইন্টারনেট নেই
+ NewPipe ডাউনলোড হচ্ছে
+ বিস্তারিত জানার জন্য আলতো চাপ
+ অনুগ্রহপূর্বক অপেক্ষা করো…
+ ক্লিপবোর্ডে অনুলিপি করা হয়েছে
+ অনুগ্রহ করে একটি ডাউনলোড ডিরেক্টরি নির্বাচন করো
+ এই অনুমতিটি পপআপ মোডে খুলতে প্রয়োজন
+
+
+
+
+ চ্যানেলের ক্রিয়াকলাপ
+ সেটিং
+ reCAPTCHA
+ reCAPTCHA চ্যালেঞ্জ
+ reCAPTCHA চ্যালেঞ্জ অনুরোধ করা হয়েছে
+
+
+
+
diff --git a/app/src/main/res/values-sw600dp/dimens.xml b/app/src/main/res/values-sw600dp/dimens.xml
index ed40aa04b..8ab553bb6 100644
--- a/app/src/main/res/values-sw600dp/dimens.xml
+++ b/app/src/main/res/values-sw600dp/dimens.xml
@@ -1,6 +1,7 @@
-
+ 230dp
+ 140dp
18sp
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index b39348290..5fe9e47aa 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -17,7 +17,8 @@
5sp
2sp
-
+ 180dp
+ 120dp
16sp
diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml
index 0d2cbbf50..34898377b 100644
--- a/app/src/main/res/values/settings_keys.xml
+++ b/app/src/main/res/values/settings_keys.xml
@@ -2,6 +2,7 @@
settings_category_video_audio
+ settings_category_popup
settings_category_appearance
settings_content_options
settings_category_other
@@ -27,6 +28,8 @@
- 144p
+ popup_remember_size_pos_key
+
default_popup_resolution_key
480p
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index bcfa65ed2..e518b7811 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -59,6 +59,8 @@
Light
Dark
Black
+ Remember popup size and position
+ Remember the last size and position set to the popup
Download
@@ -86,6 +88,7 @@
Similar videos
Preferred content language
Video & Audio
+ Popup
Appearance
Other
%1$s - NewPipe
@@ -109,6 +112,7 @@
Filter
Refresh
Clear
+ Resizing
Error
diff --git a/app/src/main/res/xml/settings.xml b/app/src/main/res/xml/settings.xml
index 55b0c8196..9e76ff57a 100644
--- a/app/src/main/res/xml/settings.xml
+++ b/app/src/main/res/xml/settings.xml
@@ -27,14 +27,6 @@
android:summary="%s"
android:defaultValue="@string/default_resolution_value"/>
-
-
+
+
+
+
+
+
+
+
+