From 13a985ca1e6fae65682c97ee523dec96b2fdeedf Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Fri, 29 Mar 2024 08:55:13 +0100 Subject: [PATCH] Restructure Echo to be more flexible (#7035) Each screen is its own file, which makes it easier to add interactive elements. --- .../antennapod/ui/home/HomeFragment.java | 6 +- .../ui/home/sections/EchoSection.java | 24 +- config/spotbugs/exclude.xml | 2 +- .../antennapod/ui/echo/EchoActivity.java | 341 ++---------------- .../danoeh/antennapod/ui/echo/EchoConfig.java | 19 + .../BaseBackground.java} | 6 +- .../BubbleBackground.java} | 6 +- .../FinalShareBackground.java} | 12 +- .../RotatingSquaresBackground.java} | 6 +- .../StripesBackground.java} | 6 +- .../WaveformBackground.java} | 6 +- .../WavesBackground.java} | 6 +- .../antennapod/ui/echo/screen/EchoScreen.java | 48 +++ .../ui/echo/screen/FinalShareScreen.java | 127 +++++++ .../ui/echo/screen/HoarderScreen.java | 75 ++++ .../ui/echo/screen/HoursPlayedScreen.java | 50 +++ .../ui/echo/screen/IntroScreen.java | 34 ++ .../ui/echo/screen/QueueScreen.java | 97 +++++ .../ui/echo/screen/ThanksScreen.java | 50 +++ .../ui/echo/screen/TimeReleasePlayScreen.java | 65 ++++ ui/echo/src/main/res/layout/echo_activity.xml | 94 +---- .../main/res/layout/simple_echo_screen.xml | 101 ++++++ ui/echo/src/main/res/values/echo-strings.xml | 1 + ui/statistics/build.gradle | 1 + .../ui/statistics/StatisticsFragment.java | 7 + .../src/main/res/menu/statistics.xml | 9 +- 26 files changed, 752 insertions(+), 447 deletions(-) create mode 100644 ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/EchoConfig.java rename ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/{screens/BaseScreen.java => background/BaseBackground.java} (95%) rename ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/{screens/BubbleScreen.java => background/BubbleBackground.java} (88%) rename ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/{screens/FinalShareScreen.java => background/FinalShareBackground.java} (93%) rename ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/{screens/RotatingSquaresScreen.java => background/RotatingSquaresBackground.java} (87%) rename ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/{screens/StripesScreen.java => background/StripesBackground.java} (87%) rename ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/{screens/WaveformScreen.java => background/WaveformBackground.java} (89%) rename ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/{screens/WavesScreen.java => background/WavesBackground.java} (88%) 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 create mode 100644 ui/echo/src/main/res/layout/simple_echo_screen.xml diff --git a/app/src/main/java/de/danoeh/antennapod/ui/home/HomeFragment.java b/app/src/main/java/de/danoeh/antennapod/ui/home/HomeFragment.java index 62bb8d684..0837b5e8d 100644 --- a/app/src/main/java/de/danoeh/antennapod/ui/home/HomeFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/ui/home/HomeFragment.java @@ -19,6 +19,7 @@ import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentContainerView; +import de.danoeh.antennapod.ui.echo.EchoConfig; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; @@ -37,7 +38,6 @@ import de.danoeh.antennapod.event.FeedListUpdateEvent; import de.danoeh.antennapod.event.FeedUpdateRunningEvent; import de.danoeh.antennapod.fragment.SearchFragment; import de.danoeh.antennapod.storage.preferences.UserPreferences; -import de.danoeh.antennapod.ui.echo.EchoActivity; import de.danoeh.antennapod.ui.home.sections.AllowNotificationsSection; import de.danoeh.antennapod.ui.home.sections.DownloadsSection; import de.danoeh.antennapod.ui.home.sections.EchoSection; @@ -98,10 +98,10 @@ public class HomeFragment extends Fragment implements Toolbar.OnMenuItemClickLis addSection(new AllowNotificationsSection()); } } - if (Calendar.getInstance().get(Calendar.YEAR) == EchoActivity.RELEASE_YEAR + if (Calendar.getInstance().get(Calendar.YEAR) == EchoConfig.RELEASE_YEAR && Calendar.getInstance().get(Calendar.MONTH) == Calendar.DECEMBER && Calendar.getInstance().get(Calendar.DAY_OF_MONTH) >= 10 - && prefs.getInt(PREF_HIDE_ECHO, 0) != EchoActivity.RELEASE_YEAR) { + && prefs.getInt(PREF_HIDE_ECHO, 0) != EchoConfig.RELEASE_YEAR) { addSection(new EchoSection()); } diff --git a/app/src/main/java/de/danoeh/antennapod/ui/home/sections/EchoSection.java b/app/src/main/java/de/danoeh/antennapod/ui/home/sections/EchoSection.java index 0a03db7f4..28ff05512 100644 --- a/app/src/main/java/de/danoeh/antennapod/ui/home/sections/EchoSection.java +++ b/app/src/main/java/de/danoeh/antennapod/ui/home/sections/EchoSection.java @@ -11,18 +11,17 @@ import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.databinding.HomeSectionEchoBinding; import de.danoeh.antennapod.storage.database.DBReader; import de.danoeh.antennapod.storage.database.StatisticsItem; -import de.danoeh.antennapod.databinding.HomeSectionEchoBinding; import de.danoeh.antennapod.ui.echo.EchoActivity; +import de.danoeh.antennapod.ui.echo.EchoConfig; import de.danoeh.antennapod.ui.home.HomeFragment; 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 EchoSection extends Fragment { private HomeSectionEchoBinding viewBinding; private Disposable disposable; @@ -32,32 +31,21 @@ public class EchoSection extends Fragment { public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { viewBinding = HomeSectionEchoBinding.inflate(inflater); - viewBinding.titleLabel.setText(getString(R.string.antennapod_echo_year, EchoActivity.RELEASE_YEAR)); + viewBinding.titleLabel.setText(getString(R.string.antennapod_echo_year, EchoConfig.RELEASE_YEAR)); viewBinding.echoButton.setOnClickListener(v -> startActivity(new Intent(getContext(), EchoActivity.class))); viewBinding.closeButton.setOnClickListener(v -> hideThisYear()); updateVisibility(); return viewBinding.getRoot(); } - 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, EchoActivity.RELEASE_YEAR); - return date.getTimeInMillis(); - } - private void updateVisibility() { if (disposable != null) { disposable.dispose(); } disposable = Observable.fromCallable( () -> { - DBReader.StatisticsResult statisticsResult = DBReader.getStatistics(false, jan1(), Long.MAX_VALUE); + DBReader.StatisticsResult statisticsResult = DBReader.getStatistics( + false, EchoConfig.jan1(), Long.MAX_VALUE); long totalTime = 0; for (StatisticsItem feedTime : statisticsResult.feedTime) { totalTime += feedTime.timePlayed; @@ -77,7 +65,7 @@ public class EchoSection extends Fragment { void hideThisYear() { getContext().getSharedPreferences(HomeFragment.PREF_NAME, Context.MODE_PRIVATE) - .edit().putInt(HomeFragment.PREF_HIDE_ECHO, EchoActivity.RELEASE_YEAR).apply(); + .edit().putInt(HomeFragment.PREF_HIDE_ECHO, EchoConfig.RELEASE_YEAR).apply(); ((MainActivity) getActivity()).loadFragment(HomeFragment.TAG, null); } } diff --git a/config/spotbugs/exclude.xml b/config/spotbugs/exclude.xml index 5bc7169e9..e4902dc1b 100644 --- a/config/spotbugs/exclude.xml +++ b/config/spotbugs/exclude.xml @@ -26,7 +26,7 @@ - + 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/screens/BaseScreen.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/background/BaseBackground.java similarity index 95% rename from ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/BaseScreen.java rename to 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 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 similarity index 88% rename from ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/BubbleScreen.java rename to 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 similarity index 93% rename from ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/FinalShareScreen.java rename to 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 favoritePodNames, ArrayList favoritePodImages) { + 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(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 similarity index 87% rename from ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/RotatingSquaresScreen.java rename to 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 similarity index 87% rename from ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/StripesScreen.java rename to 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 similarity index 89% rename from ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/WaveformScreen.java rename to 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 similarity index 88% rename from ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/WavesScreen.java rename to 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 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/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. diff --git a/ui/statistics/build.gradle b/ui/statistics/build.gradle index e5e00d32d..1e33a0f0a 100644 --- a/ui/statistics/build.gradle +++ b/ui/statistics/build.gradle @@ -15,6 +15,7 @@ dependencies { implementation project(':storage:database') implementation project(":ui:app-start-intent") implementation project(":ui:common") + implementation project(":ui:echo") annotationProcessor "androidx.annotation:annotation:$annotationVersion" implementation "androidx.appcompat:appcompat:$appcompatVersion" diff --git a/ui/statistics/src/main/java/de/danoeh/antennapod/ui/statistics/StatisticsFragment.java b/ui/statistics/src/main/java/de/danoeh/antennapod/ui/statistics/StatisticsFragment.java index 6dfdc4e8a..1c35b159b 100644 --- a/ui/statistics/src/main/java/de/danoeh/antennapod/ui/statistics/StatisticsFragment.java +++ b/ui/statistics/src/main/java/de/danoeh/antennapod/ui/statistics/StatisticsFragment.java @@ -2,6 +2,7 @@ package de.danoeh.antennapod.ui.statistics; import android.content.Context; import android.content.DialogInterface; +import android.content.Intent; import android.os.Bundle; import android.view.LayoutInflater; import android.view.MenuItem; @@ -21,6 +22,7 @@ import de.danoeh.antennapod.core.dialog.ConfirmationDialog; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.event.StatisticsEvent; import de.danoeh.antennapod.ui.common.PagedToolbarFragment; +import de.danoeh.antennapod.ui.echo.EchoActivity; import de.danoeh.antennapod.ui.statistics.downloads.DownloadStatisticsFragment; import de.danoeh.antennapod.ui.statistics.subscriptions.SubscriptionStatisticsFragment; import de.danoeh.antennapod.ui.statistics.years.YearsStatisticsFragment; @@ -61,6 +63,9 @@ public class StatisticsFragment extends PagedToolbarFragment { toolbar = rootView.findViewById(R.id.toolbar); toolbar.setTitle(getString(R.string.statistics_label)); toolbar.inflateMenu(R.menu.statistics); + if (BuildConfig.DEBUG) { + toolbar.getMenu().findItem(R.id.debug_echo).setVisible(true); + } toolbar.setNavigationOnClickListener(v -> getParentFragmentManager().popBackStack()); viewPager.setAdapter(new StatisticsPagerAdapter(this)); // Give the TabLayout the ViewPager @@ -89,6 +94,8 @@ public class StatisticsFragment extends PagedToolbarFragment { if (item.getItemId() == R.id.statistics_reset) { confirmResetStatistics(); return true; + } else if (item.getItemId() == R.id.debug_echo) { + startActivity(new Intent(getContext(), EchoActivity.class)); } return super.onOptionsItemSelected(item); } diff --git a/ui/statistics/src/main/res/menu/statistics.xml b/ui/statistics/src/main/res/menu/statistics.xml index 4610a7726..9720e6880 100644 --- a/ui/statistics/src/main/res/menu/statistics.xml +++ b/ui/statistics/src/main/res/menu/statistics.xml @@ -11,7 +11,12 @@ android:id="@+id/statistics_filter" android:icon="@drawable/ic_filter" android:title="@string/filter" - custom:showAsAction="ifRoom"> - + custom:showAsAction="ifRoom" /> + +