Merge branch 'expiring_lru' of https://github.com/karyogamy/NewPipe into info
|
@ -185,7 +185,7 @@
|
|||
android:name=".RouterPopupActivity"
|
||||
android:label="@string/popup_mode_share_menu_title"
|
||||
android:taskAffinity=""
|
||||
android:theme="@android:style/Theme.NoDisplay">
|
||||
android:theme="@style/PopupPermissionsTheme">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<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
|
||||
&& !PermissionHelper.checkSystemAlertWindowPermission(this)) {
|
||||
Toast.makeText(this, R.string.msg_popup_permission, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
StreamingService service;
|
||||
|
|
|
@ -485,6 +485,8 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
|||
|
||||
private void showStreamDialog(final StreamInfoItem item) {
|
||||
final Context context = getContext();
|
||||
if (context == null || context.getResources() == null || getActivity() == null) return;
|
||||
|
||||
final String[] commands = new String[]{
|
||||
context.getResources().getString(R.string.enqueue_on_background),
|
||||
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) {
|
||||
final Context context = getContext();
|
||||
if (context == null || context.getResources() == null || getActivity() == null) return;
|
||||
|
||||
final String[] commands = new String[]{
|
||||
context.getResources().getString(R.string.enqueue_on_background),
|
||||
context.getResources().getString(R.string.enqueue_on_popup)
|
||||
|
|
|
@ -154,6 +154,8 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
|||
@Override
|
||||
protected void showStreamDialog(final StreamInfoItem item) {
|
||||
final Context context = getContext();
|
||||
if (context == null || context.getResources() == null || getActivity() == null) return;
|
||||
|
||||
final String[] commands = new String[]{
|
||||
context.getResources().getString(R.string.enqueue_on_background),
|
||||
context.getResources().getString(R.string.enqueue_on_popup),
|
||||
|
|
|
@ -15,7 +15,6 @@ import android.view.MenuInflater;
|
|||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
|
@ -109,6 +108,8 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
@Override
|
||||
protected void showStreamDialog(final StreamInfoItem item) {
|
||||
final Context context = getContext();
|
||||
if (context == null || context.getResources() == null || getActivity() == null) return;
|
||||
|
||||
final String[] commands = new String[]{
|
||||
context.getResources().getString(R.string.enqueue_on_background),
|
||||
context.getResources().getString(R.string.enqueue_on_popup),
|
||||
|
|
|
@ -165,7 +165,7 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor
|
|||
suggestionListAdapter = new SuggestionListAdapter(activity);
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||
isSearchHistoryEnabled = preferences.getBoolean(getString(R.string.enable_search_history_key), true);
|
||||
suggestionListAdapter.setShowSugestinHistory(isSearchHistoryEnabled);
|
||||
suggestionListAdapter.setShowSuggestionHistory(isSearchHistoryEnabled);
|
||||
|
||||
searchHistoryDAO = NewPipeDatabase.getInstance().searchHistoryDAO();
|
||||
}
|
||||
|
@ -446,6 +446,12 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor
|
|||
searchEditText.setText(item.query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuggestionItemInserted(SuggestionItem item) {
|
||||
searchEditText.setText(item.query);
|
||||
searchEditText.setSelection(searchEditText.getText().length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuggestionItemLongClick(SuggestionItem 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 Context context;
|
||||
private OnSuggestionItemSelected listener;
|
||||
private boolean showSugestinHistory = true;
|
||||
private boolean showSuggestionHistory = true;
|
||||
|
||||
public interface OnSuggestionItemSelected {
|
||||
void onSuggestionItemSelected(SuggestionItem item);
|
||||
void onSuggestionItemInserted(SuggestionItem item);
|
||||
void onSuggestionItemLongClick(SuggestionItem item);
|
||||
}
|
||||
|
||||
|
@ -32,7 +33,7 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
|
|||
|
||||
public void setItems(List<SuggestionItem> items) {
|
||||
this.items.clear();
|
||||
if (showSugestinHistory) {
|
||||
if (showSuggestionHistory) {
|
||||
this.items.addAll(items);
|
||||
} else {
|
||||
// remove history items if history is disabled
|
||||
|
@ -49,8 +50,8 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
|
|||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void setShowSugestinHistory(boolean v) {
|
||||
showSugestinHistory = v;
|
||||
public void setShowSuggestionHistory(boolean v) {
|
||||
showSuggestionHistory = v;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -62,19 +63,25 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
|
|||
public void onBindViewHolder(SuggestionItemHolder holder, int position) {
|
||||
final SuggestionItem currentItem = getItem(position);
|
||||
holder.updateFrom(currentItem);
|
||||
holder.itemView.setOnClickListener(new View.OnClickListener() {
|
||||
holder.queryView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (listener != null) listener.onSuggestionItemSelected(currentItem);
|
||||
}
|
||||
});
|
||||
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
holder.queryView.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
if (listener != null) listener.onSuggestionItemLongClick(currentItem);
|
||||
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) {
|
||||
|
@ -93,6 +100,8 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
|
|||
public static class SuggestionItemHolder extends RecyclerView.ViewHolder {
|
||||
private final TextView itemSuggestionQuery;
|
||||
private final ImageView suggestionIcon;
|
||||
private final View queryView;
|
||||
private final View insertView;
|
||||
|
||||
// Cache some ids, as they can potentially be constantly updated/recycled
|
||||
private final int historyResId;
|
||||
|
@ -103,6 +112,9 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
|
|||
suggestionIcon = rootView.findViewById(R.id.item_suggestion_icon);
|
||||
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);
|
||||
searchResId = resolveResourceIdFromAttr(rootView.getContext(), R.attr.search);
|
||||
}
|
||||
|
|
|
@ -4,28 +4,44 @@ import android.app.Activity;
|
|||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
|
||||
public class InfoItemDialog {
|
||||
private final AlertDialog dialog;
|
||||
|
||||
public InfoItemDialog(@NonNull final Activity activity,
|
||||
@NonNull final InfoItem item,
|
||||
@NonNull final StreamInfoItem info,
|
||||
@NonNull final String[] commands,
|
||||
@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 View bannerView = inflater.inflate(R.layout.dialog_title, null);
|
||||
bannerView.setSelected(true);
|
||||
|
||||
TextView titleView = bannerView.findViewById(R.id.itemTitleView);
|
||||
titleView.setText(item.name);
|
||||
TextView typeView = bannerView.findViewById(R.id.itemTypeView);
|
||||
typeView.setText(item.info_type.name());
|
||||
titleView.setText(title);
|
||||
|
||||
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)
|
||||
.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 PLAY_PREV_ACTIVATION_LIMIT = 5000; // 5 seconds
|
||||
protected final static int PROGRESS_LOOP_INTERVAL = 500;
|
||||
protected final static int RECOVERY_SKIP_THRESHOLD = 3000; // 3 seconds
|
||||
|
||||
protected SimpleExoPlayer simpleExoPlayer;
|
||||
protected AudioReactor audioReactor;
|
||||
|
@ -453,16 +454,20 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
|||
final PlayQueueItem currentSourceItem = playQueue.getItem();
|
||||
|
||||
// Check if already playing correct window
|
||||
final boolean isCurrentWindowCorrect = simpleExoPlayer.getCurrentWindowIndex() == currentSourceIndex;
|
||||
final boolean isCurrentWindowCorrect =
|
||||
simpleExoPlayer.getCurrentWindowIndex() == currentSourceIndex;
|
||||
|
||||
// Check if recovering
|
||||
if (isCurrentWindowCorrect && currentSourceItem != null &&
|
||||
currentSourceItem.getRecoveryPosition() != PlayQueueItem.RECOVERY_UNSET) {
|
||||
if (isCurrentWindowCorrect && currentSourceItem != null) {
|
||||
/* Recovering with sub-second position may cause a long buffer delay in ExoPlayer,
|
||||
* rounding this position to the nearest second will help alleviate this.*/
|
||||
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());
|
||||
playQueue.unsetRecovery(currentSourceIndex);
|
||||
}
|
||||
|
@ -514,10 +519,10 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
|||
}
|
||||
break;
|
||||
case Player.STATE_READY: //3
|
||||
recover();
|
||||
if (!isPrepared) {
|
||||
isPrepared = true;
|
||||
onPrepared(playWhenReady);
|
||||
recover();
|
||||
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.
|
||||
*
|
||||
* 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},
|
||||
* we reset and prepare the media source again to resume playback.<br><br>
|
||||
* In the event that this error is produced during a valid stream playback, we save the
|
||||
* 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>
|
||||
* 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>
|
||||
* If the renderer failed, treat the error as unrecoverable.
|
||||
|
@ -568,6 +577,10 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
|||
|
||||
switch (error.type) {
|
||||
case ExoPlaybackException.TYPE_SOURCE:
|
||||
if (simpleExoPlayer.getCurrentPosition() <
|
||||
simpleExoPlayer.getDuration() - RECOVERY_SKIP_THRESHOLD) {
|
||||
setRecovery();
|
||||
}
|
||||
playQueue.error(isCurrentWindowValid());
|
||||
showStreamError(error);
|
||||
break;
|
||||
|
|
|
@ -842,6 +842,8 @@ public final class PopupVideoPlayer extends Service {
|
|||
}
|
||||
savePositionAndSize();
|
||||
}
|
||||
|
||||
v.performClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -880,23 +882,25 @@ public final class PopupVideoPlayer extends Service {
|
|||
private final Context context;
|
||||
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.context = context;
|
||||
this.url = url;
|
||||
this.serviceId = serviceId;
|
||||
}
|
||||
|
||||
/*package-private*/ void onReceive(final StreamInfo info) {
|
||||
private void onReceive(final StreamInfo info) {
|
||||
mainHandler.post(new Runnable() {
|
||||
@Override
|
||||
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 + "]");
|
||||
exception.printStackTrace();
|
||||
mainHandler.post(new Runnable() {
|
||||
|
@ -922,7 +926,7 @@ public final class PopupVideoPlayer extends Service {
|
|||
stopSelf();
|
||||
}
|
||||
|
||||
/*package-private*/ void onReCaptchaException() {
|
||||
private void onReCaptchaException() {
|
||||
Toast.makeText(context, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
|
||||
// Starting ReCaptcha Challenge Activity
|
||||
Intent intent = new Intent(context, ReCaptchaActivity.class);
|
||||
|
|
|
@ -26,6 +26,9 @@ import android.util.Log;
|
|||
import org.schabi.newpipe.MainActivity;
|
||||
import org.schabi.newpipe.extractor.Info;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
||||
public final class InfoCache {
|
||||
private static final boolean DEBUG = MainActivity.DEBUG;
|
||||
|
@ -37,9 +40,9 @@ public final class InfoCache {
|
|||
* Trim the cache to this size
|
||||
*/
|
||||
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, Info> lruCache = new LruCache<>(MAX_ITEMS_ON_CACHE);
|
||||
private static final LruCache<String, CacheData> lruCache = new LruCache<>(MAX_ITEMS_ON_CACHE);
|
||||
|
||||
private InfoCache() {
|
||||
//no instance
|
||||
|
@ -52,28 +55,29 @@ public final class InfoCache {
|
|||
public Info getFromKey(int serviceId, @NonNull String url) {
|
||||
if (DEBUG) Log.d(TAG, "getFromKey() called with: serviceId = [" + serviceId + "], url = [" + url + "]");
|
||||
synchronized (lruCache) {
|
||||
return lruCache.get(serviceId + url);
|
||||
return getInfo(lruCache, keyOf(serviceId, url));
|
||||
}
|
||||
}
|
||||
|
||||
public void putInfo(@NonNull Info info) {
|
||||
if (DEBUG) Log.d(TAG, "putInfo() called with: info = [" + info + "]");
|
||||
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) {
|
||||
if (DEBUG) Log.d(TAG, "removeInfo() called with: info = [" + info + "]");
|
||||
synchronized (lruCache) {
|
||||
lruCache.remove(info.service_id + info.url);
|
||||
lruCache.remove(keyOf(info));
|
||||
}
|
||||
}
|
||||
|
||||
public void removeInfo(int serviceId, @NonNull String url) {
|
||||
if (DEBUG) Log.d(TAG, "removeInfo() called with: serviceId = [" + serviceId + "], url = [" + url + "]");
|
||||
synchronized (lruCache) {
|
||||
lruCache.remove(serviceId + url);
|
||||
lruCache.remove(keyOf(serviceId, url));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,6 +91,7 @@ public final class InfoCache {
|
|||
public void trimCache() {
|
||||
if (DEBUG) Log.d(TAG, "trimCache() called");
|
||||
synchronized (lruCache) {
|
||||
removeStaleCache(lruCache);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
After Width: | Height: | Size: 554 B |
After Width: | Height: | Size: 546 B |
After Width: | Height: | Size: 418 B |
After Width: | Height: | Size: 427 B |
After Width: | Height: | Size: 492 B |
After Width: | Height: | Size: 500 B |
After Width: | Height: | Size: 570 B |
After Width: | Height: | Size: 573 B |
After Width: | Height: | Size: 675 B |
After Width: | Height: | Size: 730 B |
|
@ -6,7 +6,9 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
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
|
||||
android:id="@+id/itemTitleView"
|
||||
|
@ -19,11 +21,11 @@
|
|||
android:scrollHorizontally="true"
|
||||
android:singleLine="true"
|
||||
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. "/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemTypeView"
|
||||
android:id="@+id/itemAdditionalDetails"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/itemTitleView"
|
||||
|
@ -32,5 +34,7 @@
|
|||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textSize="@dimen/video_item_search_uploader_text_size"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
tools:text="TYPE" />
|
||||
</RelativeLayout>
|
|
@ -1,36 +1,71 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:orientation="horizontal"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingTop="8dp">
|
||||
android:orientation="horizontal">
|
||||
|
||||
<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"
|
||||
<LinearLayout
|
||||
android:id="@+id/suggestion_search"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||
android:textSize="14sp"
|
||||
tools:ignore="RtlHardcoded"
|
||||
tools:text="Search query"/>
|
||||
</LinearLayout>
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_toLeftOf="@id/suggestion_insert"
|
||||
android:layout_toStartOf="@id/suggestion_insert"
|
||||
android:layout_centerVertical="true"
|
||||
android:paddingBottom="8dp"
|
||||
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
|
||||
android:id="@+id/notificationSongName"
|
||||
style="@android:style/TextAppearance.StatusBar.EventContent.Title"
|
||||
android:layout_width="match_parent"
|
||||
android:ellipsize="end"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
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."/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/notificationArtist"
|
||||
android:layout_width="match_parent"
|
||||
style="@android:style/TextAppearance.StatusBar.EventContent"
|
||||
android:ellipsize="end"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@color/background_subtext_color"
|
||||
tools:text="Duis posuere arcu condimentum lobortis mattis."/>
|
||||
</LinearLayout>
|
||||
|
||||
|
|
|
@ -46,22 +46,22 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/notificationSongName"
|
||||
style="@android:style/TextAppearance.StatusBar.EventContent.Title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
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."/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/notificationArtist"
|
||||
style="@android:style/TextAppearance.StatusBar.EventContent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@color/background_subtext_color"
|
||||
tools:text="Duis posuere arcu condimentum lobortis mattis."/>
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -80,7 +80,6 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/notificationTime"
|
||||
style="@android:style/TextAppearance.StatusBar.EventContent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
|
@ -92,6 +91,7 @@
|
|||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@color/background_subtext_color"
|
||||
tools:text="Duis posuere"/>
|
||||
|
||||
<RelativeLayout
|
||||
|
|
|
@ -29,22 +29,22 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/notificationSongName"
|
||||
style="@android:style/TextAppearance.StatusBar.EventContent.Title"
|
||||
android:layout_width="match_parent"
|
||||
android:ellipsize="end"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
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."/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/notificationArtist"
|
||||
android:layout_width="match_parent"
|
||||
style="@android:style/TextAppearance.StatusBar.EventContent"
|
||||
android:ellipsize="end"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@color/background_subtext_color"
|
||||
tools:text="Duis posuere arcu condimentum lobortis mattis."/>
|
||||
|
||||
</LinearLayout>
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
<attr name="history" format="reference"/>
|
||||
<attr name="drag_handle" format="reference"/>
|
||||
<attr name="selected" format="reference"/>
|
||||
<attr name="search_add" format="reference"/>
|
||||
|
||||
<!-- Can't refer to colors directly into drawable's xml-->
|
||||
<attr name="toolbar_shadow_drawable" format="reference"/>
|
||||
|
|
|
@ -39,7 +39,10 @@
|
|||
<color name="duration_text_color">#EEFFFFFF</color>
|
||||
<color name="playlist_stream_count_text_color">#ffffff</color>
|
||||
<color name="video_overlay_color">#66000000</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_text_color">#fff</color>
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
<item name="history">@drawable/ic_history_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="search_add">@drawable/ic_arrow_top_left_black_24dp</item>
|
||||
|
||||
<item name="separator_color">@color/light_separator_color</item>
|
||||
<item name="contrast_background_color">@color/light_contrast_background_color</item>
|
||||
|
@ -65,6 +66,7 @@
|
|||
<item name="history">@drawable/ic_history_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="search_add">@drawable/ic_arrow_top_left_white_24dp</item>
|
||||
|
||||
<item name="separator_color">@color/dark_separator_color</item>
|
||||
<item name="contrast_background_color">@color/dark_contrast_background_color</item>
|
||||
|
@ -160,4 +162,12 @@
|
|||
<item name="android:background">@color/dark_youtube_primary_color</item>
|
||||
</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>
|
||||
|
|