summaryrefslogtreecommitdiff
path: root/ui/echo
diff options
context:
space:
mode:
authorByteHamster <ByteHamster@users.noreply.github.com>2024-03-29 08:55:13 +0100
committerGitHub <noreply@github.com>2024-03-29 08:55:13 +0100
commit13a985ca1e6fae65682c97ee523dec96b2fdeedf (patch)
tree2c17b528c52e63298d499a44b82c3c0aff08ae25 /ui/echo
parent1dbda2fb8a070fb30b4314feca839525cfc701eb (diff)
downloadAntennaPod-13a985ca1e6fae65682c97ee523dec96b2fdeedf.zip
Restructure Echo to be more flexible (#7035)
Each screen is its own file, which makes it easier to add interactive elements.
Diffstat (limited to 'ui/echo')
-rw-r--r--ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/EchoActivity.java341
-rw-r--r--ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/EchoConfig.java19
-rw-r--r--ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/BaseBackground.java (renamed from ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/BaseScreen.java)6
-rw-r--r--ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/BubbleBackground.java (renamed from ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/BubbleScreen.java)6
-rw-r--r--ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/FinalShareBackground.java (renamed from ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/FinalShareScreen.java)12
-rw-r--r--ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/RotatingSquaresBackground.java (renamed from ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/RotatingSquaresScreen.java)6
-rw-r--r--ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/StripesBackground.java (renamed from ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/StripesScreen.java)6
-rw-r--r--ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/WaveformBackground.java (renamed from ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/WaveformScreen.java)6
-rw-r--r--ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/WavesBackground.java (renamed from ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/WavesScreen.java)6
-rw-r--r--ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/EchoScreen.java48
-rw-r--r--ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/FinalShareScreen.java127
-rw-r--r--ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/HoarderScreen.java75
-rw-r--r--ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/HoursPlayedScreen.java50
-rw-r--r--ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/IntroScreen.java34
-rw-r--r--ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/QueueScreen.java97
-rw-r--r--ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/ThanksScreen.java50
-rw-r--r--ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/TimeReleasePlayScreen.java65
-rw-r--r--ui/echo/src/main/res/layout/echo_activity.xml94
-rw-r--r--ui/echo/src/main/res/layout/simple_echo_screen.xml101
-rw-r--r--ui/echo/src/main/res/values/echo-strings.xml1
20 files changed, 727 insertions, 423 deletions
diff --git a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/EchoActivity.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/EchoActivity.java
index bfe5fbf98..33bf85aaa 100644
--- a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/EchoActivity.java
+++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/EchoActivity.java
@@ -1,97 +1,60 @@
package de.danoeh.antennapod.ui.echo;
import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
import android.os.Bundle;
-import android.text.format.DateFormat;
import android.util.Log;
import android.view.KeyEvent;
-import android.view.View;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
-import androidx.core.app.ShareCompat;
-import androidx.core.content.FileProvider;
import androidx.core.view.WindowCompat;
-import com.bumptech.glide.Glide;
-import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
-import com.bumptech.glide.request.RequestOptions;
-import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.storage.database.DBReader;
-import de.danoeh.antennapod.storage.database.StatisticsItem;
-import de.danoeh.antennapod.storage.preferences.UserPreferences;
-import de.danoeh.antennapod.ui.common.Converter;
import de.danoeh.antennapod.ui.echo.databinding.EchoActivityBinding;
-import de.danoeh.antennapod.ui.echo.screens.BubbleScreen;
-import de.danoeh.antennapod.ui.echo.screens.FinalShareScreen;
-import de.danoeh.antennapod.ui.echo.screens.RotatingSquaresScreen;
-import de.danoeh.antennapod.ui.echo.screens.StripesScreen;
-import de.danoeh.antennapod.ui.echo.screens.WaveformScreen;
-import de.danoeh.antennapod.ui.echo.screens.WavesScreen;
-import de.danoeh.antennapod.ui.episodes.PlaybackSpeedUtils;
+import de.danoeh.antennapod.ui.echo.screen.EchoScreen;
+import de.danoeh.antennapod.ui.echo.screen.FinalShareScreen;
+import de.danoeh.antennapod.ui.echo.screen.HoarderScreen;
+import de.danoeh.antennapod.ui.echo.screen.HoursPlayedScreen;
+import de.danoeh.antennapod.ui.echo.screen.IntroScreen;
+import de.danoeh.antennapod.ui.echo.screen.QueueScreen;
+import de.danoeh.antennapod.ui.echo.screen.ThanksScreen;
+import de.danoeh.antennapod.ui.echo.screen.TimeReleasePlayScreen;
import io.reactivex.Flowable;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Calendar;
import java.util.Collections;
-import java.util.Date;
import java.util.List;
-import java.util.Locale;
import java.util.concurrent.TimeUnit;
public class EchoActivity extends AppCompatActivity {
- public static final int RELEASE_YEAR = 2023;
private static final String TAG = "EchoActivity";
private static final int NUM_SCREENS = 7;
- private static final int SHARE_SIZE = 1000;
private EchoActivityBinding viewBinding;
- private int currentScreen = -1;
+ private int currentScreenIdx = -1;
private boolean progressPaused = false;
private float progress = 0;
- private Drawable currentDrawable;
private EchoProgress echoProgress;
private Disposable redrawTimer;
private long timeTouchDown;
private long timeLastFrame;
private Disposable disposable;
- private Disposable disposableFavorite;
-
- private long totalTime = 0;
- private int totalActivePodcasts = 0;
- private int playedPodcasts = 0;
- private int playedActivePodcasts = 0;
- private String randomUnplayedActivePodcast = "";
- private int queueNumEpisodes = 0;
- private long queueSecondsLeft = 0;
- private long timeBetweenReleaseAndPlay = 0;
- private long oldestDate = 0;
- private final ArrayList<String> favoritePodNames = new ArrayList<>();
- private final ArrayList<Drawable> favoritePodImages = new ArrayList<>();
+ private List<EchoScreen> screens;
+ private EchoScreen currentScreen;
@SuppressLint("ClickableViewAccessibility")
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
super.onCreate(savedInstanceState);
+ screens = List.of(new IntroScreen(this, getLayoutInflater()),
+ new HoursPlayedScreen(this, getLayoutInflater()), new QueueScreen(this, getLayoutInflater()),
+ new TimeReleasePlayScreen(this, getLayoutInflater()), new HoarderScreen(this, getLayoutInflater()),
+ new ThanksScreen(this, getLayoutInflater()), new FinalShareScreen(this, getLayoutInflater()));
viewBinding = EchoActivityBinding.inflate(getLayoutInflater());
viewBinding.closeButton.setOnClickListener(v -> finish());
- viewBinding.shareButton.setOnClickListener(v -> share());
- viewBinding.echoImage.setOnTouchListener((v, event) -> {
+ viewBinding.screenContainer.setOnTouchListener((v, event) -> {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
progressPaused = true;
timeTouchDown = System.currentTimeMillis();
@@ -99,11 +62,11 @@ public class EchoActivity extends AppCompatActivity {
progressPaused = false;
if (timeTouchDown + 500 > System.currentTimeMillis()) {
int newScreen;
- if (event.getX() < 0.5f * viewBinding.echoImage.getMeasuredWidth()) {
- newScreen = Math.max(currentScreen - 1, 0);
+ if (event.getX() < 0.5f * viewBinding.screenContainer.getMeasuredWidth()) {
+ newScreen = Math.max(currentScreenIdx - 1, 0);
} else {
- newScreen = Math.min(currentScreen + 1, NUM_SCREENS - 1);
- if (currentScreen == NUM_SCREENS - 1) {
+ newScreen = Math.min(currentScreenIdx + 1, NUM_SCREENS - 1);
+ if (currentScreenIdx == NUM_SCREENS - 1) {
finish();
}
}
@@ -121,35 +84,9 @@ public class EchoActivity extends AppCompatActivity {
loadStatistics();
}
- private void share() {
- try {
- Bitmap bitmap = Bitmap.createBitmap(SHARE_SIZE, SHARE_SIZE, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- currentDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
- currentDrawable.draw(canvas);
- viewBinding.echoImage.setImageDrawable(null);
- viewBinding.echoImage.setImageDrawable(currentDrawable);
- File file = new File(UserPreferences.getDataFolder(null), "AntennaPodEcho.png");
- FileOutputStream stream = new FileOutputStream(file);
- bitmap.compress(Bitmap.CompressFormat.PNG, 90, stream);
- stream.close();
-
- Uri fileUri = FileProvider.getUriForFile(this, getString(R.string.provider_authority), file);
- new ShareCompat.IntentBuilder(this)
- .setType("image/png")
- .addStream(fileUri)
- .setText(getString(R.string.echo_share, RELEASE_YEAR))
- .setChooserTitle(R.string.share_file_label)
- .startChooser();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
@Override
protected void onStart() {
super.onStart();
-
redrawTimer = Flowable.timer(20, TimeUnit.MILLISECONDS)
.observeOn(Schedulers.io())
.repeat()
@@ -157,7 +94,7 @@ public class EchoActivity extends AppCompatActivity {
if (progressPaused) {
return;
}
- viewBinding.echoImage.postInvalidate();
+ currentScreen.postInvalidate();
if (progress >= NUM_SCREENS - 0.001f) {
return;
}
@@ -180,254 +117,38 @@ public class EchoActivity extends AppCompatActivity {
if (disposable != null) {
disposable.dispose();
}
- if (disposableFavorite != null) {
- disposableFavorite.dispose();
- }
}
private void loadScreen(int screen, boolean force) {
- if (screen == currentScreen && !force) {
+ if (screen == currentScreenIdx && !force) {
return;
}
- currentScreen = screen;
+ currentScreenIdx = screen;
+ currentScreen = screens.get(currentScreenIdx);
runOnUiThread(() -> {
- viewBinding.echoLogo.setVisibility(currentScreen == 0 ? View.VISIBLE : View.GONE);
- viewBinding.shareButton.setVisibility(currentScreen == 6 ? View.VISIBLE : View.GONE);
-
- switch (currentScreen) {
- case 0:
- viewBinding.aboveLabel.setText(R.string.echo_intro_your_year);
- viewBinding.largeLabel.setText(String.format(getEchoLanguage(), "%d", RELEASE_YEAR));
- viewBinding.belowLabel.setText(R.string.echo_intro_in_podcasts);
- viewBinding.smallLabel.setText(R.string.echo_intro_locally);
- currentDrawable = new BubbleScreen(this);
- break;
- case 1:
- viewBinding.aboveLabel.setText(R.string.echo_hours_this_year);
- viewBinding.largeLabel.setText(String.format(getEchoLanguage(), "%d", totalTime / 3600));
- viewBinding.belowLabel.setText(getResources()
- .getQuantityString(R.plurals.echo_hours_podcasts, playedPodcasts, playedPodcasts));
- viewBinding.smallLabel.setText("");
- currentDrawable = new WaveformScreen(this);
- break;
- case 2:
- viewBinding.largeLabel.setText(String.format(getEchoLanguage(), "%d", queueSecondsLeft / 3600));
- viewBinding.belowLabel.setText(getResources().getQuantityString(
- R.plurals.echo_queue_hours_waiting, queueNumEpisodes, queueNumEpisodes));
- Calendar dec31 = Calendar.getInstance();
- dec31.set(Calendar.DAY_OF_MONTH, 31);
- dec31.set(Calendar.MONTH, Calendar.DECEMBER);
- int daysUntilNextYear = Math.max(1,
- dec31.get(Calendar.DAY_OF_YEAR) - Calendar.getInstance().get(Calendar.DAY_OF_YEAR) + 1);
- long secondsPerDay = queueSecondsLeft / daysUntilNextYear;
- String timePerDay = Converter.getDurationStringLocalized(
- getLocalizedResources(this, getEchoLanguage()), secondsPerDay * 1000, true);
- double hoursPerDay = secondsPerDay / 3600.0;
- int nextYear = RELEASE_YEAR + 1;
- if (hoursPerDay < 1.5) {
- viewBinding.aboveLabel.setText(R.string.echo_queue_title_clean);
- viewBinding.smallLabel.setText(
- getString(R.string.echo_queue_hours_clean, timePerDay, nextYear));
- } else if (hoursPerDay <= 24) {
- viewBinding.aboveLabel.setText(R.string.echo_queue_title_many);
- viewBinding.smallLabel.setText(
- getString(R.string.echo_queue_hours_normal, timePerDay, nextYear));
- } else {
- viewBinding.aboveLabel.setText(R.string.echo_queue_title_many);
- viewBinding.smallLabel.setText(getString(R.string.echo_queue_hours_much, timePerDay, nextYear));
- }
- currentDrawable = new StripesScreen(this);
- break;
- case 3:
- viewBinding.aboveLabel.setText(R.string.echo_listened_after_title);
- if (timeBetweenReleaseAndPlay <= 1000L * 3600 * 24 * 2.5) {
- viewBinding.largeLabel.setText(R.string.echo_listened_after_emoji_run);
- viewBinding.belowLabel.setText(R.string.echo_listened_after_comment_addict);
- } else {
- viewBinding.largeLabel.setText(R.string.echo_listened_after_emoji_yoga);
- viewBinding.belowLabel.setText(R.string.echo_listened_after_comment_easy);
- }
- viewBinding.smallLabel.setText(getString(R.string.echo_listened_after_time,
- Converter.getDurationStringLocalized(
- getLocalizedResources(this, getEchoLanguage()), timeBetweenReleaseAndPlay, true)));
- currentDrawable = new RotatingSquaresScreen(this);
- break;
- case 4:
- viewBinding.aboveLabel.setText(R.string.echo_hoarder_title);
- int percentagePlayed = (int) (100.0 * playedActivePodcasts / totalActivePodcasts);
- if (percentagePlayed < 25) {
- viewBinding.largeLabel.setText(R.string.echo_hoarder_emoji_cabinet);
- viewBinding.belowLabel.setText(R.string.echo_hoarder_subtitle_hoarder);
- viewBinding.smallLabel.setText(getString(R.string.echo_hoarder_comment_hoarder,
- percentagePlayed, totalActivePodcasts));
- } else if (percentagePlayed < 75) {
- viewBinding.largeLabel.setText(R.string.echo_hoarder_emoji_check);
- viewBinding.belowLabel.setText(R.string.echo_hoarder_subtitle_medium);
- viewBinding.smallLabel.setText(getString(R.string.echo_hoarder_comment_medium,
- percentagePlayed, totalActivePodcasts, randomUnplayedActivePodcast));
- } else {
- viewBinding.largeLabel.setText(R.string.echo_hoarder_emoji_clean);
- viewBinding.belowLabel.setText(R.string.echo_hoarder_subtitle_clean);
- viewBinding.smallLabel.setText(getString(R.string.echo_hoarder_comment_clean,
- percentagePlayed, totalActivePodcasts));
- }
- currentDrawable = new WavesScreen(this);
- break;
- case 5:
- viewBinding.aboveLabel.setText("");
- viewBinding.largeLabel.setText(R.string.echo_thanks_large);
- if (oldestDate < jan1()) {
- String skeleton = DateFormat.getBestDateTimePattern(getEchoLanguage(), "MMMM yyyy");
- SimpleDateFormat dateFormat = new SimpleDateFormat(skeleton, getEchoLanguage());
- String dateFrom = dateFormat.format(new Date(oldestDate));
- viewBinding.belowLabel.setText(getString(R.string.echo_thanks_we_are_glad_old, dateFrom));
- } else {
- viewBinding.belowLabel.setText(R.string.echo_thanks_we_are_glad_new);
- }
- viewBinding.smallLabel.setText(R.string.echo_thanks_now_favorite);
- currentDrawable = new RotatingSquaresScreen(this);
- break;
- case 6:
- viewBinding.aboveLabel.setText("");
- viewBinding.largeLabel.setText("");
- viewBinding.belowLabel.setText("");
- viewBinding.smallLabel.setText("");
- currentDrawable = new FinalShareScreen(this, favoritePodNames, favoritePodImages);
- break;
- default: // Keep
- }
- viewBinding.echoImage.setImageDrawable(currentDrawable);
+ viewBinding.screenContainer.removeAllViews();
+ viewBinding.screenContainer.addView(currentScreen.getView());
});
}
- private Locale getEchoLanguage() {
- boolean hasTranslation = !getString(R.string.echo_listened_after_title)
- .equals(getLocalizedResources(this, Locale.US).getString(R.string.echo_listened_after_title));
- if (hasTranslation) {
- return Locale.getDefault();
- } else {
- return Locale.US;
- }
- }
-
- @NonNull
- private Resources getLocalizedResources(Context context, Locale desiredLocale) {
- Configuration conf = context.getResources().getConfiguration();
- conf = new Configuration(conf);
- conf.setLocale(desiredLocale);
- Context localizedContext = context.createConfigurationContext(conf);
- return localizedContext.getResources();
- }
-
- private long jan1() {
- Calendar date = Calendar.getInstance();
- date.set(Calendar.HOUR_OF_DAY, 0);
- date.set(Calendar.MINUTE, 0);
- date.set(Calendar.SECOND, 0);
- date.set(Calendar.MILLISECOND, 0);
- date.set(Calendar.DAY_OF_MONTH, 1);
- date.set(Calendar.MONTH, 0);
- date.set(Calendar.YEAR, RELEASE_YEAR);
- return date.getTimeInMillis();
- }
-
private void loadStatistics() {
if (disposable != null) {
disposable.dispose();
}
- long timeFilterFrom = jan1();
- long timeFilterTo = Long.MAX_VALUE;
disposable = Observable.fromCallable(
() -> {
DBReader.StatisticsResult statisticsData = DBReader.getStatistics(
- false, timeFilterFrom, timeFilterTo);
+ false, EchoConfig.jan1(), Long.MAX_VALUE);
Collections.sort(statisticsData.feedTime, (item1, item2) ->
Long.compare(item2.timePlayed, item1.timePlayed));
-
- favoritePodNames.clear();
- for (int i = 0; i < 5 && i < statisticsData.feedTime.size(); i++) {
- favoritePodNames.add(statisticsData.feedTime.get(i).feed.getTitle());
- }
- loadFavoritePodImages(statisticsData);
-
- totalActivePodcasts = 0;
- playedActivePodcasts = 0;
- playedPodcasts = 0;
- totalTime = 0;
- ArrayList<String> unplayedActive = new ArrayList<>();
- for (StatisticsItem item : statisticsData.feedTime) {
- totalTime += item.timePlayed;
- if (item.timePlayed > 0) {
- playedPodcasts++;
- }
- if (item.feed.getPreferences().getKeepUpdated()) {
- totalActivePodcasts++;
- if (item.timePlayed > 0) {
- playedActivePodcasts++;
- } else {
- unplayedActive.add(item.feed.getTitle());
- }
- }
- }
- if (!unplayedActive.isEmpty()) {
- randomUnplayedActivePodcast = unplayedActive.get((int) (Math.random() * unplayedActive.size()));
- }
-
- List<FeedItem> queue = DBReader.getQueue();
- queueNumEpisodes = queue.size();
- queueSecondsLeft = 0;
- for (FeedItem item : queue) {
- float playbackSpeed = 1;
- if (UserPreferences.timeRespectsSpeed()) {
- playbackSpeed = PlaybackSpeedUtils.getCurrentPlaybackSpeed(item.getMedia());
- }
- if (item.getMedia() != null) {
- long itemTimeLeft = item.getMedia().getDuration() - item.getMedia().getPosition();
- queueSecondsLeft += itemTimeLeft / playbackSpeed;
- }
- }
- queueSecondsLeft /= 1000;
-
- timeBetweenReleaseAndPlay = DBReader.getTimeBetweenReleaseAndPlayback(timeFilterFrom, timeFilterTo);
- oldestDate = statisticsData.oldestDate;
return statisticsData;
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
- .subscribe(result -> loadScreen(currentScreen, true),
- error -> Log.e(TAG, Log.getStackTraceString(error)));
- }
-
- void loadFavoritePodImages(DBReader.StatisticsResult statisticsData) {
- if (disposableFavorite != null) {
- disposableFavorite.dispose();
- }
- disposableFavorite = Observable.fromCallable(
- () -> {
- favoritePodImages.clear();
- for (int i = 0; i < 5 && i < statisticsData.feedTime.size(); i++) {
- BitmapDrawable cover = new BitmapDrawable(getResources(), (Bitmap) null);
- try {
- final int size = SHARE_SIZE / 3;
- final int radius = (i == 0) ? (size / 16) : (size / 8);
- cover = new BitmapDrawable(getResources(), Glide.with(this)
- .asBitmap()
- .load(statisticsData.feedTime.get(i).feed.getImageUrl())
- .apply(new RequestOptions()
- .fitCenter()
- .transform(new RoundedCorners(radius)))
- .submit(size, size)
- .get(5, TimeUnit.SECONDS));
- } catch (Exception e) {
- Log.d(TAG, "Loading cover: " + e.getMessage());
- }
- favoritePodImages.add(cover);
+ .subscribe(result -> {
+ for (EchoScreen screen : screens) {
+ screen.startLoading(result);
}
- return statisticsData;
- })
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(result -> { },
- error -> Log.e(TAG, Log.getStackTraceString(error)));
+ }, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
}
diff --git a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/EchoConfig.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/EchoConfig.java
new file mode 100644
index 000000000..2a93ab063
--- /dev/null
+++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/EchoConfig.java
@@ -0,0 +1,19 @@
+package de.danoeh.antennapod.ui.echo;
+
+import java.util.Calendar;
+
+public class EchoConfig {
+ public static final int RELEASE_YEAR = 2023;
+
+ public static long jan1() {
+ Calendar date = Calendar.getInstance();
+ date.set(Calendar.HOUR_OF_DAY, 0);
+ date.set(Calendar.MINUTE, 0);
+ date.set(Calendar.SECOND, 0);
+ date.set(Calendar.MILLISECOND, 0);
+ date.set(Calendar.DAY_OF_MONTH, 1);
+ date.set(Calendar.MONTH, 0);
+ date.set(Calendar.YEAR, RELEASE_YEAR);
+ return date.getTimeInMillis();
+ }
+}
diff --git a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/BaseScreen.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/BaseBackground.java
index e8bc085cd..343ce4547 100644
--- a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/BaseScreen.java
+++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/BaseBackground.java
@@ -1,4 +1,4 @@
-package de.danoeh.antennapod.ui.echo.screens;
+package de.danoeh.antennapod.ui.echo.background;
import android.content.Context;
import android.graphics.Canvas;
@@ -14,7 +14,7 @@ import androidx.core.content.ContextCompat;
import java.util.ArrayList;
import de.danoeh.antennapod.ui.echo.R;
-public abstract class BaseScreen extends Drawable {
+public abstract class BaseBackground extends Drawable {
private final Paint paintBackground;
protected final Paint paintParticles;
protected final ArrayList<Particle> particles = new ArrayList<>();
@@ -22,7 +22,7 @@ public abstract class BaseScreen extends Drawable {
private final int colorBackgroundTo;
private long lastFrame = 0;
- public BaseScreen(Context context) {
+ public BaseBackground(Context context) {
colorBackgroundFrom = ContextCompat.getColor(context, R.color.gradient_000);
colorBackgroundTo = ContextCompat.getColor(context, R.color.gradient_100);
paintBackground = new Paint();
diff --git a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/BubbleScreen.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/BubbleBackground.java
index bd79645dc..28d6cb4e2 100644
--- a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/BubbleScreen.java
+++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/BubbleBackground.java
@@ -1,14 +1,14 @@
-package de.danoeh.antennapod.ui.echo.screens;
+package de.danoeh.antennapod.ui.echo.background;
import android.content.Context;
import android.graphics.Canvas;
import androidx.annotation.NonNull;
-public class BubbleScreen extends BaseScreen {
+public class BubbleBackground extends BaseBackground {
protected static final double PARTICLE_SPEED = 0.00002;
protected static final int NUM_PARTICLES = 15;
- public BubbleScreen(Context context) {
+ public BubbleBackground(Context context) {
super(context);
for (int i = 0; i < NUM_PARTICLES; i++) {
particles.add(new Particle(Math.random(), 2.0 * Math.random() - 0.5, // Could already be off-screen
diff --git a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/FinalShareScreen.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/FinalShareBackground.java
index 4af8941d0..341bc5133 100644
--- a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/FinalShareScreen.java
+++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/FinalShareBackground.java
@@ -1,4 +1,4 @@
-package de.danoeh.antennapod.ui.echo.screens;
+package de.danoeh.antennapod.ui.echo.background;
import android.content.Context;
import android.graphics.Canvas;
@@ -9,11 +9,11 @@ import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.content.res.ResourcesCompat;
-import de.danoeh.antennapod.ui.echo.EchoActivity;
+import de.danoeh.antennapod.ui.echo.EchoConfig;
import de.danoeh.antennapod.ui.echo.R;
import java.util.ArrayList;
-public class FinalShareScreen extends BubbleScreen {
+public class FinalShareBackground extends BubbleBackground {
private static final float[][] COVER_POSITIONS = new float[][]{ new float[] {0.0f, 0.0f},
new float[] {0.4f, 0.0f}, new float[] {0.4f, 0.2f}, new float[] {0.6f, 0.2f}, new float[] {0.8f, 0.2f}};
private final Paint paintTextMain;
@@ -26,14 +26,14 @@ public class FinalShareScreen extends BubbleScreen {
private final Typeface typefaceNormal;
private final Typeface typefaceBold;
- public FinalShareScreen(Context context,
- ArrayList<String> favoritePodNames, ArrayList<Drawable> favoritePodImages) {
+ public FinalShareBackground(Context context,
+ ArrayList<String> favoritePodNames, ArrayList<Drawable> favoritePodImages) {
super(context);
this.heading = context.getString(R.string.echo_share_heading);
this.logo = AppCompatResources.getDrawable(context, R.drawable.echo);
this.favoritePodNames = favoritePodNames;
this.favoritePodImages = favoritePodImages;
- this.year = String.valueOf(EchoActivity.RELEASE_YEAR);
+ this.year = String.valueOf(EchoConfig.RELEASE_YEAR);
typefaceNormal = ResourcesCompat.getFont(context, R.font.sarabun_regular);
typefaceBold = ResourcesCompat.getFont(context, R.font.sarabun_semi_bold);
paintTextMain = new Paint();
diff --git a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/RotatingSquaresScreen.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/RotatingSquaresBackground.java
index 624b68f88..3901bc5e4 100644
--- a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/RotatingSquaresScreen.java
+++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/RotatingSquaresBackground.java
@@ -1,11 +1,11 @@
-package de.danoeh.antennapod.ui.echo.screens;
+package de.danoeh.antennapod.ui.echo.background;
import android.content.Context;
import android.graphics.Canvas;
import androidx.annotation.NonNull;
-public class RotatingSquaresScreen extends BaseScreen {
- public RotatingSquaresScreen(Context context) {
+public class RotatingSquaresBackground extends BaseBackground {
+ public RotatingSquaresBackground(Context context) {
super(context);
for (int i = 0; i < 16; i++) {
particles.add(new Particle(
diff --git a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/StripesScreen.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/StripesBackground.java
index 60906776f..5d523335b 100644
--- a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/StripesScreen.java
+++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/StripesBackground.java
@@ -1,13 +1,13 @@
-package de.danoeh.antennapod.ui.echo.screens;
+package de.danoeh.antennapod.ui.echo.background;
import android.content.Context;
import android.graphics.Canvas;
import androidx.annotation.NonNull;
-public class StripesScreen extends BaseScreen {
+public class StripesBackground extends BaseBackground {
protected static final int NUM_PARTICLES = 15;
- public StripesScreen(Context context) {
+ public StripesBackground(Context context) {
super(context);
for (int i = 0; i < NUM_PARTICLES; i++) {
particles.add(new Particle(2f * i / NUM_PARTICLES - 1f, 0, 0, 0));
diff --git a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/WaveformScreen.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/WaveformBackground.java
index d87f7fbb5..419cdf344 100644
--- a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/WaveformScreen.java
+++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/WaveformBackground.java
@@ -1,13 +1,13 @@
-package de.danoeh.antennapod.ui.echo.screens;
+package de.danoeh.antennapod.ui.echo.background;
import android.content.Context;
import android.graphics.Canvas;
import androidx.annotation.NonNull;
-public class WaveformScreen extends BaseScreen {
+public class WaveformBackground extends BaseBackground {
protected static final int NUM_PARTICLES = 40;
- public WaveformScreen(Context context) {
+ public WaveformBackground(Context context) {
super(context);
for (int i = 0; i < NUM_PARTICLES; i++) {
particles.add(new Particle(1.1f + 1.1f * i / NUM_PARTICLES - 0.05f, 0, 0, 0));
diff --git a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/WavesScreen.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/WavesBackground.java
index eb521f0a2..6ea46853e 100644
--- a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/WavesScreen.java
+++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/WavesBackground.java
@@ -1,14 +1,14 @@
-package de.danoeh.antennapod.ui.echo.screens;
+package de.danoeh.antennapod.ui.echo.background;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import androidx.annotation.NonNull;
-public class WavesScreen extends BaseScreen {
+public class WavesBackground extends BaseBackground {
protected static final int NUM_PARTICLES = 10;
- public WavesScreen(Context context) {
+ public WavesBackground(Context context) {
super(context);
paintParticles.setStyle(Paint.Style.STROKE);
for (int i = 0; i < NUM_PARTICLES; i++) {
diff --git a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/EchoScreen.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/EchoScreen.java
new file mode 100644
index 000000000..9654ca1fc
--- /dev/null
+++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/EchoScreen.java
@@ -0,0 +1,48 @@
+package de.danoeh.antennapod.ui.echo.screen;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.view.View;
+import androidx.annotation.NonNull;
+import de.danoeh.antennapod.storage.database.DBReader;
+import de.danoeh.antennapod.ui.echo.R;
+
+import java.util.Locale;
+
+public abstract class EchoScreen {
+ protected final Context context;
+
+ public EchoScreen(Context context) {
+ this.context = context;
+ }
+
+ protected final Locale getEchoLanguage() {
+ boolean hasTranslation = !context.getString(R.string.echo_listened_after_title)
+ .equals(getLocalizedResources(Locale.US).getString(R.string.echo_listened_after_title));
+ if (hasTranslation) {
+ return Locale.getDefault();
+ } else {
+ return Locale.US;
+ }
+ }
+
+ @NonNull
+ protected Resources getLocalizedResources(Locale desiredLocale) {
+ Configuration conf = context.getResources().getConfiguration();
+ conf = new Configuration(conf);
+ conf.setLocale(desiredLocale);
+ Context localizedContext = context.createConfigurationContext(conf);
+ return localizedContext.getResources();
+ }
+
+ public void postInvalidate() {
+ // Do nothing by default
+ }
+
+ public abstract View getView();
+
+ public void startLoading(DBReader.StatisticsResult statisticsResult) {
+ // Do nothing by default
+ }
+}
diff --git a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/FinalShareScreen.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/FinalShareScreen.java
new file mode 100644
index 000000000..770ca7766
--- /dev/null
+++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/FinalShareScreen.java
@@ -0,0 +1,127 @@
+package de.danoeh.antennapod.ui.echo.screen;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import androidx.core.app.ShareCompat;
+import androidx.core.content.FileProvider;
+import androidx.core.content.res.ResourcesCompat;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
+import com.bumptech.glide.request.RequestOptions;
+import de.danoeh.antennapod.storage.database.DBReader;
+import de.danoeh.antennapod.storage.preferences.UserPreferences;
+import de.danoeh.antennapod.ui.echo.EchoConfig;
+import de.danoeh.antennapod.ui.echo.R;
+import de.danoeh.antennapod.ui.echo.background.FinalShareBackground;
+import de.danoeh.antennapod.ui.echo.databinding.SimpleEchoScreenBinding;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
+
+public class FinalShareScreen extends EchoScreen {
+ private static final int SHARE_SIZE = 1000;
+ private static final String TAG = "FinalShareScreen";
+ private final SimpleEchoScreenBinding viewBinding;
+ private final ArrayList<String> favoritePodNames = new ArrayList<>();
+ private final ArrayList<Drawable> favoritePodImages = new ArrayList<>();
+ private final FinalShareBackground background;
+ private Disposable disposable;
+
+ public FinalShareScreen(Context context, LayoutInflater layoutInflater) {
+ super(context);
+ viewBinding = SimpleEchoScreenBinding.inflate(layoutInflater);
+ viewBinding.actionButton.setOnClickListener(v -> share());
+ viewBinding.actionButton.setCompoundDrawablesWithIntrinsicBounds(ResourcesCompat.getDrawable(
+ context.getResources(), R.drawable.ic_share, context.getTheme()), null, null, null);
+ viewBinding.actionButton.setVisibility(View.VISIBLE);
+ viewBinding.actionButton.setText(R.string.share_label);
+ background = new FinalShareBackground(context, favoritePodNames, favoritePodImages);
+ viewBinding.backgroundImage.setImageDrawable(background);
+ }
+
+ @Override
+ public View getView() {
+ return viewBinding.getRoot();
+ }
+
+ @Override
+ public void postInvalidate() {
+ viewBinding.backgroundImage.postInvalidate();
+ }
+
+ private void share() {
+ try {
+ Bitmap bitmap = Bitmap.createBitmap(SHARE_SIZE, SHARE_SIZE, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ background.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ background.draw(canvas);
+ viewBinding.backgroundImage.setImageDrawable(null);
+ viewBinding.backgroundImage.setImageDrawable(background);
+ File file = new File(UserPreferences.getDataFolder(null), "AntennaPodEcho.png");
+ FileOutputStream stream = new FileOutputStream(file);
+ bitmap.compress(Bitmap.CompressFormat.PNG, 90, stream);
+ stream.close();
+
+ Uri fileUri = FileProvider.getUriForFile(context, context.getString(R.string.provider_authority), file);
+ new ShareCompat.IntentBuilder(context)
+ .setType("image/png")
+ .addStream(fileUri)
+ .setText(context.getString(R.string.echo_share, EchoConfig.RELEASE_YEAR))
+ .setChooserTitle(R.string.share_file_label)
+ .startChooser();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void startLoading(DBReader.StatisticsResult statisticsData) {
+ favoritePodNames.clear();
+ for (int i = 0; i < 5 && i < statisticsData.feedTime.size(); i++) {
+ favoritePodNames.add(statisticsData.feedTime.get(i).feed.getTitle());
+ }
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ disposable = Observable.fromCallable(
+ () -> {
+ favoritePodImages.clear();
+ for (int i = 0; i < 5 && i < statisticsData.feedTime.size(); i++) {
+ BitmapDrawable cover = new BitmapDrawable(context.getResources(), (Bitmap) null);
+ try {
+ final int size = SHARE_SIZE / 3;
+ final int radius = (i == 0) ? (size / 16) : (size / 8);
+ cover = new BitmapDrawable(context.getResources(), Glide.with(context)
+ .asBitmap()
+ .load(statisticsData.feedTime.get(i).feed.getImageUrl())
+ .apply(new RequestOptions()
+ .fitCenter()
+ .transform(new RoundedCorners(radius)))
+ .submit(size, size)
+ .get(5, TimeUnit.SECONDS));
+ } catch (Exception e) {
+ Log.d(TAG, "Loading cover: " + e.getMessage());
+ }
+ favoritePodImages.add(cover);
+ }
+ return statisticsData;
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> { },
+ error -> Log.e(TAG, Log.getStackTraceString(error)));
+ }
+}
diff --git a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/HoarderScreen.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/HoarderScreen.java
new file mode 100644
index 000000000..68ec40fff
--- /dev/null
+++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/HoarderScreen.java
@@ -0,0 +1,75 @@
+package de.danoeh.antennapod.ui.echo.screen;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import de.danoeh.antennapod.storage.database.DBReader;
+import de.danoeh.antennapod.storage.database.StatisticsItem;
+import de.danoeh.antennapod.ui.echo.R;
+import de.danoeh.antennapod.ui.echo.background.WavesBackground;
+import de.danoeh.antennapod.ui.echo.databinding.SimpleEchoScreenBinding;
+
+import java.util.ArrayList;
+
+public class HoarderScreen extends EchoScreen {
+ private final SimpleEchoScreenBinding viewBinding;
+
+ public HoarderScreen(Context context, LayoutInflater layoutInflater) {
+ super(context);
+ viewBinding = SimpleEchoScreenBinding.inflate(layoutInflater);
+ viewBinding.aboveLabel.setText(R.string.echo_hoarder_title);
+ viewBinding.backgroundImage.setImageDrawable(new WavesBackground(context));
+ }
+
+ private void display(int playedActivePodcasts, int totalActivePodcasts, String randomUnplayedActivePodcast) {
+ int percentagePlayed = (int) (100.0 * playedActivePodcasts / totalActivePodcasts);
+ if (percentagePlayed < 25) {
+ viewBinding.largeLabel.setText(R.string.echo_hoarder_emoji_cabinet);
+ viewBinding.belowLabel.setText(R.string.echo_hoarder_subtitle_hoarder);
+ viewBinding.smallLabel.setText(context.getString(R.string.echo_hoarder_comment_hoarder,
+ percentagePlayed, totalActivePodcasts));
+ } else if (percentagePlayed < 75) {
+ viewBinding.largeLabel.setText(R.string.echo_hoarder_emoji_check);
+ viewBinding.belowLabel.setText(R.string.echo_hoarder_subtitle_medium);
+ viewBinding.smallLabel.setText(context.getString(R.string.echo_hoarder_comment_medium,
+ percentagePlayed, totalActivePodcasts, randomUnplayedActivePodcast));
+ } else {
+ viewBinding.largeLabel.setText(R.string.echo_hoarder_emoji_clean);
+ viewBinding.belowLabel.setText(R.string.echo_hoarder_subtitle_clean);
+ viewBinding.smallLabel.setText(context.getString(R.string.echo_hoarder_comment_clean,
+ percentagePlayed, totalActivePodcasts));
+ }
+ }
+
+ @Override
+ public View getView() {
+ return viewBinding.getRoot();
+ }
+
+ @Override
+ public void postInvalidate() {
+ viewBinding.backgroundImage.postInvalidate();
+ }
+
+ @Override
+ public void startLoading(DBReader.StatisticsResult statisticsResult) {
+ int totalActivePodcasts = 0;
+ int playedActivePodcasts = 0;
+ String randomUnplayedActivePodcast = "";
+ ArrayList<String> unplayedActive = new ArrayList<>();
+ for (StatisticsItem item : statisticsResult.feedTime) {
+ if (item.feed.getPreferences().getKeepUpdated()) {
+ totalActivePodcasts++;
+ if (item.timePlayed > 0) {
+ playedActivePodcasts++;
+ } else {
+ unplayedActive.add(item.feed.getTitle());
+ }
+ }
+ }
+ if (!unplayedActive.isEmpty()) {
+ randomUnplayedActivePodcast = unplayedActive.get((int) (Math.random() * unplayedActive.size()));
+ }
+ display(playedActivePodcasts, totalActivePodcasts, randomUnplayedActivePodcast);
+ }
+}
diff --git a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/HoursPlayedScreen.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/HoursPlayedScreen.java
new file mode 100644
index 000000000..367e21dea
--- /dev/null
+++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/HoursPlayedScreen.java
@@ -0,0 +1,50 @@
+package de.danoeh.antennapod.ui.echo.screen;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import de.danoeh.antennapod.storage.database.DBReader;
+import de.danoeh.antennapod.storage.database.StatisticsItem;
+import de.danoeh.antennapod.ui.echo.R;
+import de.danoeh.antennapod.ui.echo.background.WaveformBackground;
+import de.danoeh.antennapod.ui.echo.databinding.SimpleEchoScreenBinding;
+
+public class HoursPlayedScreen extends EchoScreen {
+ private final SimpleEchoScreenBinding viewBinding;
+
+ public HoursPlayedScreen(Context context, LayoutInflater layoutInflater) {
+ super(context);
+ viewBinding = SimpleEchoScreenBinding.inflate(layoutInflater);
+ viewBinding.aboveLabel.setText(R.string.echo_hours_this_year);
+ viewBinding.backgroundImage.setImageDrawable(new WaveformBackground(context));
+ }
+
+ private void display(long totalTime, int playedPodcasts) {
+ viewBinding.largeLabel.setText(String.format(getEchoLanguage(), "%d", totalTime / 3600));
+ viewBinding.belowLabel.setText(context.getResources()
+ .getQuantityString(R.plurals.echo_hours_podcasts, playedPodcasts, playedPodcasts));
+ }
+
+ @Override
+ public View getView() {
+ return viewBinding.getRoot();
+ }
+
+ @Override
+ public void postInvalidate() {
+ viewBinding.backgroundImage.postInvalidate();
+ }
+
+ @Override
+ public void startLoading(DBReader.StatisticsResult statisticsResult) {
+ int playedPodcasts = 0;
+ long totalTime = 0;
+ for (StatisticsItem item : statisticsResult.feedTime) {
+ totalTime += item.timePlayed;
+ if (item.timePlayed > 0) {
+ playedPodcasts++;
+ }
+ }
+ display(totalTime, playedPodcasts);
+ }
+}
diff --git a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/IntroScreen.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/IntroScreen.java
new file mode 100644
index 000000000..b9fa5bcda
--- /dev/null
+++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/IntroScreen.java
@@ -0,0 +1,34 @@
+package de.danoeh.antennapod.ui.echo.screen;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import de.danoeh.antennapod.ui.echo.EchoConfig;
+import de.danoeh.antennapod.ui.echo.R;
+import de.danoeh.antennapod.ui.echo.background.BubbleBackground;
+import de.danoeh.antennapod.ui.echo.databinding.SimpleEchoScreenBinding;
+
+public class IntroScreen extends EchoScreen {
+ private final SimpleEchoScreenBinding viewBinding;
+
+ public IntroScreen(Context context, LayoutInflater layoutInflater) {
+ super(context);
+ viewBinding = SimpleEchoScreenBinding.inflate(layoutInflater);
+ viewBinding.echoLogo.setVisibility(View.VISIBLE);
+ viewBinding.aboveLabel.setText(R.string.echo_intro_your_year);
+ viewBinding.largeLabel.setText(String.format(getEchoLanguage(), "%d", EchoConfig.RELEASE_YEAR));
+ viewBinding.belowLabel.setText(R.string.echo_intro_in_podcasts);
+ viewBinding.smallLabel.setText(R.string.echo_intro_locally);
+ viewBinding.backgroundImage.setImageDrawable(new BubbleBackground(context));
+ }
+
+ @Override
+ public View getView() {
+ return viewBinding.getRoot();
+ }
+
+ @Override
+ public void postInvalidate() {
+ viewBinding.backgroundImage.postInvalidate();
+ }
+}
diff --git a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/QueueScreen.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/QueueScreen.java
new file mode 100644
index 000000000..4b192997c
--- /dev/null
+++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/QueueScreen.java
@@ -0,0 +1,97 @@
+package de.danoeh.antennapod.ui.echo.screen;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.storage.database.DBReader;
+import de.danoeh.antennapod.storage.preferences.UserPreferences;
+import de.danoeh.antennapod.ui.common.Converter;
+import de.danoeh.antennapod.ui.echo.EchoConfig;
+import de.danoeh.antennapod.ui.echo.R;
+import de.danoeh.antennapod.ui.echo.background.StripesBackground;
+import de.danoeh.antennapod.ui.echo.databinding.SimpleEchoScreenBinding;
+import de.danoeh.antennapod.ui.episodes.PlaybackSpeedUtils;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+import java.util.Calendar;
+
+public class QueueScreen extends EchoScreen {
+ private static final String TAG = "QueueScreen";
+ private final SimpleEchoScreenBinding viewBinding;
+ private Disposable disposable;
+
+ public QueueScreen(Context context, LayoutInflater layoutInflater) {
+ super(context);
+ viewBinding = SimpleEchoScreenBinding.inflate(layoutInflater);
+ viewBinding.backgroundImage.setImageDrawable(new StripesBackground(context));
+ }
+
+ private void display(int queueNumEpisodes, long queueSecondsLeft) {
+ viewBinding.largeLabel.setText(String.format(getEchoLanguage(), "%d", queueSecondsLeft / 3600));
+ viewBinding.belowLabel.setText(context.getResources().getQuantityString(
+ R.plurals.echo_queue_hours_waiting, queueNumEpisodes, queueNumEpisodes));
+
+ Calendar dec31 = Calendar.getInstance();
+ dec31.set(Calendar.DAY_OF_MONTH, 31);
+ dec31.set(Calendar.MONTH, Calendar.DECEMBER);
+ int daysUntilNextYear = Math.max(1,
+ dec31.get(Calendar.DAY_OF_YEAR) - Calendar.getInstance().get(Calendar.DAY_OF_YEAR) + 1);
+ long secondsPerDay = queueSecondsLeft / daysUntilNextYear;
+ String timePerDay = Converter.getDurationStringLocalized(
+ getLocalizedResources(getEchoLanguage()), secondsPerDay * 1000, true);
+ double hoursPerDay = secondsPerDay / 3600.0;
+ int nextYear = EchoConfig.RELEASE_YEAR + 1;
+ if (hoursPerDay < 1.5) {
+ viewBinding.aboveLabel.setText(R.string.echo_queue_title_clean);
+ viewBinding.smallLabel.setText(
+ context.getString(R.string.echo_queue_hours_clean, timePerDay, nextYear));
+ } else if (hoursPerDay <= 24) {
+ viewBinding.aboveLabel.setText(R.string.echo_queue_title_many);
+ viewBinding.smallLabel.setText(
+ context.getString(R.string.echo_queue_hours_normal, timePerDay, nextYear));
+ } else {
+ viewBinding.aboveLabel.setText(R.string.echo_queue_title_many);
+ viewBinding.smallLabel.setText(context.getString(R.string.echo_queue_hours_much, timePerDay, nextYear));
+ }
+ }
+
+ @Override
+ public View getView() {
+ return viewBinding.getRoot();
+ }
+
+ @Override
+ public void postInvalidate() {
+ viewBinding.backgroundImage.postInvalidate();
+ }
+
+ @Override
+ public void startLoading(DBReader.StatisticsResult statisticsResult) {
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ disposable = Observable.fromCallable(DBReader::getQueue)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(queue -> {
+ long queueSecondsLeft = 0;
+ for (FeedItem item : queue) {
+ float playbackSpeed = 1;
+ if (UserPreferences.timeRespectsSpeed()) {
+ playbackSpeed = PlaybackSpeedUtils.getCurrentPlaybackSpeed(item.getMedia());
+ }
+ if (item.getMedia() != null) {
+ long itemTimeLeft = item.getMedia().getDuration() - item.getMedia().getPosition();
+ queueSecondsLeft += itemTimeLeft / playbackSpeed;
+ }
+ }
+ queueSecondsLeft /= 1000;
+ display(queue.size(), queueSecondsLeft);
+ }, error -> Log.e(TAG, Log.getStackTraceString(error)));
+ }
+}
diff --git a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/ThanksScreen.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/ThanksScreen.java
new file mode 100644
index 000000000..34f4e9e7e
--- /dev/null
+++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/ThanksScreen.java
@@ -0,0 +1,50 @@
+package de.danoeh.antennapod.ui.echo.screen;
+
+import android.content.Context;
+import android.text.format.DateFormat;
+import android.view.LayoutInflater;
+import android.view.View;
+import de.danoeh.antennapod.storage.database.DBReader;
+import de.danoeh.antennapod.ui.echo.EchoConfig;
+import de.danoeh.antennapod.ui.echo.R;
+import de.danoeh.antennapod.ui.echo.background.RotatingSquaresBackground;
+import de.danoeh.antennapod.ui.echo.databinding.SimpleEchoScreenBinding;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public class ThanksScreen extends EchoScreen {
+ private final SimpleEchoScreenBinding viewBinding;
+
+ public ThanksScreen(Context context, LayoutInflater layoutInflater) {
+ super(context);
+ viewBinding = SimpleEchoScreenBinding.inflate(layoutInflater);
+ viewBinding.aboveLabel.setText("");
+ viewBinding.largeLabel.setText(R.string.echo_thanks_large);
+
+ viewBinding.smallLabel.setText(R.string.echo_thanks_now_favorite);
+ viewBinding.backgroundImage.setImageDrawable(new RotatingSquaresBackground(context));
+ }
+
+ @Override
+ public View getView() {
+ return viewBinding.getRoot();
+ }
+
+ @Override
+ public void postInvalidate() {
+ viewBinding.backgroundImage.postInvalidate();
+ }
+
+ @Override
+ public void startLoading(DBReader.StatisticsResult statisticsResult) {
+ if (statisticsResult.oldestDate < EchoConfig.jan1()) {
+ String skeleton = DateFormat.getBestDateTimePattern(getEchoLanguage(), "MMMM yyyy");
+ SimpleDateFormat dateFormat = new SimpleDateFormat(skeleton, getEchoLanguage());
+ String dateFrom = dateFormat.format(new Date(statisticsResult.oldestDate));
+ viewBinding.belowLabel.setText(context.getString(R.string.echo_thanks_we_are_glad_old, dateFrom));
+ } else {
+ viewBinding.belowLabel.setText(R.string.echo_thanks_we_are_glad_new);
+ }
+ }
+}
diff --git a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/TimeReleasePlayScreen.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/TimeReleasePlayScreen.java
new file mode 100644
index 000000000..126522782
--- /dev/null
+++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/TimeReleasePlayScreen.java
@@ -0,0 +1,65 @@
+package de.danoeh.antennapod.ui.echo.screen;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import de.danoeh.antennapod.storage.database.DBReader;
+import de.danoeh.antennapod.ui.common.Converter;
+import de.danoeh.antennapod.ui.echo.EchoConfig;
+import de.danoeh.antennapod.ui.echo.R;
+import de.danoeh.antennapod.ui.echo.background.RotatingSquaresBackground;
+import de.danoeh.antennapod.ui.echo.databinding.SimpleEchoScreenBinding;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+public class TimeReleasePlayScreen extends EchoScreen {
+ private static final String TAG = "TimeReleasePlayScreen";
+ private final SimpleEchoScreenBinding viewBinding;
+ private Disposable disposable;
+
+ public TimeReleasePlayScreen(Context context, LayoutInflater layoutInflater) {
+ super(context);
+ viewBinding = SimpleEchoScreenBinding.inflate(layoutInflater);
+ viewBinding.aboveLabel.setText(R.string.echo_listened_after_title);
+ viewBinding.backgroundImage.setImageDrawable(new RotatingSquaresBackground(context));
+ }
+
+ private void display(long timeBetweenReleaseAndPlay) {
+ if (timeBetweenReleaseAndPlay <= 1000L * 3600 * 24 * 2.5) {
+ viewBinding.largeLabel.setText(R.string.echo_listened_after_emoji_run);
+ viewBinding.belowLabel.setText(R.string.echo_listened_after_comment_addict);
+ } else {
+ viewBinding.largeLabel.setText(R.string.echo_listened_after_emoji_yoga);
+ viewBinding.belowLabel.setText(R.string.echo_listened_after_comment_easy);
+ }
+ viewBinding.smallLabel.setText(context.getString(R.string.echo_listened_after_time,
+ Converter.getDurationStringLocalized(
+ getLocalizedResources(getEchoLanguage()), timeBetweenReleaseAndPlay, true)));
+ }
+
+ @Override
+ public View getView() {
+ return viewBinding.getRoot();
+ }
+
+ @Override
+ public void postInvalidate() {
+ viewBinding.backgroundImage.postInvalidate();
+ }
+
+ @Override
+ public void startLoading(DBReader.StatisticsResult statisticsResult) {
+ super.startLoading(statisticsResult);
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ disposable = Observable.fromCallable(() ->
+ DBReader.getTimeBetweenReleaseAndPlayback(EchoConfig.jan1(), Long.MAX_VALUE))
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(this::display, error -> Log.e(TAG, Log.getStackTraceString(error)));
+ }
+}
diff --git a/ui/echo/src/main/res/layout/echo_activity.xml b/ui/echo/src/main/res/layout/echo_activity.xml
index 3c5590d3a..593d941b6 100644
--- a/ui/echo/src/main/res/layout/echo_activity.xml
+++ b/ui/echo/src/main/res/layout/echo_activity.xml
@@ -1,16 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
- <ImageView
- android:id="@+id/echoImage"
+ <FrameLayout
+ android:id="@+id/screenContainer"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:importantForAccessibility="no" />
+ android:layout_height="match_parent" />
<RelativeLayout
android:layout_width="match_parent"
@@ -49,87 +46,6 @@
android:layout_alignParentStart="true"
android:layout_below="@id/echoProgressImage" />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_centerInParent="true"
- android:padding="32dp"
- android:orientation="vertical">
-
- <TextView
- android:id="@+id/aboveLabel"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textAlignment="center"
- android:textColor="#ffffff"
- android:fontFamily="@font/sarabun_regular"
- app:fontFamily="@font/sarabun_regular"
- tools:text="text above"
- style="@style/TextAppearance.Material3.TitleLarge" />
-
- <TextView
- android:id="@+id/largeLabel"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textAlignment="center"
- android:textColor="#ffffff"
- android:layout_marginVertical="8dp"
- android:fontFamily="@font/sarabun_semi_bold"
- app:fontFamily="@font/sarabun_semi_bold"
- tools:text="large"
- style="@style/TextAppearance.Material3.DisplayLarge"
- tools:targetApi="p" />
-
- <TextView
- android:id="@+id/belowLabel"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textAlignment="center"
- android:textColor="#ffffff"
- android:fontFamily="@font/sarabun_regular"
- app:fontFamily="@font/sarabun_regular"
- tools:text="text below"
- style="@style/TextAppearance.Material3.TitleLarge" />
-
- <TextView
- android:id="@+id/smallLabel"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textAlignment="center"
- android:textColor="#ffffff"
- android:textSize="16sp"
- android:layout_marginTop="32dp"
- android:fontFamily="@font/sarabun_regular"
- app:fontFamily="@font/sarabun_regular"
- tools:text="small" />
-
- </LinearLayout>
-
- <ImageView
- android:id="@+id/echoLogo"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:visibility="gone"
- android:layout_margin="32dp"
- android:src="@drawable/echo"
- android:importantForAccessibility="no"
- android:layout_alignParentBottom="true" />
-
- <com.google.android.material.button.MaterialButton
- android:id="@+id/shareButton"
- android:layout_width="wrap_content"
- android:layout_height="56dp"
- android:layout_centerHorizontal="true"
- android:layout_alignParentBottom="true"
- android:layout_margin="32dp"
- android:text="@string/share_label"
- android:drawableLeft="@drawable/ic_share"
- android:textColor="#fff"
- android:contentDescription="@string/share_label"
- style="@style/Widget.Material3.Button.OutlinedButton"
- app:strokeColor="#fff"
- tools:ignore="RtlHardcoded" />
-
</RelativeLayout>
-</RelativeLayout>
+</FrameLayout>
diff --git a/ui/echo/src/main/res/layout/simple_echo_screen.xml b/ui/echo/src/main/res/layout/simple_echo_screen.xml
new file mode 100644
index 000000000..c151682eb
--- /dev/null
+++ b/ui/echo/src/main/res/layout/simple_echo_screen.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ImageView
+ android:id="@+id/backgroundImage"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:importantForAccessibility="no" />
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:padding="32dp"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/aboveLabel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAlignment="center"
+ android:textColor="#ffffff"
+ android:fontFamily="@font/sarabun_regular"
+ app:fontFamily="@font/sarabun_regular"
+ tools:text="text above"
+ style="@style/TextAppearance.Material3.TitleLarge" />
+
+ <TextView
+ android:id="@+id/largeLabel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAlignment="center"
+ android:textColor="#ffffff"
+ android:layout_marginVertical="8dp"
+ android:fontFamily="@font/sarabun_semi_bold"
+ app:fontFamily="@font/sarabun_semi_bold"
+ tools:text="large"
+ style="@style/TextAppearance.Material3.DisplayLarge"
+ tools:targetApi="p" />
+
+ <TextView
+ android:id="@+id/belowLabel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAlignment="center"
+ android:textColor="#ffffff"
+ android:fontFamily="@font/sarabun_regular"
+ app:fontFamily="@font/sarabun_regular"
+ tools:text="text below"
+ style="@style/TextAppearance.Material3.TitleLarge" />
+
+ <TextView
+ android:id="@+id/smallLabel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAlignment="center"
+ android:textColor="#ffffff"
+ android:textSize="16sp"
+ android:layout_marginTop="32dp"
+ android:fontFamily="@font/sarabun_regular"
+ app:fontFamily="@font/sarabun_regular"
+ tools:text="small" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/echoLogo"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:src="@drawable/echo"
+ android:layout_margin="32dp"
+ android:importantForAccessibility="no"
+ android:layout_alignParentBottom="true" />
+
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/actionButton"
+ android:layout_width="wrap_content"
+ android:layout_height="56dp"
+ android:visibility="gone"
+ android:layout_margin="32dp"
+ android:layout_centerHorizontal="true"
+ android:layout_alignParentBottom="true"
+ android:textColor="#fff"
+ style="@style/Widget.Material3.Button.OutlinedButton"
+ app:strokeColor="#fff"
+ tools:ignore="RtlHardcoded" />
+
+ </RelativeLayout>
+
+</RelativeLayout>
diff --git a/ui/echo/src/main/res/values/echo-strings.xml b/ui/echo/src/main/res/values/echo-strings.xml
index 09a64f1e4..161fe363e 100644
--- a/ui/echo/src/main/res/values/echo-strings.xml
+++ b/ui/echo/src/main/res/values/echo-strings.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation,PluralsCandidate">
+ <string name="debug_echo" translatable="false">[Debug] Echo</string>
<string name="echo_home_header">Review the year</string>
<string name="echo_home_subtitle">Your top podcasts and stats from the past year. Exclusively on your phone.</string>