summaryrefslogtreecommitdiff
path: root/app/src/main/java/de/danoeh
diff options
context:
space:
mode:
authorByteHamster <info@bytehamster.com>2020-02-05 16:51:59 +0100
committerByteHamster <info@bytehamster.com>2020-02-05 16:51:59 +0100
commitd74b8d06b21448a1ac2441bbdd95ff8179b754e8 (patch)
treef5c9f571935c05a417ed312b3db256b51926af5a /app/src/main/java/de/danoeh
parentc3e1f8afbbb798c2bc1fde2d835d19cce5ebfc31 (diff)
parentf790b78b1a6c2aaf0fb789be9542d09ed2da47d2 (diff)
downloadAntennaPod-d74b8d06b21448a1ac2441bbdd95ff8179b754e8.zip
Merge branch 'develop' into extract-queue-item-view
Diffstat (limited to 'app/src/main/java/de/danoeh')
-rw-r--r--app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java73
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java11
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java40
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java12
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java19
-rw-r--r--app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java16
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java72
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java17
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java29
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java3
20 files changed, 180 insertions, 159 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java b/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java
index 061ea9ae2..ab4d564df 100644
--- a/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java
+++ b/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java
@@ -35,7 +35,9 @@ public class CrashReportWriter implements Thread.UncaughtExceptionHandler {
PrintWriter out = null;
try {
out = new PrintWriter(new FileWriter(path));
- out.println(getSystemInfo());
+ out.println("[ Crash info ]");
+ out.println("Time: " + new SimpleDateFormat("dd-MM-yyyy HH:mm:ss", Locale.getDefault()).format(new Date()));
+ out.println("AntennaPod version: " + BuildConfig.VERSION_NAME);
out.println();
out.println("[ StackTrace ]");
ex.printStackTrace(out);
@@ -54,7 +56,6 @@ public class CrashReportWriter implements Thread.UncaughtExceptionHandler {
"\nAntennaPod version: " + BuildConfig.VERSION_NAME +
"\nModel: " + Build.MODEL +
"\nDevice: " + Build.DEVICE +
- "\nProduct: " + Build.PRODUCT +
- "\nTime: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date()) + "\n";
+ "\nProduct: " + Build.PRODUCT;
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
index 7f8c14b03..5906cd214 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
@@ -1,28 +1,25 @@
package de.danoeh.antennapod.activity;
import android.content.Intent;
-import androidx.core.view.ViewCompat;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
-
-import java.text.DecimalFormat;
-import java.text.DecimalFormatSymbols;
-import java.util.Locale;
-import java.util.concurrent.atomic.AtomicBoolean;
-
import de.danoeh.antennapod.core.feed.MediaType;
-import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
+import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
+import java.text.DecimalFormat;
+import java.util.concurrent.atomic.AtomicBoolean;
+
/**
* Activity for playing audio files.
*/
public class AudioplayerActivity extends MediaplayerInfoActivity {
private static final String TAG = "AudioPlayerActivity";
+ private static final float EPSILON = 0.001f;
private final AtomicBoolean isSetup = new AtomicBoolean(false);
@@ -33,8 +30,8 @@ public class AudioplayerActivity extends MediaplayerInfoActivity {
playExternalMedia(getIntent(), MediaType.AUDIO);
} else if (PlaybackService.isCasting()) {
Intent intent = PlaybackService.getPlayerActivityIntent(this);
- if (intent.getComponent() != null &&
- !intent.getComponent().getClassName().equals(AudioplayerActivity.class.getName())) {
+ if (intent.getComponent() != null
+ && !intent.getComponent().getClassName().equals(AudioplayerActivity.class.getName())) {
saveCurrentFragment();
finish();
startActivity(intent);
@@ -57,7 +54,7 @@ public class AudioplayerActivity extends MediaplayerInfoActivity {
@Override
protected void updatePlaybackSpeedButton() {
- if(butPlaybackSpeed == null) {
+ if (butPlaybackSpeed == null) {
return;
}
if (controller == null) {
@@ -66,14 +63,14 @@ public class AudioplayerActivity extends MediaplayerInfoActivity {
return;
}
updatePlaybackSpeedButtonText();
- ViewCompat.setAlpha(butPlaybackSpeed, controller.canSetPlaybackSpeed() ? 1.0f : 0.5f);
+ butPlaybackSpeed.setAlpha(controller.canSetPlaybackSpeed() ? 1.0f : 0.5f);
butPlaybackSpeed.setVisibility(View.VISIBLE);
txtvPlaybackSpeed.setVisibility(View.VISIBLE);
}
@Override
protected void updatePlaybackSpeedButtonText() {
- if(butPlaybackSpeed == null) {
+ if (butPlaybackSpeed == null) {
return;
}
if (controller == null) {
@@ -82,7 +79,7 @@ public class AudioplayerActivity extends MediaplayerInfoActivity {
return;
}
float speed = 1.0f;
- if(controller.canSetPlaybackSpeed()) {
+ if (controller.canSetPlaybackSpeed()) {
speed = PlaybackSpeedUtils.getCurrentPlaybackSpeed(controller.getMedia());
}
String speedStr = new DecimalFormat("0.00").format(speed);
@@ -91,54 +88,40 @@ public class AudioplayerActivity extends MediaplayerInfoActivity {
@Override
protected void setupGUI() {
- if(isSetup.getAndSet(true)) {
+ if (isSetup.getAndSet(true)) {
return;
}
super.setupGUI();
- if(butCastDisconnect != null) {
+ if (butCastDisconnect != null) {
butCastDisconnect.setVisibility(View.GONE);
}
- if(butPlaybackSpeed != null) {
+ if (butPlaybackSpeed != null) {
butPlaybackSpeed.setOnClickListener(v -> {
if (controller == null) {
return;
}
if (controller.canSetPlaybackSpeed()) {
- String[] availableSpeeds = UserPreferences.getPlaybackSpeedArray();
- DecimalFormatSymbols format = new DecimalFormatSymbols(Locale.US);
- format.setDecimalSeparator('.');
+ float[] availableSpeeds = UserPreferences.getPlaybackSpeedArray();
+ float currentSpeed = controller.getCurrentPlaybackSpeedMultiplier();
- float currentSpeedValue = controller.getCurrentPlaybackSpeedMultiplier();
- String currentSpeed = new DecimalFormat("0.00", format).format(currentSpeedValue);
+ int newSpeedIndex = 0;
+ while (newSpeedIndex < availableSpeeds.length
+ && availableSpeeds[newSpeedIndex] < currentSpeed + EPSILON) {
+ newSpeedIndex++;
+ }
- // 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) {
+ float newSpeed;
+ if (availableSpeeds.length == 0) {
+ newSpeed = 1.0f;
+ } else if (newSpeedIndex == availableSpeeds.length) {
newSpeed = availableSpeeds[0];
} else {
- newSpeed = "1.00";
+ newSpeed = availableSpeeds[newSpeedIndex];
}
- 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;
- }
- }
-
- try {
- PlaybackPreferences.setCurrentlyPlayingTemporaryPlaybackSpeed(Float.parseFloat(newSpeed));
- } catch (NumberFormatException e) {
- // Well this was awkward...
- }
+ PlaybackPreferences.setCurrentlyPlayingTemporaryPlaybackSpeed(newSpeed);
UserPreferences.setPlaybackSpeed(newSpeed);
- controller.setPlaybackSpeed(Float.parseFloat(newSpeed));
+ controller.setPlaybackSpeed(newSpeed);
onPositionObserverUpdate();
} else {
VariableSpeedDialog.showGetPluginDialog(this);
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java
index 666eacfa8..01413bb79 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java
@@ -30,16 +30,17 @@ public class BugReportActivity extends AppCompatActivity {
getSupportActionBar().setDisplayShowHomeEnabled(true);
setContentView(R.layout.bug_report);
- TextView crashDetailsText = findViewById(R.id.crash_report_logs);
+ String crashDetailsText = CrashReportWriter.getSystemInfo() + "\n\n";
+ TextView crashDetailsTextView = findViewById(R.id.crash_report_logs);
try {
File crashFile = CrashReportWriter.getFile();
- String crashReportContent = IOUtils.toString(new FileInputStream(crashFile), Charset.forName("UTF-8"));
- crashDetailsText.setText(crashReportContent);
+ crashDetailsText += IOUtils.toString(new FileInputStream(crashFile), Charset.forName("UTF-8"));
} catch (IOException e) {
e.printStackTrace();
- crashDetailsText.setText("No crash report recorded\n" + CrashReportWriter.getSystemInfo());
+ crashDetailsText += "No crash report recorded";
}
+ crashDetailsTextView.setText(crashDetailsText);
findViewById(R.id.btn_open_bug_tracker).setOnClickListener(v -> {
IntentUtils.openInBrowser(BugReportActivity.this, "https://github.com/AntennaPod/AntennaPod/issues");
@@ -47,7 +48,7 @@ public class BugReportActivity extends AppCompatActivity {
findViewById(R.id.btn_copy_log).setOnClickListener(v -> {
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
- ClipData clip = ClipData.newPlainText(getString(R.string.bug_report_title), crashDetailsText.getText());
+ ClipData clip = ClipData.newPlainText(getString(R.string.bug_report_title), crashDetailsTextView.getText());
clipboard.setPrimaryClip(clip);
Snackbar.make(findViewById(android.R.id.content), R.string.copied_to_clipboard, Snackbar.LENGTH_SHORT).show();
});
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
index 3b81dbd0b..75819425c 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
@@ -274,7 +274,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
@Override
boolean loadMediaInfo() {
- if (controller != null) {
+ if (controller != null && controller.getMedia() != null) {
List<Chapter> chapters = controller.getMedia().getChapters();
boolean hasChapters = chapters != null && !chapters.isEmpty();
pageIndicator.setDisabledPage(hasChapters ? -1 : 2);
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
index adb127acb..3621f0b44 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
@@ -7,11 +7,6 @@ import android.content.Intent;
import android.graphics.LightingColorFilter;
import android.os.Build;
import android.os.Bundle;
-import androidx.annotation.NonNull;
-import androidx.annotation.UiThread;
-import androidx.core.app.NavUtils;
-import androidx.appcompat.app.AlertDialog;
-import androidx.appcompat.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.MenuItem;
@@ -24,26 +19,18 @@ import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.TextView;
-
+import androidx.annotation.NonNull;
+import androidx.annotation.UiThread;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.NavUtils;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
-
-import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
-import org.apache.commons.lang3.StringUtils;
-import org.greenrobot.eventbus.EventBus;
-import org.greenrobot.eventbus.Subscribe;
-import org.greenrobot.eventbus.ThreadMode;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.FeedItemlistDescriptionAdapter;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.event.DownloadEvent;
+import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedPreferences;
@@ -73,6 +60,16 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.apache.commons.lang3.StringUtils;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
/**
* Downloads a feed from a feed URL and parses it. Subclasses can display the
@@ -368,8 +365,6 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
findViewById(R.id.feedDisplay).setVisibility(View.VISIBLE);
this.feed = feed;
this.selectedDownloadUrl = feed.getDownload_url();
- listView.setSelector(android.R.color.transparent);
- listView.setAdapter(new FeedItemlistDescriptionAdapter(this, 0, feed.getItems()));
ImageView cover = findViewById(R.id.imgvCover);
ImageView headerBackground = findViewById(R.id.imgvBackground);
@@ -382,6 +377,9 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
View header = View.inflate(this, R.layout.onlinefeedview_header, null);
listView.addHeaderView(header);
+ listView.setSelector(android.R.color.transparent);
+ listView.setAdapter(new FeedItemlistDescriptionAdapter(this, 0, feed.getItems()));
+
TextView description = header.findViewById(R.id.txtvDescription);
subscribeButton = findViewById(R.id.butSubscribe);
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java
index ad23478d6..d1615b410 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java
@@ -9,15 +9,12 @@ import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
-
import com.bumptech.glide.Glide;
-
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedComponent;
import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.SearchResult;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
/**
@@ -40,7 +37,7 @@ public class SearchlistAdapter extends BaseAdapter {
}
@Override
- public SearchResult getItem(int position) {
+ public FeedComponent getItem(int position) {
return itemAccess.getItem(position);
}
@@ -52,8 +49,7 @@ public class SearchlistAdapter extends BaseAdapter {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final Holder holder;
- SearchResult result = getItem(position);
- FeedComponent component = result.getComponent();
+ FeedComponent component = getItem(position);
// Inflate Layout
if (convertView == null) {
@@ -93,7 +89,7 @@ public class SearchlistAdapter extends BaseAdapter {
} else if (component.getClass() == FeedItem.class) {
final FeedItem item = (FeedItem) component;
holder.title.setText(item.getTitle());
- holder.subtitle.setText(result.getLocation().getDescription());
+ holder.subtitle.setVisibility(View.VISIBLE);
convertView.setAlpha(item.isPlayed() ? 0.5f : 1.0f);
@@ -121,7 +117,7 @@ public class SearchlistAdapter extends BaseAdapter {
public interface ItemAccess {
int getCount();
- SearchResult getItem(int position);
+ FeedComponent getItem(int position);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java
index 970a1cfae..47eab7d05 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java
@@ -34,7 +34,7 @@ public abstract class ItemActionButton {
}
@NonNull
- public static ItemActionButton forItem(@NonNull FeedItem item, boolean isInQueue) {
+ public static ItemActionButton forItem(@NonNull FeedItem item, boolean isInQueue, boolean allowStream) {
final FeedMedia media = item.getMedia();
if (media == null) {
return new MarkAsPlayedActionButton(item);
@@ -45,9 +45,10 @@ public abstract class ItemActionButton {
return new PlayActionButton(item);
} else if (isDownloadingMedia) {
return new CancelDownloadActionButton(item);
- } else if (UserPreferences.streamOverDownload()) {
+ } else if (UserPreferences.streamOverDownload() && allowStream) {
return new StreamActionButton(item);
- } else if (MobileDownloadHelper.userAllowedMobileDownloads() || !MobileDownloadHelper.userChoseAddToQueue() || isInQueue) {
+ } else if (MobileDownloadHelper.userAllowedMobileDownloads()
+ || !MobileDownloadHelper.userChoseAddToQueue() || isInQueue) {
return new DownloadActionButton(item, isInQueue);
} else {
return new AddToQueueActionButton(item);
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java
index 9db257503..4e3068e7b 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java
@@ -10,7 +10,6 @@ import android.widget.CheckBox;
import android.widget.SeekBar;
import android.widget.TextView;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
-import java.util.Locale;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.preferences.UserPreferences;
@@ -102,13 +101,12 @@ public class PlaybackControlsDialog extends DialogFragment {
if (controller != null && controller.canSetPlaybackSpeed()) {
float playbackSpeed = (progress + 10) / 20.0f;
controller.setPlaybackSpeed(playbackSpeed);
- String speedPref = String.format(Locale.US, "%.2f", playbackSpeed);
PlaybackPreferences.setCurrentlyPlayingTemporaryPlaybackSpeed(playbackSpeed);
if (isPlayingVideo) {
- UserPreferences.setVideoPlaybackSpeed(speedPref);
+ UserPreferences.setVideoPlaybackSpeed(playbackSpeed);
} else {
- UserPreferences.setPlaybackSpeed(speedPref);
+ UserPreferences.setPlaybackSpeed(playbackSpeed);
}
String speedStr = String.format("%.2fx", playbackSpeed);
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
index ef624ebe6..3a6ba183f 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
@@ -6,8 +6,12 @@ import androidx.appcompat.app.AlertDialog;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Locale;
public class VariableSpeedDialog {
@@ -43,15 +47,22 @@ public class VariableSpeedDialog {
}
private static void showSpeedSelectorDialog(final Context context) {
+ DecimalFormatSymbols format = new DecimalFormatSymbols(Locale.US);
+ format.setDecimalSeparator('.');
+ DecimalFormat speedFormat = new DecimalFormat("0.00", format);
+
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());
+ // Build the "isChecked" array so that multiChoice dialog is populated correctly
+ List<String> selectedSpeedList = new ArrayList<>();
+ float[] selectedSpeeds = UserPreferences.getPlaybackSpeedArray();
+ for (float speed : selectedSpeeds) {
+ selectedSpeedList.add(speedFormat.format(speed));
+ }
+
for (int i = 0; i < speedValues.length; i++) {
speedChecked[i] = selectedSpeedList.contains(speedValues[i]);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java b/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java
index 79ccd9532..ee318c706 100644
--- a/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java
+++ b/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java
@@ -1,6 +1,8 @@
package de.danoeh.antennapod.discovery;
import android.content.Context;
+import android.util.Log;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
@@ -21,6 +23,7 @@ import java.util.List;
import java.util.Locale;
public class ItunesTopListLoader {
+ private static final String TAG = "ITunesTopListLoader";
private final Context context;
public ItunesTopListLoader(Context context) {
@@ -29,11 +32,11 @@ public class ItunesTopListLoader {
public Single<List<PodcastSearchResult>> loadToplist(int limit) {
return Single.create((SingleOnSubscribe<List<PodcastSearchResult>>) emitter -> {
- String lang = Locale.getDefault().getLanguage();
+ String country = Locale.getDefault().getCountry();
OkHttpClient client = AntennapodHttpClient.getHttpClient();
String feedString;
try {
- feedString = getTopListFeed(client, lang, limit);
+ feedString = getTopListFeed(client, country, limit);
} catch (IOException e) {
feedString = getTopListFeed(client, "us", limit);
}
@@ -74,11 +77,12 @@ public class ItunesTopListLoader {
.observeOn(AndroidSchedulers.mainThread());
}
- private String getTopListFeed(OkHttpClient client, String language, int limit) throws IOException {
- String url = "https://itunes.apple.com/%s/rss/toppodcasts/limit="+limit+"/explicit=true/json";
+ private String getTopListFeed(OkHttpClient client, String country, int limit) throws IOException {
+ String url = "https://itunes.apple.com/%s/rss/toppodcasts/limit=" + limit + "/explicit=true/json";
+ Log.d(TAG, "Feed URL " + String.format(url, country));
Request.Builder httpReq = new Request.Builder()
.header("User-Agent", ClientConfig.USER_AGENT)
- .url(String.format(url, language));
+ .url(String.format(url, country));
try (Response response = client.newCall(httpReq.build()).execute()) {
if (response.isSuccessful()) {
@@ -95,7 +99,7 @@ public class ItunesTopListLoader {
JSONArray entries = feed.getJSONArray("entry");
List<PodcastSearchResult> results = new ArrayList<>();
- for (int i=0; i < entries.length(); i++) {
+ for (int i = 0; i < entries.length(); i++) {
JSONObject json = entries.getJSONObject(i);
results.add(PodcastSearchResult.fromItunesToplist(json));
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
index e7d22652e..9a353e05a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
@@ -169,7 +169,7 @@ public abstract class EpisodesListFragment extends Fragment {
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- sv.setQueryHint(getString(R.string.search_hint));
+ sv.setQueryHint(getString(R.string.search_label));
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String s) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
index 5d3c811f5..16787a1c5 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
@@ -179,7 +179,7 @@ public class FeedItemlistFragment extends ListFragment {
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- sv.setQueryHint(getString(R.string.search_hint));
+ sv.setQueryHint(getString(R.string.search_label));
searchItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java
index 63160bb2c..8ff71e114 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java
@@ -117,8 +117,7 @@ public class FeedSettingsFragment extends PreferenceFragmentCompat {
private void setupPlaybackSpeedPreference() {
ListPreference feedPlaybackSpeedPreference = findPreference(PREF_FEED_PLAYBACK_SPEED);
- String[] speeds = UserPreferences.getPlaybackSpeedArray();
-
+ final String[] speeds = getResources().getStringArray(R.array.playback_speed_values);
String[] values = new String[speeds.length + 1];
values[0] = SPEED_FORMAT.format(SPEED_USE_GLOBAL);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
index e1202704a..5caaa592f 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -94,8 +94,12 @@ public class ItemFragment extends Fragment {
private ImageView imgvCover;
private ProgressBar progbarDownload;
private ProgressBar progbarLoading;
- private Button butAction1;
- private Button butAction2;
+ private TextView butAction1Text;
+ private TextView butAction2Text;
+ private ImageView butAction1Icon;
+ private ImageView butAction2Icon;
+ private View butAction1;
+ private View butAction2;
private Disposable disposable;
private PlaybackController controller;
@@ -141,12 +145,16 @@ public class ItemFragment extends Fragment {
progbarLoading = layout.findViewById(R.id.progbarLoading);
butAction1 = layout.findViewById(R.id.butAction1);
butAction2 = layout.findViewById(R.id.butAction2);
+ butAction1Icon = layout.findViewById(R.id.butAction1Icon);
+ butAction2Icon = layout.findViewById(R.id.butAction2Icon);
+ butAction1Text = layout.findViewById(R.id.butAction1Text);
+ butAction2Text = layout.findViewById(R.id.butAction2Text);
butAction1.setOnClickListener(v -> {
if (item == null) {
return;
}
- ItemActionButton actionButton = ItemActionButton.forItem(item, item.isTagged(FeedItem.TAG_QUEUE));
+ ItemActionButton actionButton = ItemActionButton.forItem(item, item.isTagged(FeedItem.TAG_QUEUE), false);
actionButton.onClick(getActivity());
FeedMedia media = item.getMedia();
@@ -267,22 +275,22 @@ public class ItemFragment extends Fragment {
}
FeedMedia media = item.getMedia();
- @AttrRes int butAction1Icon = 0;
- @StringRes int butAction1Text = 0;
- @AttrRes int butAction2Icon = 0;
- @StringRes int butAction2Text = 0;
+ @AttrRes int butAction1IconRes = 0;
+ @StringRes int butAction1TextRes = 0;
+ @AttrRes int butAction2IconRes = 0;
+ @StringRes int butAction2TextRes = 0;
if (media == null) {
if (!item.isPlayed()) {
- butAction1Icon = R.attr.navigation_accept;
+ butAction1IconRes = R.attr.navigation_accept;
if (item.hasMedia()) {
- butAction1Text = R.string.mark_read_label;
+ butAction1TextRes = R.string.mark_read_label;
} else {
- butAction1Text = R.string.mark_read_no_media_label;
+ butAction1TextRes = R.string.mark_read_no_media_label;
}
}
if (item.getLink() != null) {
- butAction2Icon = R.attr.location_web_site;
- butAction2Text = R.string.visit_website_label;
+ butAction2IconRes = R.attr.location_web_site;
+ butAction2TextRes = R.string.visit_website_label;
}
} else {
if (media.getDuration() > 0) {
@@ -290,40 +298,40 @@ public class ItemFragment extends Fragment {
}
boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media);
if (!media.isDownloaded()) {
- butAction2Icon = R.attr.action_stream;
- butAction2Text = R.string.stream_label;
+ butAction2IconRes = R.attr.action_stream;
+ butAction2TextRes = R.string.stream_label;
} else {
- butAction2Icon = R.attr.content_discard;
- butAction2Text = R.string.delete_label;
+ butAction2IconRes = R.attr.content_discard;
+ butAction2TextRes = R.string.delete_label;
}
if (isDownloading) {
- butAction1Icon = R.attr.navigation_cancel;
- butAction1Text = R.string.cancel_label;
+ butAction1IconRes = R.attr.navigation_cancel;
+ butAction1TextRes = R.string.cancel_label;
} else if (media.isDownloaded()) {
- butAction1Icon = R.attr.av_play;
- butAction1Text = R.string.play_label;
+ butAction1IconRes = R.attr.av_play;
+ butAction1TextRes = R.string.play_label;
} else {
- butAction1Icon = R.attr.av_download;
- butAction1Text = R.string.download_label;
+ butAction1IconRes = R.attr.av_download;
+ butAction1TextRes = R.string.download_label;
}
}
- if (butAction1Icon != 0 && butAction1Text != 0) {
- butAction1.setText(butAction1Text);
- butAction1.setTransformationMethod(null);
+ if (butAction1IconRes != 0 && butAction1TextRes != 0) {
+ butAction1Text.setText(butAction1TextRes);
+ butAction1Text.setTransformationMethod(null);
TypedValue typedValue = new TypedValue();
- getContext().getTheme().resolveAttribute(butAction1Icon, typedValue, true);
- butAction1.setCompoundDrawablesWithIntrinsicBounds(typedValue.resourceId, 0, 0, 0);
+ getContext().getTheme().resolveAttribute(butAction1IconRes, typedValue, true);
+ butAction1Icon.setImageResource(typedValue.resourceId);
butAction1.setVisibility(View.VISIBLE);
} else {
butAction1.setVisibility(View.INVISIBLE);
}
- if (butAction2Icon != 0 && butAction2Text != 0) {
- butAction2.setText(butAction2Text);
- butAction2.setTransformationMethod(null);
+ if (butAction2IconRes != 0 && butAction2TextRes != 0) {
+ butAction2Text.setText(butAction2TextRes);
+ butAction2Text.setTransformationMethod(null);
TypedValue typedValue = new TypedValue();
- getContext().getTheme().resolveAttribute(butAction2Icon, typedValue, true);
- butAction2.setCompoundDrawablesWithIntrinsicBounds(typedValue.resourceId, 0, 0, 0);
+ getContext().getTheme().resolveAttribute(butAction2IconRes, typedValue, true);
+ butAction2Icon.setImageResource(typedValue.resourceId);
butAction2.setVisibility(View.VISIBLE);
} else {
butAction2.setVisibility(View.INVISIBLE);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java
index b9e961535..51d1c7ba9 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java
@@ -16,6 +16,7 @@ import androidx.viewpager.widget.ViewPager;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.CastEnabledActivity;
import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.core.event.FeedItemEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.Flavors;
@@ -24,6 +25,9 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
/**
* Displays information about a list of FeedItems.
@@ -104,12 +108,14 @@ public class ItemPagerFragment extends Fragment {
}
});
+ EventBus.getDefault().register(this);
return layout;
}
@Override
public void onDestroyView() {
super.onDestroyView();
+ EventBus.getDefault().unregister(this);
if (disposable != null) {
disposable.dispose();
}
@@ -162,6 +168,17 @@ public class ItemPagerFragment extends Fragment {
}
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(FeedItemEvent event) {
+ for (FeedItem item : event.items) {
+ if (this.item != null && this.item.getId() == item.getId()) {
+ this.item = item;
+ getActivity().invalidateOptionsMenu();
+ return;
+ }
+ }
+ }
+
private void openPodcast() {
Fragment fragment = FeedItemlistFragment.newInstance(item.getFeedId());
((MainActivity) getActivity()).loadChildFragment(fragment);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
index ffe874a8c..48df5ab32 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -288,7 +288,7 @@ public class QueueFragment extends Fragment {
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- sv.setQueryHint(getString(R.string.search_hint));
+ sv.setQueryHint(getString(R.string.search_label));
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String s) {
@@ -471,15 +471,19 @@ public class QueueFragment extends Fragment {
return super.onContextItemSelected(item);
}
+ int position = FeedItemUtil.indexOfItemWithId(queue, selectedItem.getId());
+ if (position < 0) {
+ Log.i(TAG, "Selected item no longer exist, ignoring selection");
+ return super.onContextItemSelected(item);
+ }
+
switch(item.getItemId()) {
case R.id.move_to_top_item:
- int position = FeedItemUtil.indexOfItemWithId(queue, selectedItem.getId());
queue.add(0, queue.remove(position));
recyclerAdapter.notifyItemMoved(position, 0);
DBWriter.moveQueueItemToTop(selectedItem.getId(), true);
return true;
case R.id.move_to_bottom_item:
- position = FeedItemUtil.indexOfItemWithId(queue, selectedItem.getId());
queue.add(queue.size()-1, queue.remove(position));
recyclerAdapter.notifyItemMoved(position, queue.size()-1);
DBWriter.moveQueueItemToBottom(selectedItem.getId(), true);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
index 6befa7e18..c640554ff 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
@@ -25,7 +25,6 @@ import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedComponent;
import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.SearchResult;
import de.danoeh.antennapod.core.storage.FeedSearcher;
import de.danoeh.antennapod.view.EmptyViewHandler;
import io.reactivex.Observable;
@@ -47,9 +46,8 @@ public class SearchFragment extends Fragment implements AdapterView.OnItemClickL
private static final String ARG_FEED = "feed";
private SearchlistAdapter searchAdapter;
- private List<SearchResult> searchResults = new ArrayList<>();
+ private List<FeedComponent> searchResults = new ArrayList<>();
private Disposable disposable;
- private ListView listView;
private ProgressBar progressBar;
private EmptyViewHandler emptyViewHandler;
@@ -57,7 +55,9 @@ public class SearchFragment extends Fragment implements AdapterView.OnItemClickL
* Create a new SearchFragment that searches all feeds.
*/
public static SearchFragment newInstance(String query) {
- if (query == null) query = "";
+ if (query == null) {
+ query = "";
+ }
SearchFragment fragment = new SearchFragment();
Bundle args = new Bundle();
args.putString(ARG_QUERY, query);
@@ -103,7 +103,7 @@ public class SearchFragment extends Fragment implements AdapterView.OnItemClickL
((AppCompatActivity) getActivity()).getSupportActionBar().setTitle(R.string.search_label);
View layout = inflater.inflate(R.layout.search_fragment, container, false);
- listView = layout.findViewById(R.id.listview);
+ ListView listView = layout.findViewById(R.id.listview);
progressBar = layout.findViewById(R.id.progressBar);
searchAdapter = new SearchlistAdapter(getActivity(), itemAccess);
listView.setAdapter(searchAdapter);
@@ -125,15 +125,12 @@ public class SearchFragment extends Fragment implements AdapterView.OnItemClickL
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- SearchResult result = (SearchResult) listView.getAdapter().getItem(position);
- FeedComponent comp = result.getComponent();
+ FeedComponent comp = searchAdapter.getItem(position);
if (comp.getClass() == Feed.class) {
((MainActivity) getActivity()).loadFeedFragmentById(comp.getId(), null);
- } else {
- if (comp.getClass() == FeedItem.class) {
- FeedItem item = (FeedItem) comp;
- ((MainActivity) getActivity()).loadChildFragment(ItemPagerFragment.newInstance(item.getId()));
- }
+ } else if (comp.getClass() == FeedItem.class) {
+ FeedItem item = (FeedItem) comp;
+ ((MainActivity) getActivity()).loadChildFragment(ItemPagerFragment.newInstance(item.getId()));
}
}
@@ -143,7 +140,7 @@ public class SearchFragment extends Fragment implements AdapterView.OnItemClickL
MenuItem item = menu.add(Menu.NONE, R.id.search_item, Menu.NONE, R.string.search_label);
MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
final SearchView sv = new SearchView(getActivity());
- sv.setQueryHint(getString(R.string.search_hint));
+ sv.setQueryHint(getString(R.string.search_label));
sv.setQuery(getArguments().getString(ARG_QUERY), false);
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
@@ -167,7 +164,7 @@ public class SearchFragment extends Fragment implements AdapterView.OnItemClickL
search();
}
- private void onSearchResults(List<SearchResult> results) {
+ private void onSearchResults(List<FeedComponent> results) {
progressBar.setVisibility(View.GONE);
searchResults = results;
searchAdapter.notifyDataSetChanged();
@@ -182,7 +179,7 @@ public class SearchFragment extends Fragment implements AdapterView.OnItemClickL
}
@Override
- public SearchResult getItem(int position) {
+ public FeedComponent getItem(int position) {
if (0 <= position && position < searchResults.size()) {
return searchResults.get(position);
} else {
@@ -204,7 +201,7 @@ public class SearchFragment extends Fragment implements AdapterView.OnItemClickL
}
@NonNull
- private List<SearchResult> performSearch() {
+ private List<FeedComponent> performSearch() {
Bundle args = getArguments();
String query = args.getString(ARG_QUERY);
long feed = args.getLong(ARG_FEED);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
index af3649ed0..d0f6772ea 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
@@ -304,12 +304,12 @@ public class SubscriptionFragment extends Fragment {
dialog.createNewDialog().show();
}
- @Subscribe
+ @Subscribe(threadMode = ThreadMode.MAIN)
public void onFeedListChanged(FeedListUpdateEvent event) {
loadSubscriptions();
}
- @Subscribe
+ @Subscribe(threadMode = ThreadMode.MAIN)
public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
loadSubscriptions();
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java
index 9a09d55b0..8036a7506 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java
@@ -216,8 +216,8 @@ public class ImportExportPreferencesFragment extends PreferenceFragmentCompat {
private void showExportErrorDialog(final Throwable error) {
progressDialog.dismiss();
- final AlertDialog.Builder alert = new AlertDialog.Builder(getContext())
- .setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
+ final AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
+ alert.setPositiveButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
alert.setTitle(R.string.export_error_label);
alert.setMessage(error.getMessage());
alert.show();
diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
index 4686f0303..e791efe98 100644
--- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
+++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
@@ -84,5 +84,8 @@ public class PreferenceUpgrader {
UserPreferences.setEnqueueLocation(enqueueLocation);
}
}
+ if (oldVersion < 1080100) {
+ prefs.edit().putString(UserPreferences.PREF_VIDEO_BEHAVIOR, "pip").apply();
+ }
}
}