Merge remote-tracking branch 'origin/dev' into dev
@ -185,7 +185,7 @@
|
|||||||
android:name=".RouterPopupActivity"
|
android:name=".RouterPopupActivity"
|
||||||
android:label="@string/popup_mode_share_menu_title"
|
android:label="@string/popup_mode_share_menu_title"
|
||||||
android:taskAffinity=""
|
android:taskAffinity=""
|
||||||
android:theme="@android:style/Theme.NoDisplay">
|
android:theme="@style/PopupPermissionsTheme">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH"/>
|
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH"/>
|
||||||
|
@ -21,6 +21,7 @@ public class RouterPopupActivity extends RouterActivity {
|
|||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||||
&& !PermissionHelper.checkSystemAlertWindowPermission(this)) {
|
&& !PermissionHelper.checkSystemAlertWindowPermission(this)) {
|
||||||
Toast.makeText(this, R.string.msg_popup_permission, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, R.string.msg_popup_permission, Toast.LENGTH_LONG).show();
|
||||||
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
StreamingService service;
|
StreamingService service;
|
||||||
|
@ -485,6 +485,8 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
|||||||
|
|
||||||
private void showStreamDialog(final StreamInfoItem item) {
|
private void showStreamDialog(final StreamInfoItem item) {
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
|
if (context == null || context.getResources() == null || getActivity() == null) return;
|
||||||
|
|
||||||
final String[] commands = new String[]{
|
final String[] commands = new String[]{
|
||||||
context.getResources().getString(R.string.enqueue_on_background),
|
context.getResources().getString(R.string.enqueue_on_background),
|
||||||
context.getResources().getString(R.string.enqueue_on_popup)
|
context.getResources().getString(R.string.enqueue_on_popup)
|
||||||
|
@ -192,6 +192,8 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
|||||||
|
|
||||||
protected void showStreamDialog(final StreamInfoItem item) {
|
protected void showStreamDialog(final StreamInfoItem item) {
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
|
if (context == null || context.getResources() == null || getActivity() == null) return;
|
||||||
|
|
||||||
final String[] commands = new String[]{
|
final String[] commands = new String[]{
|
||||||
context.getResources().getString(R.string.enqueue_on_background),
|
context.getResources().getString(R.string.enqueue_on_background),
|
||||||
context.getResources().getString(R.string.enqueue_on_popup)
|
context.getResources().getString(R.string.enqueue_on_popup)
|
||||||
|
@ -154,6 +154,8 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
|||||||
@Override
|
@Override
|
||||||
protected void showStreamDialog(final StreamInfoItem item) {
|
protected void showStreamDialog(final StreamInfoItem item) {
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
|
if (context == null || context.getResources() == null || getActivity() == null) return;
|
||||||
|
|
||||||
final String[] commands = new String[]{
|
final String[] commands = new String[]{
|
||||||
context.getResources().getString(R.string.enqueue_on_background),
|
context.getResources().getString(R.string.enqueue_on_background),
|
||||||
context.getResources().getString(R.string.enqueue_on_popup),
|
context.getResources().getString(R.string.enqueue_on_popup),
|
||||||
|
@ -15,7 +15,6 @@ import android.view.MenuInflater;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
@ -109,6 +108,8 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||||||
@Override
|
@Override
|
||||||
protected void showStreamDialog(final StreamInfoItem item) {
|
protected void showStreamDialog(final StreamInfoItem item) {
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
|
if (context == null || context.getResources() == null || getActivity() == null) return;
|
||||||
|
|
||||||
final String[] commands = new String[]{
|
final String[] commands = new String[]{
|
||||||
context.getResources().getString(R.string.enqueue_on_background),
|
context.getResources().getString(R.string.enqueue_on_background),
|
||||||
context.getResources().getString(R.string.enqueue_on_popup),
|
context.getResources().getString(R.string.enqueue_on_popup),
|
||||||
|
@ -165,7 +165,7 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor
|
|||||||
suggestionListAdapter = new SuggestionListAdapter(activity);
|
suggestionListAdapter = new SuggestionListAdapter(activity);
|
||||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
|
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||||
isSearchHistoryEnabled = preferences.getBoolean(getString(R.string.enable_search_history_key), true);
|
isSearchHistoryEnabled = preferences.getBoolean(getString(R.string.enable_search_history_key), true);
|
||||||
suggestionListAdapter.setShowSugestinHistory(isSearchHistoryEnabled);
|
suggestionListAdapter.setShowSuggestionHistory(isSearchHistoryEnabled);
|
||||||
|
|
||||||
searchHistoryDAO = NewPipeDatabase.getInstance().searchHistoryDAO();
|
searchHistoryDAO = NewPipeDatabase.getInstance().searchHistoryDAO();
|
||||||
}
|
}
|
||||||
@ -446,6 +446,12 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor
|
|||||||
searchEditText.setText(item.query);
|
searchEditText.setText(item.query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuggestionItemInserted(SuggestionItem item) {
|
||||||
|
searchEditText.setText(item.query);
|
||||||
|
searchEditText.setSelection(searchEditText.getText().length());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuggestionItemLongClick(SuggestionItem item) {
|
public void onSuggestionItemLongClick(SuggestionItem item) {
|
||||||
if (item.fromHistory) showDeleteSuggestionDialog(item);
|
if (item.fromHistory) showDeleteSuggestionDialog(item);
|
||||||
|
@ -19,10 +19,11 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
|
|||||||
private final ArrayList<SuggestionItem> items = new ArrayList<>();
|
private final ArrayList<SuggestionItem> items = new ArrayList<>();
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private OnSuggestionItemSelected listener;
|
private OnSuggestionItemSelected listener;
|
||||||
private boolean showSugestinHistory = true;
|
private boolean showSuggestionHistory = true;
|
||||||
|
|
||||||
public interface OnSuggestionItemSelected {
|
public interface OnSuggestionItemSelected {
|
||||||
void onSuggestionItemSelected(SuggestionItem item);
|
void onSuggestionItemSelected(SuggestionItem item);
|
||||||
|
void onSuggestionItemInserted(SuggestionItem item);
|
||||||
void onSuggestionItemLongClick(SuggestionItem item);
|
void onSuggestionItemLongClick(SuggestionItem item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +33,7 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
|
|||||||
|
|
||||||
public void setItems(List<SuggestionItem> items) {
|
public void setItems(List<SuggestionItem> items) {
|
||||||
this.items.clear();
|
this.items.clear();
|
||||||
if (showSugestinHistory) {
|
if (showSuggestionHistory) {
|
||||||
this.items.addAll(items);
|
this.items.addAll(items);
|
||||||
} else {
|
} else {
|
||||||
// remove history items if history is disabled
|
// remove history items if history is disabled
|
||||||
@ -49,8 +50,8 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
|
|||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setShowSugestinHistory(boolean v) {
|
public void setShowSuggestionHistory(boolean v) {
|
||||||
showSugestinHistory = v;
|
showSuggestionHistory = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -62,19 +63,25 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
|
|||||||
public void onBindViewHolder(SuggestionItemHolder holder, int position) {
|
public void onBindViewHolder(SuggestionItemHolder holder, int position) {
|
||||||
final SuggestionItem currentItem = getItem(position);
|
final SuggestionItem currentItem = getItem(position);
|
||||||
holder.updateFrom(currentItem);
|
holder.updateFrom(currentItem);
|
||||||
holder.itemView.setOnClickListener(new View.OnClickListener() {
|
holder.queryView.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
if (listener != null) listener.onSuggestionItemSelected(currentItem);
|
if (listener != null) listener.onSuggestionItemSelected(currentItem);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
|
holder.queryView.setOnLongClickListener(new View.OnLongClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onLongClick(View v) {
|
public boolean onLongClick(View v) {
|
||||||
if (listener != null) listener.onSuggestionItemLongClick(currentItem);
|
if (listener != null) listener.onSuggestionItemLongClick(currentItem);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
holder.insertView.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
if (listener != null) listener.onSuggestionItemInserted(currentItem);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private SuggestionItem getItem(int position) {
|
private SuggestionItem getItem(int position) {
|
||||||
@ -93,6 +100,8 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
|
|||||||
public static class SuggestionItemHolder extends RecyclerView.ViewHolder {
|
public static class SuggestionItemHolder extends RecyclerView.ViewHolder {
|
||||||
private final TextView itemSuggestionQuery;
|
private final TextView itemSuggestionQuery;
|
||||||
private final ImageView suggestionIcon;
|
private final ImageView suggestionIcon;
|
||||||
|
private final View queryView;
|
||||||
|
private final View insertView;
|
||||||
|
|
||||||
// Cache some ids, as they can potentially be constantly updated/recycled
|
// Cache some ids, as they can potentially be constantly updated/recycled
|
||||||
private final int historyResId;
|
private final int historyResId;
|
||||||
@ -103,6 +112,9 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
|
|||||||
suggestionIcon = rootView.findViewById(R.id.item_suggestion_icon);
|
suggestionIcon = rootView.findViewById(R.id.item_suggestion_icon);
|
||||||
itemSuggestionQuery = rootView.findViewById(R.id.item_suggestion_query);
|
itemSuggestionQuery = rootView.findViewById(R.id.item_suggestion_query);
|
||||||
|
|
||||||
|
queryView = rootView.findViewById(R.id.suggestion_search);
|
||||||
|
insertView = rootView.findViewById(R.id.suggestion_insert);
|
||||||
|
|
||||||
historyResId = resolveResourceIdFromAttr(rootView.getContext(), R.attr.history);
|
historyResId = resolveResourceIdFromAttr(rootView.getContext(), R.attr.history);
|
||||||
searchResId = resolveResourceIdFromAttr(rootView.getContext(), R.attr.search);
|
searchResId = resolveResourceIdFromAttr(rootView.getContext(), R.attr.search);
|
||||||
}
|
}
|
||||||
|
@ -4,28 +4,44 @@ import android.app.Activity;
|
|||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.InfoItem;
|
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||||
|
|
||||||
public class InfoItemDialog {
|
public class InfoItemDialog {
|
||||||
private final AlertDialog dialog;
|
private final AlertDialog dialog;
|
||||||
|
|
||||||
public InfoItemDialog(@NonNull final Activity activity,
|
public InfoItemDialog(@NonNull final Activity activity,
|
||||||
@NonNull final InfoItem item,
|
@NonNull final StreamInfoItem info,
|
||||||
@NonNull final String[] commands,
|
@NonNull final String[] commands,
|
||||||
@NonNull final DialogInterface.OnClickListener actions) {
|
@NonNull final DialogInterface.OnClickListener actions) {
|
||||||
|
this(activity, commands, actions, info.name, info.uploader_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InfoItemDialog(@NonNull final Activity activity,
|
||||||
|
@NonNull final String[] commands,
|
||||||
|
@NonNull final DialogInterface.OnClickListener actions,
|
||||||
|
@NonNull final String title,
|
||||||
|
@Nullable final String additionalDetail) {
|
||||||
|
|
||||||
final LayoutInflater inflater = activity.getLayoutInflater();
|
final LayoutInflater inflater = activity.getLayoutInflater();
|
||||||
final View bannerView = inflater.inflate(R.layout.dialog_title, null);
|
final View bannerView = inflater.inflate(R.layout.dialog_title, null);
|
||||||
bannerView.setSelected(true);
|
bannerView.setSelected(true);
|
||||||
|
|
||||||
TextView titleView = bannerView.findViewById(R.id.itemTitleView);
|
TextView titleView = bannerView.findViewById(R.id.itemTitleView);
|
||||||
titleView.setText(item.name);
|
titleView.setText(title);
|
||||||
TextView typeView = bannerView.findViewById(R.id.itemTypeView);
|
|
||||||
typeView.setText(item.info_type.name());
|
TextView detailsView = bannerView.findViewById(R.id.itemAdditionalDetails);
|
||||||
|
if (additionalDetail != null) {
|
||||||
|
detailsView.setText(additionalDetail);
|
||||||
|
detailsView.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
detailsView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
dialog = new AlertDialog.Builder(activity)
|
dialog = new AlertDialog.Builder(activity)
|
||||||
.setCustomTitle(bannerView)
|
.setCustomTitle(bannerView)
|
||||||
|
@ -134,6 +134,7 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
|||||||
protected final static int FAST_FORWARD_REWIND_AMOUNT = 10000; // 10 Seconds
|
protected final static int FAST_FORWARD_REWIND_AMOUNT = 10000; // 10 Seconds
|
||||||
protected final static int PLAY_PREV_ACTIVATION_LIMIT = 5000; // 5 seconds
|
protected final static int PLAY_PREV_ACTIVATION_LIMIT = 5000; // 5 seconds
|
||||||
protected final static int PROGRESS_LOOP_INTERVAL = 500;
|
protected final static int PROGRESS_LOOP_INTERVAL = 500;
|
||||||
|
protected final static int RECOVERY_SKIP_THRESHOLD = 3000; // 3 seconds
|
||||||
|
|
||||||
protected SimpleExoPlayer simpleExoPlayer;
|
protected SimpleExoPlayer simpleExoPlayer;
|
||||||
protected AudioReactor audioReactor;
|
protected AudioReactor audioReactor;
|
||||||
@ -453,16 +454,20 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
|||||||
final PlayQueueItem currentSourceItem = playQueue.getItem();
|
final PlayQueueItem currentSourceItem = playQueue.getItem();
|
||||||
|
|
||||||
// Check if already playing correct window
|
// Check if already playing correct window
|
||||||
final boolean isCurrentWindowCorrect = simpleExoPlayer.getCurrentWindowIndex() == currentSourceIndex;
|
final boolean isCurrentWindowCorrect =
|
||||||
|
simpleExoPlayer.getCurrentWindowIndex() == currentSourceIndex;
|
||||||
|
|
||||||
// Check if recovering
|
// Check if recovering
|
||||||
if (isCurrentWindowCorrect && currentSourceItem != null &&
|
if (isCurrentWindowCorrect && currentSourceItem != null) {
|
||||||
currentSourceItem.getRecoveryPosition() != PlayQueueItem.RECOVERY_UNSET) {
|
|
||||||
/* Recovering with sub-second position may cause a long buffer delay in ExoPlayer,
|
/* Recovering with sub-second position may cause a long buffer delay in ExoPlayer,
|
||||||
* rounding this position to the nearest second will help alleviate this.*/
|
* rounding this position to the nearest second will help alleviate this.*/
|
||||||
final long position = currentSourceItem.getRecoveryPosition();
|
final long position = currentSourceItem.getRecoveryPosition();
|
||||||
|
|
||||||
if (DEBUG) Log.d(TAG, "Rewinding to recovery window: " + currentSourceIndex + " at: " + getTimeString((int)position));
|
/* Skip recovering if the recovery position is not set.*/
|
||||||
|
if (position == PlayQueueItem.RECOVERY_UNSET) return;
|
||||||
|
|
||||||
|
if (DEBUG) Log.d(TAG, "Rewinding to recovery window: " + currentSourceIndex +
|
||||||
|
" at: " + getTimeString((int)position));
|
||||||
simpleExoPlayer.seekTo(currentSourceItem.getRecoveryPosition());
|
simpleExoPlayer.seekTo(currentSourceItem.getRecoveryPosition());
|
||||||
playQueue.unsetRecovery(currentSourceIndex);
|
playQueue.unsetRecovery(currentSourceIndex);
|
||||||
}
|
}
|
||||||
@ -514,10 +519,10 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Player.STATE_READY: //3
|
case Player.STATE_READY: //3
|
||||||
|
recover();
|
||||||
if (!isPrepared) {
|
if (!isPrepared) {
|
||||||
isPrepared = true;
|
isPrepared = true;
|
||||||
onPrepared(playWhenReady);
|
onPrepared(playWhenReady);
|
||||||
recover();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (currentState == STATE_PAUSED_SEEK) break;
|
if (currentState == STATE_PAUSED_SEEK) break;
|
||||||
@ -544,14 +549,18 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
|||||||
* an error to the play queue based on if the current error can be skipped.
|
* an error to the play queue based on if the current error can be skipped.
|
||||||
*
|
*
|
||||||
* This is done because ExoPlayer reports the source exceptions before window is
|
* This is done because ExoPlayer reports the source exceptions before window is
|
||||||
* transitioned on seamless playback.
|
* transitioned on seamless playback. Because player error causes ExoPlayer to go
|
||||||
|
* back to {@link Player#STATE_IDLE STATE_IDLE}, we reset and prepare the media source
|
||||||
|
* again to resume playback.
|
||||||
*
|
*
|
||||||
* Because player error causes ExoPlayer to go back to {@link Player#STATE_IDLE STATE_IDLE},
|
* In the event that this error is produced during a valid stream playback, we save the
|
||||||
* we reset and prepare the media source again to resume playback.<br><br>
|
* current position so the playback may be recovered and resumed manually by the user. This
|
||||||
|
* happens only if the playback is {@link #RECOVERY_SKIP_THRESHOLD} milliseconds until complete.
|
||||||
|
* <br><br>
|
||||||
*
|
*
|
||||||
* {@link ExoPlaybackException#TYPE_UNEXPECTED TYPE_UNEXPECTED}: <br><br>
|
* {@link ExoPlaybackException#TYPE_UNEXPECTED TYPE_UNEXPECTED}: <br><br>
|
||||||
* If a runtime error occurred, then we can try to recover it by restarting the playback
|
* If a runtime error occurred, then we can try to recover it by restarting the playback
|
||||||
* after setting the timestamp recovery.
|
* after setting the timestamp recovery. <br><br>
|
||||||
*
|
*
|
||||||
* {@link ExoPlaybackException#TYPE_RENDERER TYPE_RENDERER}: <br><br>
|
* {@link ExoPlaybackException#TYPE_RENDERER TYPE_RENDERER}: <br><br>
|
||||||
* If the renderer failed, treat the error as unrecoverable.
|
* If the renderer failed, treat the error as unrecoverable.
|
||||||
@ -568,6 +577,10 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
|||||||
|
|
||||||
switch (error.type) {
|
switch (error.type) {
|
||||||
case ExoPlaybackException.TYPE_SOURCE:
|
case ExoPlaybackException.TYPE_SOURCE:
|
||||||
|
if (simpleExoPlayer.getCurrentPosition() <
|
||||||
|
simpleExoPlayer.getDuration() - RECOVERY_SKIP_THRESHOLD) {
|
||||||
|
setRecovery();
|
||||||
|
}
|
||||||
playQueue.error(isCurrentWindowValid());
|
playQueue.error(isCurrentWindowValid());
|
||||||
showStreamError(error);
|
showStreamError(error);
|
||||||
break;
|
break;
|
||||||
|
@ -23,6 +23,7 @@ import android.app.Activity;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
|
import android.content.res.Configuration;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
@ -33,6 +34,7 @@ import android.support.v7.widget.RecyclerView;
|
|||||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.GestureDetector;
|
import android.view.GestureDetector;
|
||||||
|
import android.view.MenuItem;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
@ -59,6 +61,8 @@ import org.schabi.newpipe.util.NavigationHelper;
|
|||||||
import org.schabi.newpipe.util.PermissionHelper;
|
import org.schabi.newpipe.util.PermissionHelper;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||||
@ -151,6 +155,17 @@ public final class MainVideoPlayer extends Activity {
|
|||||||
if (playerImpl != null) playerImpl.destroy();
|
if (playerImpl != null) playerImpl.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConfigurationChanged(Configuration newConfig) {
|
||||||
|
super.onConfigurationChanged(newConfig);
|
||||||
|
|
||||||
|
if (playerImpl.isSomePopupMenuVisible()) {
|
||||||
|
playerImpl.moreOptionsPopupMenu.dismiss();
|
||||||
|
playerImpl.getQualityPopupMenu().dismiss();
|
||||||
|
playerImpl.getPlaybackSpeedPopupMenu().dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Utils
|
// Utils
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
@ -223,7 +238,6 @@ public final class MainVideoPlayer extends Activity {
|
|||||||
private ImageButton repeatButton;
|
private ImageButton repeatButton;
|
||||||
private ImageButton shuffleButton;
|
private ImageButton shuffleButton;
|
||||||
|
|
||||||
private ImageButton screenRotationButton;
|
|
||||||
private ImageButton playPauseButton;
|
private ImageButton playPauseButton;
|
||||||
private ImageButton playPreviousButton;
|
private ImageButton playPreviousButton;
|
||||||
private ImageButton playNextButton;
|
private ImageButton playNextButton;
|
||||||
@ -235,6 +249,10 @@ public final class MainVideoPlayer extends Activity {
|
|||||||
|
|
||||||
private boolean queueVisible;
|
private boolean queueVisible;
|
||||||
|
|
||||||
|
private ImageButton moreOptionsButton;
|
||||||
|
public int moreOptionsPopupMenuGroupId = 89;
|
||||||
|
public PopupMenu moreOptionsPopupMenu;
|
||||||
|
|
||||||
VideoPlayerImpl(final Context context) {
|
VideoPlayerImpl(final Context context) {
|
||||||
super("VideoPlayerImpl" + MainVideoPlayer.TAG, context);
|
super("VideoPlayerImpl" + MainVideoPlayer.TAG, context);
|
||||||
}
|
}
|
||||||
@ -250,10 +268,12 @@ public final class MainVideoPlayer extends Activity {
|
|||||||
this.repeatButton = rootView.findViewById(R.id.repeatButton);
|
this.repeatButton = rootView.findViewById(R.id.repeatButton);
|
||||||
this.shuffleButton = rootView.findViewById(R.id.shuffleButton);
|
this.shuffleButton = rootView.findViewById(R.id.shuffleButton);
|
||||||
|
|
||||||
this.screenRotationButton = rootView.findViewById(R.id.screenRotationButton);
|
|
||||||
this.playPauseButton = rootView.findViewById(R.id.playPauseButton);
|
this.playPauseButton = rootView.findViewById(R.id.playPauseButton);
|
||||||
this.playPreviousButton = rootView.findViewById(R.id.playPreviousButton);
|
this.playPreviousButton = rootView.findViewById(R.id.playPreviousButton);
|
||||||
this.playNextButton = rootView.findViewById(R.id.playNextButton);
|
this.playNextButton = rootView.findViewById(R.id.playNextButton);
|
||||||
|
this.moreOptionsButton = rootView.findViewById(R.id.moreOptionsButton);
|
||||||
|
this.moreOptionsPopupMenu = new PopupMenu(context, moreOptionsButton);
|
||||||
|
this.moreOptionsPopupMenu.getMenuInflater().inflate(R.menu.menu_videooptions, moreOptionsPopupMenu.getMenu());
|
||||||
|
|
||||||
titleTextView.setSelected(true);
|
titleTextView.setSelected(true);
|
||||||
channelTextView.setSelected(true);
|
channelTextView.setSelected(true);
|
||||||
@ -277,7 +297,7 @@ public final class MainVideoPlayer extends Activity {
|
|||||||
playPauseButton.setOnClickListener(this);
|
playPauseButton.setOnClickListener(this);
|
||||||
playPreviousButton.setOnClickListener(this);
|
playPreviousButton.setOnClickListener(this);
|
||||||
playNextButton.setOnClickListener(this);
|
playNextButton.setOnClickListener(this);
|
||||||
screenRotationButton.setOnClickListener(this);
|
moreOptionsButton.setOnClickListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
@ -349,6 +369,28 @@ public final class MainVideoPlayer extends Activity {
|
|||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onPlayBackgroundButtonClicked() {
|
||||||
|
if (DEBUG) Log.d(TAG, "onPlayBackgroundButtonClicked() called");
|
||||||
|
if (playerImpl.getPlayer() == null) return;
|
||||||
|
|
||||||
|
setRecovery();
|
||||||
|
final Intent intent = NavigationHelper.getPlayerIntent(
|
||||||
|
context,
|
||||||
|
BackgroundPlayer.class,
|
||||||
|
this.getPlayQueue(),
|
||||||
|
this.getRepeatMode(),
|
||||||
|
this.getPlaybackSpeed(),
|
||||||
|
this.getPlaybackPitch(),
|
||||||
|
this.getPlaybackQuality()
|
||||||
|
);
|
||||||
|
context.startService(intent);
|
||||||
|
|
||||||
|
((View) getControlAnimationView().getParent()).setVisibility(View.GONE);
|
||||||
|
destroy();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
super.onClick(v);
|
super.onClick(v);
|
||||||
@ -361,9 +403,6 @@ public final class MainVideoPlayer extends Activity {
|
|||||||
} else if (v.getId() == playNextButton.getId()) {
|
} else if (v.getId() == playNextButton.getId()) {
|
||||||
onPlayNext();
|
onPlayNext();
|
||||||
|
|
||||||
} else if (v.getId() == screenRotationButton.getId()) {
|
|
||||||
onScreenRotationClicked();
|
|
||||||
|
|
||||||
} else if (v.getId() == queueButton.getId()) {
|
} else if (v.getId() == queueButton.getId()) {
|
||||||
onQueueClicked();
|
onQueueClicked();
|
||||||
return;
|
return;
|
||||||
@ -373,6 +412,8 @@ public final class MainVideoPlayer extends Activity {
|
|||||||
} else if (v.getId() == shuffleButton.getId()) {
|
} else if (v.getId() == shuffleButton.getId()) {
|
||||||
onShuffleClicked();
|
onShuffleClicked();
|
||||||
return;
|
return;
|
||||||
|
} else if (v.getId() == moreOptionsButton.getId()) {
|
||||||
|
onMoreOptionsClicked();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getCurrentState() != STATE_COMPLETED) {
|
if (getCurrentState() != STATE_COMPLETED) {
|
||||||
@ -406,6 +447,32 @@ public final class MainVideoPlayer extends Activity {
|
|||||||
queueVisible = false;
|
queueVisible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onMoreOptionsClicked() {
|
||||||
|
if (DEBUG) Log.d(TAG, "onMoreOptionsClicked() called");
|
||||||
|
buildMoreOptionsMenu();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Field[] fields = moreOptionsPopupMenu.getClass().getDeclaredFields();
|
||||||
|
for (Field field : fields) {
|
||||||
|
if ("mPopup".equals(field.getName())) {
|
||||||
|
field.setAccessible(true);
|
||||||
|
Object menuPopupHelper = field.get(moreOptionsPopupMenu);
|
||||||
|
Class<?> classPopupHelper = Class.forName(menuPopupHelper
|
||||||
|
.getClass().getName());
|
||||||
|
Method setForceIcons = classPopupHelper.getMethod(
|
||||||
|
"setForceShowIcon", boolean.class);
|
||||||
|
setForceIcons.invoke(menuPopupHelper, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
moreOptionsPopupMenu.show();
|
||||||
|
isSomePopupMenuVisible = true;
|
||||||
|
showControls(300);
|
||||||
|
}
|
||||||
|
|
||||||
private void onScreenRotationClicked() {
|
private void onScreenRotationClicked() {
|
||||||
if (DEBUG) Log.d(TAG, "onScreenRotationClicked() called");
|
if (DEBUG) Log.d(TAG, "onScreenRotationClicked() called");
|
||||||
toggleOrientation();
|
toggleOrientation();
|
||||||
@ -556,6 +623,27 @@ public final class MainVideoPlayer extends Activity {
|
|||||||
setShuffleButton(shuffleButton, playQueue.isShuffled());
|
setShuffleButton(shuffleButton, playQueue.isShuffled());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void buildMoreOptionsMenu() {
|
||||||
|
if (moreOptionsPopupMenu == null) return;
|
||||||
|
moreOptionsPopupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||||
|
switch (menuItem.getItemId()) {
|
||||||
|
case R.id.toggleOrientation:
|
||||||
|
onScreenRotationClicked();
|
||||||
|
break;
|
||||||
|
case R.id.switchPopup:
|
||||||
|
onFullScreenButtonClicked();
|
||||||
|
break;
|
||||||
|
case R.id.switchBackground:
|
||||||
|
onPlayBackgroundButtonClicked();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void buildQueue() {
|
private void buildQueue() {
|
||||||
queueLayout = findViewById(R.id.playQueuePanel);
|
queueLayout = findViewById(R.id.playQueuePanel);
|
||||||
|
|
||||||
|
@ -842,6 +842,8 @@ public final class PopupVideoPlayer extends Service {
|
|||||||
}
|
}
|
||||||
savePositionAndSize();
|
savePositionAndSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v.performClick();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -880,23 +882,25 @@ public final class PopupVideoPlayer extends Service {
|
|||||||
private final Context context;
|
private final Context context;
|
||||||
private final Handler mainHandler;
|
private final Handler mainHandler;
|
||||||
|
|
||||||
FetcherHandler(Context context, int serviceId, String url) {
|
private FetcherHandler(Context context, int serviceId, String url) {
|
||||||
this.mainHandler = new Handler(PopupVideoPlayer.this.getMainLooper());
|
this.mainHandler = new Handler(PopupVideoPlayer.this.getMainLooper());
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.serviceId = serviceId;
|
this.serviceId = serviceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*package-private*/ void onReceive(final StreamInfo info) {
|
private void onReceive(final StreamInfo info) {
|
||||||
mainHandler.post(new Runnable() {
|
mainHandler.post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
playerImpl.initPlayback(new SinglePlayQueue(info));
|
final Intent intent = NavigationHelper.getPlayerIntent(getApplicationContext(),
|
||||||
|
PopupVideoPlayer.class, new SinglePlayQueue(info));
|
||||||
|
playerImpl.handleIntent(intent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onError(final Throwable exception) {
|
private void onError(final Throwable exception) {
|
||||||
if (DEBUG) Log.d(TAG, "onError() called with: exception = [" + exception + "]");
|
if (DEBUG) Log.d(TAG, "onError() called with: exception = [" + exception + "]");
|
||||||
exception.printStackTrace();
|
exception.printStackTrace();
|
||||||
mainHandler.post(new Runnable() {
|
mainHandler.post(new Runnable() {
|
||||||
@ -922,7 +926,7 @@ public final class PopupVideoPlayer extends Service {
|
|||||||
stopSelf();
|
stopSelf();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*package-private*/ void onReCaptchaException() {
|
private void onReCaptchaException() {
|
||||||
Toast.makeText(context, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
|
Toast.makeText(context, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
|
||||||
// Starting ReCaptcha Challenge Activity
|
// Starting ReCaptcha Challenge Activity
|
||||||
Intent intent = new Intent(context, ReCaptchaActivity.class);
|
Intent intent = new Intent(context, ReCaptchaActivity.class);
|
||||||
|
@ -124,12 +124,11 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
|||||||
|
|
||||||
private View topControlsRoot;
|
private View topControlsRoot;
|
||||||
private TextView qualityTextView;
|
private TextView qualityTextView;
|
||||||
private ImageButton fullScreenButton;
|
|
||||||
|
|
||||||
private ValueAnimator controlViewAnimator;
|
private ValueAnimator controlViewAnimator;
|
||||||
private Handler controlsVisibilityHandler = new Handler();
|
private Handler controlsVisibilityHandler = new Handler();
|
||||||
|
|
||||||
private boolean isSomePopupMenuVisible = false;
|
boolean isSomePopupMenuVisible = false;
|
||||||
private int qualityPopupMenuGroupId = 69;
|
private int qualityPopupMenuGroupId = 69;
|
||||||
private PopupMenu qualityPopupMenu;
|
private PopupMenu qualityPopupMenu;
|
||||||
|
|
||||||
@ -166,7 +165,6 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
|||||||
this.bottomControlsRoot = rootView.findViewById(R.id.bottomControls);
|
this.bottomControlsRoot = rootView.findViewById(R.id.bottomControls);
|
||||||
this.topControlsRoot = rootView.findViewById(R.id.topControls);
|
this.topControlsRoot = rootView.findViewById(R.id.topControls);
|
||||||
this.qualityTextView = rootView.findViewById(R.id.qualityTextView);
|
this.qualityTextView = rootView.findViewById(R.id.qualityTextView);
|
||||||
this.fullScreenButton = rootView.findViewById(R.id.fullScreenButton);
|
|
||||||
|
|
||||||
//this.aspectRatioFrameLayout.setAspectRatio(16.0f / 9.0f);
|
//this.aspectRatioFrameLayout.setAspectRatio(16.0f / 9.0f);
|
||||||
|
|
||||||
@ -186,7 +184,6 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
|||||||
super.initListeners();
|
super.initListeners();
|
||||||
playbackSeekBar.setOnSeekBarChangeListener(this);
|
playbackSeekBar.setOnSeekBarChangeListener(this);
|
||||||
playbackSpeedTextView.setOnClickListener(this);
|
playbackSpeedTextView.setOnClickListener(this);
|
||||||
fullScreenButton.setOnClickListener(this);
|
|
||||||
qualityTextView.setOnClickListener(this);
|
qualityTextView.setOnClickListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -454,9 +451,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
|||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]");
|
if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]");
|
||||||
if (v.getId() == fullScreenButton.getId()) {
|
if (v.getId() == qualityTextView.getId()) {
|
||||||
onFullScreenButtonClicked();
|
|
||||||
} else if (v.getId() == qualityTextView.getId()) {
|
|
||||||
onQualitySelectorClicked();
|
onQualitySelectorClicked();
|
||||||
} else if (v.getId() == playbackSpeedTextView.getId()) {
|
} else if (v.getId() == playbackSpeedTextView.getId()) {
|
||||||
onPlaybackSpeedClicked();
|
onPlaybackSpeedClicked();
|
||||||
@ -754,14 +749,14 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
|||||||
return qualityTextView;
|
return qualityTextView;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImageButton getFullScreenButton() {
|
|
||||||
return fullScreenButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PopupMenu getQualityPopupMenu() {
|
public PopupMenu getQualityPopupMenu() {
|
||||||
return qualityPopupMenu;
|
return qualityPopupMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PopupMenu getPlaybackSpeedPopupMenu() {
|
||||||
|
return playbackSpeedPopupMenu;
|
||||||
|
}
|
||||||
|
|
||||||
public View getSurfaceForeground() {
|
public View getSurfaceForeground() {
|
||||||
return surfaceForeground;
|
return surfaceForeground;
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,9 @@ import android.util.Log;
|
|||||||
import org.schabi.newpipe.MainActivity;
|
import org.schabi.newpipe.MainActivity;
|
||||||
import org.schabi.newpipe.extractor.Info;
|
import org.schabi.newpipe.extractor.Info;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
|
||||||
public final class InfoCache {
|
public final class InfoCache {
|
||||||
private static final boolean DEBUG = MainActivity.DEBUG;
|
private static final boolean DEBUG = MainActivity.DEBUG;
|
||||||
@ -37,9 +40,9 @@ public final class InfoCache {
|
|||||||
* Trim the cache to this size
|
* Trim the cache to this size
|
||||||
*/
|
*/
|
||||||
private static final int TRIM_CACHE_TO = 30;
|
private static final int TRIM_CACHE_TO = 30;
|
||||||
|
private static final int DEFAULT_TIMEOUT_HOURS = 4;
|
||||||
|
|
||||||
// TODO: Replace to one with timeout (like the one from guava)
|
private static final LruCache<String, CacheData> lruCache = new LruCache<>(MAX_ITEMS_ON_CACHE);
|
||||||
private static final LruCache<String, Info> lruCache = new LruCache<>(MAX_ITEMS_ON_CACHE);
|
|
||||||
|
|
||||||
private InfoCache() {
|
private InfoCache() {
|
||||||
//no instance
|
//no instance
|
||||||
@ -52,28 +55,29 @@ public final class InfoCache {
|
|||||||
public Info getFromKey(int serviceId, @NonNull String url) {
|
public Info getFromKey(int serviceId, @NonNull String url) {
|
||||||
if (DEBUG) Log.d(TAG, "getFromKey() called with: serviceId = [" + serviceId + "], url = [" + url + "]");
|
if (DEBUG) Log.d(TAG, "getFromKey() called with: serviceId = [" + serviceId + "], url = [" + url + "]");
|
||||||
synchronized (lruCache) {
|
synchronized (lruCache) {
|
||||||
return lruCache.get(serviceId + url);
|
return getInfo(lruCache, keyOf(serviceId, url));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void putInfo(@NonNull Info info) {
|
public void putInfo(@NonNull Info info) {
|
||||||
if (DEBUG) Log.d(TAG, "putInfo() called with: info = [" + info + "]");
|
if (DEBUG) Log.d(TAG, "putInfo() called with: info = [" + info + "]");
|
||||||
synchronized (lruCache) {
|
synchronized (lruCache) {
|
||||||
lruCache.put(info.service_id + info.url, info);
|
final CacheData data = new CacheData(info, DEFAULT_TIMEOUT_HOURS, TimeUnit.HOURS);
|
||||||
|
lruCache.put(keyOf(info), data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeInfo(@NonNull Info info) {
|
public void removeInfo(@NonNull Info info) {
|
||||||
if (DEBUG) Log.d(TAG, "removeInfo() called with: info = [" + info + "]");
|
if (DEBUG) Log.d(TAG, "removeInfo() called with: info = [" + info + "]");
|
||||||
synchronized (lruCache) {
|
synchronized (lruCache) {
|
||||||
lruCache.remove(info.service_id + info.url);
|
lruCache.remove(keyOf(info));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeInfo(int serviceId, @NonNull String url) {
|
public void removeInfo(int serviceId, @NonNull String url) {
|
||||||
if (DEBUG) Log.d(TAG, "removeInfo() called with: serviceId = [" + serviceId + "], url = [" + url + "]");
|
if (DEBUG) Log.d(TAG, "removeInfo() called with: serviceId = [" + serviceId + "], url = [" + url + "]");
|
||||||
synchronized (lruCache) {
|
synchronized (lruCache) {
|
||||||
lruCache.remove(serviceId + url);
|
lruCache.remove(keyOf(serviceId, url));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,6 +91,7 @@ public final class InfoCache {
|
|||||||
public void trimCache() {
|
public void trimCache() {
|
||||||
if (DEBUG) Log.d(TAG, "trimCache() called");
|
if (DEBUG) Log.d(TAG, "trimCache() called");
|
||||||
synchronized (lruCache) {
|
synchronized (lruCache) {
|
||||||
|
removeStaleCache(lruCache);
|
||||||
lruCache.trimToSize(TRIM_CACHE_TO);
|
lruCache.trimToSize(TRIM_CACHE_TO);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -97,4 +102,51 @@ public final class InfoCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String keyOf(@NonNull final Info info) {
|
||||||
|
return keyOf(info.service_id, info.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String keyOf(final int serviceId, @NonNull final String url) {
|
||||||
|
return serviceId + url;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void removeStaleCache(@NonNull final LruCache<String, CacheData> cache) {
|
||||||
|
for (Map.Entry<String, CacheData> entry : cache.snapshot().entrySet()) {
|
||||||
|
final CacheData data = entry.getValue();
|
||||||
|
if (data != null && data.isExpired()) {
|
||||||
|
cache.remove(entry.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Info getInfo(@NonNull final LruCache<String, CacheData> cache,
|
||||||
|
@NonNull final String key) {
|
||||||
|
final CacheData data = cache.get(key);
|
||||||
|
if (data == null) return null;
|
||||||
|
|
||||||
|
if (data.isExpired()) {
|
||||||
|
cache.remove(key);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.info;
|
||||||
|
}
|
||||||
|
|
||||||
|
final private static class CacheData {
|
||||||
|
final private long expireTimestamp;
|
||||||
|
final private Info info;
|
||||||
|
|
||||||
|
private CacheData(@NonNull final Info info,
|
||||||
|
final long timeout,
|
||||||
|
@NonNull final TimeUnit timeUnit) {
|
||||||
|
this.expireTimestamp = System.currentTimeMillis() +
|
||||||
|
TimeUnit.MILLISECONDS.convert(timeout, timeUnit);
|
||||||
|
|
||||||
|
this.info = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isExpired() {
|
||||||
|
return System.currentTimeMillis() > expireTimestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
BIN
app/src/main/res/drawable-hdpi/ic_arrow_top_left_black_24dp.png
Normal file
After Width: | Height: | Size: 554 B |
BIN
app/src/main/res/drawable-hdpi/ic_arrow_top_left_white_24dp.png
Normal file
After Width: | Height: | Size: 546 B |
BIN
app/src/main/res/drawable-hdpi/ic_more_vert_black_24dp.png
Normal file
After Width: | Height: | Size: 132 B |
BIN
app/src/main/res/drawable-hdpi/ic_more_vert_white_24dp.png
Normal file
After Width: | Height: | Size: 134 B |
BIN
app/src/main/res/drawable-mdpi/ic_arrow_top_left_black_24dp.png
Normal file
After Width: | Height: | Size: 418 B |
BIN
app/src/main/res/drawable-mdpi/ic_arrow_top_left_white_24dp.png
Normal file
After Width: | Height: | Size: 427 B |
BIN
app/src/main/res/drawable-mdpi/ic_more_vert_black_24dp.png
Normal file
After Width: | Height: | Size: 108 B |
BIN
app/src/main/res/drawable-mdpi/ic_more_vert_white_24dp.png
Normal file
After Width: | Height: | Size: 112 B |
BIN
app/src/main/res/drawable-xhdpi/ic_arrow_top_left_black_24dp.png
Normal file
After Width: | Height: | Size: 492 B |
BIN
app/src/main/res/drawable-xhdpi/ic_arrow_top_left_white_24dp.png
Normal file
After Width: | Height: | Size: 500 B |
BIN
app/src/main/res/drawable-xhdpi/ic_more_vert_black_24dp.png
Normal file
After Width: | Height: | Size: 155 B |
BIN
app/src/main/res/drawable-xhdpi/ic_more_vert_white_24dp.png
Normal file
After Width: | Height: | Size: 158 B |
After Width: | Height: | Size: 570 B |
After Width: | Height: | Size: 573 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_more_vert_black_24dp.png
Normal file
After Width: | Height: | Size: 205 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_more_vert_white_24dp.png
Normal file
After Width: | Height: | Size: 216 B |
After Width: | Height: | Size: 675 B |
After Width: | Height: | Size: 730 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_more_vert_black_24dp.png
Normal file
After Width: | Height: | Size: 272 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_more_vert_white_24dp.png
Normal file
After Width: | Height: | Size: 305 B |
@ -209,7 +209,7 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginRight="2dp"
|
android:layout_marginRight="2dp"
|
||||||
android:layout_toLeftOf="@+id/screenRotationButton"
|
android:layout_toLeftOf="@+id/queueButton"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:minHeight="35dp"
|
android:minHeight="35dp"
|
||||||
android:minWidth="40dp"
|
android:minWidth="40dp"
|
||||||
@ -218,28 +218,13 @@
|
|||||||
tools:ignore="RtlHardcoded,RtlSymmetry"
|
tools:ignore="RtlHardcoded,RtlSymmetry"
|
||||||
tools:text="1x" />
|
tools:text="1x" />
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/screenRotationButton"
|
|
||||||
android:layout_width="35dp"
|
|
||||||
android:layout_height="35dp"
|
|
||||||
android:layout_marginLeft="2dp"
|
|
||||||
android:layout_marginRight="2dp"
|
|
||||||
android:layout_toLeftOf="@+id/queueButton"
|
|
||||||
android:background="#00ffffff"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
android:padding="8dp"
|
|
||||||
android:scaleType="fitXY"
|
|
||||||
android:src="@drawable/ic_screen_rotation_white"
|
|
||||||
tools:ignore="ContentDescription,RtlHardcoded"/>
|
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/queueButton"
|
android:id="@+id/queueButton"
|
||||||
android:layout_width="30dp"
|
android:layout_width="30dp"
|
||||||
android:layout_height="35dp"
|
android:layout_height="35dp"
|
||||||
android:layout_marginLeft="2dp"
|
android:layout_marginLeft="2dp"
|
||||||
android:layout_marginRight="2dp"
|
android:layout_marginRight="2dp"
|
||||||
android:layout_toLeftOf="@+id/fullScreenButton"
|
android:layout_toLeftOf="@+id/moreOptionsButton"
|
||||||
android:background="#00ffffff"
|
android:background="#00ffffff"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
@ -249,16 +234,17 @@
|
|||||||
tools:ignore="ContentDescription,RtlHardcoded"/>
|
tools:ignore="ContentDescription,RtlHardcoded"/>
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/fullScreenButton"
|
android:id="@+id/moreOptionsButton"
|
||||||
android:layout_width="35dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="35dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentRight="true"
|
android:layout_alignParentRight="true"
|
||||||
android:layout_marginLeft="4dp"
|
android:layout_marginLeft="2dp"
|
||||||
|
android:padding="5dp"
|
||||||
android:background="#00ffffff"
|
android:background="#00ffffff"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:scaleType="fitXY"
|
android:scaleType="fitXY"
|
||||||
android:src="@drawable/ic_fullscreen_exit_white"
|
android:src="?attr/options"
|
||||||
tools:ignore="ContentDescription,RtlHardcoded"/>
|
tools:ignore="ContentDescription,RtlHardcoded"/>
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
@ -6,7 +6,9 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:clickable="false"
|
android:clickable="false"
|
||||||
android:padding="@dimen/video_item_search_padding">
|
android:paddingLeft="@dimen/video_item_search_padding"
|
||||||
|
android:paddingRight="@dimen/video_item_search_padding"
|
||||||
|
android:paddingTop="@dimen/video_item_search_padding">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/itemTitleView"
|
android:id="@+id/itemTitleView"
|
||||||
@ -19,11 +21,11 @@
|
|||||||
android:scrollHorizontally="true"
|
android:scrollHorizontally="true"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||||
android:textSize="@dimen/video_item_search_title_text_size"
|
android:textSize="@dimen/channel_item_detail_title_text_size"
|
||||||
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. "/>
|
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. "/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/itemTypeView"
|
android:id="@+id/itemAdditionalDetails"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@+id/itemTitleView"
|
android:layout_below="@+id/itemTitleView"
|
||||||
@ -32,5 +34,7 @@
|
|||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
android:textSize="@dimen/video_item_search_uploader_text_size"
|
android:textSize="@dimen/video_item_search_uploader_text_size"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible"
|
||||||
tools:text="TYPE" />
|
tools:text="TYPE" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
@ -1,36 +1,71 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout
|
<RelativeLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?attr/selectableItemBackground"
|
android:orientation="horizontal">
|
||||||
android:clickable="true"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:paddingBottom="8dp"
|
|
||||||
android:paddingTop="8dp">
|
|
||||||
|
|
||||||
<ImageView
|
<LinearLayout
|
||||||
android:id="@+id/item_suggestion_icon"
|
android:id="@+id/suggestion_search"
|
||||||
android:layout_width="24dp"
|
|
||||||
android:layout_height="24dp"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:layout_marginLeft="16dp"
|
|
||||||
android:layout_marginRight="16dp"
|
|
||||||
tools:ignore="ContentDescription,RtlHardcoded"
|
|
||||||
tools:src="?attr/history"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/item_suggestion_query"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_vertical"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:layout_marginLeft="8dp"
|
android:clickable="true"
|
||||||
android:layout_marginRight="16dp"
|
android:focusable="true"
|
||||||
android:ellipsize="end"
|
android:layout_alignParentLeft="true"
|
||||||
android:maxLines="2"
|
android:layout_alignParentStart="true"
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
android:layout_toLeftOf="@id/suggestion_insert"
|
||||||
android:textSize="14sp"
|
android:layout_toStartOf="@id/suggestion_insert"
|
||||||
tools:ignore="RtlHardcoded"
|
android:layout_centerVertical="true"
|
||||||
tools:text="Search query"/>
|
android:paddingBottom="8dp"
|
||||||
</LinearLayout>
|
android:paddingTop="8dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/item_suggestion_icon"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
tools:ignore="ContentDescription,RtlHardcoded"
|
||||||
|
tools:src="?attr/history"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/item_suggestion_query"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||||
|
android:textSize="14sp"
|
||||||
|
tools:ignore="RtlHardcoded"
|
||||||
|
tools:text="Search query"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/suggestion_insert"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingRight="16dp"
|
||||||
|
android:paddingBottom="8dp"
|
||||||
|
android:paddingTop="10dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="20dp"
|
||||||
|
android:layout_height="20dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:src="?attr/search_add"
|
||||||
|
tools:ignore="ContentDescription,RtlHardcoded"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
@ -34,22 +34,22 @@
|
|||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/notificationSongName"
|
android:id="@+id/notificationSongName"
|
||||||
style="@android:style/TextAppearance.StatusBar.EventContent.Title"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
|
android:textColor="@color/background_title_color"
|
||||||
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis nec aliquam augue, eget cursus est. Ut id tristique enim, ut scelerisque tellus. Sed ultricies ipsum non mauris ultricies, commodo malesuada velit porta."/>
|
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis nec aliquam augue, eget cursus est. Ut id tristique enim, ut scelerisque tellus. Sed ultricies ipsum non mauris ultricies, commodo malesuada velit porta."/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/notificationArtist"
|
android:id="@+id/notificationArtist"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
style="@android:style/TextAppearance.StatusBar.EventContent"
|
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
|
android:textColor="@color/background_subtext_color"
|
||||||
tools:text="Duis posuere arcu condimentum lobortis mattis."/>
|
tools:text="Duis posuere arcu condimentum lobortis mattis."/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
@ -46,22 +46,22 @@
|
|||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/notificationSongName"
|
android:id="@+id/notificationSongName"
|
||||||
style="@android:style/TextAppearance.StatusBar.EventContent.Title"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
|
android:textColor="@color/background_title_color"
|
||||||
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis nec aliquam augue, eget cursus est. Ut id tristique enim, ut scelerisque tellus. Sed ultricies ipsum non mauris ultricies, commodo malesuada velit porta."/>
|
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis nec aliquam augue, eget cursus est. Ut id tristique enim, ut scelerisque tellus. Sed ultricies ipsum non mauris ultricies, commodo malesuada velit porta."/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/notificationArtist"
|
android:id="@+id/notificationArtist"
|
||||||
style="@android:style/TextAppearance.StatusBar.EventContent"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
|
android:textColor="@color/background_subtext_color"
|
||||||
tools:text="Duis posuere arcu condimentum lobortis mattis."/>
|
tools:text="Duis posuere arcu condimentum lobortis mattis."/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
@ -80,7 +80,6 @@
|
|||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/notificationTime"
|
android:id="@+id/notificationTime"
|
||||||
style="@android:style/TextAppearance.StatusBar.EventContent"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginLeft="8dp"
|
android:layout_marginLeft="8dp"
|
||||||
@ -92,6 +91,7 @@
|
|||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
|
android:textColor="@color/background_subtext_color"
|
||||||
tools:text="Duis posuere"/>
|
tools:text="Duis posuere"/>
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
|
@ -29,22 +29,22 @@
|
|||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/notificationSongName"
|
android:id="@+id/notificationSongName"
|
||||||
style="@android:style/TextAppearance.StatusBar.EventContent.Title"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
|
android:textColor="@color/background_title_color"
|
||||||
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis nec aliquam augue, eget cursus est. Ut id tristique enim, ut scelerisque tellus. Sed ultricies ipsum non mauris ultricies, commodo malesuada velit porta."/>
|
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis nec aliquam augue, eget cursus est. Ut id tristique enim, ut scelerisque tellus. Sed ultricies ipsum non mauris ultricies, commodo malesuada velit porta."/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/notificationArtist"
|
android:id="@+id/notificationArtist"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
style="@android:style/TextAppearance.StatusBar.EventContent"
|
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
|
android:textColor="@color/background_subtext_color"
|
||||||
tools:text="Duis posuere arcu condimentum lobortis mattis."/>
|
tools:text="Duis posuere arcu condimentum lobortis mattis."/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
21
app/src/main/res/menu/menu_videooptions.xml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu
|
||||||
|
android:id="@+id/menu_video_options"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item
|
||||||
|
android:icon="@drawable/ic_screen_rotation_white"
|
||||||
|
android:id="@+id/toggleOrientation"
|
||||||
|
android:title="Toggle orientation"
|
||||||
|
app:showAsAction="always|withText" />
|
||||||
|
<item
|
||||||
|
android:icon="@drawable/ic_fullscreen_exit_white"
|
||||||
|
android:id="@+id/switchPopup"
|
||||||
|
android:title="Switch to popup"
|
||||||
|
app:showAsAction="always|withText" />
|
||||||
|
<item android:icon="?audio"
|
||||||
|
android:id="@+id/switchBackground"
|
||||||
|
android:title="Switch to background"
|
||||||
|
app:showAsAction="always|withText" />
|
||||||
|
</menu>
|
||||||
|
|
@ -20,6 +20,8 @@
|
|||||||
<attr name="history" format="reference"/>
|
<attr name="history" format="reference"/>
|
||||||
<attr name="drag_handle" format="reference"/>
|
<attr name="drag_handle" format="reference"/>
|
||||||
<attr name="selected" format="reference"/>
|
<attr name="selected" format="reference"/>
|
||||||
|
<attr name="search_add" format="reference"/>
|
||||||
|
<attr name="options" format="reference"/>
|
||||||
|
|
||||||
<!-- Can't refer to colors directly into drawable's xml-->
|
<!-- Can't refer to colors directly into drawable's xml-->
|
||||||
<attr name="toolbar_shadow_drawable" format="reference"/>
|
<attr name="toolbar_shadow_drawable" format="reference"/>
|
||||||
|
@ -39,7 +39,10 @@
|
|||||||
<color name="duration_text_color">#EEFFFFFF</color>
|
<color name="duration_text_color">#EEFFFFFF</color>
|
||||||
<color name="playlist_stream_count_text_color">#ffffff</color>
|
<color name="playlist_stream_count_text_color">#ffffff</color>
|
||||||
<color name="video_overlay_color">#66000000</color>
|
<color name="video_overlay_color">#66000000</color>
|
||||||
|
|
||||||
<color name="background_notification_color">#323232</color>
|
<color name="background_notification_color">#323232</color>
|
||||||
|
<color name="background_title_color">#ffffff</color>
|
||||||
|
<color name="background_subtext_color">#999999</color>
|
||||||
|
|
||||||
<color name="subscribe_background_color">#e53935</color>
|
<color name="subscribe_background_color">#e53935</color>
|
||||||
<color name="subscribe_text_color">#fff</color>
|
<color name="subscribe_text_color">#fff</color>
|
||||||
|
@ -27,6 +27,8 @@
|
|||||||
<item name="history">@drawable/ic_history_black_24dp</item>
|
<item name="history">@drawable/ic_history_black_24dp</item>
|
||||||
<item name="drag_handle">@drawable/ic_drag_handle_black_24dp</item>
|
<item name="drag_handle">@drawable/ic_drag_handle_black_24dp</item>
|
||||||
<item name="selected">@drawable/ic_fiber_manual_record_black_24dp</item>
|
<item name="selected">@drawable/ic_fiber_manual_record_black_24dp</item>
|
||||||
|
<item name="search_add">@drawable/ic_arrow_top_left_black_24dp</item>
|
||||||
|
<item name="options">@drawable/ic_more_vert_black_24dp</item>
|
||||||
|
|
||||||
<item name="separator_color">@color/light_separator_color</item>
|
<item name="separator_color">@color/light_separator_color</item>
|
||||||
<item name="contrast_background_color">@color/light_contrast_background_color</item>
|
<item name="contrast_background_color">@color/light_contrast_background_color</item>
|
||||||
@ -65,6 +67,8 @@
|
|||||||
<item name="history">@drawable/ic_history_white_24dp</item>
|
<item name="history">@drawable/ic_history_white_24dp</item>
|
||||||
<item name="drag_handle">@drawable/ic_drag_handle_white_24dp</item>
|
<item name="drag_handle">@drawable/ic_drag_handle_white_24dp</item>
|
||||||
<item name="selected">@drawable/ic_fiber_manual_record_white_24dp</item>
|
<item name="selected">@drawable/ic_fiber_manual_record_white_24dp</item>
|
||||||
|
<item name="search_add">@drawable/ic_arrow_top_left_white_24dp</item>
|
||||||
|
<item name="options">@drawable/ic_more_vert_white_24dp</item>
|
||||||
|
|
||||||
<item name="separator_color">@color/dark_separator_color</item>
|
<item name="separator_color">@color/dark_separator_color</item>
|
||||||
<item name="contrast_background_color">@color/dark_contrast_background_color</item>
|
<item name="contrast_background_color">@color/dark_contrast_background_color</item>
|
||||||
@ -160,4 +164,12 @@
|
|||||||
<item name="android:background">@color/dark_youtube_primary_color</item>
|
<item name="android:background">@color/dark_youtube_primary_color</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="PopupPermissionsTheme" parent="Theme.AppCompat.NoActionBar">
|
||||||
|
<item name="android:windowNoTitle">true</item>
|
||||||
|
<item name="android:windowBackground">@android:color/transparent</item>
|
||||||
|
<item name="android:colorBackgroundCacheHint">@null</item>
|
||||||
|
<item name="android:windowIsTranslucent">true</item>
|
||||||
|
<item name="android:windowAnimationStyle">@android:style/Animation</item>
|
||||||
|
<item name="android:windowNoDisplay">true</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|