Merge branch 'playback-speed' into develop
Conflicts: proguard.cfg src/de/danoeh/antennapod/storage/DBTasks.java
This commit is contained in:
commit
b1911e5ff7
12
build.gradle
12
build.gradle
|
@ -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
10
pom.xml
|
@ -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>
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
@ -43,4 +97,5 @@
|
|||
<item>1</item>
|
||||
</string-array>
|
||||
|
||||
|
||||
</resources>
|
|
@ -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>
|
||||
|
@ -144,6 +146,12 @@
|
|||
<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>
|
||||
<string name="no_feeds_label">You haven\'t subscribed to any feeds yet.</string>
|
||||
|
@ -188,6 +196,8 @@
|
|||
<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 -->
|
||||
|
|
|
@ -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" >
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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,6 +42,7 @@ 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;
|
||||
|
||||
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -173,6 +173,12 @@ public class ExternalPlayerFragment extends Fragment {
|
|||
.newOnPlayButtonClickListener());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackSpeedChange() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,6 +123,11 @@ 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,18 +280,39 @@ 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);
|
||||
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;
|
||||
}
|
||||
|
@ -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,9 +691,22 @@ public class PlaybackService extends Service {
|
|||
}
|
||||
}
|
||||
|
||||
private MediaPlayer.OnPreparedListener preparedListener = new MediaPlayer.OnPreparedListener() {
|
||||
private final com.aocate.media.MediaPlayer.OnPreparedListener audioPreparedListener = new com.aocate.media.MediaPlayer.OnPreparedListener() {
|
||||
@Override
|
||||
public void onPrepared(MediaPlayer mp) {
|
||||
public void onPrepared(com.aocate.media.MediaPlayer mp) {
|
||||
genericOnPrepared(mp);
|
||||
}
|
||||
};
|
||||
|
||||
private final android.media.MediaPlayer.OnPreparedListener videoPreparedListener = new android.media.MediaPlayer.OnPreparedListener() {
|
||||
@Override
|
||||
public void onPrepared(android.media.MediaPlayer mp) {
|
||||
genericOnPrepared(mp);
|
||||
}
|
||||
};
|
||||
|
||||
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());
|
||||
|
@ -699,23 +741,43 @@ public class PlaybackService extends Service {
|
|||
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.OnSeekCompleteListener onSeekCompleteListener = new MediaPlayer.OnSeekCompleteListener() {
|
||||
|
||||
private final android.media.MediaPlayer.OnSeekCompleteListener videoSeekCompleteListener = new android.media.MediaPlayer.OnSeekCompleteListener() {
|
||||
@Override
|
||||
public void onSeekComplete(MediaPlayer mp) {
|
||||
public void onSeekComplete(android.media.MediaPlayer mp) {
|
||||
genericSeekCompleteListener();
|
||||
}
|
||||
};
|
||||
|
||||
private final void genericSeekCompleteListener() {
|
||||
if (status == PlayerStatus.SEEKING) {
|
||||
setStatus(statusBeforeSeek);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
private MediaPlayer.OnInfoListener onInfoListener = new MediaPlayer.OnInfoListener() {
|
||||
|
||||
private final android.media.MediaPlayer.OnInfoListener videoInfoListener = new android.media.MediaPlayer.OnInfoListener() {
|
||||
@Override
|
||||
public boolean onInfo(MediaPlayer mp, int what, int extra) {
|
||||
public boolean onInfo(android.media.MediaPlayer mp, int what, int extra) {
|
||||
return genericInfoListener(what);
|
||||
}
|
||||
};
|
||||
|
||||
private boolean genericInfoListener(int what) {
|
||||
switch (what) {
|
||||
case MediaPlayer.MEDIA_INFO_BUFFERING_START:
|
||||
sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_START, 0);
|
||||
|
@ -727,14 +789,26 @@ public class PlaybackService extends Service {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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.OnErrorListener onErrorListener = new MediaPlayer.OnErrorListener() {
|
||||
private static final String TAG = "PlaybackService.onErrorListener";
|
||||
|
||||
private final android.media.MediaPlayer.OnErrorListener videoErrorListener = new android.media.MediaPlayer.OnErrorListener() {
|
||||
@Override
|
||||
public boolean onError(MediaPlayer mp, int what, int extra) {
|
||||
Log.w(TAG, "An error has occured: " + what);
|
||||
public boolean onError(android.media.MediaPlayer mp, int what, int extra) {
|
||||
return genericOnError(mp, what, extra);
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -743,25 +817,44 @@ public class PlaybackService extends Service {
|
|||
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 MediaPlayer.OnCompletionListener completionListener = new MediaPlayer.OnCompletionListener() {
|
||||
|
||||
private final android.media.MediaPlayer.OnCompletionListener videoCompletionListener = new android.media.MediaPlayer.OnCompletionListener() {
|
||||
@Override
|
||||
public void onCompletion(MediaPlayer mp) {
|
||||
public void onCompletion(android.media.MediaPlayer mp) {
|
||||
genericOnCompletion();
|
||||
}
|
||||
};
|
||||
|
||||
private void genericOnCompletion() {
|
||||
endPlayback(true);
|
||||
}
|
||||
};
|
||||
|
||||
private MediaPlayer.OnBufferingUpdateListener onBufferingUpdateListener = new MediaPlayer.OnBufferingUpdateListener() {
|
||||
|
||||
private final com.aocate.media.MediaPlayer.OnBufferingUpdateListener audioBufferingUpdateListener = new com.aocate.media.MediaPlayer.OnBufferingUpdateListener() {
|
||||
@Override
|
||||
public void onBufferingUpdate(MediaPlayer mp, int percent) {
|
||||
sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_UPDATE, percent);
|
||||
|
||||
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)
|
||||
Log.d(TAG, "Playback ended");
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
|
|
@ -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,9 +811,14 @@ 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 {
|
||||
if (mediaplayer != null) {
|
||||
mediaplayer.release();
|
||||
}
|
||||
}
|
||||
|
||||
if (media.getItem().getChapters() == null) {
|
||||
ChapterUtils.loadChaptersFromFileUrl(media);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue