Merge branch 'playback-speed' into develop

Conflicts:
	proguard.cfg
	src/de/danoeh/antennapod/storage/DBTasks.java
This commit is contained in:
daniel oeh 2013-08-25 14:06:41 +02:00
commit b1911e5ff7
23 changed files with 1002 additions and 134 deletions

View File

@ -12,6 +12,17 @@ repositories {
mavenCentral()
}
dependencies {
def libsdir = new File('libs');
if (!libsdir.exists()) {
println "Creating libs directory"
libsdir.mkdir()
}
def prestoLib = new File('libs/presto_client-0.8.5.jar')
if (!prestoLib.exists()) {
println "Downloading presto library into libs folder"
new URL('http://www.aocate.com/presto/client/presto_client-0.8.5.jar').withInputStream{ i -> prestoLib.withOutputStream{ it << i }}
}
compile 'com.android.support:appcompat-v7:18.0.+'
compile 'org.apache.commons:commons-lang3:3.1'
compile ('org.shredzone.flattr4j:flattr4j-core:2.4') {
@ -22,6 +33,7 @@ dependencies {
compile 'commons-io:commons-io:2.4'
compile 'com.nineoldandroids:library:2.4.0'
compile project(':submodules:dslv:library')
compile files('libs/presto_client-0.8.5.jar')
}
android {

10
pom.xml
View File

@ -84,6 +84,14 @@
<artifactId>library</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>com.aocate</groupId>
<artifactId>presto_client</artifactId>
<version>0.8.5</version>
<type>jar</type>
<scope>system</scope>
<systemPath>${project.basedir}/libs/presto_client-0.8.5.jar</systemPath>
</dependency>
</dependencies>
<build>
@ -212,7 +220,7 @@
</manifest>
<proguard>
<skip>false</skip>
<config>proguard.cfg</config>
<config>proguard-mvn.cfg</config>
</proguard>
</configuration>
<executions>

66
proguard-mvn.cfg Normal file
View File

@ -0,0 +1,66 @@
-printmapping out.map
-renamesourcefileattribute SourceFile
-keepattributes SourceFile,LineNumberTable
-dontpreverify
-repackageclasses ''
-allowaccessmodification
-optimizations !code/simplification/arithmetic
-keepattributes *Annotation*
-injars libs/presto_client-0.8.5.jar
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.view.View {
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
public void set*(...);
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers class * extends android.content.Context {
public void *(android.view.View);
public void *(android.view.MenuItem);
}
-keepclassmembers class * implements android.os.Parcelable {
static android.os.Parcelable$Creator CREATOR;
}
-keepclassmembers class **.R$* {
public static <fields>;
}
-keep class android.support.v4.** { *; }
-keep interface android.support.v4.** { *; }
-keep class android.support.v7.** { *; }
-keep interface android.support.v7.** { *; }
-dontwarn android.support.v4.**
-dontwarn android.support.v7.**
-keepattributes *Annotation*
-keep class org.shredzone.flattr4j.** { *; }
-dontwarn org.shredzone.flattr4j.**
-keep class org.apache.commons.** { *; }
-dontskipnonpubliclibraryclassmembers

View File

@ -8,6 +8,8 @@
-optimizations !code/simplification/arithmetic
-keepattributes *Annotation*
#-injars libs/presto_client-0.8.5.jar
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service

View File

@ -92,14 +92,12 @@
android:layout_width="80dp"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:background="?attr/borderless_button"
android:src="?attr/av_pause" />
<ImageButton
android:id="@+id/butRev"
android:layout_width="80dp"
android:layout_width="60dp"
android:layout_height="match_parent"
android:layout_toLeftOf="@id/butPlay"
android:background="?attr/borderless_button"
@ -107,11 +105,22 @@
<ImageButton
android:id="@+id/butFF"
android:layout_width="80dp"
android:layout_width="60dp"
android:layout_height="match_parent"
android:layout_toRightOf="@id/butPlay"
android:background="?attr/borderless_button"
android:src="?attr/av_fast_forward" />
<Button
android:id="@+id/butPlaybackSpeed"
android:layout_width="60dp"
android:layout_height="match_parent"
android:layout_toRightOf="@id/butFF"
android:background="?attr/borderless_button"
android:src="?attr/av_fast_forward"
android:textColor="@color/gray"
android:textSize="@dimen/text_size_medium"
android:visibility="gone" />
</RelativeLayout>
<RelativeLayout

View File

@ -79,8 +79,6 @@
android:layout_width="80dp"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:background="?attr/borderless_button"
android:src="?attr/av_pause" />
@ -99,6 +97,17 @@
android:layout_toRightOf="@id/butPlay"
android:background="?attr/borderless_button"
android:src="?attr/av_fast_forward" />
<Button
android:id="@+id/butPlaybackSpeed"
android:layout_width="80dp"
android:layout_height="match_parent"
android:layout_toRightOf="@id/butFF"
android:background="?attr/borderless_button"
android:src="?attr/av_fast_forward"
android:textColor="@color/gray"
android:textSize="@dimen/text_size_medium"
android:visibility="gone" />
</RelativeLayout>
<RelativeLayout

View File

@ -1,6 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="update_intervall_options">
<item>Manual</item>
<item>1 hour</item>
<item>2 hours</item>
<item>4 hours</item>
<item>8 hours</item>
<item>12 hours</item>
<item>24 hours</item>
</string-array>
<string-array name="update_intervall_values">
<item>0</item>
<item>1</item>
@ -28,6 +38,50 @@
<item>80</item>
<item>100</item>
</string-array>
<string-array name="playback_speed_values">
<item>1.0</item>
<item>1.05</item>
<item>1.10</item>
<item>1.15</item>
<item>1.20</item>
<item>1.25</item>
<item>1.30</item>
<item>1.35</item>
<item>1.40</item>
<item>1.45</item>
<item>1.50</item>
<item>1.55</item>
<item>1.60</item>
<item>1.65</item>
<item>1.70</item>
<item>1.75</item>
<item>1.80</item>
<item>1.85</item>
<item>1.90</item>
<item>1.95</item>
<item>2.00</item>
<item>2.10</item>
<item>2.20</item>
<item>2.30</item>
<item>2.40</item>
<item>2.50</item>
<item>2.60</item>
<item>2.70</item>
<item>2.80</item>
<item>2.90</item>
<item>3.00</item>
<item>3.10</item>
<item>3.20</item>
<item>3.30</item>
<item>3.40</item>
<item>3.50</item>
<item>3.60</item>
<item>3.70</item>
<item>3.80</item>
<item>3.90</item>
<item>4.00</item>
</string-array>
<string-array name="autodl_select_networks_default_entries">
<item>N/A</item>
</string-array>
@ -42,5 +96,6 @@
<item>0</item>
<item>1</item>
</string-array>
</resources>

View File

@ -47,6 +47,8 @@
<string name="processing_label">Processing</string>
<string name="loading_label">Loading...</string>
<string name="image_of_prefix">Image of:\u0020</string>
<string name="close_label">Close</string>
<!-- 'Add Feed' Activity labels -->
<string name="feedurl_label">Feed URL</string>
@ -143,6 +145,12 @@
<string name="access_revoked_info">You have successfully revoked AntennaPod\'s access token to your account. In order to complete the process, you have to remove this app from the list of approved applications in your account settings on the flattr website.</string>
<string name="flattr_click_success">Successfully flattred this thing!</string>
<string name="flattring_label">Flattring</string>
<!-- Variable Speed -->
<string name="download_plugin_label">Download Plugin</string>
<string name="no_playback_plugin_title">Plugin Not Installed</string>
<string name="no_playback_plugin_msg">For variable speed playback to work, a third party library must be installed.\n\nTap \'Download Plugin\' to download a free plugin from the Play Store\n\nAny problems found using this plugin are not the responsibility of AntennaPod and should be reported to the plugin owner.</string>
<string name="set_playback_speed_label">Playback Speeds</string>
<!-- Empty list labels -->
<string name="no_items_label">There are no items in this list.</string>
@ -185,9 +193,11 @@
<string name="pref_theme_title_light">Light</string>
<string name="pref_theme_title_dark">Dark</string>
<string name="pref_episode_cache_unlimited">Unlimited</string>
<string name="pref_update_interval_hours_plural">hours</string>
<string name="pref_update_interval_hours_singular">hour</string>
<string name="pref_update_interval_hours_manual">Manual</string>
<string name="pref_update_interval_hours_plural">hours</string>
<string name="pref_update_interval_hours_singular">hour</string>
<string name="pref_update_interval_hours_manual">Manual</string>
<string name="pref_playback_speed_title">Playback Speeds</string>
<string name="pref_playback_speed_sum">Customize the speeds available for variable speed audio playback</string>
<!-- Search -->
@ -251,4 +261,4 @@
<string name="folder_not_empty_dialog_msg">The folder you have selected is not empty. Media downloads and other files will be placed directly in this folder. Continue anyway?</string>
<string name="set_to_default_folder">Choose default folder</string>
</resources>
</resources>

View File

@ -17,6 +17,10 @@
android:key="prefFollowQueue"
android:summary="@string/pref_followQueue_sum"
android:title="@string/pref_followQueue_title" />
<Preference
android:key="prefPlaybackSpeedLauncher"
android:summary="@string/pref_playback_speed_sum"
android:title="@string/pref_playback_speed_title" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/network_pref" >

View File

@ -12,7 +12,9 @@ import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.view.View.OnLongClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView.ScaleType;
import android.widget.ListView;
@ -22,11 +24,14 @@ import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.ChapterListAdapter;
import de.danoeh.antennapod.asynctask.ImageLoader;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
import de.danoeh.antennapod.feed.Chapter;
import de.danoeh.antennapod.feed.MediaType;
import de.danoeh.antennapod.feed.SimpleChapter;
import de.danoeh.antennapod.fragment.CoverFragment;
import de.danoeh.antennapod.fragment.ItemDescriptionFragment;
import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.service.PlaybackService;
import de.danoeh.antennapod.util.playback.ExternalMedia;
import de.danoeh.antennapod.util.playback.Playable;
@ -56,6 +61,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
private TextView txtvTitle;
private TextView txtvFeed;
private Button butPlaybackSpeed;
private ImageButton butNavLeft;
private ImageButton butNavRight;
@ -218,7 +224,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
if (savedPosition != -1) {
switchToFragment(savedPosition);
}
}
@Override
@ -363,6 +369,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
txtvFeed = (TextView) findViewById(R.id.txtvFeed);
butNavLeft = (ImageButton) findViewById(R.id.butNavLeft);
butNavRight = (ImageButton) findViewById(R.id.butNavRight);
butPlaybackSpeed = (Button) findViewById(R.id.butPlaybackSpeed);
butNavLeft.setOnClickListener(new OnClickListener() {
@ -390,6 +397,65 @@ public class AudioplayerActivity extends MediaplayerActivity {
}
}
});
butPlaybackSpeed.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (controller != null && controller.canSetPlaybackSpeed()) {
String[] availableSpeeds = UserPreferences
.getPlaybackSpeedArray();
String currentSpeed = UserPreferences.getPlaybackSpeed();
// Provide initial value in case the speed list has changed
// out from under us
// and our current speed isn't in the new list
String newSpeed;
if (availableSpeeds.length > 0) {
newSpeed = availableSpeeds[0];
} else {
newSpeed = "1.0";
}
for (int i = 0; i < availableSpeeds.length; i++) {
if (availableSpeeds[i].equals(currentSpeed)) {
if (i == availableSpeeds.length - 1) {
newSpeed = availableSpeeds[0];
} else {
newSpeed = availableSpeeds[i + 1];
}
break;
}
}
UserPreferences.setPlaybackSpeed(newSpeed);
controller.setPlaybackSpeed(Float.parseFloat(newSpeed));
}
}
});
butPlaybackSpeed.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
VariableSpeedDialog.showDialog(AudioplayerActivity.this);
return true;
}
});
}
@Override
protected void onPlaybackSpeedChange() {
super.onPlaybackSpeedChange();
updateButPlaybackSpeed();
}
private void updateButPlaybackSpeed() {
if (controller == null
|| (controller.getCurrentPlaybackSpeedMultiplier() == -1)) {
butPlaybackSpeed.setVisibility(View.GONE);
} else {
butPlaybackSpeed.setVisibility(View.VISIBLE);
butPlaybackSpeed.setText(UserPreferences.getPlaybackSpeed());
}
}
@Override
@ -421,7 +487,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
((AudioplayerContentFragment) currentlyShownFragment)
.onDataSetChanged(media);
}
updateButPlaybackSpeed();
}
public void notifyMediaPositionChanged() {

View File

@ -129,10 +129,19 @@ public abstract class MediaplayerActivity extends ActionBarActivity
public void onPlaybackEnd() {
finish();
}
@Override
public void onPlaybackSpeedChange() {
MediaplayerActivity.this.onPlaybackSpeedChange();
}
};
}
protected void onPlaybackSpeedChange() {
}
protected void onServiceQueried() {
supportInvalidateOptionsMenu();
}

View File

@ -25,6 +25,7 @@ import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.asynctask.FlattrClickWorker;
import de.danoeh.antennapod.asynctask.OpmlExportWorker;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.util.flattr.FlattrUtils;
@ -41,7 +42,8 @@ public class PreferenceActivity extends android.preference.PreferenceActivity {
private static final String PREF_ABOUT = "prefAbout";
private static final String PREF_CHOOSE_DATA_DIR = "prefChooseDataDir";
private static final String AUTO_DL_PREF_SCREEN = "prefAutoDownloadSettings";
private static final String PREF_PLAYBACK_SPEED_LAUNCHER = "prefPlaybackSpeedLauncher";
private CheckBoxPreference[] selectedNetworks;
@SuppressWarnings("deprecation")
@ -156,6 +158,14 @@ public class PreferenceActivity extends android.preference.PreferenceActivity {
return true;
}
});
findPreference(PREF_PLAYBACK_SPEED_LAUNCHER)
.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
VariableSpeedDialog.showDialog(PreferenceActivity.this);
return true;
}
});
buildUpdateIntervalPreference();
buildAutodownloadSelectedNetworsPreference();
setSelectedNetworksEnabled(UserPreferences

View File

@ -0,0 +1,94 @@
package de.danoeh.antennapod.dialog;
import java.util.Arrays;
import java.util.List;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.preferences.UserPreferences;
public class VariableSpeedDialog {
private VariableSpeedDialog() {
}
public static void showDialog(final Context context) {
if (com.aocate.media.MediaPlayer.isPrestoLibraryInstalled(context)) {
showSpeedSelectorDialog(context);
} else {
showGetPluginDialog(context);
}
}
private static void showGetPluginDialog(final Context context) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.no_playback_plugin_title);
builder.setMessage(R.string.no_playback_plugin_msg);
builder.setNegativeButton(R.string.close_label, null);
builder.setPositiveButton(R.string.download_plugin_label,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent playStoreIntent = new Intent(
Intent.ACTION_VIEW,
Uri.parse("market://details?id=com.falconware.prestissimo"));
context.startActivity(playStoreIntent);
}
});
builder.create().show();
}
private static void showSpeedSelectorDialog(final Context context) {
final String[] speedValues = context.getResources().getStringArray(
R.array.playback_speed_values);
// According to Java spec these get initialized to false on creation
final boolean[] speedChecked = new boolean[speedValues.length];
// Build the "isChecked" array so that multiChoice dialog is
// populated correctly
List<String> selectedSpeedList = Arrays.asList(UserPreferences
.getPlaybackSpeedArray());
for (int i = 0; i < speedValues.length; i++) {
speedChecked[i] = selectedSpeedList.contains(speedValues[i]);
}
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.set_playback_speed_label);
builder.setMultiChoiceItems(R.array.playback_speed_values,
speedChecked, new DialogInterface.OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialog, int which,
boolean isChecked) {
speedChecked[which] = isChecked;
}
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
int choiceCount = 0;
for (int i = 0; i < speedChecked.length; i++) {
if (speedChecked[i]) {
choiceCount++;
}
}
String[] newSpeedValues = new String[choiceCount];
int newSpeedIndex = 0;
for (int i = 0; i < speedChecked.length; i++) {
if (speedChecked[i]) {
newSpeedValues[newSpeedIndex++] = speedValues[i];
}
}
UserPreferences.setPlaybackSpeedArray(newSpeedValues);
}
});
builder.create().show();
}
}

View File

@ -173,6 +173,12 @@ public class ExternalPlayerFragment extends Fragment {
.newOnPlayButtonClickListener());
}
}
@Override
public void onPlaybackSpeedChange() {
// TODO Auto-generated method stub
}
};
}

View File

@ -2,9 +2,13 @@ package de.danoeh.antennapod.preferences;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONArray;
import org.json.JSONException;
import android.app.AlarmManager;
import android.app.PendingIntent;
@ -41,11 +45,13 @@ public class UserPreferences implements
public static final String PREF_ENABLE_AUTODL_WIFI_FILTER = "prefEnableAutoDownloadWifiFilter";
private static final String PREF_AUTODL_SELECTED_NETWORKS = "prefAutodownloadSelectedNetworks";
public static final String PREF_EPISODE_CACHE_SIZE = "prefEpisodeCacheSize";
private static final String PREF_PLAYBACK_SPEED = "prefPlaybackSpeed";
private static final String PREF_PLAYBACK_SPEED_ARRAY = "prefPlaybackSpeedArray";
private static int EPISODE_CACHE_SIZE_UNLIMITED = -1;
private static UserPreferences instance;
private Context context;
private final Context context;
// Preferences
private boolean pauseOnHeadsetDisconnect;
@ -60,6 +66,8 @@ public class UserPreferences implements
private boolean enableAutodownloadWifiFilter;
private String[] autodownloadSelectedNetworks;
private int episodeCacheSize;
private String playbackSpeed;
private String[] playbackSpeedArray;
private UserPreferences(Context context) {
this.context = context;
@ -83,6 +91,7 @@ public class UserPreferences implements
createNoMediaFile();
PreferenceManager.getDefaultSharedPreferences(context)
.registerOnSharedPreferenceChangeListener(instance);
}
private void loadPreferences() {
@ -108,6 +117,9 @@ public class UserPreferences implements
episodeCacheSize = readEpisodeCacheSize(sp.getString(
PREF_EPISODE_CACHE_SIZE, "20"));
enableAutodownload = sp.getBoolean(PREF_ENABLE_AUTODL, false);
playbackSpeed = sp.getString(PREF_PLAYBACK_SPEED, "1.0");
playbackSpeedArray = readPlaybackSpeedArray(sp.getString(
PREF_PLAYBACK_SPEED_ARRAY, null));
}
private int readThemeValue(String valueFromPrefs) {
@ -135,6 +147,36 @@ public class UserPreferences implements
}
}
private String[] readPlaybackSpeedArray(String valueFromPrefs) {
String[] selectedSpeeds = null;
// If this preference hasn't been set yet, return the default options
if (valueFromPrefs == null) {
String[] allSpeeds = context.getResources().getStringArray(
R.array.playback_speed_values);
List<String> speedList = new LinkedList<String>();
for (String speedStr : allSpeeds) {
float speed = Float.parseFloat(speedStr);
if (speed < 2.0001 && speed * 10 % 1 == 0) {
speedList.add(speedStr);
}
}
selectedSpeeds = speedList.toArray(new String[speedList.size()]);
} else {
try {
JSONArray jsonArray = new JSONArray(valueFromPrefs);
selectedSpeeds = new String[jsonArray.length()];
for (int i = 0; i < jsonArray.length(); i++) {
selectedSpeeds[i] = jsonArray.getString(i);
}
} catch (JSONException e) {
Log.e(TAG,
"Got JSON error when trying to get speeds from JSONArray");
e.printStackTrace();
}
}
return selectedSpeeds;
}
private static void instanceAvailable() {
if (instance == null) {
throw new IllegalStateException(
@ -196,6 +238,16 @@ public class UserPreferences implements
return EPISODE_CACHE_SIZE_UNLIMITED;
}
public static String getPlaybackSpeed() {
instanceAvailable();
return instance.playbackSpeed;
}
public static String[] getPlaybackSpeedArray() {
instanceAvailable();
return instance.playbackSpeedArray;
}
/**
* Returns the capacity of the episode cache. This method will return the
* negative integer EPISODE_CACHE_SIZE_UNLIMITED if the cache size is set to
@ -250,9 +302,29 @@ public class UserPreferences implements
PREF_EPISODE_CACHE_SIZE, "20"));
} else if (key.equals(PREF_ENABLE_AUTODL)) {
enableAutodownload = sp.getBoolean(PREF_ENABLE_AUTODL, false);
} else if (key.equals(PREF_PLAYBACK_SPEED)) {
playbackSpeed = sp.getString(PREF_PLAYBACK_SPEED, "1.0");
} else if (key.equals(PREF_PLAYBACK_SPEED_ARRAY)) {
playbackSpeedArray = readPlaybackSpeedArray(sp.getString(
PREF_PLAYBACK_SPEED_ARRAY, null));
}
}
public static void setPlaybackSpeed(String speed) {
PreferenceManager.getDefaultSharedPreferences(instance.context).edit()
.putString(PREF_PLAYBACK_SPEED, speed).apply();
}
public static void setPlaybackSpeedArray(String[] speeds) {
JSONArray jsonArray = new JSONArray();
for (String speed : speeds) {
jsonArray.put(speed);
}
PreferenceManager.getDefaultSharedPreferences(instance.context).edit()
.putString(PREF_PLAYBACK_SPEED_ARRAY, jsonArray.toString())
.apply();
}
public static void setAutodownloadSelectedNetworks(Context context,
String[] value) {
SharedPreferences.Editor editor = PreferenceManager

View File

@ -45,9 +45,13 @@ import de.danoeh.antennapod.storage.DBTasks;
import de.danoeh.antennapod.storage.DBWriter;
import de.danoeh.antennapod.util.BitmapDecoder;
import de.danoeh.antennapod.util.QueueAccess;
import de.danoeh.antennapod.util.DuckType;
import de.danoeh.antennapod.util.flattr.FlattrUtils;
import de.danoeh.antennapod.util.playback.AudioPlayer;
import de.danoeh.antennapod.util.playback.IPlayer;
import de.danoeh.antennapod.util.playback.Playable;
import de.danoeh.antennapod.util.playback.Playable.PlayableException;
import de.danoeh.antennapod.util.playback.VideoPlayer;
import de.danoeh.antennapod.util.playback.PlaybackController;
/**
@ -119,7 +123,12 @@ public class PlaybackService extends Service {
*/
public static final int NOTIFICATION_TYPE_PLAYBACK_END = 7;
/**
/**
* Playback speed has changed
* */
public static final int NOTIFICATION_TYPE_PLAYBACK_SPEED_CHANGE = 8;
/**
* Returned by getPositionSafe() or getDurationSafe() if the playbackService
* is in an invalid state.
*/
@ -132,13 +141,12 @@ public class PlaybackService extends Service {
private static final int NOTIFICATION_ID = 1;
private volatile IPlayer player;
private RemoteControlClient remoteControlClient;
private AudioManager audioManager;
private ComponentName mediaButtonReceiver;
private MediaPlayer player;
private RemoteControlClient remoteControlClient;
private Playable media;
private volatile Playable media;
/**
* True if media should be streamed (Extracted from Intent Extra) .
@ -252,7 +260,6 @@ public class PlaybackService extends Service {
}
);
dbLoaderExecutor = Executors.newSingleThreadExecutor();
player = createMediaPlayer();
mediaButtonReceiver = new ComponentName(getPackageName(),
MediaButtonReceiver.class.getName());
@ -273,21 +280,42 @@ public class PlaybackService extends Service {
loadQueue();
}
private MediaPlayer createMediaPlayer() {
return createMediaPlayer(new MediaPlayer());
private IPlayer createMediaPlayer() {
IPlayer player;
if (media == null || media.getMediaType() == MediaType.VIDEO) {
player = new VideoPlayer();
} else {
player = new AudioPlayer(this);
}
return createMediaPlayer(player);
}
private MediaPlayer createMediaPlayer(MediaPlayer mp) {
if (mp != null) {
mp.setOnPreparedListener(preparedListener);
mp.setOnCompletionListener(completionListener);
mp.setOnSeekCompleteListener(onSeekCompleteListener);
mp.setOnErrorListener(onErrorListener);
mp.setOnBufferingUpdateListener(onBufferingUpdateListener);
mp.setOnInfoListener(onInfoListener);
}
return mp;
}
private IPlayer createMediaPlayer(IPlayer mp) {
if (mp != null && media != null) {
if (media.getMediaType() == MediaType.AUDIO) {
((AudioPlayer) mp).setOnPreparedListener(audioPreparedListener);
((AudioPlayer) mp)
.setOnCompletionListener(audioCompletionListener);
((AudioPlayer) mp)
.setOnSeekCompleteListener(audioSeekCompleteListener);
((AudioPlayer) mp).setOnErrorListener(audioErrorListener);
((AudioPlayer) mp)
.setOnBufferingUpdateListener(audioBufferingUpdateListener);
((AudioPlayer) mp).setOnInfoListener(audioInfoListener);
} else {
((VideoPlayer) mp).setOnPreparedListener(videoPreparedListener);
((VideoPlayer) mp)
.setOnCompletionListener(videoCompletionListener);
((VideoPlayer) mp)
.setOnSeekCompleteListener(videoSeekCompleteListener);
((VideoPlayer) mp).setOnErrorListener(videoErrorListener);
((VideoPlayer) mp)
.setOnBufferingUpdateListener(videoBufferingUpdateListener);
((VideoPlayer) mp).setOnInfoListener(videoInfoListener);
}
}
return mp;
}
@SuppressLint("NewApi")
@Override
@ -475,7 +503,7 @@ public class PlaybackService extends Service {
seekDelta(-PlaybackController.DEFAULT_SEEK_DELTA);
break;
}
}
}
}
/**
@ -568,6 +596,7 @@ public class PlaybackService extends Service {
Log.d(TAG, "Setting up media player");
try {
MediaType mediaType = media.getMediaType();
player = createMediaPlayer();
if (mediaType == MediaType.AUDIO) {
if (AppConfig.DEBUG)
Log.d(TAG, "Mime type is audio");
@ -662,105 +691,169 @@ public class PlaybackService extends Service {
}
}
private MediaPlayer.OnPreparedListener preparedListener = new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
if (AppConfig.DEBUG)
Log.d(TAG, "Resource prepared");
mp.seekTo(media.getPosition());
if (media.getDuration() == 0) {
if (AppConfig.DEBUG)
Log.d(TAG, "Setting duration of media");
media.setDuration(mp.getDuration());
}
setStatus(PlayerStatus.PREPARED);
if (chapterLoader != null) {
chapterLoader.interrupt();
}
chapterLoader = new Thread() {
@Override
public void run() {
if (AppConfig.DEBUG)
Log.d(TAG, "Chapter loader started");
if (media != null && media.getChapters() == null) {
media.loadChapterMarks();
if (!isInterrupted() && media.getChapters() != null) {
sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD,
0);
}
}
if (AppConfig.DEBUG)
Log.d(TAG, "Chapter loader stopped");
}
};
chapterLoader.start();
private final com.aocate.media.MediaPlayer.OnPreparedListener audioPreparedListener = new com.aocate.media.MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(com.aocate.media.MediaPlayer mp) {
genericOnPrepared(mp);
}
};
if (startWhenPrepared) {
play();
}
}
};
private final android.media.MediaPlayer.OnPreparedListener videoPreparedListener = new android.media.MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(android.media.MediaPlayer mp) {
genericOnPrepared(mp);
}
};
private MediaPlayer.OnSeekCompleteListener onSeekCompleteListener = new MediaPlayer.OnSeekCompleteListener() {
private final void genericOnPrepared(Object inObj) {
IPlayer mp = DuckType.coerce(inObj).to(IPlayer.class);
if (AppConfig.DEBUG)
Log.d(TAG, "Resource prepared");
mp.seekTo(media.getPosition());
if (media.getDuration() == 0) {
if (AppConfig.DEBUG)
Log.d(TAG, "Setting duration of media");
media.setDuration(mp.getDuration());
}
setStatus(PlayerStatus.PREPARED);
if (chapterLoader != null) {
chapterLoader.interrupt();
}
chapterLoader = new Thread() {
@Override
public void run() {
if (AppConfig.DEBUG)
Log.d(TAG, "Chapter loader started");
if (media != null && media.getChapters() == null) {
media.loadChapterMarks();
if (!isInterrupted() && media.getChapters() != null) {
sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD,
0);
}
}
if (AppConfig.DEBUG)
Log.d(TAG, "Chapter loader stopped");
}
};
chapterLoader.start();
@Override
public void onSeekComplete(MediaPlayer mp) {
if (status == PlayerStatus.SEEKING) {
setStatus(statusBeforeSeek);
}
if (startWhenPrepared) {
play();
}
}
}
};
private final com.aocate.media.MediaPlayer.OnSeekCompleteListener audioSeekCompleteListener = new com.aocate.media.MediaPlayer.OnSeekCompleteListener() {
@Override
public void onSeekComplete(com.aocate.media.MediaPlayer mp) {
genericSeekCompleteListener();
}
};
private MediaPlayer.OnInfoListener onInfoListener = new MediaPlayer.OnInfoListener() {
private final android.media.MediaPlayer.OnSeekCompleteListener videoSeekCompleteListener = new android.media.MediaPlayer.OnSeekCompleteListener() {
@Override
public void onSeekComplete(android.media.MediaPlayer mp) {
genericSeekCompleteListener();
}
};
@Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
switch (what) {
case MediaPlayer.MEDIA_INFO_BUFFERING_START:
sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_START, 0);
return true;
case MediaPlayer.MEDIA_INFO_BUFFERING_END:
sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_END, 0);
return true;
default:
return false;
}
}
};
private final void genericSeekCompleteListener() {
if (status == PlayerStatus.SEEKING) {
setStatus(statusBeforeSeek);
}
}
private MediaPlayer.OnErrorListener onErrorListener = new MediaPlayer.OnErrorListener() {
private static final String TAG = "PlaybackService.onErrorListener";
private final com.aocate.media.MediaPlayer.OnInfoListener audioInfoListener = new com.aocate.media.MediaPlayer.OnInfoListener() {
@Override
public boolean onInfo(com.aocate.media.MediaPlayer mp, int what,
int extra) {
return genericInfoListener(what);
}
};
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
Log.w(TAG, "An error has occured: " + what);
if (mp.isPlaying()) {
pause(true, true);
}
sendNotificationBroadcast(NOTIFICATION_TYPE_ERROR, what);
setCurrentlyPlayingMedia(PlaybackPreferences.NO_MEDIA_PLAYING);
stopSelf();
return true;
}
};
private final android.media.MediaPlayer.OnInfoListener videoInfoListener = new android.media.MediaPlayer.OnInfoListener() {
@Override
public boolean onInfo(android.media.MediaPlayer mp, int what, int extra) {
return genericInfoListener(what);
}
};
private MediaPlayer.OnCompletionListener completionListener = new MediaPlayer.OnCompletionListener() {
private boolean genericInfoListener(int what) {
switch (what) {
case MediaPlayer.MEDIA_INFO_BUFFERING_START:
sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_START, 0);
return true;
case MediaPlayer.MEDIA_INFO_BUFFERING_END:
sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_END, 0);
return true;
default:
return false;
}
}
@Override
public void onCompletion(MediaPlayer mp) {
endPlayback(true);
}
};
private final com.aocate.media.MediaPlayer.OnErrorListener audioErrorListener = new com.aocate.media.MediaPlayer.OnErrorListener() {
@Override
public boolean onError(com.aocate.media.MediaPlayer mp, int what,
int extra) {
return genericOnError(mp, what, extra);
}
};
private MediaPlayer.OnBufferingUpdateListener onBufferingUpdateListener = new MediaPlayer.OnBufferingUpdateListener() {
private final android.media.MediaPlayer.OnErrorListener videoErrorListener = new android.media.MediaPlayer.OnErrorListener() {
@Override
public boolean onError(android.media.MediaPlayer mp, int what, int extra) {
return genericOnError(mp, what, extra);
}
};
@Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_UPDATE, percent);
private boolean genericOnError(Object inObj, int what, int extra) {
final String TAG = "PlaybackService.onErrorListener";
Log.w(TAG, "An error has occured: " + what + " " + extra);
IPlayer mp = DuckType.coerce(inObj).to(IPlayer.class);
if (mp.isPlaying()) {
pause(true, true);
}
sendNotificationBroadcast(NOTIFICATION_TYPE_ERROR, what);
setCurrentlyPlayingMedia(PlaybackPreferences.NO_MEDIA_PLAYING);
stopSelf();
return true;
}
}
};
private final com.aocate.media.MediaPlayer.OnCompletionListener audioCompletionListener = new com.aocate.media.MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(com.aocate.media.MediaPlayer mp) {
genericOnCompletion();
}
};
private final android.media.MediaPlayer.OnCompletionListener videoCompletionListener = new android.media.MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(android.media.MediaPlayer mp) {
genericOnCompletion();
}
};
private void genericOnCompletion() {
endPlayback(true);
}
private final com.aocate.media.MediaPlayer.OnBufferingUpdateListener audioBufferingUpdateListener = new com.aocate.media.MediaPlayer.OnBufferingUpdateListener() {
@Override
public void onBufferingUpdate(com.aocate.media.MediaPlayer mp,
int percent) {
genericOnBufferingUpdate(percent);
}
};
private final android.media.MediaPlayer.OnBufferingUpdateListener videoBufferingUpdateListener = new android.media.MediaPlayer.OnBufferingUpdateListener() {
@Override
public void onBufferingUpdate(android.media.MediaPlayer mp, int percent) {
genericOnBufferingUpdate(percent);
}
};
private void genericOnBufferingUpdate(int percent) {
sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_UPDATE, percent);
}
private void endPlayback(boolean playNextEpisode) {
if (AppConfig.DEBUG)
@ -862,7 +955,7 @@ public class PlaybackService extends Service {
/**
* Saves the current position and pauses playback. Note that, if audiofocus
* is abandoned, the lockscreen controls will also disapear.
*
*
* @param abandonFocus
* is true if the service should release audio focus
* @param reinit
@ -938,6 +1031,7 @@ public class PlaybackService extends Service {
Log.d(TAG, "Resuming/Starting playback");
writePlaybackPreferences();
setSpeed(Float.parseFloat(UserPreferences.getPlaybackSpeed()));
player.start();
if (status != PlayerStatus.PAUSED) {
player.seekTo((int) media.getPosition());
@ -1123,7 +1217,7 @@ public class PlaybackService extends Service {
/**
* Seek a specific position from the current position
*
*
* @param delta
* offset from current position (positive or negative)
* */
@ -1371,7 +1465,7 @@ public class PlaybackService extends Service {
}
}
}
};
};
/** Periodically saves the position of the media file */
class PositionSaver implements Runnable {
@ -1473,7 +1567,7 @@ public class PlaybackService extends Service {
return media;
}
public MediaPlayer getPlayer() {
public IPlayer getPlayer() {
return player;
}
@ -1486,6 +1580,53 @@ public class PlaybackService extends Service {
postStatusUpdateIntent();
}
public boolean canSetSpeed() {
if (player != null && media != null && media.getMediaType() == MediaType.AUDIO) {
return ((AudioPlayer) player).canSetSpeed();
}
return false;
}
public boolean canSetPitch() {
if (player != null && media != null && media.getMediaType() == MediaType.AUDIO) {
return ((AudioPlayer) player).canSetPitch();
}
return false;
}
public void setSpeed(float speed) {
if (media != null && media.getMediaType() == MediaType.AUDIO) {
AudioPlayer audioPlayer = (AudioPlayer) player;
if (audioPlayer.canSetSpeed()) {
audioPlayer.setPlaybackSpeed((float) speed);
if (AppConfig.DEBUG)
Log.d(TAG, "Playback speed was set to " + speed);
sendNotificationBroadcast(
NOTIFICATION_TYPE_PLAYBACK_SPEED_CHANGE, 0);
}
}
}
public void setPitch(float pitch) {
if (media != null && media.getMediaType() == MediaType.AUDIO) {
AudioPlayer audioPlayer = (AudioPlayer) player;
if (audioPlayer.canSetPitch()) {
audioPlayer.setPlaybackPitch((float) pitch);
}
}
}
public float getCurrentPlaybackSpeed() {
if (media.getMediaType() == MediaType.AUDIO
&& player instanceof AudioPlayer) {
AudioPlayer audioPlayer = (AudioPlayer) player;
if (audioPlayer.canSetSpeed()) {
return audioPlayer.getCurrentSpeedMultiplier();
}
}
return -1;
}
/**
* call getDuration() on mediaplayer or return INVALID_TIME if player is in
* an invalid state. This method should be used instead of calling

View File

@ -800,8 +800,9 @@ public class DownloadService extends Service {
media.setFile_url(request.getDestination());
// Get duration
MediaPlayer mediaplayer = new MediaPlayer();
MediaPlayer mediaplayer = null;
try {
mediaplayer = new MediaPlayer();
mediaplayer.setDataSource(media.getFile_url());
mediaplayer.prepare();
media.setDuration(mediaplayer.getDuration());
@ -810,8 +811,13 @@ public class DownloadService extends Service {
mediaplayer.reset();
} catch (IOException e) {
e.printStackTrace();
} catch (RuntimeException e) {
// Thrown by MediaPlayer initialization on some devices
e.printStackTrace();
} finally {
mediaplayer.release();
if (mediaplayer != null) {
mediaplayer.release();
}
}
if (media.getItem().getChapters() == null) {

View File

@ -441,13 +441,7 @@ public final class DBTasks {
}
for (FeedItem item : delete) {
try {
DBWriter.deleteFeedMediaOfItem(context, item.getId()).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
DBWriter.deleteFeedMediaOfItem(context, item.getId());
}
int counter = delete.size();

View File

@ -0,0 +1,115 @@
/* Adapted from: http://thinking-in-code.blogspot.com/2008/11/duck-typing-in-java-using-dynamic.html */
package de.danoeh.antennapod.util;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Allows "duck typing" or dynamic invocation based on method signature rather
* than type hierarchy. In other words, rather than checking whether something
* IS-a duck, check whether it WALKS-like-a duck or QUACKS-like a duck.
*
* To use first use the coerce static method to indicate the object you want to
* do Duck Typing for, then specify an interface to the to method which you want
* to coerce the type to, e.g:
*
* public interface Foo { void aMethod(); } class Bar { ... public void
* aMethod() { ... } ... } Bar bar = ...; Foo foo =
* DuckType.coerce(bar).to(Foo.class); foo.aMethod();
*
*
*/
public class DuckType {
private final Object objectToCoerce;
private DuckType(Object objectToCoerce) {
this.objectToCoerce = objectToCoerce;
}
private class CoercedProxy implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Method delegateMethod = findMethodBySignature(method);
assert delegateMethod != null;
return delegateMethod.invoke(DuckType.this.objectToCoerce, args);
}
}
/**
* Specify the duck typed object to coerce.
*
* @param object
* the object to coerce
* @return
*/
public static DuckType coerce(Object object) {
return new DuckType(object);
}
/**
* Coerce the Duck Typed object to the given interface providing it
* implements all the necessary methods.
*
* @param
* @param iface
* @return an instance of the given interface that wraps the duck typed
* class
* @throws ClassCastException
* if the object being coerced does not implement all the
* methods in the given interface.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public <T> T to(Class iface) {
assert iface.isInterface() : "cannot coerce object to a class, must be an interface";
if (isA(iface)) {
return (T) iface.cast(objectToCoerce);
}
if (quacksLikeA(iface)) {
return generateProxy(iface);
}
throw new ClassCastException("Could not coerce object of type " + objectToCoerce.getClass() + " to " + iface);
}
@SuppressWarnings("rawtypes")
private boolean isA(Class iface) {
return objectToCoerce.getClass().isInstance(iface);
}
/**
* Determine whether the duck typed object can be used with the given
* interface.
*
* @param Type
* of the interface to check.
* @param iface
* Interface class to check
* @return true if the object will support all the methods in the interface,
* false otherwise.
*/
@SuppressWarnings("rawtypes")
public boolean quacksLikeA(Class iface) {
for (Method method : iface.getMethods()) {
if (findMethodBySignature(method) == null) {
return false;
}
}
return true;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private <T> T generateProxy(Class iface) {
return (T) Proxy.newProxyInstance(iface.getClassLoader(), new Class[] { iface }, new CoercedProxy());
}
private Method findMethodBySignature(Method method) {
try {
return objectToCoerce.getClass().getMethod(method.getName(), method.getParameterTypes());
} catch (NoSuchMethodException e) {
return null;
}
}
}

View File

@ -0,0 +1,30 @@
package de.danoeh.antennapod.util.playback;
import android.content.Context;
import android.util.Log;
import android.view.SurfaceHolder;
import com.aocate.media.MediaPlayer;
public class AudioPlayer extends MediaPlayer implements IPlayer {
private static final String TAG = "AudioPlayer";
public AudioPlayer(Context context) {
super(context);
}
@Override
public void setScreenOnWhilePlaying(boolean screenOn) {
Log.e(TAG, "Setting screen on while playing not supported in Audio Player");
throw new UnsupportedOperationException("Setting screen on while playing not supported in Audio Player");
}
@Override
public void setDisplay(SurfaceHolder sh) {
if (sh != null) {
Log.e(TAG, "Setting display not supported in Audio Player");
throw new UnsupportedOperationException("Setting display not supported in Audio Player");
}
}
}

View File

@ -0,0 +1,64 @@
package de.danoeh.antennapod.util.playback;
import java.io.IOException;
import android.view.SurfaceHolder;
public interface IPlayer {
boolean canSetPitch();
boolean canSetSpeed();
float getCurrentPitchStepsAdjustment();
int getCurrentPosition();
float getCurrentSpeedMultiplier();
int getDuration();
float getMaxSpeedMultiplier();
float getMinSpeedMultiplier();
boolean isLooping();
boolean isPlaying();
void pause();
void prepare() throws IllegalStateException, IOException;
void prepareAsync();
void release();
void reset();
void seekTo(int msec);
void setAudioStreamType(int streamtype);
void setScreenOnWhilePlaying(boolean screenOn);
void setDataSource(String path) throws IllegalStateException, IOException,
IllegalArgumentException, SecurityException;
void setDisplay(SurfaceHolder sh);
void setEnableSpeedAdjustment(boolean enableSpeedAdjustment);
void setLooping(boolean looping);
void setPitchStepsAdjustment(float pitchSteps);
void setPlaybackPitch(float f);
void setPlaybackSpeed(float f);
void setVolume(float left, float right);
void start();
void stop();
}

View File

@ -342,6 +342,9 @@ public abstract class PlaybackController {
case PlaybackService.NOTIFICATION_TYPE_PLAYBACK_END:
onPlaybackEnd();
break;
case PlaybackService.NOTIFICATION_TYPE_PLAYBACK_SPEED_CHANGE:
onPlaybackSpeedChange();
break;
}
} else {
@ -369,6 +372,8 @@ public abstract class PlaybackController {
}
};
public abstract void onPlaybackSpeedChange();
public abstract void onShutdownNotification();
/**
@ -663,6 +668,24 @@ public abstract class PlaybackController {
return status;
}
public boolean canSetPlaybackSpeed() {
return playbackService != null && playbackService.canSetSpeed();
}
public void setPlaybackSpeed(float speed) {
if (playbackService != null) {
playbackService.setSpeed(speed);
}
}
public float getCurrentPlaybackSpeedMultiplier() {
if (canSetPlaybackSpeed()) {
return playbackService.getCurrentPlaybackSpeed();
} else {
return -1;
}
}
public boolean isPlayingVideo() {
if (playbackService != null) {
return PlaybackService.isPlayingVideo();
@ -670,6 +693,7 @@ public abstract class PlaybackController {
return false;
}
/**
* Returns true if PlaybackController can communicate with the playback
* service.

View File

@ -0,0 +1,62 @@
package de.danoeh.antennapod.util.playback;
import android.media.MediaPlayer;
import android.util.Log;
public class VideoPlayer extends MediaPlayer implements IPlayer {
private static final String TAG = "VideoPlayer";
@Override
public boolean canSetPitch() {
return false;
}
@Override
public boolean canSetSpeed() {
return false;
}
@Override
public float getCurrentPitchStepsAdjustment() {
return 1;
}
@Override
public float getCurrentSpeedMultiplier() {
return 1;
}
@Override
public float getMaxSpeedMultiplier() {
return 1;
}
@Override
public float getMinSpeedMultiplier() {
return 1;
}
@Override
public void setEnableSpeedAdjustment(boolean enableSpeedAdjustment) throws UnsupportedOperationException {
Log.e(TAG, "Setting enable speed adjustment unsupported in video player");
throw new UnsupportedOperationException("Setting enable speed adjustment unsupported in video player");
}
@Override
public void setPitchStepsAdjustment(float pitchSteps) {
Log.e(TAG, "Setting pitch steps adjustment unsupported in video player");
throw new UnsupportedOperationException("Setting pitch steps adjustment unsupported in video player");
}
@Override
public void setPlaybackPitch(float f) {
Log.e(TAG, "Setting playback pitch unsupported in video player");
throw new UnsupportedOperationException("Setting playback pitch unsupported in video player");
}
@Override
public void setPlaybackSpeed(float f) {
Log.e(TAG, "Setting playback speed unsupported in video player");
throw new UnsupportedOperationException("Setting playback speed unsupported in video player");
}
}