Add TrackSelectionDialog (with swipe tabs) to demo app.

PiperOrigin-RevId: 233582549
This commit is contained in:
tonihei 2019-02-12 12:20:46 +00:00 committed by Andrew Lewis
parent 00c31265cd
commit 1dd36ae391
14 changed files with 335 additions and 211 deletions

View File

@ -63,6 +63,9 @@ android {
dependencies {
implementation 'com.android.support:support-annotations:' + supportLibraryVersion
implementation 'com.android.support:support-core-ui:' + supportLibraryVersion
implementation 'com.android.support:support-fragment:' + supportLibraryVersion
implementation 'com.android.support:design:' + supportLibraryVersion
implementation project(modulePrefix + 'library-core')
implementation project(modulePrefix + 'library-dash')
implementation project(modulePrefix + 'library-hls')

View File

@ -39,7 +39,8 @@
<activity android:name="com.google.android.exoplayer2.demo.SampleChooserActivity"
android:configChanges="keyboardHidden"
android:label="@string/application_name">
android:label="@string/application_name"
android:theme="@style/Theme.AppCompat">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>

View File

@ -15,20 +15,12 @@
*/
package com.google.android.exoplayer2.demo;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.support.v4.app.FragmentManager;
import android.widget.Toast;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.RenderersFactory;
@ -46,11 +38,6 @@ import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.SelectionOverride;
import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionUtil;
import com.google.android.exoplayer2.ui.DefaultTrackNameProvider;
import com.google.android.exoplayer2.ui.TrackNameProvider;
import com.google.android.exoplayer2.ui.TrackSelectionDialogBuilder;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.Util;
@ -81,7 +68,6 @@ public class DownloadTracker implements DownloadManager.Listener {
private final Context context;
private final DataSource.Factory dataSourceFactory;
private final TrackNameProvider trackNameProvider;
private final CopyOnWriteArraySet<Listener> listeners;
private final HashMap<Uri, DownloadState> trackedDownloadStates;
private final DefaultDownloadIndex downloadIndex;
@ -92,7 +78,6 @@ public class DownloadTracker implements DownloadManager.Listener {
this.context = context.getApplicationContext();
this.dataSourceFactory = dataSourceFactory;
this.downloadIndex = downloadIndex;
trackNameProvider = new DefaultTrackNameProvider(context.getResources());
listeners = new CopyOnWriteArraySet<>();
trackedDownloadStates = new HashMap<>();
HandlerThread actionFileWriteThread = new HandlerThread("DownloadTracker");
@ -122,7 +107,7 @@ public class DownloadTracker implements DownloadManager.Listener {
}
public void toggleDownload(
Activity activity,
FragmentManager fragmentManager,
String name,
Uri uri,
String extension,
@ -133,7 +118,7 @@ public class DownloadTracker implements DownloadManager.Listener {
startServiceWithAction(removeAction);
} else {
new StartDownloadDialogHelper(
activity, getDownloadHelper(uri, extension, renderersFactory), name);
fragmentManager, getDownloadHelper(uri, extension, renderersFactory), name);
}
}
@ -225,42 +210,23 @@ public class DownloadTracker implements DownloadManager.Listener {
}
}
@SuppressWarnings("UngroupedOverloads")
private final class StartDownloadDialogHelper
implements DownloadHelper.Callback,
DialogInterface.OnClickListener,
DialogInterface.OnDismissListener,
View.OnClickListener {
DialogInterface.OnDismissListener {
private final FragmentManager fragmentManager;
private final DownloadHelper downloadHelper;
private final String name;
private final LayoutInflater dialogInflater;
private final AlertDialog dialog;
private final LinearLayout selectionList;
private TrackSelectionDialog trackSelectionDialog;
private MappedTrackInfo mappedTrackInfo;
private DefaultTrackSelector.Parameters parameters;
private StartDownloadDialogHelper(
Activity activity, DownloadHelper downloadHelper, String name) {
public StartDownloadDialogHelper(
FragmentManager fragmentManager, DownloadHelper downloadHelper, String name) {
this.fragmentManager = fragmentManager;
this.downloadHelper = downloadHelper;
this.name = name;
AlertDialog.Builder builder =
new AlertDialog.Builder(activity)
.setTitle(R.string.download_preparing)
.setPositiveButton(android.R.string.ok, /* listener= */ this)
.setNegativeButton(android.R.string.cancel, /* listener= */ null);
// Inflate with the builder's context to ensure the correct style is used.
dialogInflater = LayoutInflater.from(builder.getContext());
selectionList = (LinearLayout) dialogInflater.inflate(R.layout.start_download_dialog, null);
builder.setView(selectionList);
dialog = builder.create();
dialog.setOnDismissListener(/* listener= */ this);
dialog.show();
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
parameters = DownloadHelper.DEFAULT_TRACK_SELECTOR_PARAMETERS;
downloadHelper.prepare(this);
}
@ -268,12 +234,19 @@ public class DownloadTracker implements DownloadManager.Listener {
@Override
public void onPrepared(DownloadHelper helper) {
if (helper.getPeriodCount() > 0) {
mappedTrackInfo = downloadHelper.getMappedTrackInfo(/* periodIndex= */ 0);
updateSelectionList();
if (helper.getPeriodCount() == 0) {
onPrepareError(helper, new IOException("No periods found."));
return;
}
dialog.setTitle(R.string.exo_download_description);
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true);
mappedTrackInfo = downloadHelper.getMappedTrackInfo(/* periodIndex= */ 0);
trackSelectionDialog = new TrackSelectionDialog();
trackSelectionDialog.init(
mappedTrackInfo,
/* allowAdaptiveSelections =*/ false,
/* allowMultipleOverrides= */ true,
/* onClickListener= */ this,
/* onDismissListener= */ this);
trackSelectionDialog.show(fragmentManager, "download");
}
@Override
@ -282,33 +255,44 @@ public class DownloadTracker implements DownloadManager.Listener {
context.getApplicationContext(), R.string.download_start_error, Toast.LENGTH_LONG)
.show();
Log.e(TAG, "Failed to start download", e);
dialog.cancel();
}
// View.OnClickListener implementation.
@Override
public void onClick(View v) {
Integer rendererIndex = (Integer) v.getTag();
TrackGroupArray trackGroupArray = mappedTrackInfo.getTrackGroups(rendererIndex);
String dialogTitle = getTrackTypeString(mappedTrackInfo.getRendererType(rendererIndex));
new TrackSelectionDialogBuilder(
dialog.getContext(),
dialogTitle,
mappedTrackInfo,
rendererIndex,
(isDisabled, overrides) -> updateTracks(rendererIndex, isDisabled, overrides))
.setShowDisableOption(true)
.setIsDisabled(parameters.getRendererDisabled(rendererIndex))
.setOverride(parameters.getSelectionOverride(rendererIndex, trackGroupArray))
.build()
.show();
}
// DialogInterface.OnClickListener implementation.
@Override
public void onClick(DialogInterface dialog, int which) {
DefaultTrackSelector.ParametersBuilder builder =
DownloadHelper.DEFAULT_TRACK_SELECTOR_PARAMETERS.buildUpon();
for (int i = 0; i < mappedTrackInfo.getRendererCount(); i++) {
builder.setRendererDisabled(/* rendererIndex= */ i, /* disabled= */ true);
}
for (int i = 0; i < downloadHelper.getPeriodCount(); i++) {
downloadHelper.clearTrackSelections(/* periodIndex = */ i);
}
for (int i = 0; i < mappedTrackInfo.getRendererCount(); i++) {
if (trackSelectionDialog.getIsDisabled(/* rendererIndex= */ i)) {
continue;
}
builder.setRendererDisabled(/* rendererIndex= */ i, /* disabled= */ false);
List<SelectionOverride> overrides =
trackSelectionDialog.getOverrides(/* rendererIndex= */ i);
if (overrides.isEmpty()) {
for (int j = 0; j < downloadHelper.getPeriodCount(); j++) {
downloadHelper.addTrackSelection(/* periodIndex = */ j, builder.build());
}
} else {
TrackGroupArray trackGroupArray = mappedTrackInfo.getTrackGroups(/* rendererIndex= */ i);
for (int overrideIndex = 0; overrideIndex < overrides.size(); overrideIndex++) {
builder.setSelectionOverride(
/* rendererIndex= */ i, trackGroupArray, overrides.get(overrideIndex));
for (int j = 0; j < downloadHelper.getPeriodCount(); j++) {
downloadHelper.addTrackSelection(/* periodIndex = */ j, builder.build());
}
}
builder.clearSelectionOverrides();
}
builder.setRendererDisabled(/* rendererIndex= */ i, /* disabled= */ true);
}
DownloadAction downloadAction = downloadHelper.getDownloadAction(Util.getUtf8Bytes(name));
startDownload(downloadAction);
}
@ -316,86 +300,8 @@ public class DownloadTracker implements DownloadManager.Listener {
// DialogInterface.OnDismissListener implementation.
@Override
public void onDismiss(DialogInterface dialog) {
public void onDismiss(DialogInterface dialogInterface) {
downloadHelper.release();
}
// Internal methods.
private void updateTracks(
int rendererIndex, boolean isDisabled, List<SelectionOverride> overrides) {
parameters =
TrackSelectionUtil.updateParametersWithOverride(
parameters,
rendererIndex,
mappedTrackInfo.getTrackGroups(rendererIndex),
isDisabled,
overrides.isEmpty() ? null : overrides.get(0));
for (int i = 0; i < downloadHelper.getPeriodCount(); i++) {
downloadHelper.replaceTrackSelections(/* periodIndex= */ i, parameters);
}
updateSelectionList();
}
private void updateSelectionList() {
selectionList.removeAllViews();
for (int i = 0; i < mappedTrackInfo.getRendererCount(); i++) {
TrackGroupArray trackGroupArray = mappedTrackInfo.getTrackGroups(i);
if (trackGroupArray.length == 0) {
continue;
}
String trackTypeString =
getTrackTypeString(mappedTrackInfo.getRendererType(/* rendererIndex= */ i));
if (trackTypeString == null) {
return;
}
String trackSelectionsString = getTrackSelectionString(/* rendererIndex= */ i);
View view = dialogInflater.inflate(R.layout.download_track_item, selectionList, false);
TextView trackTitleView = view.findViewById(R.id.track_title);
TextView trackDescView = view.findViewById(R.id.track_desc);
ImageButton editButton = view.findViewById(R.id.edit_button);
trackTitleView.setText(trackTypeString);
trackDescView.setText(trackSelectionsString);
editButton.setTag(i);
editButton.setOnClickListener(this);
selectionList.addView(view);
}
}
private String getTrackSelectionString(int rendererIndex) {
List<TrackSelection> trackSelections =
downloadHelper.getTrackSelections(/* periodIndex= */ 0, rendererIndex);
String selectedTracks = "";
Resources resources = selectionList.getResources();
for (int i = 0; i < trackSelections.size(); i++) {
TrackSelection selection = trackSelections.get(i);
for (int j = 0; j < selection.length(); j++) {
String trackName = trackNameProvider.getTrackName(selection.getFormat(j));
if (i == 0 && j == 0) {
selectedTracks = trackName;
} else {
selectedTracks = resources.getString(R.string.exo_item_list, selectedTracks, trackName);
}
}
}
return selectedTracks.isEmpty()
? resources.getString(R.string.exo_track_selection_none)
: selectedTracks;
}
@Nullable
private String getTrackTypeString(int trackType) {
Resources resources = selectionList.getResources();
switch (trackType) {
case C.TRACK_TYPE_VIDEO:
return resources.getString(R.string.exo_track_selection_title_video);
case C.TRACK_TYPE_AUDIO:
return resources.getString(R.string.exo_track_selection_title_audio);
case C.TRACK_TYPE_TEXT:
return resources.getString(R.string.exo_track_selection_title_text);
default:
return null;
}
}
}
}

View File

@ -15,7 +15,6 @@
*/
package com.google.android.exoplayer2.demo;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.AssetManager;
@ -23,6 +22,7 @@ import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.JsonReader;
import android.view.Menu;
import android.view.MenuInflater;
@ -55,7 +55,7 @@ import java.util.Collections;
import java.util.List;
/** An activity for selecting from a list of media samples. */
public class SampleChooserActivity extends Activity
public class SampleChooserActivity extends AppCompatActivity
implements DownloadTracker.Listener, OnChildClickListener {
private static final String TAG = "SampleChooserActivity";
@ -182,7 +182,11 @@ public class SampleChooserActivity extends Activity
((DemoApplication) getApplication())
.buildRenderersFactory(isNonNullAndChecked(preferExtensionDecodersMenuItem));
downloadTracker.toggleDownload(
this, sample.name, uriSample.uri, uriSample.extension, renderersFactory);
getSupportFragmentManager(),
sample.name,
uriSample.uri,
uriSample.extension,
renderersFactory);
}
}

View File

@ -0,0 +1,235 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.demo;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.TabLayout;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.SelectionOverride;
import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo;
import com.google.android.exoplayer2.ui.TrackSelectionView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/** Dialog to select tracks. */
public final class TrackSelectionDialog extends DialogFragment {
private final SparseArray<TrackSelectionViewFragment> tabFragments;
private final List<CharSequence> tabTitles;
private MappedTrackInfo mappedTrackInfo;
private boolean allowAdaptiveSelections;
private boolean allowMultipleOverrides;
private DialogInterface.OnClickListener onClickListener;
private DialogInterface.OnDismissListener onDismissListener;
/** Creates the dialog. */
public TrackSelectionDialog() {
tabFragments = new SparseArray<>();
tabTitles = new ArrayList<>();
// Retain instance across orientation changes to prevent loosing access to init data.
setRetainInstance(true);
}
/**
* Initializes the dialog.
*
* @param mappedTrackInfo The {@link MappedTrackInfo} to display.
* @param allowAdaptiveSelections Whether adaptive selections (consisting of more than one track)
* can be made.
* @param allowMultipleOverrides Whether tracks from multiple track groups can be selected.
* @param onClickListener {@link DialogInterface.OnClickListener} called when tracks are selected.
* @param onDismissListener {@link DialogInterface.OnDismissListener} called when the dialog is
* dismissed.
*/
public void init(
MappedTrackInfo mappedTrackInfo,
boolean allowAdaptiveSelections,
boolean allowMultipleOverrides,
DialogInterface.OnClickListener onClickListener,
DialogInterface.OnDismissListener onDismissListener) {
this.mappedTrackInfo = mappedTrackInfo;
this.allowAdaptiveSelections = allowAdaptiveSelections;
this.allowMultipleOverrides = allowMultipleOverrides;
this.onClickListener = onClickListener;
this.onDismissListener = onDismissListener;
}
/**
* Returns whether a renderer is disabled.
*
* @param rendererIndex Renderer index.
* @return Whether the renderer is disabled.
*/
public boolean getIsDisabled(int rendererIndex) {
TrackSelectionViewFragment rendererView = tabFragments.get(rendererIndex);
return rendererView != null && rendererView.trackSelectionView.getIsDisabled();
}
/**
* Returns the list of selected track selection overrides for the specified renderer. There will
* be at most one override for each track group.
*
* @param rendererIndex Renderer index.
* @return The list of track selection overrides for this renderer.
*/
public List<SelectionOverride> getOverrides(int rendererIndex) {
TrackSelectionViewFragment rendererView = tabFragments.get(rendererIndex);
return rendererView == null
? Collections.emptyList()
: rendererView.trackSelectionView.getOverrides();
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.exo_download_description)
.setPositiveButton(android.R.string.ok, onClickListener)
.setNegativeButton(android.R.string.cancel, /* listener= */ null)
.create();
}
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
onDismissListener.onDismiss(dialog);
}
@Nullable
@Override
public View onCreateView(
LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
for (int i = 0; i < mappedTrackInfo.getRendererCount(); i++) {
TrackGroupArray trackGroupArray = mappedTrackInfo.getTrackGroups(i);
if (trackGroupArray.length == 0) {
continue;
}
String trackTypeString =
getTrackTypeString(mappedTrackInfo.getRendererType(/* rendererIndex= */ i));
if (trackTypeString == null) {
continue;
}
TrackSelectionViewFragment tabFragment = new TrackSelectionViewFragment();
tabFragment.init(
mappedTrackInfo, /* rendererIndex= */ i, allowAdaptiveSelections, allowMultipleOverrides);
tabFragments.put(i, tabFragment);
tabTitles.add(trackTypeString);
}
View dialogView = inflater.inflate(R.layout.download_dialog, container, false);
TabLayout tabLayout = dialogView.findViewById(R.id.download_dialog_tab_layout);
ViewPager viewPager = dialogView.findViewById(R.id.download_dialog_view_pager);
viewPager.setAdapter(new FragmentAdapter(getChildFragmentManager()));
tabLayout.setupWithViewPager(viewPager);
((AlertDialog) getDialog()).setView(dialogView);
return dialogView;
}
@Nullable
private String getTrackTypeString(int trackType) {
Resources resources = getResources();
switch (trackType) {
case C.TRACK_TYPE_VIDEO:
return resources.getString(R.string.exo_track_selection_title_video);
case C.TRACK_TYPE_AUDIO:
return resources.getString(R.string.exo_track_selection_title_audio);
case C.TRACK_TYPE_TEXT:
return resources.getString(R.string.exo_track_selection_title_text);
default:
return null;
}
}
private final class FragmentAdapter extends FragmentPagerAdapter {
public FragmentAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
@Override
public Fragment getItem(int position) {
return tabFragments.valueAt(position);
}
@Override
public int getCount() {
return tabFragments.size();
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
return tabTitles.get(position);
}
}
/** Fragment to show a track seleciton in tab of the track selection dialog. */
public static final class TrackSelectionViewFragment extends Fragment {
private MappedTrackInfo mappedTrackInfo;
private int rendererIndex;
private boolean allowAdaptiveSelections;
private boolean allowMultipleOverrides;
/* package */ TrackSelectionView trackSelectionView;
public void init(
MappedTrackInfo mappedTrackInfo,
int rendererIndex,
boolean allowAdaptiveSelections,
boolean allowMultipleOverrides) {
this.mappedTrackInfo = mappedTrackInfo;
this.rendererIndex = rendererIndex;
this.allowAdaptiveSelections = allowAdaptiveSelections;
this.allowMultipleOverrides = allowMultipleOverrides;
}
@Nullable
@Override
public View onCreateView(
LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View rootView =
inflater.inflate(R.layout.download_dialog_tab, container, /* attachToRoot= */ false);
trackSelectionView = rootView.findViewById(R.id.download_dialog_track_selection_view);
trackSelectionView.setShowDisableOption(true);
trackSelectionView.setAllowMultipleOverrides(allowMultipleOverrides);
trackSelectionView.setAllowAdaptiveSelections(allowAdaptiveSelections);
trackSelectionView.init(
mappedTrackInfo,
rendererIndex,
/* isDisabled= */ false,
/* overrides= */ Collections.emptyList());
return rootView;
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 299 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 413 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 B

View File

@ -14,7 +14,15 @@
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/selection_list"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
android:layout_height="wrap_content">
<android.support.design.widget.TabLayout
android:id="@+id/download_dialog_tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<android.support.v4.view.ViewPager
android:id="@+id/download_dialog_view_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.exoplayer2.ui.TrackSelectionView
android:id="@+id/download_dialog_track_selection_view"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</ScrollView>

View File

@ -1,53 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:orientation="horizontal"
android:gravity="center_vertical">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/track_title"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="4dp"/>
<TextView
android:id="@+id/track_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="4dp"/>
</LinearLayout>
<ImageButton
android:id="@+id/edit_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:contentDescription="@string/download_edit_track"
android:src="@drawable/ic_edit"/>
</LinearLayout>

View File

@ -51,10 +51,6 @@
<string name="ima_not_loaded">Playing sample without ads, as the IMA extension was not loaded</string>
<string name="download_edit_track">Edit selection</string>
<string name="download_preparing">Preparing download…</string>
<string name="download_start_error">Failed to start download</string>
<string name="download_playlist_unsupported">This demo app does not support downloading playlists</string>