= 26 /*Oreo*/) updateNotificationThumbnail();
if (bigNotRemoteView != null) {
bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, duration, currentProgress, false);
bigNotRemoteView.setTextViewText(R.id.notificationTime, getTimeString(currentProgress) + " / " + getTimeString(duration));
diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
index 7339dd50f..d87df3666 100644
--- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
@@ -51,6 +51,7 @@ import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
+import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.Downloader;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.stream.StreamInfo;
@@ -69,6 +70,7 @@ import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.PlayQueueAdapter;
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.player.resolver.MediaSourceTag;
+import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.util.ImageDisplayConstants;
import org.schabi.newpipe.util.SerializedCache;
@@ -86,6 +88,7 @@ import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_INTERNAL
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_PERIOD_TRANSITION;
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK;
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT;
+import static org.schabi.newpipe.report.UserAction.PLAY_STREAM;
/**
* Base for the players, joining the common properties
@@ -96,7 +99,7 @@ import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK_ADJ
public abstract class BasePlayer implements
Player.EventListener, PlaybackListener, ImageLoadingListener {
- public static final boolean DEBUG = true;
+ public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");
@NonNull public static final String TAG = "BasePlayer";
@NonNull final protected Context context;
@@ -363,7 +366,10 @@ public abstract class BasePlayer implements
try {
context.unregisterReceiver(broadcastReceiver);
} catch (final IllegalArgumentException unregisteredException) {
- Log.e(TAG, "Broadcast receiver already unregistered.", unregisteredException);
+ ErrorActivity.reportError(context, unregisteredException, null, null,
+ ErrorActivity.ErrorInfo.make(PLAY_STREAM,
+ "none",
+ "play stream", R.string.general_error));
}
}
@@ -1001,6 +1007,8 @@ public abstract class BasePlayer implements
try {
metadata = (MediaSourceTag) simpleExoPlayer.getCurrentTag();
} catch (IndexOutOfBoundsException | ClassCastException error) {
+ if(DEBUG) Log.d(TAG, "Could not update metadata: " + error.getMessage());
+ if(DEBUG) error.printStackTrace();
return;
}
@@ -1087,6 +1095,9 @@ public abstract class BasePlayer implements
return simpleExoPlayer.isCurrentWindowDynamic();
} catch (@NonNull IndexOutOfBoundsException ignored) {
// Why would this even happen =(
+ // But lets log it anyway. Save is save
+ if(DEBUG) Log.d(TAG, "Could not update metadata: " + ignored.getMessage());
+ if(DEBUG) ignored.printStackTrace();
return false;
}
}
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 9f3b5d020..41e7c305d 100644
--- a/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java
@@ -36,6 +36,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
+import android.support.v7.content.res.AppCompatResources;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.DisplayMetrics;
@@ -46,7 +47,9 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageButton;
+import android.widget.ImageView;
import android.widget.PopupMenu;
+import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.TextView;
@@ -82,6 +85,7 @@ import java.util.UUID;
import static org.schabi.newpipe.player.BasePlayer.STATE_PLAYING;
import static org.schabi.newpipe.player.VideoPlayer.DEFAULT_CONTROLS_DURATION;
import static org.schabi.newpipe.player.VideoPlayer.DEFAULT_CONTROLS_HIDE_TIME;
+import static org.schabi.newpipe.util.AnimationUtils.Type.SCALE_AND_ALPHA;
import static org.schabi.newpipe.util.AnimationUtils.Type.SLIDE_AND_ALPHA;
import static org.schabi.newpipe.util.AnimationUtils.animateRotation;
import static org.schabi.newpipe.util.AnimationUtils.animateView;
@@ -365,10 +369,16 @@ public final class MainVideoPlayer extends AppCompatActivity
@SuppressWarnings({"unused", "WeakerAccess"})
private class VideoPlayerImpl extends VideoPlayer {
+ private final float MAX_GESTURE_LENGTH = 0.75f;
+
private TextView titleTextView;
private TextView channelTextView;
- private TextView volumeTextView;
- private TextView brightnessTextView;
+ private RelativeLayout volumeRelativeLayout;
+ private ProgressBar volumeProgressBar;
+ private ImageView volumeImageView;
+ private RelativeLayout brightnessRelativeLayout;
+ private ProgressBar brightnessProgressBar;
+ private ImageView brightnessImageView;
private ImageButton queueButton;
private ImageButton repeatButton;
private ImageButton shuffleButton;
@@ -392,6 +402,8 @@ public final class MainVideoPlayer extends AppCompatActivity
private RelativeLayout windowRootLayout;
private View secondaryControls;
+ private int maxGestureLength;
+
VideoPlayerImpl(final Context context) {
super("VideoPlayerImpl" + MainVideoPlayer.TAG, context);
}
@@ -401,8 +413,12 @@ public final class MainVideoPlayer extends AppCompatActivity
super.initViews(rootView);
this.titleTextView = rootView.findViewById(R.id.titleTextView);
this.channelTextView = rootView.findViewById(R.id.channelTextView);
- this.volumeTextView = rootView.findViewById(R.id.volumeTextView);
- this.brightnessTextView = rootView.findViewById(R.id.brightnessTextView);
+ this.volumeRelativeLayout = rootView.findViewById(R.id.volumeRelativeLayout);
+ this.volumeProgressBar = rootView.findViewById(R.id.volumeProgressBar);
+ this.volumeImageView = rootView.findViewById(R.id.volumeImageView);
+ this.brightnessRelativeLayout = rootView.findViewById(R.id.brightnessRelativeLayout);
+ this.brightnessProgressBar = rootView.findViewById(R.id.brightnessProgressBar);
+ this.brightnessImageView = rootView.findViewById(R.id.brightnessImageView);
this.queueButton = rootView.findViewById(R.id.queueButton);
this.repeatButton = rootView.findViewById(R.id.repeatButton);
this.shuffleButton = rootView.findViewById(R.id.shuffleButton);
@@ -461,6 +477,20 @@ public final class MainVideoPlayer extends AppCompatActivity
toggleOrientationButton.setOnClickListener(this);
switchBackgroundButton.setOnClickListener(this);
switchPopupButton.setOnClickListener(this);
+
+ getRootView().addOnLayoutChangeListener((view, l, t, r, b, ol, ot, or, ob) -> {
+ if (l != ol || t != ot || r != or || b != ob) {
+ // Use smaller value to be consistent between screen orientations
+ // (and to make usage easier)
+ int width = r - l, height = b - t;
+ maxGestureLength = (int) (Math.min(width, height) * MAX_GESTURE_LENGTH);
+
+ if (DEBUG) Log.d(TAG, "maxGestureLength = " + maxGestureLength);
+
+ volumeProgressBar.setMax(maxGestureLength);
+ brightnessProgressBar.setMax(maxGestureLength);
+ }
+ });
}
public void minimize() {
@@ -872,12 +902,28 @@ public final class MainVideoPlayer extends AppCompatActivity
return channelTextView;
}
- public TextView getVolumeTextView() {
- return volumeTextView;
+ public RelativeLayout getVolumeRelativeLayout() {
+ return volumeRelativeLayout;
}
- public TextView getBrightnessTextView() {
- return brightnessTextView;
+ public ProgressBar getVolumeProgressBar() {
+ return volumeProgressBar;
+ }
+
+ public ImageView getVolumeImageView() {
+ return volumeImageView;
+ }
+
+ public RelativeLayout getBrightnessRelativeLayout() {
+ return brightnessRelativeLayout;
+ }
+
+ public ProgressBar getBrightnessProgressBar() {
+ return brightnessProgressBar;
+ }
+
+ public ImageView getBrightnessImageView() {
+ return brightnessImageView;
}
public ImageButton getRepeatButton() {
@@ -887,6 +933,10 @@ public final class MainVideoPlayer extends AppCompatActivity
public ImageButton getPlayPauseButton() {
return playPauseButton;
}
+
+ public int getMaxGestureLength() {
+ return maxGestureLength;
+ }
}
private class MySimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener {
@@ -930,23 +980,10 @@ public final class MainVideoPlayer extends AppCompatActivity
private final boolean isPlayerGestureEnabled = PlayerHelper.isPlayerGestureEnabled(getApplicationContext());
- private final float stepsBrightness = 15, stepBrightness = (1f / stepsBrightness), minBrightness = .01f;
- private float currentBrightness = getWindow().getAttributes().screenBrightness > 0
- ? getWindow().getAttributes().screenBrightness
- : 0.5f;
-
- private int currentVolume, maxVolume = playerImpl.getAudioReactor().getMaxVolume();
- private final float stepsVolume = 15, stepVolume = (float) Math.ceil(maxVolume / stepsVolume), minVolume = 0;
-
- private final String brightnessUnicode = new String(Character.toChars(0x2600));
- private final String volumeUnicode = new String(Character.toChars(0x1F508));
+ private final int maxVolume = playerImpl.getAudioReactor().getMaxVolume();
private final int MOVEMENT_THRESHOLD = 40;
- private final int eventsThreshold = 8;
- private boolean triggered = false;
- private int eventsNum;
- // TODO: Improve video gesture controls
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (!isPlayerGestureEnabled) return false;
@@ -956,63 +993,77 @@ public final class MainVideoPlayer extends AppCompatActivity
", e1.getRaw = [" + e1.getRawX() + ", " + e1.getRawY() + "]" +
", e2.getRaw = [" + e2.getRawX() + ", " + e2.getRawY() + "]" +
", distanceXy = [" + distanceX + ", " + distanceY + "]");
- float abs = Math.abs(e2.getY() - e1.getY());
- if (!triggered) {
- triggered = abs > MOVEMENT_THRESHOLD;
+
+ if (!isMoving && (
+ Math.abs(e2.getY() - e1.getY()) <= MOVEMENT_THRESHOLD
+ || Math.abs(distanceX) > Math.abs(distanceY)
+ ) || playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED)
return false;
- }
- if (eventsNum++ % eventsThreshold != 0 || playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) return false;
isMoving = true;
-// boolean up = !((e2.getY() - e1.getY()) > 0) && distanceY > 0; // Android's origin point is on top
- boolean up = distanceY > 0;
-
if (e1.getX() > playerImpl.getRootView().getWidth() / 2) {
- double floor = Math.floor(up ? stepVolume : -stepVolume);
- currentVolume = (int) (playerImpl.getAudioReactor().getVolume() + floor);
- if (currentVolume >= maxVolume) currentVolume = maxVolume;
- if (currentVolume <= minVolume) currentVolume = (int) minVolume;
+ playerImpl.getVolumeProgressBar().incrementProgressBy((int) distanceY);
+ float currentProgressPercent =
+ (float) playerImpl.getVolumeProgressBar().getProgress() / playerImpl.getMaxGestureLength();
+ int currentVolume = (int) (maxVolume * currentProgressPercent);
playerImpl.getAudioReactor().setVolume(currentVolume);
- currentVolume = playerImpl.getAudioReactor().getVolume();
if (DEBUG) Log.d(TAG, "onScroll().volumeControl, currentVolume = " + currentVolume);
- final String volumeText = volumeUnicode + " " + Math.round((((float) currentVolume) / maxVolume) * 100) + "%";
- playerImpl.getVolumeTextView().setText(volumeText);
- if (playerImpl.getVolumeTextView().getVisibility() != View.VISIBLE) animateView(playerImpl.getVolumeTextView(), true, 200);
- if (playerImpl.getBrightnessTextView().getVisibility() == View.VISIBLE) playerImpl.getBrightnessTextView().setVisibility(View.GONE);
+ final int resId =
+ currentProgressPercent <= 0 ? R.drawable.ic_volume_off_white_72dp
+ : currentProgressPercent < 0.25 ? R.drawable.ic_volume_mute_white_72dp
+ : currentProgressPercent < 0.75 ? R.drawable.ic_volume_down_white_72dp
+ : R.drawable.ic_volume_up_white_72dp;
+
+ playerImpl.getVolumeImageView().setImageDrawable(
+ AppCompatResources.getDrawable(getApplicationContext(), resId)
+ );
+
+ if (playerImpl.getVolumeRelativeLayout().getVisibility() != View.VISIBLE) {
+ animateView(playerImpl.getVolumeRelativeLayout(), SCALE_AND_ALPHA, true, 200);
+ }
+ if (playerImpl.getBrightnessRelativeLayout().getVisibility() == View.VISIBLE) {
+ playerImpl.getBrightnessRelativeLayout().setVisibility(View.GONE);
+ }
} else {
- WindowManager.LayoutParams lp = getWindow().getAttributes();
- currentBrightness += up ? stepBrightness : -stepBrightness;
- if (currentBrightness >= 1f) currentBrightness = 1f;
- if (currentBrightness <= minBrightness) currentBrightness = minBrightness;
+ playerImpl.getBrightnessProgressBar().incrementProgressBy((int) distanceY);
+ float currentProgressPercent =
+ (float) playerImpl.getBrightnessProgressBar().getProgress() / playerImpl.getMaxGestureLength();
+ WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
+ layoutParams.screenBrightness = currentProgressPercent;
+ getWindow().setAttributes(layoutParams);
- lp.screenBrightness = currentBrightness;
- getWindow().setAttributes(lp);
- if (DEBUG) Log.d(TAG, "onScroll().brightnessControl, currentBrightness = " + currentBrightness);
- int brightnessNormalized = Math.round(currentBrightness * 100);
+ if (DEBUG) Log.d(TAG, "onScroll().brightnessControl, currentBrightness = " + currentProgressPercent);
- final String brightnessText = brightnessUnicode + " " + (brightnessNormalized == 1 ? 0 : brightnessNormalized) + "%";
- playerImpl.getBrightnessTextView().setText(brightnessText);
+ final int resId =
+ currentProgressPercent < 0.25 ? R.drawable.ic_brightness_low_white_72dp
+ : currentProgressPercent < 0.75 ? R.drawable.ic_brightness_medium_white_72dp
+ : R.drawable.ic_brightness_high_white_72dp;
- if (playerImpl.getBrightnessTextView().getVisibility() != View.VISIBLE) animateView(playerImpl.getBrightnessTextView(), true, 200);
- if (playerImpl.getVolumeTextView().getVisibility() == View.VISIBLE) playerImpl.getVolumeTextView().setVisibility(View.GONE);
+ playerImpl.getBrightnessImageView().setImageDrawable(
+ AppCompatResources.getDrawable(getApplicationContext(), resId)
+ );
+
+ if (playerImpl.getBrightnessRelativeLayout().getVisibility() != View.VISIBLE) {
+ animateView(playerImpl.getBrightnessRelativeLayout(), SCALE_AND_ALPHA, true, 200);
+ }
+ if (playerImpl.getVolumeRelativeLayout().getVisibility() == View.VISIBLE) {
+ playerImpl.getVolumeRelativeLayout().setVisibility(View.GONE);
+ }
}
return true;
}
private void onScrollEnd() {
if (DEBUG) Log.d(TAG, "onScrollEnd() called");
- triggered = false;
- 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) {
- animateView(playerImpl.getVolumeTextView(), false, 200, 200);
+
+ if (playerImpl.getVolumeRelativeLayout().getVisibility() == View.VISIBLE) {
+ animateView(playerImpl.getVolumeRelativeLayout(), SCALE_AND_ALPHA, false, 200, 200);
}
- if (playerImpl.getBrightnessTextView().getVisibility() == View.VISIBLE) {
- animateView(playerImpl.getBrightnessTextView(), false, 200, 200);
+ if (playerImpl.getBrightnessRelativeLayout().getVisibility() == View.VISIBLE) {
+ animateView(playerImpl.getBrightnessRelativeLayout(), SCALE_AND_ALPHA, false, 200, 200);
}
if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == STATE_PLAYING) {
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 86998e0ea..0e7328020 100644
--- a/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java
@@ -19,6 +19,8 @@
package org.schabi.newpipe.player;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.annotation.SuppressLint;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -34,6 +36,7 @@ import android.os.Build;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
+import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.NotificationCompat;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -41,7 +44,9 @@ import android.view.GestureDetector;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewGroup;
import android.view.WindowManager;
+import android.view.animation.AnticipateInterpolator;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.PopupMenu;
@@ -104,10 +109,13 @@ public final class PopupVideoPlayer extends Service {
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
private WindowManager windowManager;
- private WindowManager.LayoutParams windowLayoutParams;
- private GestureDetector gestureDetector;
+ private WindowManager.LayoutParams popupLayoutParams;
+ private GestureDetector popupGestureDetector;
+
+ private View closeOverlayView;
+ private FloatingActionButton closeOverlayButton;
+ private WindowManager.LayoutParams closeOverlayLayoutParams;
- private int shutdownFlingVelocity;
private int tossFlingVelocity;
private float screenWidth, screenHeight;
@@ -122,6 +130,7 @@ public final class PopupVideoPlayer extends Service {
private VideoPlayerImpl playerImpl;
private LockManager lockManager;
+ private boolean isPopupClosing = false;
/*//////////////////////////////////////////////////////////////////////////
// Service-Activity Binder
@@ -150,7 +159,10 @@ public final class PopupVideoPlayer extends Service {
public int onStartCommand(final Intent intent, int flags, int startId) {
if (DEBUG)
Log.d(TAG, "onStartCommand() called with: intent = [" + intent + "], flags = [" + flags + "], startId = [" + startId + "]");
- if (playerImpl.getPlayer() == null) initPopup();
+ if (playerImpl.getPlayer() == null) {
+ initPopup();
+ initPopupCloseOverlay();
+ }
if (!playerImpl.isPlaying()) playerImpl.getPlayer().setPlayWhenReady(true);
playerImpl.handleIntent(intent);
@@ -160,15 +172,16 @@ public final class PopupVideoPlayer extends Service {
@Override
public void onConfigurationChanged(Configuration newConfig) {
+ if (DEBUG) Log.d(TAG, "onConfigurationChanged() called with: newConfig = [" + newConfig + "]");
updateScreenSize();
- updatePopupSize(windowLayoutParams.width, -1);
- checkPositionBounds();
+ updatePopupSize(popupLayoutParams.width, -1);
+ checkPopupPositionBounds();
}
@Override
public void onDestroy() {
if (DEBUG) Log.d(TAG, "onDestroy() called");
- onClose();
+ closePopup();
}
@Override
@@ -186,7 +199,6 @@ public final class PopupVideoPlayer extends Service {
View rootView = View.inflate(this, R.layout.player_popup, null);
playerImpl.setup(rootView);
- shutdownFlingVelocity = PlayerHelper.getShutdownFlingVelocity(this);
tossFlingVelocity = PlayerHelper.getTossFlingVelocity(this);
updateScreenSize();
@@ -200,27 +212,52 @@ public final class PopupVideoPlayer extends Service {
WindowManager.LayoutParams.TYPE_PHONE :
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
- windowLayoutParams = new WindowManager.LayoutParams(
+ popupLayoutParams = new WindowManager.LayoutParams(
(int) popupWidth, (int) getMinimumVideoHeight(popupWidth),
layoutParamType,
IDLE_WINDOW_FLAGS,
PixelFormat.TRANSLUCENT);
- windowLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
- windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+ popupLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
+ popupLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
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;
+ popupLayoutParams.x = popupRememberSizeAndPos ? sharedPreferences.getInt(POPUP_SAVED_X, centerX) : centerX;
+ popupLayoutParams.y = popupRememberSizeAndPos ? sharedPreferences.getInt(POPUP_SAVED_Y, centerY) : centerY;
- checkPositionBounds();
+ checkPopupPositionBounds();
- MySimpleOnGestureListener listener = new MySimpleOnGestureListener();
- gestureDetector = new GestureDetector(this, listener);
+ PopupWindowGestureListener listener = new PopupWindowGestureListener();
+ popupGestureDetector = new GestureDetector(this, listener);
rootView.setOnTouchListener(listener);
- playerImpl.getLoadingPanel().setMinimumWidth(windowLayoutParams.width);
- playerImpl.getLoadingPanel().setMinimumHeight(windowLayoutParams.height);
- windowManager.addView(rootView, windowLayoutParams);
+
+ playerImpl.getLoadingPanel().setMinimumWidth(popupLayoutParams.width);
+ playerImpl.getLoadingPanel().setMinimumHeight(popupLayoutParams.height);
+ windowManager.addView(rootView, popupLayoutParams);
+ }
+
+ @SuppressLint("RtlHardcoded")
+ private void initPopupCloseOverlay() {
+ if (DEBUG) Log.d(TAG, "initPopupCloseOverlay() called");
+ closeOverlayView = View.inflate(this, R.layout.player_popup_close_overlay, null);
+ closeOverlayButton = closeOverlayView.findViewById(R.id.closeButton);
+
+ final int layoutParamType = Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O ?
+ WindowManager.LayoutParams.TYPE_PHONE :
+ WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+ final int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+ | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+
+ closeOverlayLayoutParams = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
+ layoutParamType,
+ flags,
+ PixelFormat.TRANSLUCENT);
+ closeOverlayLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
+ closeOverlayLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+
+ closeOverlayButton.setVisibility(View.GONE);
+ windowManager.addView(closeOverlayView, closeOverlayLayoutParams);
}
/*//////////////////////////////////////////////////////////////////////////
@@ -280,44 +317,105 @@ public final class PopupVideoPlayer extends Service {
// Misc
//////////////////////////////////////////////////////////////////////////*/
- public void onClose() {
- if (DEBUG) Log.d(TAG, "onClose() called");
+ public void closePopup() {
+ if (DEBUG) Log.d(TAG, "closePopup() called, isPopupClosing = " + isPopupClosing);
+ if (isPopupClosing) return;
+ isPopupClosing = true;
if (playerImpl != null) {
if (playerImpl.getRootView() != null) {
windowManager.removeView(playerImpl.getRootView());
- playerImpl.setRootView(null);
}
+ playerImpl.setRootView(null);
playerImpl.stopActivityBinding();
playerImpl.destroy();
+ playerImpl = null;
}
+
+ mBinder = null;
if (lockManager != null) lockManager.releaseWifiAndCpu();
if (notificationManager != null) notificationManager.cancel(NOTIFICATION_ID);
- mBinder = null;
- playerImpl = null;
- stopForeground(true);
- stopSelf();
+ animateOverlayAndFinishService();
+ }
+
+ private void animateOverlayAndFinishService() {
+ final int targetTranslationY = (int) (closeOverlayButton.getRootView().getHeight() - closeOverlayButton.getY());
+
+ closeOverlayButton.animate().setListener(null).cancel();
+ closeOverlayButton.animate()
+ .setInterpolator(new AnticipateInterpolator())
+ .translationY(targetTranslationY)
+ .setDuration(400)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ end();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ end();
+ }
+
+ private void end() {
+ windowManager.removeView(closeOverlayView);
+
+ stopForeground(true);
+ stopSelf();
+ }
+ }).start();
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
- 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;
+ /**
+ * @see #checkPopupPositionBounds(float, float)
+ */
+ @SuppressWarnings("UnusedReturnValue")
+ private boolean checkPopupPositionBounds() {
+ return checkPopupPositionBounds(screenWidth, screenHeight);
+ }
+
+ /**
+ * Check if {@link #popupLayoutParams}' position is within a arbitrary boundary that goes from (0,0) to (boundaryWidth,boundaryHeight).
+ *
+ * If it's out of these boundaries, {@link #popupLayoutParams}' position is changed and {@code true} is returned
+ * to represent this change.
+ *
+ * @return if the popup was out of bounds and have been moved back to it
+ */
+ private boolean checkPopupPositionBounds(final float boundaryWidth, final float boundaryHeight) {
+ if (DEBUG) {
+ Log.d(TAG, "checkPopupPositionBounds() called with: boundaryWidth = [" + boundaryWidth + "], boundaryHeight = [" + boundaryHeight + "]");
+ }
+
+ if (popupLayoutParams.x < 0) {
+ popupLayoutParams.x = 0;
+ return true;
+ } else if (popupLayoutParams.x > boundaryWidth - popupLayoutParams.width) {
+ popupLayoutParams.x = (int) (boundaryWidth - popupLayoutParams.width);
+ return true;
+ }
+
+ if (popupLayoutParams.y < 0) {
+ popupLayoutParams.y = 0;
+ return true;
+ } else if (popupLayoutParams.y > boundaryHeight - popupLayoutParams.height) {
+ popupLayoutParams.y = (int) (boundaryHeight - popupLayoutParams.height);
+ return true;
+ }
+
+ return false;
}
private void savePositionAndSize() {
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();
+ sharedPreferences.edit().putInt(POPUP_SAVED_X, popupLayoutParams.x).apply();
+ sharedPreferences.edit().putInt(POPUP_SAVED_Y, popupLayoutParams.y).apply();
+ sharedPreferences.edit().putFloat(POPUP_SAVED_WIDTH, popupLayoutParams.width).apply();
}
private float getMinimumVideoHeight(float width) {
@@ -352,13 +450,13 @@ public final class PopupVideoPlayer extends Service {
if (height == -1) height = (int) getMinimumVideoHeight(width);
else height = (int) (height > maximumHeight ? maximumHeight : height < minimumHeight ? minimumHeight : height);
- windowLayoutParams.width = width;
- windowLayoutParams.height = height;
+ popupLayoutParams.width = width;
+ popupLayoutParams.height = height;
popupWidth = width;
popupHeight = height;
if (DEBUG) Log.d(TAG, "updatePopupSize() updated values: width = [" + width + "], height = [" + height + "]");
- windowManager.updateViewLayout(playerImpl.getRootView(), windowLayoutParams);
+ windowManager.updateViewLayout(playerImpl.getRootView(), popupLayoutParams);
}
protected void setRepeatModeRemote(final RemoteViews remoteViews, final int repeatMode) {
@@ -380,10 +478,10 @@ public final class PopupVideoPlayer extends Service {
}
private void updateWindowFlags(final int flags) {
- if (windowLayoutParams == null || windowManager == null || playerImpl == null) return;
+ if (popupLayoutParams == null || windowManager == null || playerImpl == null) return;
- windowLayoutParams.flags = flags;
- windowManager.updateViewLayout(playerImpl.getRootView(), windowLayoutParams);
+ popupLayoutParams.flags = flags;
+ windowManager.updateViewLayout(playerImpl.getRootView(), popupLayoutParams);
}
///////////////////////////////////////////////////////////////////////////
@@ -393,6 +491,7 @@ public final class PopupVideoPlayer extends Service {
private ImageView videoPlayPause;
private View extraOptionsView;
+ private View closingOverlayView;
@Override
public void handleIntent(Intent intent) {
@@ -413,12 +512,18 @@ public final class PopupVideoPlayer extends Service {
fullScreenButton = rootView.findViewById(R.id.fullScreenButton);
fullScreenButton.setOnClickListener(v -> onFullScreenButtonClicked());
videoPlayPause = rootView.findViewById(R.id.videoPlayPause);
- videoPlayPause.setOnClickListener(this::onPlayPauseButtonPressed);
extraOptionsView = rootView.findViewById(R.id.extraOptionsView);
+ closingOverlayView = rootView.findViewById(R.id.closingOverlay);
rootView.addOnLayoutChangeListener(this);
}
+ @Override
+ public void initListeners() {
+ super.initListeners();
+ videoPlayPause.setOnClickListener(v -> onPlayPause());
+ }
+
@Override
protected void setupSubtitleView(@NonNull SubtitleView view,
final float captionScale,
@@ -429,10 +534,6 @@ public final class PopupVideoPlayer extends Service {
view.setStyle(captionStyle);
}
- private void onPlayPauseButtonPressed(View ib) {
- onPlayPause();
- }
-
@Override
public void onLayoutChange(final View view, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
@@ -476,7 +577,7 @@ public final class PopupVideoPlayer extends Service {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
context.startActivity(intent);
- onClose();
+ closePopup();
}
@Override
@@ -634,7 +735,7 @@ public final class PopupVideoPlayer extends Service {
@Override
public void onPlaybackShutdown() {
super.onPlaybackShutdown();
- onClose();
+ closePopup();
}
/*//////////////////////////////////////////////////////////////////////////
@@ -660,7 +761,7 @@ public final class PopupVideoPlayer extends Service {
if (DEBUG) Log.d(TAG, "onBroadcastReceived() called with: intent = [" + intent + "]");
switch (intent.getAction()) {
case ACTION_CLOSE:
- onClose();
+ closePopup();
break;
case ACTION_PLAY_PAUSE:
onPlayPause();
@@ -791,12 +892,15 @@ public final class PopupVideoPlayer extends Service {
public TextView getResizingIndicator() {
return resizingIndicator;
}
+
+ public View getClosingOverlayView() {
+ return closingOverlayView;
+ }
}
- private class MySimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener {
+ private class PopupWindowGestureListener extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener {
private int initialPopupX, initialPopupY;
private boolean isMoving;
-
private boolean isResizing;
@Override
@@ -832,10 +936,15 @@ public final class PopupVideoPlayer extends Service {
@Override
public boolean onDown(MotionEvent e) {
if (DEBUG) Log.d(TAG, "onDown() called with: e = [" + e + "]");
- initialPopupX = windowLayoutParams.x;
- initialPopupY = windowLayoutParams.y;
- popupWidth = windowLayoutParams.width;
- popupHeight = windowLayoutParams.height;
+
+ // Fix popup position when the user touch it, it may have the wrong one
+ // because the soft input is visible (the draggable area is currently resized).
+ checkPopupPositionBounds(closeOverlayView.getWidth(), closeOverlayView.getHeight());
+
+ initialPopupX = popupLayoutParams.x;
+ initialPopupY = popupLayoutParams.y;
+ popupWidth = popupLayoutParams.width;
+ popupHeight = popupLayoutParams.height;
return super.onDown(e);
}
@@ -843,20 +952,22 @@ public final class PopupVideoPlayer extends Service {
public void onLongPress(MotionEvent e) {
if (DEBUG) Log.d(TAG, "onLongPress() called with: e = [" + e + "]");
updateScreenSize();
- checkPositionBounds();
+ checkPopupPositionBounds();
updatePopupSize((int) screenWidth, -1);
}
@Override
- public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
- if (isResizing || playerImpl == null) return super.onScroll(e1, e2, distanceX, distanceY);
+ public boolean onScroll(MotionEvent initialEvent, MotionEvent movingEvent, float distanceX, float distanceY) {
+ if (isResizing || playerImpl == null) return super.onScroll(initialEvent, movingEvent, distanceX, distanceY);
+
+ if (!isMoving) {
+ animateView(closeOverlayButton, true, 200);
+ }
- if (playerImpl.getCurrentState() != BasePlayer.STATE_BUFFERING
- && (!isMoving || playerImpl.getControlsRoot().getAlpha() != 1f)) playerImpl.showControls(0);
isMoving = true;
- float diffX = (int) (e2.getRawX() - e1.getRawX()), posX = (int) (initialPopupX + diffX);
- float diffY = (int) (e2.getRawY() - e1.getRawY()), posY = (int) (initialPopupY + diffY);
+ float diffX = (int) (movingEvent.getRawX() - initialEvent.getRawX()), posX = (int) (initialPopupX + diffX);
+ float diffY = (int) (movingEvent.getRawY() - initialEvent.getRawY()), posY = (int) (initialPopupY + diffY);
if (posX > (screenWidth - popupWidth)) posX = (int) (screenWidth - popupWidth);
else if (posX < 0) posX = 0;
@@ -864,26 +975,49 @@ public final class PopupVideoPlayer extends Service {
if (posY > (screenHeight - popupHeight)) posY = (int) (screenHeight - popupHeight);
else if (posY < 0) posY = 0;
- windowLayoutParams.x = (int) posX;
- windowLayoutParams.y = (int) posY;
+ popupLayoutParams.x = (int) posX;
+ popupLayoutParams.y = (int) posY;
+
+ final View closingOverlayView = playerImpl.getClosingOverlayView();
+ if (isInsideClosingRadius(movingEvent)) {
+ if (closingOverlayView.getVisibility() == View.GONE) {
+ animateView(closingOverlayView, true, 250);
+ }
+ } else {
+ if (closingOverlayView.getVisibility() == View.VISIBLE) {
+ animateView(closingOverlayView, false, 0);
+ }
+ }
//noinspection PointlessBooleanExpression
- if (DEBUG && false) Log.d(TAG, "PopupVideoPlayer.onScroll = " +
- ", e1.getRaw = [" + e1.getRawX() + ", " + e1.getRawY() + "]" +
- ", e2.getRaw = [" + e2.getRawX() + ", " + e2.getRawY() + "]" +
- ", distanceXy = [" + distanceX + ", " + distanceY + "]" +
- ", posXy = [" + posX + ", " + posY + "]" +
- ", popupWh = [" + popupWidth + " x " + popupHeight + "]");
- windowManager.updateViewLayout(playerImpl.getRootView(), windowLayoutParams);
+ if (DEBUG && false) {
+ Log.d(TAG, "PopupVideoPlayer.onScroll = " +
+ ", e1.getRaw = [" + initialEvent.getRawX() + ", " + initialEvent.getRawY() + "]" + ", e1.getX,Y = [" + initialEvent.getX() + ", " + initialEvent.getY() + "]" +
+ ", e2.getRaw = [" + movingEvent.getRawX() + ", " + movingEvent.getRawY() + "]" + ", e2.getX,Y = [" + movingEvent.getX() + ", " + movingEvent.getY() + "]" +
+ ", distanceX,Y = [" + distanceX + ", " + distanceY + "]" +
+ ", posX,Y = [" + posX + ", " + posY + "]" +
+ ", popupW,H = [" + popupWidth + " x " + popupHeight + "]");
+ }
+ windowManager.updateViewLayout(playerImpl.getRootView(), popupLayoutParams);
return true;
}
- private void onScrollEnd() {
+ private void onScrollEnd(MotionEvent event) {
if (DEBUG) Log.d(TAG, "onScrollEnd() called");
if (playerImpl == null) return;
if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == STATE_PLAYING) {
playerImpl.hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME);
}
+
+ if (isInsideClosingRadius(event)) {
+ closePopup();
+ } else {
+ animateView(playerImpl.getClosingOverlayView(), false, 0);
+
+ if (!isPopupClosing) {
+ animateView(closeOverlayButton, false, 200);
+ }
+ }
}
@Override
@@ -893,14 +1027,11 @@ public final class PopupVideoPlayer extends Service {
final float absVelocityX = Math.abs(velocityX);
final float absVelocityY = Math.abs(velocityY);
- if (absVelocityX > shutdownFlingVelocity) {
- onClose();
- return true;
- } else if (Math.max(absVelocityX, absVelocityY) > tossFlingVelocity) {
- if (absVelocityX > tossFlingVelocity) windowLayoutParams.x = (int) velocityX;
- if (absVelocityY > tossFlingVelocity) windowLayoutParams.y = (int) velocityY;
- checkPositionBounds();
- windowManager.updateViewLayout(playerImpl.getRootView(), windowLayoutParams);
+ if (Math.max(absVelocityX, absVelocityY) > tossFlingVelocity) {
+ if (absVelocityX > tossFlingVelocity) popupLayoutParams.x = (int) velocityX;
+ if (absVelocityY > tossFlingVelocity) popupLayoutParams.y = (int) velocityY;
+ checkPopupPositionBounds();
+ windowManager.updateViewLayout(playerImpl.getRootView(), popupLayoutParams);
return true;
}
return false;
@@ -908,7 +1039,7 @@ public final class PopupVideoPlayer extends Service {
@Override
public boolean onTouch(View v, MotionEvent event) {
- gestureDetector.onTouchEvent(event);
+ popupGestureDetector.onTouchEvent(event);
if (playerImpl == null) return false;
if (event.getPointerCount() == 2 && !isResizing) {
if (DEBUG) Log.d(TAG, "onTouch() 2 finger pointer detected, enabling resizing.");
@@ -931,7 +1062,7 @@ public final class PopupVideoPlayer extends Service {
Log.d(TAG, "onTouch() ACTION_UP > v = [" + v + "], e1.getRaw = [" + event.getRawX() + ", " + event.getRawY() + "]");
if (isMoving) {
isMoving = false;
- onScrollEnd();
+ onScrollEnd(event);
}
if (isResizing) {
@@ -939,7 +1070,10 @@ public final class PopupVideoPlayer extends Service {
animateView(playerImpl.getResizingIndicator(), false, 100, 0);
playerImpl.changeState(playerImpl.getCurrentState());
}
- savePositionAndSize();
+
+ if (!isPopupClosing) {
+ savePositionAndSize();
+ }
}
v.performClick();
@@ -955,13 +1089,13 @@ public final class PopupVideoPlayer extends Service {
final float diff = Math.abs(firstPointerX - secondPointerX);
if (firstPointerX > secondPointerX) {
// second pointer is the anchor (the leftmost pointer)
- windowLayoutParams.x = (int) (event.getRawX() - diff);
+ popupLayoutParams.x = (int) (event.getRawX() - diff);
} else {
// first pointer is the anchor
- windowLayoutParams.x = (int) event.getRawX();
+ popupLayoutParams.x = (int) event.getRawX();
}
- checkPositionBounds();
+ checkPopupPositionBounds();
updateScreenSize();
final int width = (int) Math.min(screenWidth, diff);
@@ -969,5 +1103,29 @@ public final class PopupVideoPlayer extends Service {
return true;
}
+
+ /*//////////////////////////////////////////////////////////////////////////
+ // Utils
+ //////////////////////////////////////////////////////////////////////////*/
+
+ private int distanceFromCloseButton(MotionEvent popupMotionEvent) {
+ final int closeOverlayButtonX = closeOverlayButton.getLeft() + closeOverlayButton.getWidth() / 2;
+ final int closeOverlayButtonY = closeOverlayButton.getTop() + closeOverlayButton.getHeight() / 2;
+
+ float fingerX = popupLayoutParams.x + popupMotionEvent.getX();
+ float fingerY = popupLayoutParams.y + popupMotionEvent.getY();
+
+ return (int) Math.sqrt(Math.pow(closeOverlayButtonX - fingerX, 2) + Math.pow(closeOverlayButtonY - fingerY, 2));
+ }
+
+ private float getClosingRadius() {
+ final int buttonRadius = closeOverlayButton.getWidth() / 2;
+ // 20% wider than the button itself
+ return buttonRadius * 1.2f;
+ }
+
+ private boolean isInsideClosingRadius(MotionEvent popupMotionEvent) {
+ return distanceFromCloseButton(popupMotionEvent) <= getClosingRadius();
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java b/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java
index b57a710ed..94305e6c4 100644
--- a/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java
@@ -16,6 +16,7 @@ import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.PopupMenu;
@@ -562,6 +563,12 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
if (player != null) {
progressLiveSync.setClickable(!player.isLiveEdge());
}
+
+ // this will make shure progressCurrentTime has the same width as progressEndTime
+ final ViewGroup.LayoutParams endTimeParams = progressEndTime.getLayoutParams();
+ final ViewGroup.LayoutParams currentTimeParams = progressCurrentTime.getLayoutParams();
+ currentTimeParams.width = progressEndTime.getWidth();
+ progressCurrentTime.setLayoutParams(currentTimeParams);
}
@Override
diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
index 275f488e3..ae187a834 100644
--- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
@@ -251,10 +251,6 @@ public class PlayerHelper {
return true;
}
- public static int getShutdownFlingVelocity(@NonNull final Context context) {
- return 6000;
- }
-
public static int getTossFlingVelocity(@NonNull final Context context) {
return 2500;
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java
index a21560abd..c9e07c96a 100644
--- a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java
+++ b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java
@@ -6,6 +6,7 @@ import android.util.Log;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
+import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.player.playqueue.events.AppendEvent;
import org.schabi.newpipe.player.playqueue.events.ErrorEvent;
import org.schabi.newpipe.player.playqueue.events.InitEvent;
@@ -41,7 +42,7 @@ import io.reactivex.subjects.BehaviorSubject;
public abstract class PlayQueue implements Serializable {
private final String TAG = "PlayQueue@" + Integer.toHexString(hashCode());
- public static final boolean DEBUG = true;
+ public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");
private ArrayList backup;
private ArrayList streams;
diff --git a/app/src/main/java/org/schabi/newpipe/report/UserAction.java b/app/src/main/java/org/schabi/newpipe/report/UserAction.java
index 93a3ce16c..00a25ed8d 100644
--- a/app/src/main/java/org/schabi/newpipe/report/UserAction.java
+++ b/app/src/main/java/org/schabi/newpipe/report/UserAction.java
@@ -15,7 +15,8 @@ public enum UserAction {
REQUESTED_CHANNEL("requested channel"),
REQUESTED_PLAYLIST("requested playlist"),
REQUESTED_KIOSK("requested kiosk"),
- DELETE_FROM_HISTORY("delete from history");
+ DELETE_FROM_HISTORY("delete from history"),
+ PLAY_STREAM("Play stream");
private final String message;
diff --git a/app/src/main/java/org/schabi/newpipe/settings/AddTabsDialog.java b/app/src/main/java/org/schabi/newpipe/settings/AddTabsDialog.java
new file mode 100644
index 000000000..5130df3bf
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/settings/AddTabsDialog.java
@@ -0,0 +1,41 @@
+package org.schabi.newpipe.settings;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.View;
+import android.widget.TextView;
+
+import org.schabi.newpipe.R;
+import org.schabi.newpipe.extractor.stream.StreamInfoItem;
+
+public class AddTabsDialog {
+ private final AlertDialog dialog;
+
+ public AddTabsDialog(@NonNull final Context context,
+ @NonNull final String title,
+ @NonNull final String[] commands,
+ @NonNull final DialogInterface.OnClickListener actions) {
+
+ final View bannerView = View.inflate(context, R.layout.dialog_title, null);
+ bannerView.setSelected(true);
+
+ TextView titleView = bannerView.findViewById(R.id.itemTitleView);
+ titleView.setText(title);
+
+ TextView detailsView = bannerView.findViewById(R.id.itemAdditionalDetails);
+ detailsView.setVisibility(View.GONE);
+
+ dialog = new AlertDialog.Builder(context)
+ .setCustomTitle(bannerView)
+ .setItems(commands, actions)
+ .create();
+ }
+
+ public void show() {
+ dialog.show();
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/ChoseTabsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ChoseTabsFragment.java
new file mode 100644
index 000000000..d6238c7c4
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/settings/ChoseTabsFragment.java
@@ -0,0 +1,291 @@
+package org.schabi.newpipe.settings;
+
+import android.app.Dialog;
+import android.content.SharedPreferences;
+import android.content.res.ColorStateList;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.design.widget.FloatingActionButton;
+import android.support.v4.app.Fragment;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.CardView;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.helper.ItemTouchHelper;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.schabi.newpipe.R;
+import org.schabi.newpipe.util.ThemeHelper;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class ChoseTabsFragment extends Fragment {
+
+ public ChoseTabsFragment.SelectedTabsAdapter selectedTabsAdapter;
+
+ RecyclerView selectedTabsView;
+
+ List selectedTabs = new ArrayList<>();
+ private String saveString;
+ public String[] availableTabs = new String[7];
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ ((AppCompatActivity)getContext()).getSupportActionBar().setTitle(R.string.main_page_content);
+ return inflater.inflate(R.layout.fragment_chose_tabs, container, false);
+ }
+
+
+ @Override
+ public void onViewCreated(@NonNull View rootView, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(rootView, savedInstanceState);
+
+ tabNames();
+ initUsedTabs();
+ initButton(rootView);
+
+ selectedTabsView = rootView.findViewById(R.id.usedTabs);
+ selectedTabsView.setLayoutManager(new LinearLayoutManager(getContext()));
+ selectedTabsAdapter = new ChoseTabsFragment.SelectedTabsAdapter();
+
+
+ ItemTouchHelper itemTouchHelper = new ItemTouchHelper(getItemTouchCallback());
+ itemTouchHelper.attachToRecyclerView(selectedTabsView);
+ selectedTabsAdapter.setOnItemSelectedListener(itemTouchHelper);
+
+ selectedTabsView.setAdapter(selectedTabsAdapter);
+ }
+
+ private void saveChanges() {
+ StringBuilder save = new StringBuilder();
+ if(selectedTabs.size()==0) {
+ save = new StringBuilder("0");
+ } else {
+ for(String s: selectedTabs) {
+ save.append(s);
+ save.append("\n");
+ }
+ }
+ saveString = save.toString();
+ }
+
+ @Override
+ public void onPause() {
+ saveChanges();
+ SharedPreferences sharedPreferences = android.preference.PreferenceManager.getDefaultSharedPreferences(getContext());
+ SharedPreferences.Editor editor = sharedPreferences.edit();
+ editor.putString("saveUsedTabs", saveString);
+ editor.commit();
+ super.onPause();
+ }
+
+ private void initUsedTabs() {
+ String save = android.preference.PreferenceManager.getDefaultSharedPreferences(getContext()).getString("saveUsedTabs", "1\tTrending\t0\n2\n4\n");
+ String tabs[] = save.trim().split("\n");
+ selectedTabs.addAll(Arrays.asList(tabs));
+ }
+
+ private void tabNames() {
+ availableTabs[0] = getString(R.string.blank_page_summary);
+ availableTabs[1] = getString(R.string.kiosk_page_summary);
+ availableTabs[2] = getString(R.string.subscription_page_summary);
+ availableTabs[3] = getString(R.string.feed_page_summary);
+ availableTabs[4] = getString(R.string.tab_bookmarks);
+ availableTabs[5] = getString(R.string.title_activity_history);
+ availableTabs[6] = getString(R.string.channel_page_summary);
+ }
+
+ private void initButton(View rootView) {
+ FloatingActionButton fab = rootView.findViewById(R.id.floatingActionButton);
+ fab.setImageResource(ThemeHelper.getIconByAttr(R.attr.ic_add, getContext()));
+ fab.setOnClickListener(v -> {
+ Dialog.OnClickListener onClickListener = (dialog, which) -> addTab(which);
+
+ new AddTabsDialog(getContext(),
+ getString(R.string.tab_chose),
+ availableTabs,
+ onClickListener)
+ .show();
+ });
+
+ TypedValue typedValue = new TypedValue();
+ getActivity().getTheme().resolveAttribute(R.attr.colorPrimary, typedValue, true);
+ int color = typedValue.data;
+ fab.setBackgroundTintList(ColorStateList.valueOf(color));
+ }
+
+
+ private void addTab(int position) {
+ if(position==6) {
+ SelectChannelFragment selectChannelFragment = new SelectChannelFragment();
+ selectChannelFragment.setOnSelectedLisener((String url, String name, int service) -> {
+ selectedTabs.add(position+"\t"+url+"\t"+name+"\t"+service);
+ selectedTabsAdapter.notifyDataSetChanged();
+ saveChanges();
+ });
+ selectChannelFragment.show(getFragmentManager(), "select_channel");
+ } else if(position==1) {
+ SelectKioskFragment selectKioskFragment = new SelectKioskFragment();
+ selectKioskFragment.setOnSelectedLisener((String kioskId, int service_id) -> {
+ selectedTabs.add(position+"\t"+kioskId+"\t"+service_id);
+ selectedTabsAdapter.notifyDataSetChanged();
+ saveChanges();
+ });
+ selectKioskFragment.show(getFragmentManager(), "select_kiosk");
+ } else {
+ selectedTabs.add(String.valueOf(position));
+ selectedTabsAdapter.notifyDataSetChanged();
+ saveChanges();
+ }
+ }
+
+ public class SelectedTabsAdapter extends RecyclerView.Adapter{
+ private ItemTouchHelper itemTouchHelper;
+
+ public void setOnItemSelectedListener(ItemTouchHelper mItemTouchHelper) {
+ itemTouchHelper = mItemTouchHelper;
+ }
+
+ public void swapItems(int fromPosition, int toPosition) {
+ String temp = selectedTabs.get(fromPosition);
+ selectedTabs.set(fromPosition, selectedTabs.get(toPosition));
+ selectedTabs.set(toPosition, temp);
+ notifyItemMoved(fromPosition, toPosition);
+ saveChanges();
+ }
+
+ @Override
+ public ChoseTabsFragment.SelectedTabsAdapter.TabViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+
+ LayoutInflater inflater = LayoutInflater.from(getContext());
+ View view = inflater.inflate(R.layout.viewholder_chose_tabs, parent, false);
+ return new ChoseTabsFragment.SelectedTabsAdapter.TabViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ChoseTabsFragment.SelectedTabsAdapter.TabViewHolder holder, int position) {
+ holder.bind(position, holder);
+ }
+
+ @Override
+ public int getItemCount() {
+ return selectedTabs.size();
+ }
+
+ class TabViewHolder extends RecyclerView.ViewHolder {
+
+ TextView text;
+ View view;
+ CardView cardView;
+ ImageView handle;
+
+ public TabViewHolder(View itemView) {
+ super(itemView);
+
+ text = itemView.findViewById(R.id.tabName);
+ cardView = itemView.findViewById(R.id.layoutCard);
+ handle = itemView.findViewById(R.id.handle);
+ view = itemView;
+ }
+
+ void bind(int position, TabViewHolder holder) {
+ handle.setImageResource(ThemeHelper.getIconByAttr(R.attr.drag_handle, getContext()));
+ handle.setOnTouchListener(getOnTouchListener(holder));
+
+ view.setOnLongClickListener(getOnLongClickListener(holder));
+
+ if(selectedTabs.get(position).startsWith("6\t")) {
+ String channelInfo[] = selectedTabs.get(position).split("\t");
+ String channelName = "";
+ if (channelInfo.length == 4) channelName = channelInfo[2];
+ String textToSet = availableTabs[6] + ": " + channelName;
+ text.setText(textToSet);
+ } else if(selectedTabs.get(position).startsWith("1\t")) {
+ String kioskInfo[] = selectedTabs.get(position).split("\t");
+ String kioskName = "";
+ if (kioskInfo.length == 3) kioskName = kioskInfo[1];
+ String textToSet = availableTabs[1] + ": " + kioskName;
+ text.setText(textToSet);
+ } else {
+ text.setText(availableTabs[Integer.parseInt(selectedTabs.get(position))]);
+ }
+ }
+
+ private View.OnTouchListener getOnTouchListener(final RecyclerView.ViewHolder item) {
+ return (view, motionEvent) -> {
+ view.performClick();
+ if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ if(itemTouchHelper != null) itemTouchHelper.startDrag(item);
+ }
+ return false;
+ };
+ }
+
+ private View.OnLongClickListener getOnLongClickListener(TabViewHolder holder) {
+ return (view) -> {
+ if(itemTouchHelper != null) itemTouchHelper.startSwipe(holder);
+ return false;
+ };
+ }
+
+ }
+ }
+
+ private ItemTouchHelper.SimpleCallback getItemTouchCallback() {
+ return new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN,
+ ItemTouchHelper.START | ItemTouchHelper.END) {
+ @Override
+ public int interpolateOutOfBoundsScroll(RecyclerView recyclerView, int viewSize,
+ int viewSizeOutOfBounds, int totalSize,
+ long msSinceStartScroll) {
+ final int standardSpeed = super.interpolateOutOfBoundsScroll(recyclerView, viewSize,
+ viewSizeOutOfBounds, totalSize, msSinceStartScroll);
+ final int minimumAbsVelocity = Math.max(12,
+ Math.abs(standardSpeed));
+ return minimumAbsVelocity * (int) Math.signum(viewSizeOutOfBounds);
+ }
+
+ @Override
+ public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source,
+ RecyclerView.ViewHolder target) {
+ if (source.getItemViewType() != target.getItemViewType() ||
+ selectedTabsAdapter == null) {
+ return false;
+ }
+
+ final int sourceIndex = source.getAdapterPosition();
+ final int targetIndex = target.getAdapterPosition();
+ selectedTabsAdapter.swapItems(sourceIndex, targetIndex);
+ return true;
+ }
+
+ @Override
+ public boolean isLongPressDragEnabled() {
+ return false;
+ }
+
+ @Override
+ public boolean isItemViewSwipeEnabled() {
+ return false;
+ }
+
+ @Override
+ public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
+ int position = viewHolder.getAdapterPosition();
+ selectedTabs.remove(position);
+ selectedTabsAdapter.notifyItemRemoved(position);
+ saveChanges();
+ }
+ };
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java
index 8f4e6d471..0ca78b34a 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java
@@ -9,6 +9,7 @@ import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference;
import android.util.Log;
@@ -20,15 +21,12 @@ import com.nostra13.universalimageloader.core.ImageLoader;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
-import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
-import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.FilePickerActivityHelper;
import org.schabi.newpipe.util.KioskTranslator;
import org.schabi.newpipe.util.ZipHelper;
-import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -42,11 +40,8 @@ import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.zip.ZipFile;
-import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
-import static android.content.Context.MODE_PRIVATE;
-
public class ContentSettingsFragment extends BasePreferenceFragment {
private static final int REQUEST_IMPORT_PATH = 8945;
@@ -98,68 +93,6 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
addPreferencesFromResource(R.xml.content_settings);
- final ListPreference mainPageContentPref = (ListPreference) findPreference(getString(R.string.main_page_content_key));
- mainPageContentPref.setOnPreferenceChangeListener((Preference preference, Object newValueO) -> {
- final String newValue = newValueO.toString();
-
- final String mainPrefOldValue =
- defaultPreferences.getString(getString(R.string.main_page_content_key), "blank_page");
- final String mainPrefOldSummary = getMainPagePrefSummery(mainPrefOldValue, mainPageContentPref);
-
- if(newValue.equals(getString(R.string.kiosk_page_key))) {
- SelectKioskFragment selectKioskFragment = new SelectKioskFragment();
- selectKioskFragment.setOnSelectedLisener((String kioskId, int service_id) -> {
- defaultPreferences.edit()
- .putInt(getString(R.string.main_page_selected_service), service_id).apply();
- defaultPreferences.edit()
- .putString(getString(R.string.main_page_selectd_kiosk_id), kioskId).apply();
- String serviceName = "";
- try {
- serviceName = NewPipe.getService(service_id).getServiceInfo().getName();
- } catch (ExtractionException e) {
- onError(e);
- }
- String kioskName = KioskTranslator.getTranslatedKioskName(kioskId,
- getContext());
-
- String summary =
- String.format(getString(R.string.service_kiosk_string),
- serviceName,
- kioskName);
-
- mainPageContentPref.setSummary(summary);
- });
- selectKioskFragment.setOnCancelListener(() -> {
- mainPageContentPref.setSummary(mainPrefOldSummary);
- mainPageContentPref.setValue(mainPrefOldValue);
- });
- selectKioskFragment.show(getFragmentManager(), "select_kiosk");
- } else if(newValue.equals(getString(R.string.channel_page_key))) {
- SelectChannelFragment selectChannelFragment = new SelectChannelFragment();
- selectChannelFragment.setOnSelectedLisener((String url, String name, int service) -> {
- defaultPreferences.edit()
- .putInt(getString(R.string.main_page_selected_service), service).apply();
- defaultPreferences.edit()
- .putString(getString(R.string.main_page_selected_channel_url), url).apply();
- defaultPreferences.edit()
- .putString(getString(R.string.main_page_selected_channel_name), name).apply();
-
- mainPageContentPref.setSummary(name);
- });
- selectChannelFragment.setOnCancelListener(() -> {
- mainPageContentPref.setSummary(mainPrefOldSummary);
- mainPageContentPref.setValue(mainPrefOldValue);
- });
- selectChannelFragment.show(getFragmentManager(), "select_channel");
- } else {
- mainPageContentPref.setSummary(getMainPageSummeryByKey(newValue));
- }
-
- defaultPreferences.edit().putBoolean(Constants.KEY_MAIN_PAGE_CHANGE, true).apply();
-
- return true;
- });
-
Preference importDataPreference = findPreference(getString(R.string.import_data));
importDataPreference.setOnPreferenceClickListener((Preference p) -> {
Intent i = new Intent(getActivity(), FilePickerActivityHelper.class)
@@ -349,66 +282,6 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
}
}
- @Override
- public void onResume() {
- super.onResume();
-
- final String mainPageContentKey = getString(R.string.main_page_content_key);
- final Preference mainPagePref = findPreference(getString(R.string.main_page_content_key));
- final String bpk = getString(R.string.blank_page_key);
- if(defaultPreferences.getString(mainPageContentKey, bpk)
- .equals(getString(R.string.channel_page_key))) {
- mainPagePref.setSummary(defaultPreferences.getString(getString(R.string.main_page_selected_channel_name), "error"));
- } else if(defaultPreferences.getString(mainPageContentKey, bpk)
- .equals(getString(R.string.kiosk_page_key))) {
- try {
- StreamingService service = NewPipe.getService(
- defaultPreferences.getInt(
- getString(R.string.main_page_selected_service), 0));
-
- String kioskName = KioskTranslator.getTranslatedKioskName(
- defaultPreferences.getString(
- getString(R.string.main_page_selectd_kiosk_id), "Trending"),
- getContext());
-
- String summary =
- String.format(getString(R.string.service_kiosk_string),
- service.getServiceInfo().getName(),
- kioskName);
-
- mainPagePref.setSummary(summary);
- } catch (Exception e) {
- onError(e);
- }
- }
- }
-
- /*//////////////////////////////////////////////////////////////////////////
- // Utils
- //////////////////////////////////////////////////////////////////////////*/
- private String getMainPagePrefSummery(final String mainPrefOldValue, final ListPreference mainPageContentPref) {
- if(mainPrefOldValue.equals(getString(R.string.channel_page_key))) {
- return defaultPreferences.getString(getString(R.string.main_page_selected_channel_name), "error");
- } else {
- return mainPageContentPref.getSummary().toString();
- }
- }
-
- private int getMainPageSummeryByKey(final String key) {
- if(key.equals(getString(R.string.blank_page_key))) {
- return R.string.blank_page_summary;
- } else if(key.equals(getString(R.string.kiosk_page_key))) {
- return R.string.kiosk_page_summary;
- } else if(key.equals(getString(R.string.feed_page_key))) {
- return R.string.feed_page_summary;
- } else if(key.equals(getString(R.string.subscription_page_key))) {
- return R.string.subscription_page_summary;
- } else if(key.equals(getString(R.string.channel_page_key))) {
- return R.string.channel_page_summary;
- }
- return R.string.blank_page_summary;
- }
-
/*//////////////////////////////////////////////////////////////////////////
// Error
//////////////////////////////////////////////////////////////////////////*/
diff --git a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java
index 92f98a9a2..2a0e2645b 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java
@@ -71,7 +71,7 @@ public class NewPipeSettings {
}
public static File getVideoDownloadFolder(Context context) {
- return getFolder(context, R.string.download_path_key, Environment.DIRECTORY_MOVIES);
+ return getDir(context, R.string.download_path_key, Environment.DIRECTORY_MOVIES);
}
public static String getVideoDownloadPath(Context context) {
@@ -81,7 +81,7 @@ public class NewPipeSettings {
}
public static File getAudioDownloadFolder(Context context) {
- return getFolder(context, R.string.download_path_audio_key, Environment.DIRECTORY_MUSIC);
+ return getDir(context, R.string.download_path_audio_key, Environment.DIRECTORY_MUSIC);
}
public static String getAudioDownloadPath(Context context) {
@@ -90,21 +90,37 @@ public class NewPipeSettings {
return prefs.getString(key, Environment.DIRECTORY_MUSIC);
}
- private static File getFolder(Context context, int keyID, String defaultDirectoryName) {
+ private static File getDir(Context context, int keyID, String defaultDirectoryName) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
final String key = context.getString(keyID);
String downloadPath = prefs.getString(key, null);
if ((downloadPath != null) && (!downloadPath.isEmpty())) return new File(downloadPath.trim());
- final File folder = getFolder(defaultDirectoryName);
+ final File dir = getDir(defaultDirectoryName);
SharedPreferences.Editor spEditor = prefs.edit();
- spEditor.putString(key, new File(folder, "NewPipe").getAbsolutePath());
+ spEditor.putString(key, getNewPipeChildFolderPathForDir(dir));
spEditor.apply();
- return folder;
+ return dir;
}
@NonNull
- private static File getFolder(String defaultDirectoryName) {
+ private static File getDir(String defaultDirectoryName) {
return new File(Environment.getExternalStorageDirectory(), defaultDirectoryName);
}
+
+ public static void resetDownloadFolders(Context context) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ resetDownloadFolder(prefs, context.getString(R.string.download_path_audio_key), Environment.DIRECTORY_MUSIC);
+ resetDownloadFolder(prefs, context.getString(R.string.download_path_key), Environment.DIRECTORY_MOVIES);
+ }
+
+ private static void resetDownloadFolder(SharedPreferences prefs, String key, String defaultDirectoryName) {
+ SharedPreferences.Editor spEditor = prefs.edit();
+ spEditor.putString(key, getNewPipeChildFolderPathForDir(getDir(defaultDirectoryName)));
+ spEditor.apply();
+ }
+
+ private static String getNewPipeChildFolderPathForDir(File dir) {
+ return new File(dir, "NewPipe").getAbsolutePath();
+ }
}
diff --git a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java
index 74e2d4cd5..e445233c3 100644
--- a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java
@@ -73,7 +73,7 @@ public final class ExtractorHelper {
return Single.fromCallable(() ->
SearchInfo.getInfo(NewPipe.getService(serviceId),
NewPipe.getService(serviceId)
- .getSearchQIHFactory()
+ .getSearchQHFactory()
.fromQuery(searchString, contentFilter, sortFilter),
contentCountry));
}
@@ -88,7 +88,7 @@ public final class ExtractorHelper {
return Single.fromCallable(() ->
SearchInfo.getMoreItems(NewPipe.getService(serviceId),
NewPipe.getService(serviceId)
- .getSearchQIHFactory()
+ .getSearchQHFactory()
.fromQuery(searchString, contentFilter, sortFilter),
contentCountry,
pageUrl));
diff --git a/app/src/main/java/org/schabi/newpipe/util/KioskTranslator.java b/app/src/main/java/org/schabi/newpipe/util/KioskTranslator.java
index 4740b82e0..392892cef 100644
--- a/app/src/main/java/org/schabi/newpipe/util/KioskTranslator.java
+++ b/app/src/main/java/org/schabi/newpipe/util/KioskTranslator.java
@@ -24,7 +24,7 @@ import org.schabi.newpipe.R;
public class KioskTranslator {
public static String getTranslatedKioskName(String kioskId, Context c) {
- switch(kioskId) {
+ switch (kioskId) {
case "Trending":
return c.getString(R.string.trending);
case "Top 50":
@@ -35,4 +35,17 @@ public class KioskTranslator {
return kioskId;
}
}
+
+ public static int getKioskIcons(String kioskId, Context c) {
+ switch(kioskId) {
+ case "Trending":
+ return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_hot);
+ case "Top 50":
+ return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_hot);
+ case "New & hot":
+ return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_hot);
+ default:
+ return 0;
+ }
+ }
}
diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
index 85367e2a5..13767125d 100644
--- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
@@ -31,17 +31,17 @@ import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.Stream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream;
-import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
-import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler;
import org.schabi.newpipe.fragments.MainFragment;
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
import org.schabi.newpipe.fragments.list.channel.ChannelFragment;
+import org.schabi.newpipe.local.bookmark.BookmarkFragment;
import org.schabi.newpipe.local.feed.FeedFragment;
import org.schabi.newpipe.fragments.list.kiosk.KioskFragment;
import org.schabi.newpipe.fragments.list.playlist.PlaylistFragment;
import org.schabi.newpipe.fragments.list.search.SearchFragment;
import org.schabi.newpipe.local.history.StatisticsPlaylistFragment;
import org.schabi.newpipe.local.playlist.LocalPlaylistFragment;
+import org.schabi.newpipe.local.subscription.SubscriptionFragment;
import org.schabi.newpipe.local.subscription.SubscriptionsImportFragment;
import org.schabi.newpipe.player.BackgroundPlayer;
import org.schabi.newpipe.player.BackgroundPlayerActivity;
@@ -349,6 +349,20 @@ public class NavigationHelper {
.commit();
}
+ public static void openBookmarksFragment(FragmentManager fragmentManager) {
+ defaultTransaction(fragmentManager)
+ .replace(R.id.fragment_holder, new BookmarkFragment())
+ .addToBackStack(null)
+ .commit();
+ }
+
+ public static void openSubscriptionFragment(FragmentManager fragmentManager) {
+ defaultTransaction(fragmentManager)
+ .replace(R.id.fragment_holder, new SubscriptionFragment())
+ .addToBackStack(null)
+ .commit();
+ }
+
public static void openKioskFragment(FragmentManager fragmentManager, int serviceId, String kioskId) throws ExtractionException {
defaultTransaction(fragmentManager)
.replace(R.id.fragment_holder, KioskFragment.getInstance(serviceId, kioskId))
diff --git a/app/src/main/java/us/shandian/giga/get/DownloadManagerImpl.java b/app/src/main/java/us/shandian/giga/get/DownloadManagerImpl.java
index ecd3ce562..3294f5164 100755
--- a/app/src/main/java/us/shandian/giga/get/DownloadManagerImpl.java
+++ b/app/src/main/java/us/shandian/giga/get/DownloadManagerImpl.java
@@ -1,10 +1,17 @@
package us.shandian.giga.get;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
+import org.schabi.newpipe.download.ExtSDDownloadFailedActivity;
+
import java.io.File;
import java.io.FilenameFilter;
+import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
@@ -23,7 +30,9 @@ public class DownloadManagerImpl implements DownloadManager {
private static final String TAG = DownloadManagerImpl.class.getSimpleName();
private final DownloadDataSource mDownloadDataSource;
- private final ArrayList mMissions = new ArrayList();
+ private final ArrayList mMissions = new ArrayList<>();
+ @NonNull
+ private final Context context;
/**
* Create a new instance
@@ -33,6 +42,13 @@ public class DownloadManagerImpl implements DownloadManager {
*/
public DownloadManagerImpl(Collection searchLocations, DownloadDataSource downloadDataSource) {
mDownloadDataSource = downloadDataSource;
+ this.context = null;
+ loadMissions(searchLocations);
+ }
+
+ public DownloadManagerImpl(Collection searchLocations, DownloadDataSource downloadDataSource, Context context) {
+ mDownloadDataSource = downloadDataSource;
+ this.context = context;
loadMissions(searchLocations);
}
@@ -277,10 +293,12 @@ public class DownloadManagerImpl implements DownloadManager {
}
private class Initializer extends Thread {
- private DownloadMission mission;
+ private final DownloadMission mission;
+ private final Handler handler;
public Initializer(DownloadMission mission) {
this.mission = mission;
+ this.handler = new Handler();
}
@Override
@@ -335,6 +353,13 @@ public class DownloadManagerImpl implements DownloadManager {
af.close();
mission.start();
+ } catch (IOException ie) {
+ if(context == null) throw new RuntimeException(ie);
+
+ if(ie.getMessage().contains("Permission denied")) {
+ handler.post(() ->
+ context.startActivity(new Intent(context, ExtSDDownloadFailedActivity.class)));
+ } else throw new RuntimeException(ie);
} catch (Exception e) {
// TODO Notify
throw new RuntimeException(e);
diff --git a/app/src/main/java/us/shandian/giga/service/DownloadManagerService.java b/app/src/main/java/us/shandian/giga/service/DownloadManagerService.java
index 50975728f..59f5e2225 100755
--- a/app/src/main/java/us/shandian/giga/service/DownloadManagerService.java
+++ b/app/src/main/java/us/shandian/giga/service/DownloadManagerService.java
@@ -81,7 +81,7 @@ public class DownloadManagerService extends Service {
ArrayList paths = new ArrayList<>(2);
paths.add(NewPipeSettings.getVideoDownloadPath(this));
paths.add(NewPipeSettings.getAudioDownloadPath(this));
- mManager = new DownloadManagerImpl(paths, mDataSource);
+ mManager = new DownloadManagerImpl(paths, mDataSource, this);
if (DEBUG) {
Log.d(TAG, "mManager == null");
Log.d(TAG, "Download directory: " + paths);
diff --git a/app/src/main/res/drawable-hdpi/ic_add.png b/app/src/main/res/drawable-hdpi/ic_add.png
new file mode 100644
index 000000000..1ae5b2dc4
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_add.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_arrow_down_white.png b/app/src/main/res/drawable-hdpi/ic_arrow_down_white.png
new file mode 100644
index 000000000..33939600d
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_arrow_down_white.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_arrow_up_white.png b/app/src/main/res/drawable-hdpi/ic_arrow_up_white.png
new file mode 100644
index 000000000..0972a9bca
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_arrow_up_white.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_remove.png b/app/src/main/res/drawable-hdpi/ic_remove.png
new file mode 100644
index 000000000..75e65bc9c
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_remove.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_add.png b/app/src/main/res/drawable-mdpi/ic_add.png
new file mode 100644
index 000000000..d51f0ddad
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_add.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_arrow_down_white.png b/app/src/main/res/drawable-mdpi/ic_arrow_down_white.png
new file mode 100644
index 000000000..40a0f499e
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_arrow_down_white.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_arrow_up_white.png b/app/src/main/res/drawable-mdpi/ic_arrow_up_white.png
new file mode 100644
index 000000000..fe67b4673
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_arrow_up_white.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_remove.png b/app/src/main/res/drawable-mdpi/ic_remove.png
new file mode 100644
index 000000000..a1816d4c6
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_remove.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_add.png b/app/src/main/res/drawable-xhdpi/ic_add.png
new file mode 100644
index 000000000..9ea0eeb7e
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_add.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_arrow_down_white.png b/app/src/main/res/drawable-xhdpi/ic_arrow_down_white.png
new file mode 100644
index 000000000..86bc5db3b
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_arrow_down_white.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_arrow_up_white.png b/app/src/main/res/drawable-xhdpi/ic_arrow_up_white.png
new file mode 100644
index 000000000..dda36882e
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_arrow_up_white.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_remove.png b/app/src/main/res/drawable-xhdpi/ic_remove.png
new file mode 100644
index 000000000..ffbdaa6ed
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_remove.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_add.png b/app/src/main/res/drawable-xxhdpi/ic_add.png
new file mode 100644
index 000000000..75f192aab
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_add.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_arrow_down_white.png b/app/src/main/res/drawable-xxhdpi/ic_arrow_down_white.png
new file mode 100644
index 000000000..7e901e098
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_arrow_down_white.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_arrow_up_white.png b/app/src/main/res/drawable-xxhdpi/ic_arrow_up_white.png
new file mode 100644
index 000000000..bc71e23de
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_arrow_up_white.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_remove.png b/app/src/main/res/drawable-xxhdpi/ic_remove.png
new file mode 100644
index 000000000..d35469d3c
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_remove.png differ
diff --git a/app/src/main/res/drawable/background_oval_black_transparent.xml b/app/src/main/res/drawable/background_oval_black_transparent.xml
new file mode 100644
index 000000000..5db5969c6
--- /dev/null
+++ b/app/src/main/res/drawable/background_oval_black_transparent.xml
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_add_black_24dp.xml b/app/src/main/res/drawable/ic_add_black_24dp.xml
new file mode 100644
index 000000000..0258249cc
--- /dev/null
+++ b/app/src/main/res/drawable/ic_add_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_add_white_24dp.xml b/app/src/main/res/drawable/ic_add_white_24dp.xml
new file mode 100644
index 000000000..e3979cd7f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_add_white_24dp.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_brightness_high_white_72dp.xml b/app/src/main/res/drawable/ic_brightness_high_white_72dp.xml
new file mode 100644
index 000000000..12d0084a8
--- /dev/null
+++ b/app/src/main/res/drawable/ic_brightness_high_white_72dp.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_brightness_low_white_72dp.xml b/app/src/main/res/drawable/ic_brightness_low_white_72dp.xml
new file mode 100644
index 000000000..9c4f2f71e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_brightness_low_white_72dp.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_brightness_medium_white_72dp.xml b/app/src/main/res/drawable/ic_brightness_medium_white_72dp.xml
new file mode 100644
index 000000000..fc100086f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_brightness_medium_white_72dp.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_volume_down_white_72dp.xml b/app/src/main/res/drawable/ic_volume_down_white_72dp.xml
new file mode 100644
index 000000000..a7fafb3a5
--- /dev/null
+++ b/app/src/main/res/drawable/ic_volume_down_white_72dp.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_volume_mute_white_72dp.xml b/app/src/main/res/drawable/ic_volume_mute_white_72dp.xml
new file mode 100644
index 000000000..1a8ab7e86
--- /dev/null
+++ b/app/src/main/res/drawable/ic_volume_mute_white_72dp.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_volume_off_white_72dp.xml b/app/src/main/res/drawable/ic_volume_off_white_72dp.xml
new file mode 100644
index 000000000..07f24d7aa
--- /dev/null
+++ b/app/src/main/res/drawable/ic_volume_off_white_72dp.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_volume_up_white_72dp.xml b/app/src/main/res/drawable/ic_volume_up_white_72dp.xml
new file mode 100644
index 000000000..b2fb235a6
--- /dev/null
+++ b/app/src/main/res/drawable/ic_volume_up_white_72dp.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/progress_circular_white.xml b/app/src/main/res/drawable/progress_circular_white.xml
new file mode 100644
index 000000000..daa6649bc
--- /dev/null
+++ b/app/src/main/res/drawable/progress_circular_white.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-v21/drawer_header.xml b/app/src/main/res/layout-v21/drawer_header.xml
new file mode 100644
index 000000000..918bbaf43
--- /dev/null
+++ b/app/src/main/res/layout-v21/drawer_header.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 99c637389..b70d73250 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -4,7 +4,8 @@
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true">
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/activity_main_player.xml b/app/src/main/res/layout/activity_main_player.xml
index 27aa56025..3dccc5c4c 100644
--- a/app/src/main/res/layout/activity_main_player.xml
+++ b/app/src/main/res/layout/activity_main_player.xml
@@ -471,10 +471,11 @@
android:id="@+id/controlAnimationView"
android:layout_width="100dp"
android:layout_height="100dp"
- android:src="@drawable/ic_action_av_fast_rewind"
+ android:background="@drawable/background_oval_black_transparent"
android:visibility="gone"
tools:ignore="ContentDescription"
- tools:visibility="visible"/>
+ tools:src="@drawable/ic_action_av_fast_rewind"
+ tools:visibility="visible" />
@@ -503,43 +504,57 @@
android:layout_toRightOf="@+id/loading_panel"
tools:ignore="RtlHardcoded">
-
+ tools:visibility="visible">
-
+
+
+
+
+
+ tools:visibility="visible">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/drawer_layout.xml b/app/src/main/res/layout/drawer_layout.xml
index c0186a02c..4732df719 100644
--- a/app/src/main/res/layout/drawer_layout.xml
+++ b/app/src/main/res/layout/drawer_layout.xml
@@ -1,81 +1,22 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:focusable="true"
+ >
+ app:elevation="0dp"
+ android:background="?attr/android:windowBackground"
+ app:headerLayout="@layout/drawer_header"/>
-
-
-
-
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_chose_tabs.xml b/app/src/main/res/layout/fragment_chose_tabs.xml
new file mode 100644
index 000000000..2bdf261f4
--- /dev/null
+++ b/app/src/main/res/layout/fragment_chose_tabs.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/player_popup.xml b/app/src/main/res/layout/player_popup.xml
index 001d43bf6..7a92c6712 100644
--- a/app/src/main/res/layout/player_popup.xml
+++ b/app/src/main/res/layout/player_popup.xml
@@ -9,7 +9,6 @@
tools:layout_height="84dp"
tools:layout_width="@dimen/popup_minimum_width">
-
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/player_popup_close_overlay.xml b/app/src/main/res/layout/player_popup_close_overlay.xml
new file mode 100644
index 000000000..f7aceadec
--- /dev/null
+++ b/app/src/main/res/layout/player_popup_close_overlay.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/viewholder_chose_tabs.xml b/app/src/main/res/layout/viewholder_chose_tabs.xml
new file mode 100644
index 000000000..311d1441f
--- /dev/null
+++ b/app/src/main/res/layout/viewholder_chose_tabs.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/drawer_items.xml b/app/src/main/res/menu/drawer_items.xml
index ae4598edd..9d5e67bb4 100644
--- a/app/src/main/res/menu/drawer_items.xml
+++ b/app/src/main/res/menu/drawer_items.xml
@@ -2,13 +2,11 @@
\ No newline at end of file
diff --git a/app/src/main/res/menu/main_menu.xml b/app/src/main/res/menu/main_menu.xml
index b6372433e..05920099a 100644
--- a/app/src/main/res/menu/main_menu.xml
+++ b/app/src/main/res/menu/main_menu.xml
@@ -1,6 +1,6 @@
\ No newline at end of file
diff --git a/app/src/main/res/values-v21/styles_services.xml b/app/src/main/res/values-v21/styles_services.xml
new file mode 100644
index 000000000..e3d6c24e2
--- /dev/null
+++ b/app/src/main/res/values-v21/styles_services.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index d07b4a0a5..25b27ee12 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -35,6 +35,7 @@
+
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index df2c73ec8..8fb5ed7c7 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -29,13 +29,13 @@
#af000000
- #000
+ #000000
@color/dark_settings_accent_color
#1effffff
#23454545
- #AA000000
+ #aa000000
#c8E53935
#e6000000
#EEFFFFFF
@@ -70,5 +70,6 @@
#616161
#000
+ #be757575
diff --git a/app/src/main/res/values/colors_services.xml b/app/src/main/res/values/colors_services.xml
index 36a292453..761b721d0 100644
--- a/app/src/main/res/values/colors_services.xml
+++ b/app/src/main/res/values/colors_services.xml
@@ -2,20 +2,24 @@
#e53935
- #d32f2f
+ #992722
#000000
+ #ff4336
#CD322E
- #BC211D
+ #992722
#FFFFFF
+ #ff4336
#f57c00
- #ef6c00
+ #995700
#000000
+ #ff9100
#f57c00
- #ef6c00
+ #995700
#FFFFFF
+ #ff9100
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index f3976c6a4..28c0bfe27 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -35,6 +35,8 @@
Main
Subscriptions
Bookmarks
+ New Tab
+ Chose Tab
What\'s New
@@ -168,6 +170,8 @@
Search history deleted.
Error
+ External storage not available.
+ Download to external SD Card is not possible yet. Should the download place be reset?
Network error
Could not load all thumbnails
Could not decrypt video URL signature
@@ -357,6 +361,9 @@
Content of main page
+ What tabs are shown on the main page
+ Selection
+ Your tabs
Blank Page
Kiosk Page
Subscription Page
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 7d93e0994..1f7280e6f 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -51,6 +51,7 @@
- @drawable/ic_import_export_black_24dp
- @drawable/ic_save_black_24dp
- @drawable/ic_backup_black_24dp
+ - @drawable/ic_add_black_24dp
- @color/light_separator_color
- @color/light_contrast_background_color
@@ -108,6 +109,7 @@
- @drawable/ic_import_export_white_24dp
- @drawable/ic_save_white_24dp
- @drawable/ic_backup_white_24dp
+ - @drawable/ic_add_white_24dp
- @color/dark_separator_color
- @color/dark_contrast_background_color
diff --git a/app/src/main/res/values/styles_services.xml b/app/src/main/res/values/styles_services.xml
index 6ed8c29e9..7ca9dacde 100644
--- a/app/src/main/res/values/styles_services.xml
+++ b/app/src/main/res/values/styles_services.xml
@@ -1,12 +1,14 @@
-
+
-
-
-
+
+