From 13a985ca1e6fae65682c97ee523dec96b2fdeedf Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Fri, 29 Mar 2024 08:55:13 +0100 Subject: Restructure Echo to be more flexible (#7035) Each screen is its own file, which makes it easier to add interactive elements. --- .../de/danoeh/antennapod/ui/echo/EchoActivity.java | 341 ++------------------- .../de/danoeh/antennapod/ui/echo/EchoConfig.java | 19 ++ .../ui/echo/background/BaseBackground.java | 96 ++++++ .../ui/echo/background/BubbleBackground.java | 35 +++ .../ui/echo/background/FinalShareBackground.java | 111 +++++++ .../echo/background/RotatingSquaresBackground.java | 37 +++ .../ui/echo/background/StripesBackground.java | 38 +++ .../ui/echo/background/WaveformBackground.java | 39 +++ .../ui/echo/background/WavesBackground.java | 38 +++ .../antennapod/ui/echo/screen/EchoScreen.java | 48 +++ .../ui/echo/screen/FinalShareScreen.java | 127 ++++++++ .../antennapod/ui/echo/screen/HoarderScreen.java | 75 +++++ .../ui/echo/screen/HoursPlayedScreen.java | 50 +++ .../antennapod/ui/echo/screen/IntroScreen.java | 34 ++ .../antennapod/ui/echo/screen/QueueScreen.java | 97 ++++++ .../antennapod/ui/echo/screen/ThanksScreen.java | 50 +++ .../ui/echo/screen/TimeReleasePlayScreen.java | 65 ++++ .../antennapod/ui/echo/screens/BaseScreen.java | 96 ------ .../antennapod/ui/echo/screens/BubbleScreen.java | 35 --- .../ui/echo/screens/FinalShareScreen.java | 111 ------- .../ui/echo/screens/RotatingSquaresScreen.java | 37 --- .../antennapod/ui/echo/screens/StripesScreen.java | 38 --- .../antennapod/ui/echo/screens/WaveformScreen.java | 39 --- .../antennapod/ui/echo/screens/WavesScreen.java | 38 --- ui/echo/src/main/res/layout/echo_activity.xml | 94 +----- ui/echo/src/main/res/layout/simple_echo_screen.xml | 101 ++++++ ui/echo/src/main/res/values/echo-strings.xml | 1 + 27 files changed, 1097 insertions(+), 793 deletions(-) create mode 100644 ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/EchoConfig.java create mode 100644 ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/BaseBackground.java create mode 100644 ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/BubbleBackground.java create mode 100644 ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/FinalShareBackground.java create mode 100644 ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/RotatingSquaresBackground.java create mode 100644 ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/StripesBackground.java create mode 100644 ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/WaveformBackground.java create mode 100644 ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/WavesBackground.java create mode 100644 ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/EchoScreen.java create mode 100644 ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/FinalShareScreen.java create mode 100644 ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/HoarderScreen.java create mode 100644 ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/HoursPlayedScreen.java create mode 100644 ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/IntroScreen.java create mode 100644 ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/QueueScreen.java create mode 100644 ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/ThanksScreen.java create mode 100644 ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screen/TimeReleasePlayScreen.java delete mode 100644 ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/BaseScreen.java delete mode 100644 ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/BubbleScreen.java delete mode 100644 ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/FinalShareScreen.java delete mode 100644 ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/RotatingSquaresScreen.java delete mode 100644 ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/StripesScreen.java delete mode 100644 ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/WaveformScreen.java delete mode 100644 ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/WavesScreen.java create mode 100644 ui/echo/src/main/res/layout/simple_echo_screen.xml (limited to 'ui/echo/src') 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 favoritePodNames = new ArrayList<>(); - private final ArrayList favoritePodImages = new ArrayList<>(); + private List 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 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 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/background/BaseBackground.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/BaseBackground.java new file mode 100644 index 000000000..343ce4547 --- /dev/null +++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/BaseBackground.java @@ -0,0 +1,96 @@ +package de.danoeh.antennapod.ui.echo.background; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.LinearGradient; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Shader; +import android.graphics.drawable.Drawable; +import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; + +import java.util.ArrayList; +import de.danoeh.antennapod.ui.echo.R; + +public abstract class BaseBackground extends Drawable { + private final Paint paintBackground; + protected final Paint paintParticles; + protected final ArrayList particles = new ArrayList<>(); + private final int colorBackgroundFrom; + private final int colorBackgroundTo; + private long lastFrame = 0; + + public BaseBackground(Context context) { + colorBackgroundFrom = ContextCompat.getColor(context, R.color.gradient_000); + colorBackgroundTo = ContextCompat.getColor(context, R.color.gradient_100); + paintBackground = new Paint(); + paintParticles = new Paint(); + paintParticles.setColor(0xffffffff); + paintParticles.setFlags(Paint.ANTI_ALIAS_FLAG); + paintParticles.setStyle(Paint.Style.FILL); + paintParticles.setAlpha(25); + } + + @Override + public void draw(@NonNull Canvas canvas) { + float width = getBounds().width(); + float height = getBounds().height(); + paintBackground.setShader(new LinearGradient(0, 0, 0, height, + colorBackgroundFrom, colorBackgroundTo, Shader.TileMode.CLAMP)); + canvas.drawRect(0, 0, width, height, paintBackground); + + long timeSinceLastFrame = System.currentTimeMillis() - lastFrame; + lastFrame = System.currentTimeMillis(); + if (timeSinceLastFrame > 500) { + timeSinceLastFrame = 0; + } + final float innerBoxSize = (Math.abs(width - height) < 0.001f) // Square share version + ? (0.9f * width) : (0.9f * Math.min(width, 0.7f * height)); + final float innerBoxX = (width - innerBoxSize) / 2; + final float innerBoxY = (height - innerBoxSize) / 2; + + for (Particle p : particles) { + drawParticle(canvas, p, width, height, innerBoxX, innerBoxY, innerBoxSize); + particleTick(p, timeSinceLastFrame); + } + + drawInner(canvas, innerBoxX, innerBoxY, innerBoxSize); + } + + protected void drawInner(Canvas canvas, float innerBoxX, float innerBoxY, float innerBoxSize) { + } + + protected abstract void particleTick(Particle p, long timeSinceLastFrame); + + protected abstract void drawParticle(@NonNull Canvas canvas, Particle p, float width, float height, + float innerBoxX, float innerBoxY, float innerBoxSize); + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + + @Override + public void setAlpha(int alpha) { + } + + @Override + public void setColorFilter(ColorFilter cf) { + } + + protected static class Particle { + double positionX; + double positionY; + double positionZ; + double speed; + + public Particle(double positionX, double positionY, double positionZ, double speed) { + this.positionX = positionX; + this.positionY = positionY; + this.positionZ = positionZ; + this.speed = speed; + } + } +} diff --git a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/BubbleBackground.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/BubbleBackground.java new file mode 100644 index 000000000..28d6cb4e2 --- /dev/null +++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/BubbleBackground.java @@ -0,0 +1,35 @@ +package de.danoeh.antennapod.ui.echo.background; + +import android.content.Context; +import android.graphics.Canvas; +import androidx.annotation.NonNull; + +public class BubbleBackground extends BaseBackground { + protected static final double PARTICLE_SPEED = 0.00002; + protected static final int NUM_PARTICLES = 15; + + 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 + 0, PARTICLE_SPEED + 2 * PARTICLE_SPEED * Math.random())); + } + } + + @Override + protected void drawParticle(@NonNull Canvas canvas, Particle p, float width, float height, + float innerBoxX, float innerBoxY, float innerBoxSize) { + canvas.drawCircle((float) (width * p.positionX), (float) (p.positionY * height), + innerBoxSize / 5, paintParticles); + } + + @Override + protected void particleTick(Particle p, long timeSinceLastFrame) { + p.positionY -= p.speed * timeSinceLastFrame; + if (p.positionY < -0.5) { + p.positionX = Math.random(); + p.positionY = 1.5f; + p.speed = PARTICLE_SPEED + 2 * PARTICLE_SPEED * Math.random(); + } + } +} diff --git a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/FinalShareBackground.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/FinalShareBackground.java new file mode 100644 index 000000000..341bc5133 --- /dev/null +++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/FinalShareBackground.java @@ -0,0 +1,111 @@ +package de.danoeh.antennapod.ui.echo.background; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.RectF; +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.EchoConfig; +import de.danoeh.antennapod.ui.echo.R; +import java.util.ArrayList; + +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; + private final Paint paintCoverBorder; + private final String heading; + private final String year; + private final Drawable logo; + private final ArrayList favoritePodNames; + private final ArrayList favoritePodImages; + private final Typeface typefaceNormal; + private final Typeface typefaceBold; + + public FinalShareBackground(Context context, + ArrayList favoritePodNames, ArrayList 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(EchoConfig.RELEASE_YEAR); + typefaceNormal = ResourcesCompat.getFont(context, R.font.sarabun_regular); + typefaceBold = ResourcesCompat.getFont(context, R.font.sarabun_semi_bold); + paintTextMain = new Paint(); + paintTextMain.setColor(0xffffffff); + paintTextMain.setFlags(Paint.ANTI_ALIAS_FLAG); + paintTextMain.setStyle(Paint.Style.FILL); + paintCoverBorder = new Paint(); + paintCoverBorder.setColor(0xffffffff); + paintCoverBorder.setFlags(Paint.ANTI_ALIAS_FLAG); + paintCoverBorder.setStyle(Paint.Style.FILL); + paintCoverBorder.setAlpha(70); + } + + protected void drawInner(Canvas canvas, float innerBoxX, float innerBoxY, float innerBoxSize) { + paintTextMain.setTextAlign(Paint.Align.CENTER); + paintTextMain.setTypeface(typefaceBold); + float headingSize = innerBoxSize / 14; + paintTextMain.setTextSize(headingSize); + canvas.drawText(heading, innerBoxX + 0.5f * innerBoxSize, innerBoxY + headingSize, paintTextMain); + paintTextMain.setTextSize(0.12f * innerBoxSize); + canvas.drawText(year, innerBoxX + 0.8f * innerBoxSize, innerBoxY + 0.25f * innerBoxSize, paintTextMain); + + float fontSizePods = innerBoxSize / 18; // First one only + float textY = innerBoxY + 0.62f * innerBoxSize; + for (int i = 0; i < favoritePodNames.size(); i++) { + float coverSize = (i == 0) ? (0.4f * innerBoxSize) : (0.2f * innerBoxSize); + float coverX = COVER_POSITIONS[i][0]; + float coverY = COVER_POSITIONS[i][1]; + RectF logo1Pos = new RectF(innerBoxX + coverX * innerBoxSize, + innerBoxY + (coverY + 0.12f) * innerBoxSize, + innerBoxX + coverX * innerBoxSize + coverSize, + innerBoxY + (coverY + 0.12f) * innerBoxSize + coverSize); + logo1Pos.inset((int) (0.01f * innerBoxSize), (int) (0.01f * innerBoxSize)); + float radius = (i == 0) ? (coverSize / 16) : (coverSize / 8); + canvas.drawRoundRect(logo1Pos, radius, radius, paintCoverBorder); + logo1Pos.inset((int) (0.003f * innerBoxSize), (int) (0.003f * innerBoxSize)); + Rect pos = new Rect(); + logo1Pos.round(pos); + if (favoritePodImages.size() > i) { + favoritePodImages.get(i).setBounds(pos); + favoritePodImages.get(i).draw(canvas); + } else { + canvas.drawText(" ...", pos.left, pos.centerY(), paintTextMain); + } + + paintTextMain.setTextAlign(Paint.Align.CENTER); + paintTextMain.setTextSize(fontSizePods); + final float numberWidth = 0.06f * innerBoxSize; + canvas.drawText((i + 1) + ".", innerBoxX + numberWidth / 2, textY, paintTextMain); + paintTextMain.setTextAlign(Paint.Align.LEFT); + String ellipsizedTitle = ellipsize(favoritePodNames.get(i), paintTextMain, innerBoxSize - numberWidth); + canvas.drawText(ellipsizedTitle, innerBoxX + numberWidth, textY, paintTextMain); + fontSizePods = innerBoxSize / 24; // Starting with second text is smaller + textY += 1.3f * fontSizePods; + paintTextMain.setTypeface(typefaceNormal); + } + + double ratio = (1.0 * logo.getIntrinsicHeight()) / logo.getIntrinsicWidth(); + logo.setBounds((int) (innerBoxX + 0.1 * innerBoxSize), + (int) (innerBoxY + innerBoxSize - 0.8 * innerBoxSize * ratio), + (int) (innerBoxX + 0.9 * innerBoxSize), + (int) (innerBoxY + innerBoxSize)); + logo.draw(canvas); + } + + String ellipsize(String string, Paint paint, float maxWidth) { + if (paint.measureText(string) <= maxWidth) { + return string; + } + while (paint.measureText(string + "…") > maxWidth || string.endsWith(" ")) { + string = string.substring(0, string.length() - 1); + } + return string + "…"; + } +} diff --git a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/RotatingSquaresBackground.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/RotatingSquaresBackground.java new file mode 100644 index 000000000..3901bc5e4 --- /dev/null +++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/RotatingSquaresBackground.java @@ -0,0 +1,37 @@ +package de.danoeh.antennapod.ui.echo.background; + +import android.content.Context; +import android.graphics.Canvas; +import androidx.annotation.NonNull; + +public class RotatingSquaresBackground extends BaseBackground { + public RotatingSquaresBackground(Context context) { + super(context); + for (int i = 0; i < 16; i++) { + particles.add(new Particle( + 0.3 * (float) (i % 4) + 0.05 + 0.1 * Math.random() - 0.05, + 0.2 * (float) (i / 4) + 0.20 + 0.1 * Math.random() - 0.05, + Math.random(), 0.00001 * (2 * Math.random() + 2))); + } + } + + @Override + protected void drawParticle(@NonNull Canvas canvas, Particle p, float width, float height, + float innerBoxX, float innerBoxY, float innerBoxSize) { + float x = (float) (p.positionX * width); + float y = (float) (p.positionY * height); + float size = innerBoxSize / 6; + canvas.save(); + canvas.rotate((float) (360 * p.positionZ), x, y); + canvas.drawRect(x - size, y - size, x + size, y + size, paintParticles); + canvas.restore(); + } + + @Override + protected void particleTick(Particle p, long timeSinceLastFrame) { + p.positionZ += p.speed * timeSinceLastFrame; + if (p.positionZ > 1) { + p.positionZ -= 1; + } + } +} \ No newline at end of file diff --git a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/StripesBackground.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/StripesBackground.java new file mode 100644 index 000000000..5d523335b --- /dev/null +++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/StripesBackground.java @@ -0,0 +1,38 @@ +package de.danoeh.antennapod.ui.echo.background; + +import android.content.Context; +import android.graphics.Canvas; +import androidx.annotation.NonNull; + +public class StripesBackground extends BaseBackground { + protected static final int NUM_PARTICLES = 15; + + 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)); + } + } + + @Override + public void draw(@NonNull Canvas canvas) { + paintParticles.setStrokeWidth(0.05f * getBounds().width()); + super.draw(canvas); + } + + @Override + protected void drawParticle(@NonNull Canvas canvas, Particle p, float width, float height, + float innerBoxX, float innerBoxY, float innerBoxSize) { + float strokeWidth = 0.05f * width; + float x = (float) (width * p.positionX); + canvas.drawLine(x, -strokeWidth, x + width, height + strokeWidth, paintParticles); + } + + @Override + protected void particleTick(Particle p, long timeSinceLastFrame) { + p.positionX += 0.00005 * timeSinceLastFrame; + if (p.positionX > 1f) { + p.positionX -= 2f; + } + } +} diff --git a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/WaveformBackground.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/WaveformBackground.java new file mode 100644 index 000000000..419cdf344 --- /dev/null +++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/WaveformBackground.java @@ -0,0 +1,39 @@ +package de.danoeh.antennapod.ui.echo.background; + +import android.content.Context; +import android.graphics.Canvas; +import androidx.annotation.NonNull; + +public class WaveformBackground extends BaseBackground { + protected static final int NUM_PARTICLES = 40; + + 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)); + } + } + + @Override + protected void drawParticle(@NonNull Canvas canvas, Particle p, float width, float height, + float innerBoxX, float innerBoxY, float innerBoxSize) { + float x = (float) (width * p.positionX); + canvas.drawRect(x, height, x + (1.1f * width) / NUM_PARTICLES, + (float) (0.95f * height - 0.3f * p.positionY * height), paintParticles); + } + + @Override + protected void particleTick(Particle p, long timeSinceLastFrame) { + p.positionX += 0.0001 * timeSinceLastFrame; + if (p.positionY <= 0.2 || p.positionY >= 1) { + p.speed = -p.speed; + p.positionY -= p.speed * timeSinceLastFrame; + } + p.positionY -= p.speed * timeSinceLastFrame; + if (p.positionX > 1.05f) { + p.positionX -= 1.1; + p.positionY = 0.2 + 0.8 * Math.random(); + p.speed = 0.0008 * Math.random() - 0.0004; + } + } +} diff --git a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/WavesBackground.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/WavesBackground.java new file mode 100644 index 000000000..6ea46853e --- /dev/null +++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/WavesBackground.java @@ -0,0 +1,38 @@ +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 WavesBackground extends BaseBackground { + protected static final int NUM_PARTICLES = 10; + + public WavesBackground(Context context) { + super(context); + paintParticles.setStyle(Paint.Style.STROKE); + for (int i = 0; i < NUM_PARTICLES; i++) { + particles.add(new Particle(0, 0, 1.0f * i / NUM_PARTICLES, 0)); + } + } + + @Override + public void draw(@NonNull Canvas canvas) { + paintParticles.setStrokeWidth(0.05f * getBounds().height()); + super.draw(canvas); + } + + @Override + protected void drawParticle(@NonNull Canvas canvas, Particle p, float width, float height, + float innerBoxX, float innerBoxY, float innerBoxSize) { + canvas.drawCircle(width / 2, 1.1f * height, (float) (p.positionZ * 1.2f * height), paintParticles); + } + + @Override + protected void particleTick(Particle p, long timeSinceLastFrame) { + p.positionZ += 0.00005 * timeSinceLastFrame; + if (p.positionZ > 1f) { + p.positionZ -= 1f; + } + } +} 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 favoritePodNames = new ArrayList<>(); + private final ArrayList 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 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/java/de/danoeh/antennapod/ui/echo/screens/BaseScreen.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/BaseScreen.java deleted file mode 100644 index e8bc085cd..000000000 --- a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/BaseScreen.java +++ /dev/null @@ -1,96 +0,0 @@ -package de.danoeh.antennapod.ui.echo.screens; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.ColorFilter; -import android.graphics.LinearGradient; -import android.graphics.Paint; -import android.graphics.PixelFormat; -import android.graphics.Shader; -import android.graphics.drawable.Drawable; -import androidx.annotation.NonNull; -import androidx.core.content.ContextCompat; - -import java.util.ArrayList; -import de.danoeh.antennapod.ui.echo.R; - -public abstract class BaseScreen extends Drawable { - private final Paint paintBackground; - protected final Paint paintParticles; - protected final ArrayList particles = new ArrayList<>(); - private final int colorBackgroundFrom; - private final int colorBackgroundTo; - private long lastFrame = 0; - - public BaseScreen(Context context) { - colorBackgroundFrom = ContextCompat.getColor(context, R.color.gradient_000); - colorBackgroundTo = ContextCompat.getColor(context, R.color.gradient_100); - paintBackground = new Paint(); - paintParticles = new Paint(); - paintParticles.setColor(0xffffffff); - paintParticles.setFlags(Paint.ANTI_ALIAS_FLAG); - paintParticles.setStyle(Paint.Style.FILL); - paintParticles.setAlpha(25); - } - - @Override - public void draw(@NonNull Canvas canvas) { - float width = getBounds().width(); - float height = getBounds().height(); - paintBackground.setShader(new LinearGradient(0, 0, 0, height, - colorBackgroundFrom, colorBackgroundTo, Shader.TileMode.CLAMP)); - canvas.drawRect(0, 0, width, height, paintBackground); - - long timeSinceLastFrame = System.currentTimeMillis() - lastFrame; - lastFrame = System.currentTimeMillis(); - if (timeSinceLastFrame > 500) { - timeSinceLastFrame = 0; - } - final float innerBoxSize = (Math.abs(width - height) < 0.001f) // Square share version - ? (0.9f * width) : (0.9f * Math.min(width, 0.7f * height)); - final float innerBoxX = (width - innerBoxSize) / 2; - final float innerBoxY = (height - innerBoxSize) / 2; - - for (Particle p : particles) { - drawParticle(canvas, p, width, height, innerBoxX, innerBoxY, innerBoxSize); - particleTick(p, timeSinceLastFrame); - } - - drawInner(canvas, innerBoxX, innerBoxY, innerBoxSize); - } - - protected void drawInner(Canvas canvas, float innerBoxX, float innerBoxY, float innerBoxSize) { - } - - protected abstract void particleTick(Particle p, long timeSinceLastFrame); - - protected abstract void drawParticle(@NonNull Canvas canvas, Particle p, float width, float height, - float innerBoxX, float innerBoxY, float innerBoxSize); - - @Override - public int getOpacity() { - return PixelFormat.TRANSLUCENT; - } - - @Override - public void setAlpha(int alpha) { - } - - @Override - public void setColorFilter(ColorFilter cf) { - } - - protected static class Particle { - double positionX; - double positionY; - double positionZ; - double speed; - - public Particle(double positionX, double positionY, double positionZ, double speed) { - this.positionX = positionX; - this.positionY = positionY; - this.positionZ = positionZ; - this.speed = speed; - } - } -} 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/screens/BubbleScreen.java deleted file mode 100644 index bd79645dc..000000000 --- a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/BubbleScreen.java +++ /dev/null @@ -1,35 +0,0 @@ -package de.danoeh.antennapod.ui.echo.screens; - -import android.content.Context; -import android.graphics.Canvas; -import androidx.annotation.NonNull; - -public class BubbleScreen extends BaseScreen { - protected static final double PARTICLE_SPEED = 0.00002; - protected static final int NUM_PARTICLES = 15; - - public BubbleScreen(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 - 0, PARTICLE_SPEED + 2 * PARTICLE_SPEED * Math.random())); - } - } - - @Override - protected void drawParticle(@NonNull Canvas canvas, Particle p, float width, float height, - float innerBoxX, float innerBoxY, float innerBoxSize) { - canvas.drawCircle((float) (width * p.positionX), (float) (p.positionY * height), - innerBoxSize / 5, paintParticles); - } - - @Override - protected void particleTick(Particle p, long timeSinceLastFrame) { - p.positionY -= p.speed * timeSinceLastFrame; - if (p.positionY < -0.5) { - p.positionX = Math.random(); - p.positionY = 1.5f; - p.speed = PARTICLE_SPEED + 2 * PARTICLE_SPEED * Math.random(); - } - } -} 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/screens/FinalShareScreen.java deleted file mode 100644 index 4af8941d0..000000000 --- a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/FinalShareScreen.java +++ /dev/null @@ -1,111 +0,0 @@ -package de.danoeh.antennapod.ui.echo.screens; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Rect; -import android.graphics.RectF; -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.R; -import java.util.ArrayList; - -public class FinalShareScreen extends BubbleScreen { - 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; - private final Paint paintCoverBorder; - private final String heading; - private final String year; - private final Drawable logo; - private final ArrayList favoritePodNames; - private final ArrayList favoritePodImages; - private final Typeface typefaceNormal; - private final Typeface typefaceBold; - - public FinalShareScreen(Context context, - ArrayList favoritePodNames, ArrayList 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); - typefaceNormal = ResourcesCompat.getFont(context, R.font.sarabun_regular); - typefaceBold = ResourcesCompat.getFont(context, R.font.sarabun_semi_bold); - paintTextMain = new Paint(); - paintTextMain.setColor(0xffffffff); - paintTextMain.setFlags(Paint.ANTI_ALIAS_FLAG); - paintTextMain.setStyle(Paint.Style.FILL); - paintCoverBorder = new Paint(); - paintCoverBorder.setColor(0xffffffff); - paintCoverBorder.setFlags(Paint.ANTI_ALIAS_FLAG); - paintCoverBorder.setStyle(Paint.Style.FILL); - paintCoverBorder.setAlpha(70); - } - - protected void drawInner(Canvas canvas, float innerBoxX, float innerBoxY, float innerBoxSize) { - paintTextMain.setTextAlign(Paint.Align.CENTER); - paintTextMain.setTypeface(typefaceBold); - float headingSize = innerBoxSize / 14; - paintTextMain.setTextSize(headingSize); - canvas.drawText(heading, innerBoxX + 0.5f * innerBoxSize, innerBoxY + headingSize, paintTextMain); - paintTextMain.setTextSize(0.12f * innerBoxSize); - canvas.drawText(year, innerBoxX + 0.8f * innerBoxSize, innerBoxY + 0.25f * innerBoxSize, paintTextMain); - - float fontSizePods = innerBoxSize / 18; // First one only - float textY = innerBoxY + 0.62f * innerBoxSize; - for (int i = 0; i < favoritePodNames.size(); i++) { - float coverSize = (i == 0) ? (0.4f * innerBoxSize) : (0.2f * innerBoxSize); - float coverX = COVER_POSITIONS[i][0]; - float coverY = COVER_POSITIONS[i][1]; - RectF logo1Pos = new RectF(innerBoxX + coverX * innerBoxSize, - innerBoxY + (coverY + 0.12f) * innerBoxSize, - innerBoxX + coverX * innerBoxSize + coverSize, - innerBoxY + (coverY + 0.12f) * innerBoxSize + coverSize); - logo1Pos.inset((int) (0.01f * innerBoxSize), (int) (0.01f * innerBoxSize)); - float radius = (i == 0) ? (coverSize / 16) : (coverSize / 8); - canvas.drawRoundRect(logo1Pos, radius, radius, paintCoverBorder); - logo1Pos.inset((int) (0.003f * innerBoxSize), (int) (0.003f * innerBoxSize)); - Rect pos = new Rect(); - logo1Pos.round(pos); - if (favoritePodImages.size() > i) { - favoritePodImages.get(i).setBounds(pos); - favoritePodImages.get(i).draw(canvas); - } else { - canvas.drawText(" ...", pos.left, pos.centerY(), paintTextMain); - } - - paintTextMain.setTextAlign(Paint.Align.CENTER); - paintTextMain.setTextSize(fontSizePods); - final float numberWidth = 0.06f * innerBoxSize; - canvas.drawText((i + 1) + ".", innerBoxX + numberWidth / 2, textY, paintTextMain); - paintTextMain.setTextAlign(Paint.Align.LEFT); - String ellipsizedTitle = ellipsize(favoritePodNames.get(i), paintTextMain, innerBoxSize - numberWidth); - canvas.drawText(ellipsizedTitle, innerBoxX + numberWidth, textY, paintTextMain); - fontSizePods = innerBoxSize / 24; // Starting with second text is smaller - textY += 1.3f * fontSizePods; - paintTextMain.setTypeface(typefaceNormal); - } - - double ratio = (1.0 * logo.getIntrinsicHeight()) / logo.getIntrinsicWidth(); - logo.setBounds((int) (innerBoxX + 0.1 * innerBoxSize), - (int) (innerBoxY + innerBoxSize - 0.8 * innerBoxSize * ratio), - (int) (innerBoxX + 0.9 * innerBoxSize), - (int) (innerBoxY + innerBoxSize)); - logo.draw(canvas); - } - - String ellipsize(String string, Paint paint, float maxWidth) { - if (paint.measureText(string) <= maxWidth) { - return string; - } - while (paint.measureText(string + "…") > maxWidth || string.endsWith(" ")) { - string = string.substring(0, string.length() - 1); - } - return string + "…"; - } -} 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/screens/RotatingSquaresScreen.java deleted file mode 100644 index 624b68f88..000000000 --- a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/RotatingSquaresScreen.java +++ /dev/null @@ -1,37 +0,0 @@ -package de.danoeh.antennapod.ui.echo.screens; - -import android.content.Context; -import android.graphics.Canvas; -import androidx.annotation.NonNull; - -public class RotatingSquaresScreen extends BaseScreen { - public RotatingSquaresScreen(Context context) { - super(context); - for (int i = 0; i < 16; i++) { - particles.add(new Particle( - 0.3 * (float) (i % 4) + 0.05 + 0.1 * Math.random() - 0.05, - 0.2 * (float) (i / 4) + 0.20 + 0.1 * Math.random() - 0.05, - Math.random(), 0.00001 * (2 * Math.random() + 2))); - } - } - - @Override - protected void drawParticle(@NonNull Canvas canvas, Particle p, float width, float height, - float innerBoxX, float innerBoxY, float innerBoxSize) { - float x = (float) (p.positionX * width); - float y = (float) (p.positionY * height); - float size = innerBoxSize / 6; - canvas.save(); - canvas.rotate((float) (360 * p.positionZ), x, y); - canvas.drawRect(x - size, y - size, x + size, y + size, paintParticles); - canvas.restore(); - } - - @Override - protected void particleTick(Particle p, long timeSinceLastFrame) { - p.positionZ += p.speed * timeSinceLastFrame; - if (p.positionZ > 1) { - p.positionZ -= 1; - } - } -} \ No newline at end of file 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/screens/StripesScreen.java deleted file mode 100644 index 60906776f..000000000 --- a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/StripesScreen.java +++ /dev/null @@ -1,38 +0,0 @@ -package de.danoeh.antennapod.ui.echo.screens; - -import android.content.Context; -import android.graphics.Canvas; -import androidx.annotation.NonNull; - -public class StripesScreen extends BaseScreen { - protected static final int NUM_PARTICLES = 15; - - public StripesScreen(Context context) { - super(context); - for (int i = 0; i < NUM_PARTICLES; i++) { - particles.add(new Particle(2f * i / NUM_PARTICLES - 1f, 0, 0, 0)); - } - } - - @Override - public void draw(@NonNull Canvas canvas) { - paintParticles.setStrokeWidth(0.05f * getBounds().width()); - super.draw(canvas); - } - - @Override - protected void drawParticle(@NonNull Canvas canvas, Particle p, float width, float height, - float innerBoxX, float innerBoxY, float innerBoxSize) { - float strokeWidth = 0.05f * width; - float x = (float) (width * p.positionX); - canvas.drawLine(x, -strokeWidth, x + width, height + strokeWidth, paintParticles); - } - - @Override - protected void particleTick(Particle p, long timeSinceLastFrame) { - p.positionX += 0.00005 * timeSinceLastFrame; - if (p.positionX > 1f) { - p.positionX -= 2f; - } - } -} 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/screens/WaveformScreen.java deleted file mode 100644 index d87f7fbb5..000000000 --- a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/WaveformScreen.java +++ /dev/null @@ -1,39 +0,0 @@ -package de.danoeh.antennapod.ui.echo.screens; - -import android.content.Context; -import android.graphics.Canvas; -import androidx.annotation.NonNull; - -public class WaveformScreen extends BaseScreen { - protected static final int NUM_PARTICLES = 40; - - public WaveformScreen(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)); - } - } - - @Override - protected void drawParticle(@NonNull Canvas canvas, Particle p, float width, float height, - float innerBoxX, float innerBoxY, float innerBoxSize) { - float x = (float) (width * p.positionX); - canvas.drawRect(x, height, x + (1.1f * width) / NUM_PARTICLES, - (float) (0.95f * height - 0.3f * p.positionY * height), paintParticles); - } - - @Override - protected void particleTick(Particle p, long timeSinceLastFrame) { - p.positionX += 0.0001 * timeSinceLastFrame; - if (p.positionY <= 0.2 || p.positionY >= 1) { - p.speed = -p.speed; - p.positionY -= p.speed * timeSinceLastFrame; - } - p.positionY -= p.speed * timeSinceLastFrame; - if (p.positionX > 1.05f) { - p.positionX -= 1.1; - p.positionY = 0.2 + 0.8 * Math.random(); - p.speed = 0.0008 * Math.random() - 0.0004; - } - } -} 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/screens/WavesScreen.java deleted file mode 100644 index eb521f0a2..000000000 --- a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/WavesScreen.java +++ /dev/null @@ -1,38 +0,0 @@ -package de.danoeh.antennapod.ui.echo.screens; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Paint; -import androidx.annotation.NonNull; - -public class WavesScreen extends BaseScreen { - protected static final int NUM_PARTICLES = 10; - - public WavesScreen(Context context) { - super(context); - paintParticles.setStyle(Paint.Style.STROKE); - for (int i = 0; i < NUM_PARTICLES; i++) { - particles.add(new Particle(0, 0, 1.0f * i / NUM_PARTICLES, 0)); - } - } - - @Override - public void draw(@NonNull Canvas canvas) { - paintParticles.setStrokeWidth(0.05f * getBounds().height()); - super.draw(canvas); - } - - @Override - protected void drawParticle(@NonNull Canvas canvas, Particle p, float width, float height, - float innerBoxX, float innerBoxY, float innerBoxSize) { - canvas.drawCircle(width / 2, 1.1f * height, (float) (p.positionZ * 1.2f * height), paintParticles); - } - - @Override - protected void particleTick(Particle p, long timeSinceLastFrame) { - p.positionZ += 0.00005 * timeSinceLastFrame; - if (p.positionZ > 1f) { - p.positionZ -= 1f; - } - } -} 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 @@ - - + android:layout_height="match_parent" /> - - - - - - - - - - - - - - - - - + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + [Debug] Echo Review the year Your top podcasts and stats from the past year. Exclusively on your phone. -- cgit v1.2.3