diff options
Diffstat (limited to 'app/src/main/java/de/danoeh/antennapod')
29 files changed, 1697 insertions, 1403 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java index c1d4bc4fd..829a49a15 100644 --- a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java +++ b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java @@ -57,8 +57,8 @@ public class PodcastApp extends Application { singleton = this; PodDBAdapter.init(this); - UpdateManager.init(this); UserPreferences.init(this); + UpdateManager.init(this); PlaybackPreferences.init(this); NetworkUtils.init(this); EventDistributor.getInstance(); diff --git a/app/src/main/java/de/danoeh/antennapod/UpdateManager.java b/app/src/main/java/de/danoeh/antennapod/UpdateManager.java index b1d7fffc8..0b3c43381 100644 --- a/app/src/main/java/de/danoeh/antennapod/UpdateManager.java +++ b/app/src/main/java/de/danoeh/antennapod/UpdateManager.java @@ -5,14 +5,18 @@ import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.os.Build; import android.util.Log; +import org.antennapod.audio.MediaPlayer; + import java.io.File; import java.util.List; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.FeedImage; import de.danoeh.antennapod.core.feed.FeedItem; +import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; @@ -83,6 +87,11 @@ public class UpdateManager { } }.start(); } + if(oldVersionCode < 1050004) { + if(MediaPlayer.isPrestoLibraryInstalled(context) && Build.VERSION.SDK_INT >= 16) { + UserPreferences.enableSonic(true); + } + } } } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java index 835ac29cd..12bae2f51 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java @@ -1,54 +1,44 @@ package de.danoeh.antennapod.activity; +import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; -import android.content.res.TypedArray; -import android.graphics.drawable.BitmapDrawable; -import android.os.Bundle; +import android.os.Build; +import android.support.annotation.Nullable; +import android.support.design.widget.AppBarLayout; import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentTransaction; -import android.support.v4.app.ListFragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentStatePagerAdapter; +import android.support.v4.view.ViewPager; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.widget.Toolbar; import android.text.TextUtils; import android.util.Log; +import android.util.TypedValue; import android.view.ContextMenu; -import android.view.Gravity; -import android.view.LayoutInflater; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; -import android.view.ViewGroup; -import android.view.animation.LinearInterpolator; -import android.view.animation.ScaleAnimation; import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.ImageButton; import android.widget.ListView; -import android.widget.PopupWindow; -import android.widget.SeekBar; -import android.widget.TextView; -import com.bumptech.glide.Glide; +import com.viewpagerindicator.CirclePageIndicator; -import org.apache.commons.lang3.ArrayUtils; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.adapter.ChapterListAdapter; +import de.danoeh.antennapod.adapter.ChaptersListAdapter; import de.danoeh.antennapod.adapter.NavListAdapter; import de.danoeh.antennapod.core.asynctask.FeedRemover; import de.danoeh.antennapod.core.dialog.ConfirmationDialog; -import de.danoeh.antennapod.core.feed.Chapter; import de.danoeh.antennapod.core.feed.EventDistributor; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.MediaType; -import de.danoeh.antennapod.core.feed.SimpleChapter; -import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.service.playback.PlayerStatus; @@ -57,8 +47,14 @@ import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.util.playback.ExternalMedia; import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.PlaybackController; +import de.danoeh.antennapod.fragment.AddFeedFragment; +import de.danoeh.antennapod.fragment.ChaptersFragment; import de.danoeh.antennapod.fragment.CoverFragment; +import de.danoeh.antennapod.fragment.DownloadsFragment; +import de.danoeh.antennapod.fragment.EpisodesFragment; import de.danoeh.antennapod.fragment.ItemDescriptionFragment; +import de.danoeh.antennapod.fragment.PlaybackHistoryFragment; +import de.danoeh.antennapod.fragment.QueueFragment; import de.danoeh.antennapod.menuhandler.NavDrawerActivity; import de.danoeh.antennapod.preferences.PreferenceController; import rx.Observable; @@ -69,85 +65,40 @@ import rx.schedulers.Schedulers; /** * Activity for playing audio files. */ -public class AudioplayerActivity extends MediaplayerActivity implements ItemDescriptionFragment.ItemDescriptionFragmentCallback, - NavDrawerActivity { +public class AudioplayerActivity extends MediaplayerActivity implements NavDrawerActivity { private static final int POS_COVER = 0; private static final int POS_DESCR = 1; private static final int POS_CHAPTERS = 2; private static final int NUM_CONTENT_FRAGMENTS = 3; - private static final int POS_NONE = -1; final String TAG = "AudioplayerActivity"; private static final String PREFS = "AudioPlayerActivityPreferences"; private static final String PREF_KEY_SELECTED_FRAGMENT_POSITION = "selectedFragmentPosition"; - private static final String PREF_PLAYABLE_ID = "playableId"; + + public static final String[] NAV_DRAWER_TAGS = { + QueueFragment.TAG, + EpisodesFragment.TAG, + DownloadsFragment.TAG, + PlaybackHistoryFragment.TAG, + AddFeedFragment.TAG + }; + + private AtomicBoolean isSetup = new AtomicBoolean(false); private DrawerLayout drawerLayout; private NavListAdapter navAdapter; private ListView navList; - private AdapterView.AdapterContextMenuInfo lastMenuInfo = null; private View navDrawer; private ActionBarDrawerToggle drawerToggle; + private int mPosition = -1; - private Fragment[] detachedFragments; - - private CoverFragment coverFragment; - private ItemDescriptionFragment descriptionFragment; - private ListFragment chapterFragment; - - private Fragment currentlyShownFragment; - private int currentlyShownPosition = -1; - private int lastShownPosition = POS_NONE; - /** - * Used if onResume was called without loadMediaInfo. - */ - private int savedPosition = -1; - - private TextView txtvTitle; - private Button butPlaybackSpeed; - private ImageButton butNavChaptersShownotes; - private ImageButton butShowCover; + private Playable media; + private ViewPager mPager; + private AudioplayerPagerAdapter mPagerAdapter; private Subscription subscription; - private PopupWindow popupWindow; - - private void resetFragmentView() { - FragmentTransaction fT = getSupportFragmentManager().beginTransaction(); - - if (coverFragment != null) { - Log.d(TAG, "Removing cover fragment"); - fT.remove(coverFragment); - } - if (descriptionFragment != null) { - Log.d(TAG, "Removing description fragment"); - fT.remove(descriptionFragment); - } - if (chapterFragment != null) { - Log.d(TAG, "Removing chapter fragment"); - fT.remove(chapterFragment); - } - if (currentlyShownFragment != null) { - Log.d(TAG, "Removing currently shown fragment"); - fT.remove(currentlyShownFragment); - } - for (int i = 0; i < detachedFragments.length; i++) { - Fragment f = detachedFragments[i]; - if (f != null) { - Log.d(TAG, "Removing detached fragment"); - fT.remove(f); - } - } - fT.commit(); - currentlyShownFragment = null; - coverFragment = null; - descriptionFragment = null; - chapterFragment = null; - currentlyShownPosition = -1; - detachedFragments = new Fragment[NUM_CONTENT_FRAGMENTS]; - } - @Override protected void onStop() { super.onStop(); @@ -156,36 +107,33 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc subscription.unsubscribe(); } EventDistributor.getInstance().unregister(contentUpdate); + saveCurrentFragment(); } @Override - protected void chooseTheme() { - setTheme(UserPreferences.getNoTitleTheme()); + public void onDestroy() { + super.onDestroy(); + // don't risk creating memory leaks + navAdapter = null; + drawerToggle = null; + mPager = null; + mPagerAdapter = null; } @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - detachedFragments = new Fragment[NUM_CONTENT_FRAGMENTS]; + protected void chooseTheme() { + setTheme(UserPreferences.getNoTitleTheme()); } - private void savePreferences() { + private void saveCurrentFragment() { + if(mPager == null) { + return; + } Log.d(TAG, "Saving preferences"); SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE); - SharedPreferences.Editor editor = prefs.edit(); - if (currentlyShownPosition >= 0 && controller != null - && controller.getMedia() != null) { - editor.putInt(PREF_KEY_SELECTED_FRAGMENT_POSITION, - currentlyShownPosition); - editor.putString(PREF_PLAYABLE_ID, controller.getMedia() - .getIdentifier().toString()); - } else { - editor.putInt(PREF_KEY_SELECTED_FRAGMENT_POSITION, -1); - editor.putString(PREF_PLAYABLE_ID, ""); - } - editor.commit(); - - savedPosition = currentlyShownPosition; + prefs.edit() + .putInt(PREF_KEY_SELECTED_FRAGMENT_POSITION, mPager.getCurrentItem()) + .commit(); } @Override @@ -194,53 +142,11 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc drawerToggle.onConfigurationChanged(newConfig); } - @Override - protected void onSaveInstanceState(Bundle outState) { - // super.onSaveInstanceState(outState); would cause crash - Log.d(TAG, "onSaveInstanceState"); - } - - @Override - protected void onPause() { - savePreferences(); - resetFragmentView(); - super.onPause(); - } - - @Override - protected void onRestoreInstanceState(Bundle savedInstanceState) { - super.onRestoreInstanceState(savedInstanceState); - restoreFromPreferences(); - } - - /** - * Tries to restore the selected fragment position from the Activity's - * preferences. - * - * @return true if restoreFromPrefernces changed the activity's state - */ - private boolean restoreFromPreferences() { + private void loadLastFragment() { Log.d(TAG, "Restoring instance state"); SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE); - int savedPosition = prefs.getInt(PREF_KEY_SELECTED_FRAGMENT_POSITION, - -1); - String playableId = prefs.getString(PREF_PLAYABLE_ID, ""); - - if (savedPosition != -1 - && controller != null - && controller.getMedia() != null - && controller.getMedia().getIdentifier().toString() - .equals(playableId)) { - switchToFragment(savedPosition); - return true; - } else if (controller == null || controller.getMedia() == null) { - Log.d(TAG, "Couldn't restore from preferences: controller or media was null"); - } else { - Log.d(TAG, "Couldn't restore from preferences: savedPosition was -1 or saved identifier and playable identifier didn't match.\nsavedPosition: " - + savedPosition + ", id: " + playableId); - - } - return false; + int lastPosition = prefs.getInt(PREF_KEY_SELECTED_FRAGMENT_POSITION, -1); + mPager.setCurrentItem(lastPosition); } @Override @@ -260,8 +166,9 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc true); startService(launchIntent); } - if (savedPosition != -1) { - switchToFragment(savedPosition); + if(mPagerAdapter != null && controller != null && controller.getMedia() != media) { + media = controller.getMedia(); + mPagerAdapter.onMediaChanged(media); } EventDistributor.getInstance().register(contentUpdate); @@ -292,147 +199,28 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc @Override protected void clearStatusMsg() { // TODO Hide progress bar here - } - /** - * Changes the currently displayed fragment. - * - * @param pos Must be POS_COVER, POS_DESCR, or POS_CHAPTERS - */ - private void switchToFragment(int pos) { - Log.d(TAG, "Switching contentView to position " + pos); - if (currentlyShownPosition != pos && controller != null) { - Playable media = controller.getMedia(); - if (media != null) { - FragmentTransaction ft = getSupportFragmentManager() - .beginTransaction(); - if (currentlyShownFragment != null) { - detachedFragments[currentlyShownPosition] = currentlyShownFragment; - ft.detach(currentlyShownFragment); - } - switch (pos) { - case POS_COVER: - if (coverFragment == null) { - Log.i(TAG, "Using new coverfragment"); - coverFragment = CoverFragment.newInstance(media); - } - currentlyShownFragment = coverFragment; - break; - case POS_DESCR: - if (descriptionFragment == null) { - descriptionFragment = ItemDescriptionFragment - .newInstance(media, true, true); - } - currentlyShownFragment = descriptionFragment; - break; - case POS_CHAPTERS: - if (chapterFragment == null) { - chapterFragment = new ListFragment() { - - @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - // add padding - final ListView lv = getListView(); - lv.setClipToPadding(false); - final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding); - lv.setPadding(0, vertPadding, 0, vertPadding); - } - }; - chapterFragment.setListAdapter(new ChapterListAdapter( - AudioplayerActivity.this, 0, media - .getChapters(), media, position -> { - Chapter chapter = (Chapter) - chapterFragment.getListAdapter().getItem(position); - controller.seekToChapter(chapter); - } - )); - } - currentlyShownFragment = chapterFragment; - break; - } - if (currentlyShownFragment != null) { - lastShownPosition = currentlyShownPosition; - currentlyShownPosition = pos; - if (detachedFragments[pos] != null) { - Log.d(TAG, "Reattaching fragment at position " + pos); - ft.attach(detachedFragments[pos]); - } else { - ft.add(R.id.contentView, currentlyShownFragment); - } - ft.disallowAddToBackStack(); - ft.commit(); - updateNavButtonDrawable(); - } - } - } - } - - /** - * Switches to the fragment that was displayed before the current one or the description fragment - * if no fragment was previously displayed. - */ - public void switchToLastFragment() { - if (lastShownPosition != POS_NONE) { - switchToFragment(lastShownPosition); - } else { - switchToFragment(POS_DESCR); - } - } - - private void updateNavButtonDrawable() { - - final int[] buttonTexts = new int[]{R.string.show_shownotes_label, - R.string.show_chapters_label}; - - final TypedArray drawables = obtainStyledAttributes(new int[]{ - R.attr.navigation_shownotes, R.attr.navigation_chapters}); - final Playable media = controller.getMedia(); - if (butNavChaptersShownotes != null && butShowCover != null && media != null) { - - butNavChaptersShownotes.setTag(R.id.imageloader_key, null); - setNavButtonVisibility(); - switch (currentlyShownPosition) { - case POS_COVER: - butShowCover.setVisibility(View.GONE); - if (lastShownPosition == POS_CHAPTERS) { - butNavChaptersShownotes.setImageDrawable(drawables.getDrawable(1)); - butNavChaptersShownotes.setContentDescription(getString(buttonTexts[1])); - } else { - butNavChaptersShownotes.setImageDrawable(drawables.getDrawable(0)); - butNavChaptersShownotes.setContentDescription(getString(buttonTexts[0])); - } - break; - case POS_DESCR: - butShowCover.setVisibility(View.VISIBLE); - butNavChaptersShownotes.setImageDrawable(drawables.getDrawable(1)); - butNavChaptersShownotes.setContentDescription(getString(buttonTexts[1])); - break; - case POS_CHAPTERS: - butShowCover.setVisibility(View.VISIBLE); - butNavChaptersShownotes.setImageDrawable(drawables.getDrawable(0)); - butNavChaptersShownotes.setContentDescription(getString(buttonTexts[0])); - break; - } - } - drawables.recycle(); - } @Override protected void setupGUI() { + if(isSetup.getAndSet(true)) { + return; + } super.setupGUI(); - resetFragmentView(); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setTitle(""); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + findViewById(R.id.shadow).setVisibility(View.GONE); + AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.appBar); + float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getResources().getDisplayMetrics()); + appBarLayout.setElevation(px); + } drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); navList = (ListView) findViewById(R.id.nav_list); navDrawer = findViewById(R.id.nav_layout); - butPlaybackSpeed = (Button) findViewById(R.id.butPlaybackSpeed); - butNavChaptersShownotes = (ImageButton) findViewById(R.id.butNavChaptersShownotes); - butShowCover = (ImageButton) findViewById(R.id.butCover); - txtvTitle = (TextView) findViewById(R.id.txtvTitle); drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.drawer_open, R.string.drawer_close); drawerToggle.setDrawerIndicatorEnabled(false); @@ -450,6 +238,15 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc } drawerLayout.closeDrawer(navDrawer); }); + navList.setOnItemLongClickListener((parent, view, position, id) -> { + if (position < navAdapter.getTags().size()) { + showDrawerPreferencesDialog(); + return true; + } else { + mPosition = position; + return false; + } + }); registerForContextMenu(navList); drawerToggle.syncState(); @@ -458,128 +255,13 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc startActivity(new Intent(AudioplayerActivity.this, PreferenceController.getPreferenceActivity())); }); - butNavChaptersShownotes.setOnClickListener(v -> { - if (currentlyShownPosition == POS_CHAPTERS) { - switchToFragment(POS_DESCR); - } else if (currentlyShownPosition == POS_DESCR) { - switchToFragment(POS_CHAPTERS); - } else if (currentlyShownPosition == POS_COVER) { - switchToLastFragment(); - } - }); - - butShowCover.setOnClickListener(v -> switchToFragment(POS_COVER)); - - butPlaybackSpeed.setOnClickListener(v -> { - if (controller != null && controller.canSetPlaybackSpeed()) { - String[] availableSpeeds = UserPreferences - .getPlaybackSpeedArray(); - String currentSpeed = UserPreferences.getPlaybackSpeed(); - - // Provide initial value in case the speed list has changed - // out from under us - // and our current speed isn't in the new list - String newSpeed; - if (availableSpeeds.length > 0) { - newSpeed = availableSpeeds[0]; - } else { - newSpeed = "1.0"; - } - - for (int i = 0; i < availableSpeeds.length; i++) { - if (availableSpeeds[i].equals(currentSpeed)) { - if (i == availableSpeeds.length - 1) { - newSpeed = availableSpeeds[0]; - } else { - newSpeed = availableSpeeds[i + 1]; - } - break; - } - } - UserPreferences.setPlaybackSpeed(newSpeed); - controller.setPlaybackSpeed(Float.parseFloat(newSpeed)); - } - }); - - butPlaybackSpeed.setOnLongClickListener(v -> { - - String[] availableSpeeds = getResources().getStringArray(R.array.playback_speed_values); - String currentSpeed = UserPreferences.getPlaybackSpeed(); - - LayoutInflater inflater = getLayoutInflater(); - View popupView = inflater.inflate(R.layout.choose_speed_dialog, null); - TextView txtvSelectedSpeed = (TextView) popupView.findViewById(R.id.txtvSelectedSpeed); - SeekBar sbSelectSpeed = (SeekBar) popupView.findViewById(R.id.sbSelectSpeed); - - txtvSelectedSpeed.setText(currentSpeed); - int progress = ArrayUtils.indexOf(availableSpeeds, currentSpeed); - int max = Math.max(progress, ArrayUtils.indexOf(availableSpeeds, "2.50")); - sbSelectSpeed.setMax(max); - sbSelectSpeed.setProgress(progress); - sbSelectSpeed.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - txtvSelectedSpeed.setText(availableSpeeds[progress]); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - String selectedSpeed = availableSpeeds[sbSelectSpeed.getProgress()]; - UserPreferences.setPlaybackSpeed(selectedSpeed); - controller.setPlaybackSpeed(Float.parseFloat(selectedSpeed)); - if (popupWindow != null && popupWindow.isShowing()) { - popupWindow.dismiss(); - } - ScaleAnimation anim = new ScaleAnimation(1.0f, 1.33f, 1.0f, 1.33f, - butPlaybackSpeed.getWidth()/2, butPlaybackSpeed.getHeight()/2); - anim.setDuration(150); - anim.setRepeatMode(ScaleAnimation.REVERSE); - anim.setRepeatCount(1); - anim.setInterpolator(new LinearInterpolator()); - butPlaybackSpeed.startAnimation(anim); - } - }); - popupWindow = new PopupWindow(popupView, - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT, - true); - popupWindow.setBackgroundDrawable(new BitmapDrawable()); - popupWindow.setOutsideTouchable(true); - popupWindow.showAtLocation(popupView, Gravity.CENTER, 0, 0); - return true; - }); - } - - private void setNavButtonVisibility() { - if (butNavChaptersShownotes != null) { - if (controller != null) { - Playable media = controller.getMedia(); - if (media != null) { - if (media.getChapters() != null || currentlyShownPosition == POS_COVER) { - butNavChaptersShownotes.setVisibility(View.VISIBLE); - return; - } - } - } - butNavChaptersShownotes.setVisibility(View.GONE); - } - - } - - @Override - protected void onPlaybackSpeedChange() { - super.onPlaybackSpeedChange(); - updateButPlaybackSpeed(); - } - - private void updateButPlaybackSpeed() { - if (controller != null && controller.canSetPlaybackSpeed()) { - butPlaybackSpeed.setText(UserPreferences.getPlaybackSpeed()); - } + mPager = (ViewPager) findViewById(R.id.pager); + mPagerAdapter = new AudioplayerPagerAdapter(getSupportFragmentManager()); + mPager.setAdapter(mPagerAdapter); + CirclePageIndicator pageIndicator = (CirclePageIndicator) findViewById(R.id.page_indicator); + pageIndicator.setViewPager(mPager); + loadLastFragment(); + mPager.onSaveInstanceState(); } @Override @@ -593,49 +275,20 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc if (!super.loadMediaInfo()) { return false; } - final Playable media = controller.getMedia(); - if (media == null) { - return false; - } - txtvTitle.setText(media.getEpisodeTitle()); - getSupportActionBar().setTitle(""); - Glide.with(this) - .load(media.getImageUri()) - .placeholder(R.color.light_gray) - .error(R.color.light_gray) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .fitCenter() - .dontAnimate() - .into(butShowCover); - - setNavButtonVisibility(); - - if (currentlyShownPosition == -1) { - if (!restoreFromPreferences()) { - switchToFragment(POS_COVER); - } - } - if (currentlyShownFragment instanceof AudioplayerContentFragment) { - ((AudioplayerContentFragment) currentlyShownFragment) - .onDataSetChanged(media); + if(controller.getMedia() != media) { + media = controller.getMedia(); + mPagerAdapter.onMediaChanged(media); } - - if (controller == null - || !controller.canSetPlaybackSpeed()) { - butPlaybackSpeed.setVisibility(View.GONE); - } else { - butPlaybackSpeed.setVisibility(View.VISIBLE); - } - - updateButPlaybackSpeed(); return true; } public void notifyMediaPositionChanged() { - if (chapterFragment != null) { - ArrayAdapter<SimpleChapter> adapter = (ArrayAdapter<SimpleChapter>) chapterFragment - .getListAdapter(); - adapter.notifyDataSetChanged(); + ChaptersFragment chaptersFragment = mPagerAdapter.getChaptersFragment(); + if(chaptersFragment != null) { + ChaptersListAdapter adapter = (ChaptersListAdapter) chaptersFragment.getListAdapter(); + if (adapter != null) { + adapter.notifyDataSetChanged(); + } } } @@ -659,7 +312,6 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc clearStatusMsg(); } - @Override public PlaybackController getPlaybackController() { return controller; } @@ -669,10 +321,6 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc return drawerLayout != null && navDrawer != null && drawerLayout.isDrawerOpen(navDrawer); } - public interface AudioplayerContentFragment { - void onDataSetChanged(Playable media); - } - @Override protected int getContentViewResourceId() { return R.layout.audioplayer_activity; @@ -704,24 +352,15 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset()); menu.setHeaderTitle(feed.getTitle()); // episodes are not loaded, so we cannot check if the podcast has new or unplayed ones! - - // we may need to reference this elsewhere... - lastMenuInfo = (AdapterView.AdapterContextMenuInfo) menuInfo; } @Override public boolean onContextItemSelected(MenuItem item) { - AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); - - if(menuInfo == null) { - menuInfo = lastMenuInfo; - } - - if(menuInfo.targetView.getParent() instanceof ListView == false - || ((ListView)menuInfo.targetView.getParent()).getId() != R.id.nav_list) { + final int position = mPosition; + mPosition = -1; // reset + if(position < 0) { return false; } - int position = menuInfo.position; Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset()); switch(item.getItemId()) { case R.id.mark_all_seen_item: @@ -772,11 +411,43 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc public void onBackPressed() { if(isDrawerOpen()) { drawerLayout.closeDrawer(navDrawer); - } else { + } else if (mPager.getCurrentItem() == 0) { + // If the user is currently looking at the first step, allow the system to handle the + // Back button. This calls finish() on this activity and pops the back stack. super.onBackPressed(); + } else { + // Otherwise, select the previous step. + mPager.setCurrentItem(mPager.getCurrentItem() - 1); } } + public void showDrawerPreferencesDialog() { + final List<String> hiddenDrawerItems = UserPreferences.getHiddenDrawerItems(); + String[] navLabels = new String[NAV_DRAWER_TAGS.length]; + final boolean[] checked = new boolean[NAV_DRAWER_TAGS.length]; + for (int i = 0; i < NAV_DRAWER_TAGS.length; i++) { + String tag = NAV_DRAWER_TAGS[i]; + navLabels[i] = navAdapter.getLabel(tag); + if (!hiddenDrawerItems.contains(tag)) { + checked[i] = true; + } + } + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.drawer_preferences); + builder.setMultiChoiceItems(navLabels, checked, (dialog, which, isChecked) -> { + if (isChecked) { + hiddenDrawerItems.remove(NAV_DRAWER_TAGS[which]); + } else { + hiddenDrawerItems.add(NAV_DRAWER_TAGS[which]); + } + }); + builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> { + UserPreferences.setHiddenDrawerItems(hiddenDrawerItems); + }); + builder.setNegativeButton(R.string.cancel_label, null); + builder.create().show(); + } private DBReader.NavDrawerData navDrawerData; @@ -852,4 +523,65 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc } }; + public interface AudioplayerContentFragment { + void onMediaChanged(Playable media); + } + + private class AudioplayerPagerAdapter extends FragmentStatePagerAdapter { + + public AudioplayerPagerAdapter(FragmentManager fm) { + super(fm); + } + + private CoverFragment coverFragment; + private ItemDescriptionFragment itemDescriptionFragment; + private ChaptersFragment chaptersFragment; + + public void onMediaChanged(Playable media) { + if(coverFragment != null) { + coverFragment.onMediaChanged(media); + } + if(itemDescriptionFragment != null) { + itemDescriptionFragment.onMediaChanged(media); + } + if(chaptersFragment != null) { + chaptersFragment.onMediaChanged(media); + } + } + + @Nullable + public ChaptersFragment getChaptersFragment() { + return chaptersFragment; + } + + @Override + public Fragment getItem(int position) { + Log.d(TAG, "getItem(" + position + ")"); + switch (position) { + case POS_COVER: + if(coverFragment == null) { + coverFragment = CoverFragment.newInstance(media); + } + return coverFragment; + case POS_DESCR: + if(itemDescriptionFragment == null) { + itemDescriptionFragment = ItemDescriptionFragment.newInstance(media, true, true); + } + return itemDescriptionFragment; + case POS_CHAPTERS: + if(chaptersFragment == null) { + chaptersFragment = ChaptersFragment.newInstance(media, controller); + } + return chaptersFragment; + default: + return null; + } + } + + @Override + public int getCount() { + return NUM_CONTENT_FRAGMENTS; + } + } + } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java index 80883e4ae..edb973a0c 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java @@ -18,9 +18,9 @@ import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.CheckBox; -import android.widget.CompoundButton; import android.widget.EditText; import android.widget.ImageView; +import android.widget.RadioButton; import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; @@ -32,6 +32,7 @@ import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.dialog.ConfirmationDialog; import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator; import de.danoeh.antennapod.core.feed.Feed; +import de.danoeh.antennapod.core.feed.FeedFilter; import de.danoeh.antennapod.core.feed.FeedPreferences; import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.preferences.UserPreferences; @@ -61,8 +62,13 @@ public class FeedInfoActivity extends ActionBarActivity { private TextView txtvUrl; private EditText etxtUsername; private EditText etxtPassword; + private EditText etxtFilterText; + private RadioButton rdoFilterInclude; + private RadioButton rdoFilterExclude; private CheckBox cbxAutoDownload; + private CheckBox cbxKeepUpdated; private Spinner spnAutoDelete; + private boolean filterInclude = true; private final View.OnClickListener copyUrlToClipboard = new View.OnClickListener() { @Override @@ -100,9 +106,21 @@ public class FeedInfoActivity extends ActionBarActivity { txtvAuthor = (TextView) findViewById(R.id.txtvAuthor); txtvUrl = (TextView) findViewById(R.id.txtvUrl); cbxAutoDownload = (CheckBox) findViewById(R.id.cbxAutoDownload); + cbxKeepUpdated = (CheckBox) findViewById(R.id.cbxKeepUpdated); spnAutoDelete = (Spinner) findViewById(R.id.spnAutoDelete); etxtUsername = (EditText) findViewById(R.id.etxtUsername); etxtPassword = (EditText) findViewById(R.id.etxtPassword); + etxtFilterText = (EditText) findViewById(R.id.etxtEpisodeFilterText); + rdoFilterInclude = (RadioButton) findViewById(R.id.radio_filter_include); + rdoFilterInclude.setOnClickListener(v -> { + filterInclude = true; + filterTextChanged = true; + }); + rdoFilterExclude = (RadioButton) findViewById(R.id.radio_filter_exclude); + rdoFilterExclude.setOnClickListener(v -> { + filterInclude = false; + filterTextChanged = true; + }); txtvUrl.setOnClickListener(copyUrlToClipboard); @@ -151,15 +169,18 @@ public class FeedInfoActivity extends ActionBarActivity { cbxAutoDownload.setEnabled(UserPreferences.isEnableAutodownload()); cbxAutoDownload.setChecked(prefs.getAutoDownload()); - cbxAutoDownload.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { - feed.getPreferences().setAutoDownload(checked); - feed.savePreferences(FeedInfoActivity.this); - ApplyToEpisodesDialog dialog = new ApplyToEpisodesDialog(FeedInfoActivity.this, - feed, checked); - dialog.createNewDialog().show(); - } + cbxAutoDownload.setOnCheckedChangeListener((compoundButton, checked) -> { + feed.getPreferences().setAutoDownload(checked); + feed.savePreferences(FeedInfoActivity.this); + updateAutoDownloadSettings(); + ApplyToEpisodesDialog dialog = new ApplyToEpisodesDialog(FeedInfoActivity.this, + feed, checked); + dialog.createNewDialog().show(); + }); + cbxKeepUpdated.setChecked(prefs.getKeepUpdated()); + cbxKeepUpdated.setOnCheckedChangeListener((compoundButton, checked) -> { + feed.getPreferences().setKeepUpdated(checked); + feed.savePreferences(FeedInfoActivity.this); }); spnAutoDelete.setOnItemSelectedListener(new OnItemSelectedListener() { @Override @@ -184,6 +205,7 @@ public class FeedInfoActivity extends ActionBarActivity { feed.getPreferences().setAutoDeleteAction(auto_delete_action);// p autoDeleteChanged = true; } + @Override public void onNothingSelected(AdapterView<?> parent) { // Another interface callback @@ -197,8 +219,25 @@ public class FeedInfoActivity extends ActionBarActivity { etxtUsername.addTextChangedListener(authTextWatcher); etxtPassword.addTextChangedListener(authTextWatcher); - supportInvalidateOptionsMenu(); + FeedFilter filter = prefs.getFilter(); + if (filter.includeOnly()) { + etxtFilterText.setText(filter.getIncludeFilter()); + rdoFilterInclude.setChecked(true); + rdoFilterExclude.setChecked(false); + } else if (filter.excludeOnly()) { + etxtFilterText.setText(filter.getExcludeFilter()); + rdoFilterInclude.setChecked(false); + rdoFilterExclude.setChecked(true); + } else { + Log.d(TAG, "No filter set"); + rdoFilterInclude.setChecked(false); + rdoFilterExclude.setChecked(false); + etxtFilterText.setText(""); + } + etxtFilterText.addTextChangedListener(filterTextWatcher); + supportInvalidateOptionsMenu(); + updateAutoDownloadSettings(); } else { Log.e(TAG, "Activity was started with invalid arguments"); } @@ -227,6 +266,25 @@ public class FeedInfoActivity extends ActionBarActivity { } }; + private boolean filterTextChanged = false; + + private TextWatcher filterTextWatcher = new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + } + + @Override + public void afterTextChanged(Editable s) { + filterTextChanged = true; + } + }; + @Override protected void onPause() { super.onPause(); @@ -237,11 +295,24 @@ public class FeedInfoActivity extends ActionBarActivity { prefs.setUsername(etxtUsername.getText().toString()); prefs.setPassword(etxtPassword.getText().toString()); } - if (authInfoChanged || autoDeleteChanged) { + if (filterTextChanged) { + Log.d(TAG, "Filter info changed, saving..."); + String filterText = etxtFilterText.getText().toString(); + String includeString = ""; + String excludeString = ""; + if (filterInclude) { + includeString = filterText; + } else { + excludeString = filterText; + } + prefs.setFilter(new FeedFilter(includeString, excludeString)); + } + if (authInfoChanged || autoDeleteChanged || filterTextChanged) { DBWriter.setFeedPreferences(prefs); } authInfoChanged = false; autoDeleteChanged = false; + filterTextChanged = false; } } @@ -282,6 +353,15 @@ public class FeedInfoActivity extends ActionBarActivity { } } + private void updateAutoDownloadSettings() { + if (feed != null && feed.getPreferences() != null) { + boolean enabled = feed.getPreferences().getAutoDownload() && UserPreferences.isEnableAutodownload(); + rdoFilterInclude.setEnabled(enabled); + rdoFilterExclude.setEnabled(enabled); + etxtFilterText.setEnabled(enabled); + } + } + private class ApplyToEpisodesDialog extends ConfirmationDialog { private final Feed feed; diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java index 2181e7d2d..f96764b42 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java @@ -224,6 +224,9 @@ public class MainActivity extends AppCompatActivity implements NavDrawerActivity if (prefs.getBoolean(PREF_IS_FIRST_LAUNCH, true)) { new Handler().postDelayed(() -> drawerLayout.openDrawer(navDrawer), 1500); + // for backward compatibility, we only change defaults for fresh installs + UserPreferences.setUpdateInterval(12); + SharedPreferences.Editor edit = prefs.edit(); edit.putBoolean(PREF_IS_FIRST_LAUNCH, false); edit.commit(); diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java index bfbf3d86f..bdc210651 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java @@ -1,34 +1,44 @@ package de.danoeh.antennapod.activity; import android.annotation.TargetApi; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; +import android.content.res.TypedArray; +import android.graphics.Color; import android.graphics.PixelFormat; import android.media.AudioManager; import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.support.v7.app.ActionBarActivity; +import android.support.v4.view.ViewCompat; import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; +import android.widget.Button; +import android.widget.CheckBox; import android.widget.ImageButton; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; +import android.widget.Toast; import com.afollestad.materialdialogs.MaterialDialog; import com.bumptech.glide.Glide; +import com.joanzapata.iconify.IconDrawable; +import com.joanzapata.iconify.fonts.FontAwesomeIcons; import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.playback.PlaybackService; +import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBTasks; +import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.util.Converter; import de.danoeh.antennapod.core.util.ShareUtils; import de.danoeh.antennapod.core.util.StorageUtils; @@ -36,13 +46,17 @@ import de.danoeh.antennapod.core.util.playback.MediaPlayerError; import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.PlaybackController; import de.danoeh.antennapod.dialog.SleepTimerDialog; +import de.danoeh.antennapod.dialog.VariableSpeedDialog; +import rx.Observable; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; + /** * Provides general features which are both needed for playing audio and video * files. */ -public abstract class MediaplayerActivity extends ActionBarActivity - implements OnSeekBarChangeListener { +public abstract class MediaplayerActivity extends AppCompatActivity implements OnSeekBarChangeListener { private static final String TAG = "MediaplayerActivity"; private static final String PREFS = "MediaPlayerActivityPreferences"; private static final String PREF_SHOW_TIME_LEFT = "showTimeLeft"; @@ -52,12 +66,17 @@ public abstract class MediaplayerActivity extends ActionBarActivity protected TextView txtvPosition; protected TextView txtvLength; protected SeekBar sbPosition; - protected ImageButton butPlay; + protected Button butPlaybackSpeed; protected ImageButton butRev; - protected boolean showTimeLeft = false; protected TextView txtvRev; + protected ImageButton butPlay; protected ImageButton butFF; protected TextView txtvFF; + protected ImageButton butSkip; + + protected boolean showTimeLeft = false; + + private boolean isFavorite = false; private PlaybackController newPlaybackController() { return new PlaybackController(this, false) { @@ -157,7 +176,7 @@ public abstract class MediaplayerActivity extends ActionBarActivity } protected void onPlaybackSpeedChange() { - + updateButPlaybackSpeed(); } protected void onServiceQueried() { @@ -238,12 +257,6 @@ public abstract class MediaplayerActivity extends ActionBarActivity } } - @Override - protected void onDestroy() { - super.onDestroy(); - Log.d(TAG, "onDestroy()"); - } - @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) @Override public void onTrimMemory(int level) { @@ -268,7 +281,7 @@ public abstract class MediaplayerActivity extends ActionBarActivity @Override public boolean onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); - if(controller == null) { + if (controller == null) { return false; } Playable media = controller.getMedia(); @@ -294,19 +307,35 @@ public abstract class MediaplayerActivity extends ActionBarActivity menu.findItem(R.id.share_item).setVisible(hasWebsiteLink || isItemAndHasLink || isItemHasDownloadLink); - menu.findItem(R.id.skip_episode_item).setVisible(media != null); + menu.findItem(R.id.add_to_favorites_item).setVisible(false); + menu.findItem(R.id.remove_from_favorites_item).setVisible(false); + if(media != null && media instanceof FeedMedia) { + menu.findItem(R.id.add_to_favorites_item).setVisible(!isFavorite); + menu.findItem(R.id.remove_from_favorites_item).setVisible(isFavorite); + } boolean sleepTimerSet = controller.sleepTimerActive(); boolean sleepTimerNotSet = controller.sleepTimerNotActive(); menu.findItem(R.id.set_sleeptimer_item).setVisible(sleepTimerNotSet); menu.findItem(R.id.disable_sleeptimer_item).setVisible(sleepTimerSet); + if (this instanceof AudioplayerActivity) { + int[] attrs = {R.attr.action_bar_icon_color}; + TypedArray ta = obtainStyledAttributes(UserPreferences.getTheme(), attrs); + int textColor = ta.getColor(0, Color.GRAY); + ta.recycle(); + menu.findItem(R.id.audio_controls).setIcon(new IconDrawable(this, + FontAwesomeIcons.fa_sliders).color(textColor).actionBarSize()); + } else { + menu.findItem(R.id.audio_controls).setVisible(false); + } + return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { - if(controller == null) { + if (controller == null) { return false; } Playable media = controller.getMedia(); @@ -317,84 +346,235 @@ public abstract class MediaplayerActivity extends ActionBarActivity | Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); return true; - } else if (media != null) { - switch (item.getItemId()) { - case R.id.disable_sleeptimer_item: - if (controller.serviceAvailable()) { - - MaterialDialog.Builder stDialog = new MaterialDialog.Builder(this); - stDialog.title(R.string.sleep_timer_label); - stDialog.content(getString(R.string.time_left_label) - + Converter.getDurationStringLong((int) controller - .getSleepTimerTimeLeft())); - stDialog.positiveText(R.string.disable_sleeptimer_label); - stDialog.negativeText(R.string.cancel_label); - stDialog.callback(new MaterialDialog.ButtonCallback() { + } else { + if (media != null) { + switch (item.getItemId()) { + case R.id.add_to_favorites_item: + if(media instanceof FeedMedia) { + FeedItem feedItem = ((FeedMedia)media).getItem(); + if(feedItem != null) { + DBWriter.addFavoriteItem(feedItem); + isFavorite = true; + invalidateOptionsMenu(); + Toast.makeText(this, R.string.added_to_favorites, Toast.LENGTH_SHORT) + .show(); + } + } + break; + case R.id.remove_from_favorites_item: + if(media instanceof FeedMedia) { + FeedItem feedItem = ((FeedMedia)media).getItem(); + if(feedItem != null) { + DBWriter.removeFavoriteItem(feedItem); + isFavorite = false; + invalidateOptionsMenu(); + Toast.makeText(this, R.string.removed_from_favorites, Toast.LENGTH_SHORT) + .show(); + } + } + break; + case R.id.disable_sleeptimer_item: + if (controller.serviceAvailable()) { + + MaterialDialog.Builder stDialog = new MaterialDialog.Builder(this); + stDialog.title(R.string.sleep_timer_label); + stDialog.content(getString(R.string.time_left_label) + + Converter.getDurationStringLong((int) controller + .getSleepTimerTimeLeft())); + stDialog.positiveText(R.string.disable_sleeptimer_label); + stDialog.negativeText(R.string.cancel_label); + stDialog.callback(new MaterialDialog.ButtonCallback() { + @Override + public void onPositive(MaterialDialog dialog) { + dialog.dismiss(); + controller.disableSleepTimer(); + } + + @Override + public void onNegative(MaterialDialog dialog) { + dialog.dismiss(); + } + }); + stDialog.build().show(); + } + break; + case R.id.set_sleeptimer_item: + if (controller.serviceAvailable()) { + SleepTimerDialog td = new SleepTimerDialog(this) { + @Override + public void onTimerSet(long millis, boolean shakeToReset, boolean vibrate) { + controller.setSleepTimer(millis, shakeToReset, vibrate); + } + }; + td.createNewDialog().show(); + } + break; + case R.id.audio_controls: + MaterialDialog dialog = new MaterialDialog.Builder(this) + .title(R.string.audio_controls) + .customView(R.layout.audio_controls, false) + .neutralText(R.string.close_label) + .onNeutral((dialog1, which) -> { + final SeekBar left = (SeekBar) dialog1.findViewById(R.id.volume_left); + final SeekBar right = (SeekBar) dialog1.findViewById(R.id.volume_right); + UserPreferences.setVolume(left.getProgress(), right.getProgress()); + }) + .show(); + final SeekBar barPlaybackSpeed = (SeekBar) dialog.findViewById(R.id.playback_speed); + final Button butDecSpeed = (Button) dialog.findViewById(R.id.butDecSpeed); + butDecSpeed.setOnClickListener(v -> { + if(controller != null && controller.canSetPlaybackSpeed()) { + barPlaybackSpeed.setProgress(barPlaybackSpeed.getProgress() - 2); + } else { + VariableSpeedDialog.showGetPluginDialog(this); + } + }); + final Button butIncSpeed = (Button) dialog.findViewById(R.id.butIncSpeed); + butIncSpeed.setOnClickListener(v -> { + if(controller != null && controller.canSetPlaybackSpeed()) { + barPlaybackSpeed.setProgress(barPlaybackSpeed.getProgress() + 2); + } else { + VariableSpeedDialog.showGetPluginDialog(this); + } + }); + + final TextView txtvPlaybackSpeed = (TextView) dialog.findViewById(R.id.txtvPlaybackSpeed); + float currentSpeed = 1.0f; + try { + currentSpeed = Float.parseFloat(UserPreferences.getPlaybackSpeed()); + } catch (NumberFormatException e) { + Log.e(TAG, Log.getStackTraceString(e)); + UserPreferences.setPlaybackSpeed(String.valueOf(currentSpeed)); + } + + txtvPlaybackSpeed.setText(String.format("%.2fx", currentSpeed)); + barPlaybackSpeed.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { @Override - public void onPositive(MaterialDialog dialog) { - dialog.dismiss(); - controller.disableSleepTimer(); + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + if(controller != null && controller.canSetPlaybackSpeed()) { + float playbackSpeed = (progress + 10) / 20.0f; + controller.setPlaybackSpeed(playbackSpeed); + String speed = String.format("%.2f", playbackSpeed); + UserPreferences.setPlaybackSpeed(speed); + txtvPlaybackSpeed.setText(speed + "x"); + } else if(fromUser) { + float speed = Float.valueOf(UserPreferences.getPlaybackSpeed()); + barPlaybackSpeed.post(() -> { + barPlaybackSpeed.setProgress((int) (20 * speed) - 10); + }); + } } @Override - public void onNegative(MaterialDialog dialog) { - dialog.dismiss(); + public void onStartTrackingTouch(SeekBar seekBar) { + if(controller != null && !controller.canSetPlaybackSpeed()) { + VariableSpeedDialog.showGetPluginDialog(MediaplayerActivity.this); + } + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { } }); - stDialog.build().show(); - } - break; - case R.id.set_sleeptimer_item: - if (controller.serviceAvailable()) { - SleepTimerDialog td = new SleepTimerDialog(this) { + barPlaybackSpeed.setProgress((int) (20 * currentSpeed) - 10); + + final SeekBar barLeftVolume = (SeekBar) dialog.findViewById(R.id.volume_left); + barLeftVolume.setProgress(100); + final SeekBar barRightVolume = (SeekBar) dialog.findViewById(R.id.volume_right); + barRightVolume.setProgress(100); + final CheckBox stereoToMono = (CheckBox) dialog.findViewById(R.id.stereo_to_mono); + stereoToMono.setChecked(UserPreferences.stereoToMono()); + if (controller != null && !controller.canDownmix()) { + stereoToMono.setEnabled(false); + String sonicOnly = getString(R.string.sonic_only); + stereoToMono.setText(stereoToMono.getText() + " [" + sonicOnly + "]"); + } + + barLeftVolume.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { @Override - public void onTimerSet(long millis, boolean shakeToReset, boolean vibrate) { - controller.setSleepTimer(millis, shakeToReset, vibrate); + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + float leftVolume = 1.0f, rightVolume = 1.0f; + if (progress < 100) { + leftVolume = progress / 100.0f; + } + if (barRightVolume.getProgress() < 100) { + rightVolume = barRightVolume.getProgress() / 100.0f; + } + controller.setVolume(leftVolume, rightVolume); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { } - }; - td.createNewDialog().show(); - } - break; - case R.id.visit_website_item: - Uri uri = Uri.parse(media.getWebsiteLink()); - startActivity(new Intent(Intent.ACTION_VIEW, uri)); - break; - case R.id.support_item: - if (media instanceof FeedMedia) { - DBTasks.flattrItemIfLoggedIn(this, ((FeedMedia) media).getItem()); - } - break; - case R.id.share_link_item: - if (media instanceof FeedMedia) { - ShareUtils.shareFeedItemLink(this, ((FeedMedia) media).getItem()); - } - break; - case R.id.share_download_url_item: - if (media instanceof FeedMedia) { - ShareUtils.shareFeedItemDownloadLink(this, ((FeedMedia) media).getItem()); - } - break; - case R.id.share_link_with_position_item: - if (media instanceof FeedMedia) { - ShareUtils.shareFeedItemLink(this, ((FeedMedia) media).getItem(), true); - } - break; - case R.id.share_download_url_with_position_item: - if (media instanceof FeedMedia) { - ShareUtils.shareFeedItemDownloadLink(this, ((FeedMedia) media).getItem(), true); - } - break; - case R.id.skip_episode_item: - sendBroadcast(new Intent( - PlaybackService.ACTION_SKIP_CURRENT_EPISODE)); - break; - default: - return false; + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + } + }); + barRightVolume.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + float leftVolume = 1.0f, rightVolume = 1.0f; + if (progress < 100) { + rightVolume = progress / 100.0f; + } + if (barLeftVolume.getProgress() < 100) { + leftVolume = barLeftVolume.getProgress() / 100.0f; + } + controller.setVolume(leftVolume, rightVolume); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + } + }); + stereoToMono.setOnCheckedChangeListener((buttonView, isChecked) -> { + UserPreferences.stereoToMono(isChecked); + if (controller != null) { + controller.setDownmix(isChecked); + } + }); + break; + case R.id.visit_website_item: + Uri uri = Uri.parse(media.getWebsiteLink()); + startActivity(new Intent(Intent.ACTION_VIEW, uri)); + break; + case R.id.support_item: + if (media instanceof FeedMedia) { + DBTasks.flattrItemIfLoggedIn(this, ((FeedMedia) media).getItem()); + } + break; + case R.id.share_link_item: + if (media instanceof FeedMedia) { + ShareUtils.shareFeedItemLink(this, ((FeedMedia) media).getItem()); + } + break; + case R.id.share_download_url_item: + if (media instanceof FeedMedia) { + ShareUtils.shareFeedItemDownloadLink(this, ((FeedMedia) media).getItem()); + } + break; + case R.id.share_link_with_position_item: + if (media instanceof FeedMedia) { + ShareUtils.shareFeedItemLink(this, ((FeedMedia) media).getItem(), true); + } + break; + case R.id.share_download_url_with_position_item: + if (media instanceof FeedMedia) { + ShareUtils.shareFeedItemDownloadLink(this, ((FeedMedia) media).getItem(), true); + } + break; + default: + return false; + } + return true; + } else { + return false; } - return true; - } else { - return false; } } @@ -427,11 +607,10 @@ public abstract class MediaplayerActivity extends ActionBarActivity && controller.getMedia() != null) { txtvPosition.setText(Converter .getDurationStringLong(currentPosition)); - if(showTimeLeft) { - txtvLength.setText("-"+Converter + if (showTimeLeft) { + txtvLength.setText("-" + Converter .getDurationStringLong(duration - currentPosition)); - } - else { + } else { txtvLength.setText(Converter .getDurationStringLong(duration)); } @@ -443,7 +622,7 @@ public abstract class MediaplayerActivity extends ActionBarActivity } private void updateProgressbarPosition(int position, int duration) { - Log.d(TAG, "updateProgressbarPosition(" + position + ", " + duration +")"); + Log.d(TAG, "updateProgressbarPosition(" + position + ", " + duration + ")"); float progress = ((float) position) / duration; sbPosition.setProgress((int) (progress * sbPosition.getMax())); } @@ -458,21 +637,32 @@ public abstract class MediaplayerActivity extends ActionBarActivity Log.d(TAG, "loadMediaInfo()"); Playable media = controller.getMedia(); SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE); - showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT,false); + showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT, false); if (media != null) { - txtvPosition.setText(Converter.getDurationStringLong((media - .getPosition()))); + txtvPosition.setText(Converter.getDurationStringLong((media.getPosition()))); if (media.getDuration() != 0) { - txtvLength.setText(Converter.getDurationStringLong(media - .getDuration())); - float progress = ((float) media.getPosition()) - / media.getDuration(); + txtvLength.setText(Converter.getDurationStringLong(media.getDuration())); + float progress = ((float) media.getPosition()) / media.getDuration(); sbPosition.setProgress((int) (progress * sbPosition.getMax())); - if(showTimeLeft) { - txtvLength.setText("-"+Converter.getDurationStringLong((media - .getDuration()-media.getPosition()))); + if (showTimeLeft) { + int timeLeft = media.getDuration() - media.getPosition(); + txtvLength.setText("-" + Converter.getDurationStringLong(timeLeft)); + } + } + checkFavorite(); + if(butPlaybackSpeed != null) { + if (controller == null) { + butPlaybackSpeed.setVisibility(View.GONE); + } else { + butPlaybackSpeed.setVisibility(View.VISIBLE); + if (controller.canSetPlaybackSpeed()) { + ViewCompat.setAlpha(butPlaybackSpeed, 1.0f); + } else { + ViewCompat.setAlpha(butPlaybackSpeed, 0.5f); + } } + updateButPlaybackSpeed(); } return true; } else { @@ -486,43 +676,43 @@ public abstract class MediaplayerActivity extends ActionBarActivity txtvPosition = (TextView) findViewById(R.id.txtvPosition); SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE); - showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT,false); - Log.d("timeleft",showTimeLeft? "true":"false"); + showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT, false); + Log.d("timeleft", showTimeLeft ? "true" : "false"); txtvLength = (TextView) findViewById(R.id.txtvLength); - txtvLength.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - showTimeLeft = !showTimeLeft; - Playable media = controller.getMedia(); - if (media == null) { - return; - } - - if (showTimeLeft) { - txtvLength.setText("-"+Converter.getDurationStringLong((media - .getDuration()-media.getPosition()))); - } else { - txtvLength.setText(Converter.getDurationStringLong((media.getDuration()))); - } + txtvLength.setOnClickListener(v -> { + showTimeLeft = !showTimeLeft; + Playable media = controller.getMedia(); + if (media == null) { + return; + } - SharedPreferences.Editor editor = prefs.edit(); - editor.putBoolean(PREF_SHOW_TIME_LEFT,showTimeLeft); - editor.apply(); - Log.d("timeleft on click",showTimeLeft? "true":"false"); + String length; + if (showTimeLeft) { + length = "-" + Converter.getDurationStringLong(media.getDuration() - media.getPosition()); + } else { + length = Converter.getDurationStringLong(media.getDuration()); } + txtvLength.setText(length); + + SharedPreferences.Editor editor = prefs.edit(); + editor.putBoolean(PREF_SHOW_TIME_LEFT, showTimeLeft); + editor.apply(); + Log.d("timeleft on click", showTimeLeft ? "true" : "false"); }); - butPlay = (ImageButton) findViewById(R.id.butPlay); + butPlaybackSpeed = (Button) findViewById(R.id.butPlaybackSpeed); butRev = (ImageButton) findViewById(R.id.butRev); txtvRev = (TextView) findViewById(R.id.txtvRev); - if(txtvRev != null) { + if (txtvRev != null) { txtvRev.setText(String.valueOf(UserPreferences.getRewindSecs())); } + butPlay = (ImageButton) findViewById(R.id.butPlay); butFF = (ImageButton) findViewById(R.id.butFF); txtvFF = (TextView) findViewById(R.id.txtvFF); - if(txtvFF != null) { + if (txtvFF != null) { txtvFF.setText(String.valueOf(UserPreferences.getFastFowardSecs())); } + butSkip = (ImageButton) findViewById(R.id.butSkip); // SEEKBAR SETUP @@ -530,92 +720,120 @@ public abstract class MediaplayerActivity extends ActionBarActivity // BUTTON SETUP - butPlay.setOnClickListener(controller.newOnPlayButtonClickListener()); + if(butPlaybackSpeed != null) { + butPlaybackSpeed.setOnClickListener(v -> { + if (controller == null) { + return; + } + if (controller.canSetPlaybackSpeed()) { + String[] availableSpeeds = UserPreferences.getPlaybackSpeedArray(); + String currentSpeed = UserPreferences.getPlaybackSpeed(); + + // Provide initial value in case the speed list has changed + // out from under us + // and our current speed isn't in the new list + String newSpeed; + if (availableSpeeds.length > 0) { + newSpeed = availableSpeeds[0]; + } else { + newSpeed = "1.00"; + } - if (butFF != null) { - butFF.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - int curr = controller.getPosition(); - controller.seekTo(curr + UserPreferences.getFastFowardSecs() * 1000); + for (int i = 0; i < availableSpeeds.length; i++) { + if (availableSpeeds[i].equals(currentSpeed)) { + if (i == availableSpeeds.length - 1) { + newSpeed = availableSpeeds[0]; + } else { + newSpeed = availableSpeeds[i + 1]; + } + break; + } + } + UserPreferences.setPlaybackSpeed(newSpeed); + controller.setPlaybackSpeed(Float.parseFloat(newSpeed)); + } else { + VariableSpeedDialog.showGetPluginDialog(this); } }); - butFF.setOnLongClickListener(new View.OnLongClickListener() { + butPlaybackSpeed.setOnLongClickListener(v -> { + VariableSpeedDialog.showDialog(this); + return true; + }); + } + + if (butRev != null) { + butRev.setOnClickListener(v -> { + int curr = controller.getPosition(); + controller.seekTo(curr - UserPreferences.getRewindSecs() * 1000); + }); + butRev.setOnLongClickListener(new View.OnLongClickListener() { int choice; @Override public boolean onLongClick(View v) { int checked = 0; - int rewindSecs = UserPreferences.getFastFowardSecs(); + int rewindSecs = UserPreferences.getRewindSecs(); final int[] values = getResources().getIntArray(R.array.seek_delta_values); final String[] choices = new String[values.length]; - for(int i=0; i < values.length; i++) { + for (int i = 0; i < values.length; i++) { if (rewindSecs == values[i]) { checked = i; } - choices[i] = String.valueOf(values[i]) + " " - + getString(R.string.time_seconds); + choices[i] = String.valueOf(values[i]) + " " + getString(R.string.time_seconds); } choice = values[checked]; AlertDialog.Builder builder = new AlertDialog.Builder(MediaplayerActivity.this); - builder.setTitle(R.string.pref_fast_forward); + builder.setTitle(R.string.pref_rewind); builder.setSingleChoiceItems(choices, checked, (dialog, which) -> { choice = values[which]; }); builder.setNegativeButton(R.string.cancel_label, null); builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> { - UserPreferences.setPrefFastForwardSecs(choice); - txtvFF.setText(String.valueOf(choice)); + UserPreferences.setPrefRewindSecs(choice); + txtvRev.setText(String.valueOf(choice)); }); builder.create().show(); return true; } }); } - if (butRev != null) { - butRev.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - int curr = controller.getPosition(); - controller.seekTo(curr - UserPreferences.getRewindSecs() * 1000); - } + + butPlay.setOnClickListener(controller.newOnPlayButtonClickListener()); + + if (butFF != null) { + butFF.setOnClickListener(v -> { + int curr = controller.getPosition(); + controller.seekTo(curr + UserPreferences.getFastFowardSecs() * 1000); }); - butRev.setOnLongClickListener(new View.OnLongClickListener() { + butFF.setOnLongClickListener(new View.OnLongClickListener() { int choice; @Override public boolean onLongClick(View v) { int checked = 0; - int rewindSecs = UserPreferences.getRewindSecs(); + int rewindSecs = UserPreferences.getFastFowardSecs(); final int[] values = getResources().getIntArray(R.array.seek_delta_values); final String[] choices = new String[values.length]; - for(int i=0; i < values.length; i++) { + for (int i = 0; i < values.length; i++) { if (rewindSecs == values[i]) { checked = i; } - choices[i] = String.valueOf(values[i]) + " " - + getString(R.string.time_seconds); + choices[i] = String.valueOf(values[i]) + " " + getString(R.string.time_seconds); } choice = values[checked]; AlertDialog.Builder builder = new AlertDialog.Builder(MediaplayerActivity.this); - builder.setTitle(R.string.pref_rewind); + builder.setTitle(R.string.pref_fast_forward); builder.setSingleChoiceItems(choices, checked, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - choice = values[which]; - } + (dialog, which) -> { + choice = values[which]; }); builder.setNegativeButton(R.string.cancel_label, null); - builder.setPositiveButton(R.string.confirm_label, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - UserPreferences.setPrefRewindSecs(choice); - txtvRev.setText(String.valueOf(choice)); - } + builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> { + UserPreferences.setPrefFastForwardSecs(choice); + txtvFF.setText(String.valueOf(choice)); }); builder.create().show(); return true; @@ -623,6 +841,11 @@ public abstract class MediaplayerActivity extends ActionBarActivity }); } + if (butSkip != null) { + butSkip.setOnClickListener(v -> { + sendBroadcast(new Intent(PlaybackService.ACTION_SKIP_CURRENT_EPISODE)); + }); + } } protected abstract int getContentViewResourceId(); @@ -633,12 +856,9 @@ public abstract class MediaplayerActivity extends ActionBarActivity errorDialog .setMessage(MediaPlayerError.getErrorString(this, errorCode)); errorDialog.setNeutralButton("OK", - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - finish(); - } + (dialog, which) -> { + dialog.dismiss(); + finish(); } ); errorDialog.create().show(); @@ -647,19 +867,23 @@ public abstract class MediaplayerActivity extends ActionBarActivity float prog; @Override - public void onProgressChanged(SeekBar seekBar, int progress, - boolean fromUser) { + public void onProgressChanged (SeekBar seekBar,int progress, boolean fromUser) { if (controller != null) { - prog = controller.onSeekBarProgressChanged(seekBar, progress, fromUser, - txtvPosition); - if(showTimeLeft && prog!=0) { + prog = controller.onSeekBarProgressChanged(seekBar, progress, fromUser, txtvPosition); + if (showTimeLeft && prog != 0) { int duration = controller.getDuration(); - txtvLength.setText("-"+Converter - .getDurationStringLong(duration - (int) (prog * duration))); + String length = "-" + Converter.getDurationStringLong(duration - (int) (prog * duration)); + txtvLength.setText(length); } } } + private void updateButPlaybackSpeed() { + if (controller != null && butPlaybackSpeed != null) { + butPlaybackSpeed.setText(UserPreferences.getPlaybackSpeed() + "x"); + } + } + @Override public void onStartTrackingTouch(SeekBar seekBar) { if (controller != null) { @@ -674,4 +898,23 @@ public abstract class MediaplayerActivity extends ActionBarActivity } } + private void checkFavorite() { + Playable playable = controller.getMedia(); + if (playable != null && playable instanceof FeedMedia) { + FeedItem feedItem = ((FeedMedia) playable).getItem(); + if (feedItem != null) { + Observable.fromCallable(() -> DBReader.getFeedItem(feedItem.getId())) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(item -> { + boolean isFav = item.isTagged(FeedItem.TAG_FAVORITE); + if(isFavorite != isFav) { + isFavorite = isFav; + invalidateOptionsMenu(); + } + }); + } + } + } + } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java index 60eb290b5..ee459dbc6 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java @@ -3,9 +3,9 @@ package de.danoeh.antennapod.activity; import android.annotation.SuppressLint; import android.content.Intent; import android.graphics.drawable.ColorDrawable; -import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; +import android.os.Handler; import android.support.v4.view.WindowCompat; import android.util.Log; import android.util.Pair; @@ -19,7 +19,9 @@ import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.SeekBar; -import de.danoeh.antennapod.BuildConfig; +import java.lang.ref.WeakReference; +import java.util.concurrent.atomic.AtomicBoolean; + import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.feed.MediaType; import de.danoeh.antennapod.core.service.playback.PlaybackService; @@ -39,8 +41,12 @@ public class VideoplayerActivity extends MediaplayerActivity { */ private boolean videoControlsShowing = true; private boolean videoSurfaceCreated = false; - private VideoControlsHider videoControlsToggler; + private VideoControlsHider videoControlsHider = new VideoControlsHider(this); + + private AtomicBoolean isSetup = new AtomicBoolean(false); + + private LinearLayout controls; private LinearLayout videoOverlay; private AspectRatioVideoView videoview; private ProgressBar progressIndicator; @@ -61,25 +67,12 @@ public class VideoplayerActivity extends MediaplayerActivity { } @Override - protected void onPause() { - super.onPause(); - if (videoControlsToggler != null) { - videoControlsToggler.cancel(true); - } - if (controller != null && controller.getStatus() == PlayerStatus.PLAYING) { - controller.pause(); - } - } - - @Override protected void onResume() { super.onResume(); if (getIntent().getAction() != null && getIntent().getAction().equals(Intent.ACTION_VIEW)) { Intent intent = getIntent(); - if (BuildConfig.DEBUG) - Log.d(TAG, "Received VIEW intent: " - + intent.getData().getPath()); + Log.d(TAG, "Received VIEW intent: " + intent.getData().getPath()); ExternalMedia media = new ExternalMedia(intent.getData().getPath(), MediaType.VIDEO); Intent launchIntent = new Intent(this, PlaybackService.class); @@ -94,6 +87,22 @@ public class VideoplayerActivity extends MediaplayerActivity { } @Override + protected void onPause() { + super.onPause(); + videoControlsHider.stop(); + if (controller != null && controller.getStatus() == PlayerStatus.PLAYING) { + controller.pause(); + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + videoControlsHider.stop(); + videoControlsHider = null; + } + + @Override protected boolean loadMediaInfo() { if (!super.loadMediaInfo()) { return false; @@ -104,14 +113,17 @@ public class VideoplayerActivity extends MediaplayerActivity { getSupportActionBar().setTitle(media.getFeedTitle()); return true; } - return false; } @Override protected void setupGUI() { + if(isSetup.getAndSet(true)) { + return; + } super.setupGUI(); getSupportActionBar().setDisplayHomeAsUpEnabled(true); + controls = (LinearLayout) findViewById(R.id.controls); videoOverlay = (LinearLayout) findViewById(R.id.overlay); videoview = (AspectRatioVideoView) findViewById(R.id.videoview); progressIndicator = (ProgressBar) findViewById(R.id.progressIndicator); @@ -133,14 +145,11 @@ public class VideoplayerActivity extends MediaplayerActivity { @Override protected void onAwaitingVideoSurface() { if (videoSurfaceCreated) { - if (BuildConfig.DEBUG) - Log.d(TAG, - "Videosurface already created, setting videosurface now"); + Log.d(TAG, "Videosurface already created, setting videosurface now"); Pair<Integer, Integer> videoSize = controller.getVideoSize(); if (videoSize != null && videoSize.first > 0 && videoSize.second > 0) { - if (BuildConfig.DEBUG) - Log.d(TAG, "Width,height of video: " + videoSize.first + ", " + videoSize.second); + Log.d(TAG, "Width,height of video: " + videoSize.first + ", " + videoSize.second); videoview.setVideoSize(videoSize.first, videoSize.second); } else { Log.e(TAG, "Could not determine video size"); @@ -156,7 +165,6 @@ public class VideoplayerActivity extends MediaplayerActivity { } else { progressIndicator.setVisibility(View.INVISIBLE); } - } @Override @@ -164,38 +172,23 @@ public class VideoplayerActivity extends MediaplayerActivity { progressIndicator.setVisibility(View.INVISIBLE); } - View.OnTouchListener onVideoviewTouched = new View.OnTouchListener() { - - @Override - public boolean onTouch(View v, MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_DOWN) { - if (videoControlsToggler != null) { - videoControlsToggler.cancel(true); - } - toggleVideoControlsVisibility(); - if (videoControlsShowing) { - setupVideoControlsToggler(); - } - - return true; - } else { - return false; + View.OnTouchListener onVideoviewTouched = (v, event) -> { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + videoControlsHider.stop(); + toggleVideoControlsVisibility(); + if (videoControlsShowing) { + setupVideoControlsToggler(); } + return true; + } else { + return false; } }; @SuppressLint("NewApi") void setupVideoControlsToggler() { - if (videoControlsToggler != null) { - videoControlsToggler.cancel(true); - } - videoControlsToggler = new VideoControlsHider(); - if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) { - videoControlsToggler - .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } else { - videoControlsToggler.execute(); - } + videoControlsHider.stop(); + videoControlsHider.start(); } private void toggleVideoControlsVisibility() { @@ -209,46 +202,6 @@ public class VideoplayerActivity extends MediaplayerActivity { videoControlsShowing = !videoControlsShowing; } - /** - * Hides the videocontrols after a certain period of time. - */ - public class VideoControlsHider extends AsyncTask<Void, Void, Void> { - @Override - protected void onCancelled() { - videoControlsToggler = null; - } - - @Override - protected void onPostExecute(Void result) { - videoControlsToggler = null; - } - - private static final int WAITING_INTERVALL = 5000; - private static final String TAG = "VideoControlsToggler"; - - @Override - protected void onProgressUpdate(Void... values) { - if (videoControlsShowing) { - if (BuildConfig.DEBUG) - Log.d(TAG, "Hiding video controls"); - getSupportActionBar().hide(); - hideVideoControls(); - videoControlsShowing = false; - } - } - - @Override - protected Void doInBackground(Void... params) { - try { - Thread.sleep(WAITING_INTERVALL); - } catch (InterruptedException e) { - return null; - } - publishProgress(); - return null; - } - - } private final SurfaceHolder.Callback surfaceHolderCallback = new SurfaceHolder.Callback() { @Override @@ -259,15 +212,13 @@ public class VideoplayerActivity extends MediaplayerActivity { @Override public void surfaceCreated(SurfaceHolder holder) { - if (BuildConfig.DEBUG) - Log.d(TAG, "Videoview holder created"); + Log.d(TAG, "Videoview holder created"); videoSurfaceCreated = true; if (controller.getStatus() == PlayerStatus.PLAYING) { if (controller.serviceAvailable()) { controller.setVideoSurface(holder); } else { - Log.e(TAG, - "Could'nt attach surface to mediaplayer - reference to service was null"); + Log.e(TAG, "Could'nt attach surface to mediaplayer - reference to service was null"); } } @@ -275,8 +226,7 @@ public class VideoplayerActivity extends MediaplayerActivity { @Override public void surfaceDestroyed(SurfaceHolder holder) { - if (BuildConfig.DEBUG) - Log.d(TAG, "Videosurface was destroyed"); + Log.d(TAG, "Videosurface was destroyed"); videoSurfaceCreated = false; controller.notifyVideoSurfaceAbandoned(); } @@ -286,9 +236,7 @@ public class VideoplayerActivity extends MediaplayerActivity { @Override protected void onReloadNotification(int notificationCode) { if (notificationCode == PlaybackService.EXTRA_CODE_AUDIO) { - if (BuildConfig.DEBUG) - Log.d(TAG, - "ReloadNotification received, switching to Audioplayer now"); + Log.d(TAG, "ReloadNotification received, switching to Audioplayer now"); finish(); startActivity(new Intent(this, AudioplayerActivity.class)); } @@ -297,9 +245,7 @@ public class VideoplayerActivity extends MediaplayerActivity { @Override public void onStartTrackingTouch(SeekBar seekBar) { super.onStartTrackingTouch(seekBar); - if (videoControlsToggler != null) { - videoControlsToggler.cancel(true); - } + videoControlsHider.stop(); } @Override @@ -321,12 +267,11 @@ public class VideoplayerActivity extends MediaplayerActivity { @SuppressLint("NewApi") private void showVideoControls() { videoOverlay.setVisibility(View.VISIBLE); - butPlay.setVisibility(View.VISIBLE); - final Animation animation = AnimationUtils.loadAnimation(this, - R.anim.fade_in); + controls.setVisibility(View.VISIBLE); + final Animation animation = AnimationUtils.loadAnimation(this, R.anim.fade_in); if (animation != null) { videoOverlay.startAnimation(animation); - butPlay.startAnimation(animation); + controls.startAnimation(animation); } if (Build.VERSION.SDK_INT >= 14) { videoview.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); @@ -335,11 +280,10 @@ public class VideoplayerActivity extends MediaplayerActivity { @SuppressLint("NewApi") private void hideVideoControls() { - final Animation animation = AnimationUtils.loadAnimation(this, - R.anim.fade_out); + final Animation animation = AnimationUtils.loadAnimation(this, R.anim.fade_out); if (animation != null) { videoOverlay.startAnimation(animation); - butPlay.startAnimation(animation); + controls.startAnimation(animation); } if (Build.VERSION.SDK_INT >= 14) { int videoviewFlag = (Build.VERSION.SDK_INT >= 16) ? View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION : 0; @@ -348,7 +292,7 @@ public class VideoplayerActivity extends MediaplayerActivity { videoOverlay.setFitsSystemWindows(true); } videoOverlay.setVisibility(View.GONE); - butPlay.setVisibility(View.GONE); + controls.setVisibility(View.GONE); } @Override @@ -356,7 +300,6 @@ public class VideoplayerActivity extends MediaplayerActivity { return R.layout.videoplayer_activity; } - @Override protected void setScreenOn(boolean enable) { super.setScreenOn(enable); @@ -366,4 +309,38 @@ public class VideoplayerActivity extends MediaplayerActivity { getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } } + + private static class VideoControlsHider extends Handler { + + private static final int DELAY = 5000; + + private WeakReference<VideoplayerActivity> activity; + + public VideoControlsHider(VideoplayerActivity activity) { + this.activity = new WeakReference<>(activity); + } + + private final Runnable hideVideoControls = () -> { + VideoplayerActivity vpa = activity.get(); + if(vpa == null) { + return; + } + if (vpa.videoControlsShowing) { + Log.d(TAG, "Hiding video controls"); + vpa.getSupportActionBar().hide(); + vpa.hideVideoControls(); + vpa.videoControlsShowing = false; + } + }; + + public void start() { + this.postDelayed(hideVideoControls, DELAY); + } + + public void stop() { + this.removeCallbacks(hideVideoControls); + } + + } + } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java index 1fa4b7c48..d749b0313 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java @@ -35,6 +35,7 @@ import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.util.Converter; import de.danoeh.antennapod.core.util.DateUtils; +import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.NetworkUtils; import de.danoeh.antennapod.fragment.ItemFragment; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; @@ -328,8 +329,8 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR item1.setVisible(visible); } }; - FeedItemMenuHandler.onPrepareMenu(mainActivityRef.get(), contextMenuInterface, item, true, - null); + FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, + itemAccess.getQueueIds(), itemAccess.getFavoritesIds()); } } @@ -344,6 +345,10 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR boolean isInQueue(FeedItem item); + LongList getQueueIds(); + + LongList getFavoritesIds(); + } /** diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/ChapterListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/ChapterListAdapter.java deleted file mode 100644 index cf0532cf1..000000000 --- a/app/src/main/java/de/danoeh/antennapod/adapter/ChapterListAdapter.java +++ /dev/null @@ -1,199 +0,0 @@ -package de.danoeh.antennapod.adapter; - -import android.content.Context; -import android.text.Layout; -import android.text.Selection; -import android.text.Spannable; -import android.text.Spanned; -import android.text.style.ClickableSpan; -import android.text.util.Linkify; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnTouchListener; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.ImageButton; -import android.widget.TextView; -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.feed.Chapter; -import de.danoeh.antennapod.core.util.ChapterUtils; -import de.danoeh.antennapod.core.util.Converter; -import de.danoeh.antennapod.core.util.playback.Playable; - -import java.util.List; - -public class ChapterListAdapter extends ArrayAdapter<Chapter> { - - private static final String TAG = "ChapterListAdapter"; - - private List<Chapter> chapters; - private Playable media; - - private int defaultTextColor; - private final Callback callback; - - public ChapterListAdapter(Context context, int textViewResourceId, - List<Chapter> objects, Playable media, Callback callback) { - super(context, textViewResourceId, objects); - this.chapters = objects; - this.media = media; - this.callback = callback; - } - - @Override - public View getView(final int position, View convertView, ViewGroup parent) { - Holder holder; - - Chapter sc = getItem(position); - - // Inflate Layout - if (convertView == null) { - holder = new Holder(); - LayoutInflater inflater = (LayoutInflater) getContext() - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - - convertView = inflater.inflate(R.layout.simplechapter_item, parent, false); - holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); - defaultTextColor = holder.title.getTextColors().getDefaultColor(); - holder.start = (TextView) convertView.findViewById(R.id.txtvStart); - holder.link = (TextView) convertView.findViewById(R.id.txtvLink); - holder.butPlayChapter = (ImageButton) convertView.findViewById(R.id.butPlayChapter); - convertView.setTag(holder); - } else { - holder = (Holder) convertView.getTag(); - - } - - holder.title.setText(sc.getTitle()); - holder.start.setText(Converter.getDurationStringLong((int) sc - .getStart())); - if (sc.getLink() != null) { - holder.link.setVisibility(View.VISIBLE); - holder.link.setText(sc.getLink()); - Linkify.addLinks(holder.link, Linkify.WEB_URLS); - } else { - holder.link.setVisibility(View.GONE); - } - holder.link.setMovementMethod(null); - holder.link.setOnTouchListener(new OnTouchListener() { - - @Override - public boolean onTouch(View v, MotionEvent event) { - // from - // http://stackoverflow.com/questions/7236840/android-textview-linkify-intercepts-with-parent-view-gestures - TextView widget = (TextView) v; - Object text = widget.getText(); - if (text instanceof Spanned) { - Spannable buffer = (Spannable) text; - - int action = event.getAction(); - - if (action == MotionEvent.ACTION_UP - || action == MotionEvent.ACTION_DOWN) { - int x = (int) event.getX(); - int y = (int) event.getY(); - - x -= widget.getTotalPaddingLeft(); - y -= widget.getTotalPaddingTop(); - - x += widget.getScrollX(); - y += widget.getScrollY(); - - Layout layout = widget.getLayout(); - int line = layout.getLineForVertical(y); - int off = layout.getOffsetForHorizontal(line, x); - - ClickableSpan[] link = buffer.getSpans(off, off, - ClickableSpan.class); - - if (link.length != 0) { - if (action == MotionEvent.ACTION_UP) { - link[0].onClick(widget); - } else if (action == MotionEvent.ACTION_DOWN) { - Selection.setSelection(buffer, - buffer.getSpanStart(link[0]), - buffer.getSpanEnd(link[0])); - } - return true; - } - } - - } - - return false; - - } - }); - holder.butPlayChapter.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (callback != null) { - callback.onPlayChapterButtonClicked(position); - } - } - }); - Chapter current = ChapterUtils.getCurrentChapter(media); - if (current != null) { - if (current == sc) { - holder.title.setTextColor(convertView.getResources().getColor( - R.color.holo_blue_light)); - holder.start.setTextColor(convertView.getResources().getColor( - R.color.holo_blue_light)); - } else { - holder.title.setTextColor(defaultTextColor); - holder.start.setTextColor(defaultTextColor); - } - } else { - Log.w(TAG, "Could not find out what the current chapter is."); - } - - return convertView; - } - - static class Holder { - TextView title; - TextView start; - TextView link; - ImageButton butPlayChapter; - } - - @Override - public int getCount() { - // ignore invalid chapters - int counter = 0; - if (chapters != null) { - for (Chapter chapter : chapters) { - if (!ignoreChapter(chapter)) { - counter++; - } - } - } - return counter; - } - - private boolean ignoreChapter(Chapter c) { - return media.getDuration() > 0 && media.getDuration() < c.getStart(); - } - - @Override - public Chapter getItem(int position) { - int i = 0; - for (Chapter chapter : chapters) { - if (!ignoreChapter(chapter)) { - if (i == position) { - return chapter; - } else { - i++; - } - } - } - return super.getItem(position); - } - - public static interface Callback { - public void onPlayChapterButtonClicked(int position); - } - -} diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java new file mode 100644 index 000000000..1c9439ee6 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java @@ -0,0 +1,198 @@ +package de.danoeh.antennapod.adapter; + +import android.content.Context; +import android.text.Layout; +import android.text.Selection; +import android.text.Spannable; +import android.text.Spanned; +import android.text.style.ClickableSpan; +import android.text.util.Linkify; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageButton; +import android.widget.TextView; + +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.feed.Chapter; +import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.core.util.ChapterUtils; +import de.danoeh.antennapod.core.util.Converter; +import de.danoeh.antennapod.core.util.playback.Playable; + +public class ChaptersListAdapter extends ArrayAdapter<Chapter> { + + private static final String TAG = "ChapterListAdapter"; + + private Playable media; + + private int defaultTextColor; + private final Callback callback; + + public ChaptersListAdapter(Context context, int textViewResourceId, Callback callback) { + super(context, textViewResourceId); + this.callback = callback; + } + + public void setMedia(Playable media) { + this.media = media; + } + + @Override + public View getView(final int position, View convertView, ViewGroup parent) { + Holder holder; + + Chapter sc = getItem(position); + + // Inflate Layout + if (convertView == null) { + holder = new Holder(); + LayoutInflater inflater = (LayoutInflater) getContext() + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + convertView = inflater.inflate(R.layout.simplechapter_item, parent, false); + holder.view = convertView; + holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); + defaultTextColor = holder.title.getTextColors().getDefaultColor(); + holder.start = (TextView) convertView.findViewById(R.id.txtvStart); + holder.link = (TextView) convertView.findViewById(R.id.txtvLink); + holder.butPlayChapter = (ImageButton) convertView.findViewById(R.id.butPlayChapter); + convertView.setTag(holder); + } else { + holder = (Holder) convertView.getTag(); + + } + + holder.title.setText(sc.getTitle()); + holder.start.setText(Converter.getDurationStringLong((int) sc + .getStart())); + if (sc.getLink() != null) { + holder.link.setVisibility(View.VISIBLE); + holder.link.setText(sc.getLink()); + Linkify.addLinks(holder.link, Linkify.WEB_URLS); + } else { + holder.link.setVisibility(View.GONE); + } + holder.link.setMovementMethod(null); + holder.link.setOnTouchListener((v, event) -> { + // from + // http://stackoverflow.com/questions/7236840/android-textview-linkify-intercepts-with-parent-view-gestures + TextView widget = (TextView) v; + Object text = widget.getText(); + if (text instanceof Spanned) { + Spannable buffer = (Spannable) text; + + int action = event.getAction(); + + if (action == MotionEvent.ACTION_UP + || action == MotionEvent.ACTION_DOWN) { + int x = (int) event.getX(); + int y = (int) event.getY(); + + x -= widget.getTotalPaddingLeft(); + y -= widget.getTotalPaddingTop(); + + x += widget.getScrollX(); + y += widget.getScrollY(); + + Layout layout = widget.getLayout(); + int line = layout.getLineForVertical(y); + int off = layout.getOffsetForHorizontal(line, x); + + ClickableSpan[] link = buffer.getSpans(off, off, + ClickableSpan.class); + + if (link.length != 0) { + if (action == MotionEvent.ACTION_UP) { + link[0].onClick(widget); + } else if (action == MotionEvent.ACTION_DOWN) { + Selection.setSelection(buffer, + buffer.getSpanStart(link[0]), + buffer.getSpanEnd(link[0])); + } + return true; + } + } + + } + + return false; + + }); + holder.butPlayChapter.setOnClickListener(v -> { + if (callback != null) { + callback.onPlayChapterButtonClicked(position); + } + }); + Chapter current = ChapterUtils.getCurrentChapter(media); + if (current != null) { + if (current == sc) { + int playingBackGroundColor; + if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) { + playingBackGroundColor = getContext().getResources().getColor(R.color.highlight_dark); + } else { + playingBackGroundColor = getContext().getResources().getColor(R.color.highlight_light); + } + holder.view.setBackgroundColor(playingBackGroundColor); + } else { + holder.view.setBackgroundColor(getContext().getResources().getColor(android.R.color.transparent)); + holder.title.setTextColor(defaultTextColor); + holder.start.setTextColor(defaultTextColor); + } + } else { + Log.w(TAG, "Could not find out what the current chapter is."); + } + + return convertView; + } + + static class Holder { + View view; + TextView title; + TextView start; + TextView link; + ImageButton butPlayChapter; + } + + @Override + public int getCount() { + if(media == null || media.getChapters() == null) { + return 0; + } + // ignore invalid chapters + int counter = 0; + for (Chapter chapter : media.getChapters()) { + if (!ignoreChapter(chapter)) { + counter++; + } + } + return counter; + } + + private boolean ignoreChapter(Chapter c) { + return media.getDuration() > 0 && media.getDuration() < c.getStart(); + } + + @Override + public Chapter getItem(int position) { + int i = 0; + for (Chapter chapter : media.getChapters()) { + if (!ignoreChapter(chapter)) { + if (i == position) { + return chapter; + } else { + i++; + } + } + } + return super.getItem(position); + } + + public interface Callback { + void onPlayChapterButtonClicked(int position); + } + +} diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java index 9011c8b02..5b205e91f 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java @@ -6,11 +6,13 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.TextView; -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.feed.FeedItem; import java.util.List; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.feed.FeedItem; +import de.danoeh.antennapod.core.util.DateUtils; + /** * List adapter for showing a list of FeedItems with their title and description. */ @@ -33,6 +35,7 @@ public class FeedItemlistDescriptionAdapter extends ArrayAdapter<FeedItem> { .getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.itemdescription_listitem, parent, false); holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); + holder.pubDate = (TextView) convertView.findViewById(R.id.txtvPubDate); holder.description = (TextView) convertView.findViewById(R.id.txtvDescription); convertView.setTag(holder); @@ -41,15 +44,20 @@ public class FeedItemlistDescriptionAdapter extends ArrayAdapter<FeedItem> { } holder.title.setText(item.getTitle()); + holder.pubDate.setText(DateUtils.formatAbbrev(getContext(), item.getPubDate())); if (item.getDescription() != null) { - holder.description.setText(item.getDescription()); + String description = item.getDescription() + .replaceAll("\n", " ") + .replaceAll("\\s+", " ") + .trim(); + holder.description.setText(description); } - return convertView; } static class Holder { TextView title; + TextView pubDate; TextView description; } } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java index ca1f59e8d..d0266be6d 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java @@ -183,8 +183,8 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap item1.setVisible(visible); } }; - FeedItemMenuHandler.onPrepareMenu(mainActivity.get(), contextMenuInterface, item, true, - itemAccess.getQueueIds()); + FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, + itemAccess.getQueueIds(), itemAccess.getFavoritesIds()); } @Override @@ -351,6 +351,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap long getItemDownloadSize(FeedItem item); int getItemDownloadProgressPercent(FeedItem item); LongList getQueueIds(); + LongList getFavoritesIds(); } /** diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java index 08ffdd197..47ac4c757 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java @@ -10,6 +10,7 @@ import android.widget.TextView; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -65,6 +66,12 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> { //Set the title viewHolder.titleView.setText(podcast.title); + if(!podcast.feedUrl.contains("itunes.apple.com")) { + viewHolder.urlView.setText(podcast.feedUrl); + viewHolder.urlView.setVisibility(View.VISIBLE); + } else { + viewHolder.urlView.setVisibility(View.GONE); + } //Update the empty imageView with the image from the feed Glide.with(context) @@ -94,6 +101,8 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> { */ public final TextView titleView; + public final TextView urlView; + /** * Constructor @@ -102,6 +111,7 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> { PodcastViewHolder(View view){ coverView = (ImageView) view.findViewById(R.id.imgvCover); titleView = (TextView) view.findViewById(R.id.txtvTitle); + urlView = (TextView) view.findViewById(R.id.txtvUrl); } } @@ -124,16 +134,47 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> { */ public final String feedUrl; + + private Podcast(String title, String imageUrl, String feedUrl) { + this.title = title; + this.imageUrl = imageUrl; + this.feedUrl = feedUrl; + } + + /** + * Constructs a Podcast instance from a iTunes search result + * + * @param json object holding the podcast information + * @throws JSONException + */ + public static Podcast fromSearch(JSONObject json) throws JSONException { + String title = json.getString("collectionName"); + String imageUrl = json.getString("artworkUrl100"); + String feedUrl = json.getString("feedUrl"); + return new Podcast(title, imageUrl, feedUrl); + } + /** - * Constructor. + * Constructs a Podcast instance from iTunes toplist entry * * @param json object holding the podcast information * @throws JSONException */ - public Podcast(JSONObject json) throws JSONException { - title = json.getString("collectionName"); - imageUrl = json.getString("artworkUrl100"); - feedUrl = json.getString("feedUrl"); + public static Podcast fromToplist(JSONObject json) throws JSONException { + String title = json.getJSONObject("title").getString("label"); + String imageUrl = null; + JSONArray images = json.getJSONArray("im:image"); + for(int i=0; imageUrl == null && i < images.length(); i++) { + JSONObject image = images.getJSONObject(i); + String height = image.getJSONObject("attributes").getString("height"); + if(Integer.valueOf(height) >= 100) { + imageUrl = image.getString("label"); + } + } + String feedUrl = "https://itunes.apple.com/lookup?id=" + + json.getJSONObject("id").getJSONObject("attributes").getString("im:id"); + return new Podcast(title, imageUrl, feedUrl); } + } } diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java index 16909cbb3..6432ebd4e 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java @@ -2,6 +2,7 @@ package de.danoeh.antennapod.dialog; import android.content.res.TypedArray; import android.graphics.Color; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.support.v4.app.ActivityCompat; import android.support.v4.app.Fragment; @@ -126,9 +127,6 @@ public class EpisodesApplyActionFragment extends Fragment { textColor = ta.getColor(0, Color.GRAY); ta.recycle(); - menu.findItem(R.id.sort).setIcon(new IconDrawable(getActivity(), - FontAwesomeIcons.fa_sort).color(textColor).actionBarSize()); - mSelectToggle = menu.findItem(R.id.select_toggle); mSelectToggle.setOnMenuItemClickListener(item -> { if (checkedIds.size() == episodes.size()) { @@ -138,23 +136,27 @@ public class EpisodesApplyActionFragment extends Fragment { } return true; }); - - menu.findItem(R.id.select_options).setIcon(new IconDrawable(getActivity(), - FontAwesomeIcons.fa_caret_down).color(textColor).actionBarSize()); } @Override public void onPrepareOptionsMenu (Menu menu) { - Icon icon; - if(checkedIds.size() == episodes.size()) { - icon = FontAwesomeIcons.fa_check_square_o; - } else if(checkedIds.size() == 0) { - icon = FontAwesomeIcons.fa_square_o; - } else { - icon = FontAwesomeIcons.fa_minus_square_o; - } - mSelectToggle.setIcon(new IconDrawable(getActivity(), icon).color(textColor).actionBarSize()); - + /* + * Prepare icon for select toggle button + */ + + // Find icon attribute + int[] icon = new int[1]; + if(checkedIds.size() == episodes.size()) icon[0] = R.attr.ic_check_box; + else if(checkedIds.size() == 0) icon[0] = R.attr.ic_check_box_outline; + else icon[0] = R.attr.ic_indeterminate_check_box; + + // Get Drawable from attribute + TypedArray a = getActivity().obtainStyledAttributes(icon); + Drawable iconDrawable = a.getDrawable(0); + a.recycle(); + + // Set icon + mSelectToggle.setIcon(iconDrawable); } @Override diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java index 4b512a48d..3ed82b9bd 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java @@ -21,107 +21,104 @@ import de.danoeh.antennapod.core.util.IntentUtils; public class VariableSpeedDialog { - private static final String TAG = VariableSpeedDialog.class.getSimpleName(); - - private static final Intent playStoreIntent = new Intent(Intent.ACTION_VIEW, - Uri.parse("market://details?id=com.falconware.prestissimo")); - - private VariableSpeedDialog() { - } - - public static void showDialog(final Context context) { - if (org.antennapod.audio.MediaPlayer.isPrestoLibraryInstalled(context) - || UserPreferences.useSonic()) { - showSpeedSelectorDialog(context); - } else { - showGetPluginDialog(context); - } - } - - private static void showGetPluginDialog(final Context context) { - MaterialDialog.Builder builder = new MaterialDialog.Builder(context); - builder.title(R.string.no_playback_plugin_title); - builder.content(R.string.no_playback_plugin_or_sonic_msg); - builder.positiveText(R.string.download_plugin_label); - builder.negativeText(R.string.enable_sonic); - builder.neutralText(R.string.close_label); - builder.callback(new MaterialDialog.ButtonCallback() { - @Override - public void onPositive(MaterialDialog dialog) { - try { - context.startActivity(playStoreIntent); - } catch (ActivityNotFoundException e) { - // this is usually thrown on an emulator if the Android market is not installed - Log.e(TAG, Log.getStackTraceString(e)); - } - } - - @Override - public void onNegative(MaterialDialog dialog) { - if (Build.VERSION.SDK_INT >= 16) { // just to be safe - UserPreferences.enableSonic(true); - showSpeedSelectorDialog(context); - } - } - - @Override - public void onNeutral(MaterialDialog dialog) { - super.onNeutral(dialog); - } - }); - builder.forceStacking(true); - MaterialDialog dialog = builder.show(); - if(!IntentUtils.isCallable(context.getApplicationContext(), playStoreIntent)) { - View pos = dialog.getActionButton(DialogAction.POSITIVE); - pos.setEnabled(false); - } - if (Build.VERSION.SDK_INT < 16) { - View pos = dialog.getActionButton(DialogAction.NEGATIVE); - pos.setEnabled(false); - } - } - - private static void showSpeedSelectorDialog(final Context context) { - final String[] speedValues = context.getResources().getStringArray( - R.array.playback_speed_values); - // According to Java spec these get initialized to false on creation - final boolean[] speedChecked = new boolean[speedValues.length]; - - // Build the "isChecked" array so that multiChoice dialog is - // populated correctly - List<String> selectedSpeedList = Arrays.asList(UserPreferences - .getPlaybackSpeedArray()); - for (int i = 0; i < speedValues.length; i++) { - speedChecked[i] = selectedSpeedList.contains(speedValues[i]); - } - - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(R.string.set_playback_speed_label); - builder.setMultiChoiceItems(R.array.playback_speed_values, - speedChecked, (dialog, which, isChecked) -> { - speedChecked[which] = isChecked; - }); - builder.setNegativeButton(android.R.string.cancel, null); - builder.setPositiveButton(android.R.string.ok, - (dialog, which) -> { - int choiceCount = 0; - for (int i = 0; i < speedChecked.length; i++) { - if (speedChecked[i]) { - choiceCount++; - } - } - String[] newSpeedValues = new String[choiceCount]; - int newSpeedIndex = 0; - for (int i = 0; i < speedChecked.length; i++) { - if (speedChecked[i]) { - newSpeedValues[newSpeedIndex++] = speedValues[i]; - } - } - - UserPreferences.setPlaybackSpeedArray(newSpeedValues); - - }); - builder.create().show(); - } + private static final String TAG = VariableSpeedDialog.class.getSimpleName(); + + private static final Intent playStoreIntent = new Intent(Intent.ACTION_VIEW, + Uri.parse("market://details?id=com.falconware.prestissimo")); + + private VariableSpeedDialog() { + } + + public static void showDialog(final Context context) { + if (org.antennapod.audio.MediaPlayer.isPrestoLibraryInstalled(context) + || UserPreferences.useSonic() + || Build.VERSION.SDK_INT >= 23) { + showSpeedSelectorDialog(context); + } else { + showGetPluginDialog(context, true); + } + } + + public static void showGetPluginDialog(final Context context) { + showGetPluginDialog(context, false); + } + + private static void showGetPluginDialog(final Context context, boolean showSpeedSelector) { + MaterialDialog.Builder builder = new MaterialDialog.Builder(context); + builder.title(R.string.no_playback_plugin_title); + builder.content(R.string.no_playback_plugin_or_sonic_msg); + builder.positiveText(R.string.enable_sonic); + builder.negativeText(R.string.download_plugin_label); + builder.neutralText(R.string.close_label); + builder.onPositive((dialog, which) -> { + if (Build.VERSION.SDK_INT >= 16) { // just to be safe + UserPreferences.enableSonic(true); + if(showSpeedSelector) { + showSpeedSelectorDialog(context); + } + } + }); + builder.onNegative((dialog, which) -> { + try { + context.startActivity(playStoreIntent); + } catch (ActivityNotFoundException e) { + // this is usually thrown on an emulator if the Android market is not installed + Log.e(TAG, Log.getStackTraceString(e)); + } + }); + builder.forceStacking(true); + MaterialDialog dialog = builder.show(); + if (Build.VERSION.SDK_INT < 16) { + View pos = dialog.getActionButton(DialogAction.POSITIVE); + pos.setEnabled(false); + } + if(!IntentUtils.isCallable(context.getApplicationContext(), playStoreIntent)) { + View pos = dialog.getActionButton(DialogAction.NEGATIVE); + pos.setEnabled(false); + } + } + + private static void showSpeedSelectorDialog(final Context context) { + final String[] speedValues = context.getResources().getStringArray( + R.array.playback_speed_values); + // According to Java spec these get initialized to false on creation + final boolean[] speedChecked = new boolean[speedValues.length]; + + // Build the "isChecked" array so that multiChoice dialog is + // populated correctly + List<String> selectedSpeedList = Arrays.asList(UserPreferences + .getPlaybackSpeedArray()); + for (int i = 0; i < speedValues.length; i++) { + speedChecked[i] = selectedSpeedList.contains(speedValues[i]); + } + + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(R.string.set_playback_speed_label); + builder.setMultiChoiceItems(R.array.playback_speed_values, + speedChecked, (dialog, which, isChecked) -> { + speedChecked[which] = isChecked; + }); + builder.setNegativeButton(android.R.string.cancel, null); + builder.setPositiveButton(android.R.string.ok, + (dialog, which) -> { + int choiceCount = 0; + for (int i = 0; i < speedChecked.length; i++) { + if (speedChecked[i]) { + choiceCount++; + } + } + String[] newSpeedValues = new String[choiceCount]; + int newSpeedIndex = 0; + for (int i = 0; i < speedChecked.length; i++) { + if (speedChecked[i]) { + newSpeedValues[newSpeedIndex++] = speedValues[i]; + } + } + + UserPreferences.setPlaybackSpeedArray(newSpeedValues); + + }); + builder.create().show(); + } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java index 2bc4e9ded..273c75240 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java @@ -46,6 +46,7 @@ import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.storage.DownloadRequestException; import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.util.FeedItemUtil; +import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.menuhandler.MenuItemUtils; import de.greenrobot.event.EventBus; @@ -382,6 +383,34 @@ public class AllEpisodesFragment extends Fragment { } return false; } + + @Override + public LongList getQueueIds() { + LongList queueIds = new LongList(); + if(episodes == null) { + return queueIds; + } + for(FeedItem item : episodes) { + if(item.isTagged(FeedItem.TAG_QUEUE)) { + queueIds.add(item.getId()); + } + } + return queueIds; + } + + @Override + public LongList getFavoritesIds() { + LongList favoritesIds = new LongList(); + if(episodes == null) { + return favoritesIds; + } + for(FeedItem item : episodes) { + if(item.isTagged(FeedItem.TAG_FAVORITE)) { + favoritesIds.add(item.getId()); + } + } + return favoritesIds; + } }; public void onEventMainThread(FeedItemEvent event) { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java new file mode 100644 index 000000000..96abdd835 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java @@ -0,0 +1,77 @@ +package de.danoeh.antennapod.fragment; + +import android.os.Bundle; +import android.support.v4.app.ListFragment; +import android.view.View; +import android.widget.ListView; + +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.AudioplayerActivity.AudioplayerContentFragment; +import de.danoeh.antennapod.adapter.ChaptersListAdapter; +import de.danoeh.antennapod.core.feed.Chapter; +import de.danoeh.antennapod.core.util.playback.Playable; +import de.danoeh.antennapod.core.util.playback.PlaybackController; + + +public class ChaptersFragment extends ListFragment implements AudioplayerContentFragment { + + private Playable media; + private PlaybackController controller; + + private ChaptersListAdapter adapter; + + public static ChaptersFragment newInstance(Playable media, PlaybackController controller) { + ChaptersFragment f = new ChaptersFragment(); + f.media = media; + f.controller = controller; + return f; + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + // add padding + final ListView lv = getListView(); + lv.setClipToPadding(false); + final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding); + lv.setPadding(0, vertPadding, 0, vertPadding); + + adapter = new ChaptersListAdapter(getActivity(), 0, pos -> { + Chapter chapter = (Chapter) getListAdapter().getItem(pos); + controller.seekToChapter(chapter); + }); + setListAdapter(adapter); + } + + @Override + public void onResume() { + super.onResume(); + adapter.setMedia(media); + adapter.notifyDataSetChanged(); + if(media == null || media.getChapters() == null) { + setEmptyText(getString(R.string.no_chapters_label)); + } else { + setEmptyText(null); + } + } + + public void onDestroy() { + super.onDestroy(); + adapter = null; + } + + @Override + public void onMediaChanged(Playable media) { + if(this.media == media || adapter == null) { + return; + } + this.media = media; + adapter.setMedia(media); + adapter.notifyDataSetChanged(); + if(media == null || media.getChapters() == null || media.getChapters().size() == 0) { + setEmptyText(getString(R.string.no_items_label)); + } else { + setEmptyText(null); + } + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java index a1667cce0..931d14924 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java @@ -1,7 +1,5 @@ package de.danoeh.antennapod.fragment; -import android.app.Activity; -import android.content.Context; import android.os.Bundle; import android.support.v4.app.Fragment; import android.util.Log; @@ -9,13 +7,11 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; +import android.widget.TextView; import com.bumptech.glide.Glide; -import com.bumptech.glide.load.engine.DiskCacheStrategy; -import de.danoeh.antennapod.BuildConfig; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.activity.AudioplayerActivity; import de.danoeh.antennapod.activity.AudioplayerActivity.AudioplayerContentFragment; import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.util.playback.Playable; @@ -23,17 +19,18 @@ import de.danoeh.antennapod.core.util.playback.Playable; /** * Displays the cover and the title of a FeedItem. */ -public class CoverFragment extends Fragment implements - AudioplayerContentFragment { +public class CoverFragment extends Fragment implements AudioplayerContentFragment { + private static final String TAG = "CoverFragment"; private static final String ARG_PLAYABLE = "arg.playable"; private Playable media; + private View root; + private TextView txtvPodcastTitle; + private TextView txtvEpisodeTitle; private ImageView imgvCover; - private boolean viewCreated = false; - public static CoverFragment newInstance(Playable item) { CoverFragment f = new CoverFragment(); if (item != null) { @@ -47,7 +44,6 @@ public class CoverFragment extends Fragment implements @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setRetainInstance(true); Bundle args = getArguments(); if (args != null) { media = args.getParcelable(ARG_PLAYABLE); @@ -59,39 +55,28 @@ public class CoverFragment extends Fragment implements @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View root = inflater.inflate(R.layout.cover_fragment, container, false); + root = inflater.inflate(R.layout.cover_fragment, container, false); + txtvPodcastTitle = (TextView) root.findViewById(R.id.txtvPodcastTitle); + txtvEpisodeTitle = (TextView) root.findViewById(R.id.txtvEpisodeTitle); imgvCover = (ImageView) root.findViewById(R.id.imgvCover); - imgvCover.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Activity activity = getActivity(); - if (activity != null && activity instanceof AudioplayerActivity) { - ((AudioplayerActivity)activity).switchToLastFragment(); - } - } - }); - viewCreated = true; return root; } private void loadMediaInfo() { + if(imgvCover == null) { + return; + } if (media != null) { - imgvCover.post(new Runnable() { - - @Override - public void run() { - Context c = getActivity(); - if (c != null) { - Glide.with(c) - .load(media.getImageUri()) - .placeholder(R.color.light_gray) - .error(R.color.light_gray) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .dontAnimate() - .into(imgvCover); - } - } - }); + Log.d(TAG, "feed title: " + media.getFeedTitle()); + Log.d(TAG, "episode title: " + media.getEpisodeTitle()); + txtvPodcastTitle.setText(media.getFeedTitle()); + txtvEpisodeTitle.setText(media.getEpisodeTitle()); + Glide.with(this) + .load(media.getImageUri()) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .dontAnimate() + .fitCenter() + .into(imgvCover); } else { Log.w(TAG, "loadMediaInfo was called while media was null"); } @@ -99,12 +84,10 @@ public class CoverFragment extends Fragment implements @Override public void onStart() { - if (BuildConfig.DEBUG) - Log.d(TAG, "On Start"); + Log.d(TAG, "On Start"); super.onStart(); if (media != null) { - if (BuildConfig.DEBUG) - Log.d(TAG, "Loading media info"); + Log.d(TAG, "Loading media info"); loadMediaInfo(); } else { Log.w(TAG, "Unable to load media info: media was null"); @@ -112,12 +95,19 @@ public class CoverFragment extends Fragment implements } @Override - public void onDataSetChanged(Playable media) { - this.media = media; - if (viewCreated) { - loadMediaInfo(); - } + public void onDestroy() { + super.onDestroy(); + // prevent memory leaks + root = null; + } + @Override + public void onMediaChanged(Playable media) { + if(this.media == media) { + return; + } + this.media = media; + loadMediaInfo(); } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java index 888684313..80a9bf0b3 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java @@ -13,7 +13,6 @@ import android.widget.ProgressBar; import android.widget.TextView; import com.bumptech.glide.Glide; -import com.bumptech.glide.load.engine.DiskCacheStrategy; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.glide.ApGlideSettings; @@ -80,50 +79,15 @@ public class ExternalPlayerFragment extends Fragment { return new PlaybackController(getActivity(), true) { @Override - public void setupGUI() { - } - - @Override public void onPositionObserverUpdate() { ExternalPlayerFragment.this.onPositionObserverUpdate(); } @Override - public void onReloadNotification(int code) { - } - - @Override - public void onBufferStart() { - } - - @Override - public void onBufferEnd() { - } - - @Override - public void onBufferUpdate(float progress) { - } - - @Override - public void onSleepTimerUpdate() { - } - - @Override - public void handleError(int code) { - } - - @Override public ImageButton getPlayButton() { return butPlay; } - @Override - public void postStatusMsg(int msg) { - } - - @Override - public void clearStatusMsg() { - } @Override public boolean loadMediaInfo() { @@ -136,14 +100,6 @@ public class ExternalPlayerFragment extends Fragment { } @Override - public void onAwaitingVideoSurface() { - } - - @Override - public void onServiceQueried() { - } - - @Override public void onShutdownNotification() { playbackDone(); } @@ -152,10 +108,6 @@ public class ExternalPlayerFragment extends Fragment { public void onPlaybackEnd() { playbackDone(); } - - @Override - public void onPlaybackSpeedChange() { - } }; } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java index faa4413bb..4c723e5ff 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java @@ -7,8 +7,9 @@ import android.content.ClipData; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.content.res.TypedArray; +import android.graphics.Color; import android.net.Uri; -import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -20,12 +21,14 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.webkit.WebSettings.LayoutAlgorithm; +import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.Toast; import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.AudioplayerActivity; +import de.danoeh.antennapod.activity.AudioplayerActivity.AudioplayerContentFragment; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.DBReader; @@ -36,11 +39,15 @@ import de.danoeh.antennapod.core.util.ShownotesProvider; import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.PlaybackController; import de.danoeh.antennapod.core.util.playback.Timeline; +import rx.Observable; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; /** * Displays the description of a Playable object in a Webview. */ -public class ItemDescriptionFragment extends Fragment { +public class ItemDescriptionFragment extends Fragment implements AudioplayerContentFragment { private static final String TAG = "ItemDescriptionFragment"; @@ -55,12 +62,12 @@ public class ItemDescriptionFragment extends Fragment { private static final String ARG_HIGHLIGHT_TIMECODES = "arg.highlightTimecodes"; private WebView webvDescription; + private String webvData; private ShownotesProvider shownotesProvider; private Playable media; - - private AsyncTask<Void, Void, Void> webViewLoader; + private Subscription webViewLoader; /** * URL that was selected via long-press. @@ -106,17 +113,17 @@ public class ItemDescriptionFragment extends Fragment { Bundle savedInstanceState) { Log.d(TAG, "Creating view"); webvDescription = new WebView(getActivity()); - if (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) { - if (Build.VERSION.SDK_INT >= 11 - && Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { - webvDescription.setLayerType(View.LAYER_TYPE_SOFTWARE, null); - } - webvDescription.setBackgroundColor(getResources().getColor( - R.color.black)); + if (Build.VERSION.SDK_INT >= 11) { + webvDescription.setLayerType(View.LAYER_TYPE_SOFTWARE, null); } + TypedArray ta = getActivity().getTheme().obtainStyledAttributes(new int[] + {android.R.attr.colorBackground}); + int backgroundColor = ta.getColor(0, UserPreferences.getTheme() == + R.style.Theme_AntennaPod_Dark ? Color.BLACK : Color.WHITE); + ta.recycle(); + webvDescription.setBackgroundColor(backgroundColor); webvDescription.getSettings().setUseWideViewPort(false); - webvDescription.getSettings().setLayoutAlgorithm( - LayoutAlgorithm.NARROW_COLUMNS); + webvDescription.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN); webvDescription.getSettings().setLoadWithOverviewMode(true); webvDescription.setOnLongClickListener(webViewLongClickListener); webvDescription.setWebViewClient(new WebViewClient() { @@ -142,14 +149,7 @@ public class ItemDescriptionFragment extends Fragment { super.onPageFinished(view, url); Log.d(TAG, "Page finished"); // Restoring the scroll position might not always work - view.postDelayed(new Runnable() { - - @Override - public void run() { - restoreFromPreference(); - } - - }, 50); + view.postDelayed(() -> restoreFromPreference(), 50); } }); @@ -159,26 +159,11 @@ public class ItemDescriptionFragment extends Fragment { } @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - Log.d(TAG, "Fragment attached"); - } - - @Override - public void onDetach() { - super.onDetach(); - Log.d(TAG, "Fragment detached"); - if (webViewLoader != null) { - webViewLoader.cancel(true); - } - } - - @Override public void onDestroy() { super.onDestroy(); Log.d(TAG, "Fragment destroyed"); if (webViewLoader != null) { - webViewLoader.cancel(true); + webViewLoader.unsubscribe(); } if (webvDescription != null) { webvDescription.removeAllViews(); @@ -194,7 +179,6 @@ public class ItemDescriptionFragment extends Fragment { Bundle args = getArguments(); saveState = args.getBoolean(ARG_SAVE_STATE, false); highlightTimecodes = args.getBoolean(ARG_HIGHLIGHT_TIMECODES, false); - } @Override @@ -204,46 +188,21 @@ public class ItemDescriptionFragment extends Fragment { if (args.containsKey(ARG_PLAYABLE)) { media = args.getParcelable(ARG_PLAYABLE); shownotesProvider = media; - startLoader(); + load(); } else if (args.containsKey(ARG_FEEDITEM_ID)) { - AsyncTask<Void, Void, FeedItem> itemLoadTask = new AsyncTask<Void, Void, FeedItem>() { - - @Override - protected FeedItem doInBackground(Void... voids) { - return DBReader.getFeedItem(getArguments().getLong(ARG_FEEDITEM_ID)); - } - - @Override - protected void onPostExecute(FeedItem feedItem) { - super.onPostExecute(feedItem); - shownotesProvider = feedItem; - startLoader(); - } - }; - if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) { - itemLoadTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } else { - itemLoadTask.execute(); - } + long id = getArguments().getLong(ARG_FEEDITEM_ID); + Observable.defer(() -> Observable.just(DBReader.getFeedItem(id))) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(feedItem -> { + shownotesProvider = feedItem; + load(); + }, error -> { + Log.e(TAG, Log.getStackTraceString(error)); + }); } - - - } - - @Override - public void onResume() { - super.onResume(); } - @SuppressLint("NewApi") - private void startLoader() { - webViewLoader = createLoader(); - if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) { - webViewLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } else { - webViewLoader.execute(); - } - } private View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() { @@ -338,49 +297,31 @@ public class ItemDescriptionFragment extends Fragment { } } - private AsyncTask<Void, Void, Void> createLoader() { - return new AsyncTask<Void, Void, Void>() { - @Override - protected void onCancelled() { - super.onCancelled(); - webViewLoader = null; - } - - String data; - - @Override - protected void onPostExecute(Void result) { - super.onPostExecute(result); - // /webvDescription.loadData(url, "text/html", "utf-8"); - webvDescription.loadDataWithBaseURL(null, data, "text/html", - "utf-8", "about:blank"); - Log.d(TAG, "Webview loaded"); - webViewLoader = null; - } - - @Override - protected void onPreExecute() { - super.onPreExecute(); - } - - @Override - protected Void doInBackground(Void... params) { - Log.d(TAG, "Loading Webview"); - try { - Activity activity = getActivity(); - if (activity != null) { - Timeline timeline = new Timeline(activity, shownotesProvider); - data = timeline.processShownotes(highlightTimecodes); - } else { - cancel(true); - } - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } + private void load() { + Log.d(TAG, "load()"); + if(webViewLoader != null) { + webViewLoader.unsubscribe(); + } + if(shownotesProvider == null) { + return; + } + webViewLoader = Observable.defer(() -> Observable.just(loadData())) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(data -> { + webvData = data; + webvDescription.loadDataWithBaseURL(null, data, "text/html", + "utf-8", "about:blank"); + Log.d(TAG, "Webview loaded"); + }, error -> { + Log.e(TAG, Log.getStackTraceString(error)); + }); + } - }; + private String loadData() { + Timeline timeline = new Timeline(getActivity(), shownotesProvider); + String data = timeline.processShownotes(highlightTimecodes); + return data; } @Override @@ -410,7 +351,7 @@ public class ItemDescriptionFragment extends Fragment { } private boolean restoreFromPreference() { - if (saveState) { + if (!saveState) { Log.d(TAG, "Restoring from preferences"); Activity activity = getActivity(); if (activity != null) { @@ -433,15 +374,22 @@ public class ItemDescriptionFragment extends Fragment { private void onTimecodeLinkSelected(String link) { int time = Timeline.getTimecodeLinkTime(link); - if (getActivity() != null && getActivity() instanceof ItemDescriptionFragmentCallback) { - PlaybackController pc = ((ItemDescriptionFragmentCallback) getActivity()).getPlaybackController(); + if (getActivity() != null && getActivity() instanceof AudioplayerActivity) { + PlaybackController pc = ((AudioplayerActivity) getActivity()).getPlaybackController(); if (pc != null) { pc.seekTo(time); } } } - public interface ItemDescriptionFragmentCallback { - public PlaybackController getPlaybackController(); + @Override + public void onMediaChanged(Playable media) { + if(this.media == media) { + return; + } + this.media = media; + this.shownotesProvider = media; + load(); } + } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java index 21268815a..ce80dc827 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java @@ -9,7 +9,6 @@ import android.os.Build; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; -import android.support.v4.util.Pair; import android.text.TextUtils; import android.util.Log; import android.view.ContextMenu; @@ -40,6 +39,7 @@ import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.adapter.DefaultActionButtonCallback; import de.danoeh.antennapod.core.event.DownloadEvent; import de.danoeh.antennapod.core.event.DownloaderUpdate; +import de.danoeh.antennapod.core.event.FavoritesEvent; import de.danoeh.antennapod.core.event.FeedItemEvent; import de.danoeh.antennapod.core.event.QueueEvent; import de.danoeh.antennapod.core.feed.EventDistributor; @@ -95,6 +95,7 @@ public class ItemFragment extends Fragment { private long itemID; private FeedItem item; private LongList queue; + private LongList favorites; private String webviewData; private List<Downloader> downloaderList; @@ -249,10 +250,10 @@ public class ItemFragment extends Fragment { inflater.inflate(R.menu.feeditem_options, menu); popupMenu = menu; if (item.hasMedia()) { - FeedItemMenuHandler.onPrepareMenu(getActivity(), popupMenuInterface, item, true, queue); + FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, queue, favorites); } else { // these are already available via button1 and button2 - FeedItemMenuHandler.onPrepareMenu(getActivity(), popupMenuInterface, item, true, queue, + FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, queue, favorites, R.id.mark_read_item, R.id.visit_website_item); } } @@ -310,7 +311,7 @@ public class ItemFragment extends Fragment { .dontAnimate() .into(imgvCover); - progbarDownload.setVisibility(View.INVISIBLE); + progbarDownload.setVisibility(View.GONE); if (item.hasMedia() && downloaderList != null) { for (Downloader downloader : downloaderList) { if (downloader.getDownloadRequest().getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA @@ -332,7 +333,7 @@ public class ItemFragment extends Fragment { butAction1Text = R.string.mark_read_label; } if (item.getLink() != null) { - butAction2Icon = "{ma-web 24sp}"; + butAction2Icon = "{md-web 24sp}"; butAction2Text = R.string.visit_website_label; } } else { @@ -457,6 +458,12 @@ public class ItemFragment extends Fragment { } } + public void onEventMainThread(FavoritesEvent event) { + if(event.item.getId() == itemID) { + load(); + } + } + public void onEventMainThread(FeedItemEvent event) { Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); for(FeedItem item : event.items) { @@ -500,8 +507,9 @@ public class ItemFragment extends Fragment { .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(result -> { - item = result.first; - queue = result.second; + item = (FeedItem) result[0]; + queue = (LongList) result[1]; + favorites = (LongList) result[2]; if (!itemsLoaded) { itemsLoaded = true; onFragmentLoaded(); @@ -513,14 +521,25 @@ public class ItemFragment extends Fragment { }); } - private Pair<FeedItem,LongList> loadInBackground() { - FeedItem data1 = DBReader.getFeedItem(itemID); - if (data1 != null) { - Timeline t = new Timeline(getActivity(), data1); + private Object[] loadInBackground() { + FeedItem feedItem = DBReader.getFeedItem(itemID); + if (feedItem != null) { + Timeline t = new Timeline(getActivity(), feedItem); webviewData = t.processShownotes(false); } - LongList data2 = DBReader.getQueueIDList(); - return Pair.create(data1, data2); + LongList queue; + if(feedItem.isTagged(FeedItem.TAG_QUEUE)) { + queue = LongList.of(feedItem.getId()); + } else { + queue = new LongList(0); + } + LongList favorites; + if(feedItem.isTagged(FeedItem.TAG_FAVORITE)) { + favorites = LongList.of(feedItem.getId()); + } else { + favorites = new LongList(0); + } + return new Object[] { feedItem, queue, favorites }; } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java index 97c446fab..7a9b73982 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java @@ -10,7 +10,6 @@ import android.graphics.LightingColorFilter; import android.os.Build; import android.os.Bundle; import android.support.v4.app.ListFragment; -import android.support.v4.util.Pair; import android.support.v4.view.MenuItemCompat; import android.support.v7.widget.SearchView; import android.util.Log; @@ -48,6 +47,7 @@ import de.danoeh.antennapod.core.dialog.ConfirmationDialog; import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator; import de.danoeh.antennapod.core.event.DownloadEvent; import de.danoeh.antennapod.core.event.DownloaderUpdate; +import de.danoeh.antennapod.core.event.FavoritesEvent; import de.danoeh.antennapod.core.event.FeedItemEvent; import de.danoeh.antennapod.core.event.QueueEvent; import de.danoeh.antennapod.core.feed.EventDistributor; @@ -99,6 +99,7 @@ public class ItemlistFragment extends ListFragment { private long feedID; private Feed feed; private LongList queuedItemsIds; + private LongList favoritedItemsId; private boolean itemsLoaded = false; private boolean viewsCreated = false; @@ -324,7 +325,8 @@ public class ItemlistFragment extends ListFragment { contextMenu = menu; lastMenuInfo = (AdapterView.AdapterContextMenuInfo) menuInfo; - FeedItemMenuHandler.onPrepareMenu(getActivity(), contextMenuInterface, item, true, queuedItemsIds); + FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, queuedItemsIds, + favoritedItemsId); } @Override @@ -389,6 +391,11 @@ public class ItemlistFragment extends ListFragment { loadItems(); } + public void onEvent(FavoritesEvent event) { + Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]"); + loadItems(); + } + public void onEvent(FeedEvent event) { Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]"); if(event.feedId == feedID) { @@ -626,8 +633,9 @@ public class ItemlistFragment extends ListFragment { .observeOn(AndroidSchedulers.mainThread()) .subscribe(result -> { if (result != null) { - feed = result.first; - queuedItemsIds = result.second; + feed = (Feed) result[0]; + queuedItemsIds = (LongList) result[1]; + favoritedItemsId = (LongList) result[2]; itemsLoaded = true; if (viewsCreated) { onFragmentLoaded(); @@ -638,14 +646,15 @@ public class ItemlistFragment extends ListFragment { }); } - private Pair<Feed, LongList> loadData() { + private Object[] loadData() { Feed feed = DBReader.getFeed(feedID); if(feed != null && feed.getItemFilter() != null) { FeedItemFilter filter = feed.getItemFilter(); feed.setItems(filter.filter(feed.getItems())); } LongList queuedItemsIds = DBReader.getQueueIDList(); - return Pair.create(feed, queuedItemsIds); + LongList favoritedItemsId = DBReader.getFavoriteIDList(); + return new Object[] { feed, queuedItemsIds, favoritedItemsId }; } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java index e92df4885..eb947dc2b 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java @@ -1,17 +1,23 @@ package de.danoeh.antennapod.fragment; import android.content.Intent; -import android.content.res.Resources; import android.os.Bundle; import android.support.v4.app.Fragment; +import android.support.v4.view.MenuItemCompat; import android.support.v7.widget.SearchView; import android.util.Log; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.AdapterView; +import android.widget.Button; import android.widget.GridView; +import android.widget.ProgressBar; +import android.widget.TextView; +import com.afollestad.materialdialogs.MaterialDialog; import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.Request; import com.squareup.okhttp.Response; @@ -25,13 +31,14 @@ import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.OnlineFeedViewActivity; import de.danoeh.antennapod.adapter.itunes.ItunesAdapter; import de.danoeh.antennapod.core.ClientConfig; -import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.download.AntennapodHttpClient; +import de.danoeh.antennapod.menuhandler.MenuItemUtils; import rx.Observable; import rx.Subscription; import rx.android.schedulers.AndroidSchedulers; @@ -46,21 +53,22 @@ public class ItunesSearchFragment extends Fragment { private static final String API_URL = "https://itunes.apple.com/search?media=podcast&term=%s"; - /** - * Search input field - */ - private SearchView searchView; /** * Adapter responsible with the search results */ private ItunesAdapter adapter; + private GridView gridView; + private ProgressBar progressBar; + private TextView txtvError; + private Button butRetry; + private TextView txtvEmpty; /** * List of podcasts retreived from the search */ private List<Podcast> searchResults; - + private List<Podcast> topList; private Subscription subscription; /** @@ -70,13 +78,17 @@ public class ItunesSearchFragment extends Fragment { void updateData(List<Podcast> result) { this.searchResults = result; adapter.clear(); - - //ArrayAdapter.addAll() requires minsdk > 10 - for(Podcast p: result) { - adapter.add(p); + if (result != null && result.size() > 0) { + gridView.setVisibility(View.VISIBLE); + txtvEmpty.setVisibility(View.GONE); + for (Podcast p : result) { + adapter.add(p); + } + adapter.notifyDataSetInvalidated(); + } else { + gridView.setVisibility(View.GONE); + txtvEmpty.setVisibility(View.VISIBLE); } - - adapter.notifyDataSetInvalidated(); } /** @@ -89,47 +101,105 @@ public class ItunesSearchFragment extends Fragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - adapter = new ItunesAdapter(getActivity(), new ArrayList<Podcast>()); - + setHasOptionsMenu(true); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment - View view = inflater.inflate(R.layout.fragment_itunes_search, container, false); - GridView gridView = (GridView) view.findViewById(R.id.gridView); + View root = inflater.inflate(R.layout.fragment_itunes_search, container, false); + gridView = (GridView) root.findViewById(R.id.gridView); + adapter = new ItunesAdapter(getActivity(), new ArrayList<>()); gridView.setAdapter(adapter); //Show information about the podcast when the list item is clicked - gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - Intent intent = new Intent(getActivity(), - OnlineFeedViewActivity.class); - - //Tell the OnlineFeedViewActivity where to go - String url = searchResults.get(position).feedUrl; - intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, url); - + gridView.setOnItemClickListener((parent, view1, position, id) -> { + Podcast podcast = searchResults.get(position); + if (!podcast.feedUrl.contains("itunes.apple.com")) { + Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class); + intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, podcast.feedUrl); intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, "iTunes"); startActivity(intent); + } else { + gridView.setVisibility(View.GONE); + progressBar.setVisibility(View.VISIBLE); + rx.Observable.create((Observable.OnSubscribe<String>) subscriber -> { + OkHttpClient client = AntennapodHttpClient.getHttpClient(); + Request.Builder httpReq = new Request.Builder() + .url(podcast.feedUrl) + .header("User-Agent", ClientConfig.USER_AGENT); + try { + Response response = client.newCall(httpReq.build()).execute(); + if (response.isSuccessful()) { + String resultString = response.body().string(); + JSONObject result = new JSONObject(resultString); + JSONObject results = result.getJSONArray("results").getJSONObject(0); + String feedUrl = results.getString("feedUrl"); + subscriber.onNext(feedUrl); + } else { + String prefix = getString(R.string.error_msg_prefix); + subscriber.onError(new IOException(prefix + response)); + } + } catch (IOException | JSONException e) { + subscriber.onError(e); + } + subscriber.onCompleted(); + }) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(feedUrl -> { + progressBar.setVisibility(View.GONE); + gridView.setVisibility(View.VISIBLE); + Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class); + intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, feedUrl); + intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, "iTunes"); + startActivity(intent); + }, error -> { + Log.e(TAG, Log.getStackTraceString(error)); + progressBar.setVisibility(View.GONE); + gridView.setVisibility(View.VISIBLE); + String prefix = getString(R.string.error_msg_prefix); + new MaterialDialog.Builder(getActivity()) + .content(prefix + " " + error.getMessage()) + .neutralText(android.R.string.ok) + .show(); + }); } }); + progressBar = (ProgressBar) root.findViewById(R.id.progressBar); + txtvError = (TextView) root.findViewById(R.id.txtvError); + butRetry = (Button) root.findViewById(R.id.butRetry); + txtvEmpty = (TextView) root.findViewById(android.R.id.empty); + + loadToplist(); + + return root; + } - //Configure search input view to be expanded by default with a visible submit button - searchView = (SearchView) view.findViewById(R.id.itunes_search_view); - searchView.setIconifiedByDefault(false); - searchView.setIconified(false); - searchView.setSubmitButtonEnabled(true); - searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + @Override + public void onDestroy() { + super.onDestroy(); + if (subscription != null) { + subscription.unsubscribe(); + } + adapter = null; + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.itunes_search, menu); + MenuItem searchItem = menu.findItem(R.id.action_search); + final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem); + MenuItemUtils.adjustTextColor(getActivity(), sv); + sv.setQueryHint(getString(R.string.search_itunes_label)); + sv.setOnQueryTextListener(new android.support.v7.widget.SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String s) { - //This prevents onQueryTextSubmit() from being called twice when keyboard is used - //to submit the query. - searchView.clearFocus(); + sv.clearFocus(); search(s); - return false; + return true; } @Override @@ -137,21 +207,97 @@ public class ItunesSearchFragment extends Fragment { return false; } }); + MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() { + @Override + public boolean onMenuItemActionExpand(MenuItem item) { + return true; + } - SearchView.SearchAutoComplete textField = (SearchView.SearchAutoComplete) searchView.findViewById(de.danoeh.antennapod.R.id.search_src_text); - if(UserPreferences.getTheme() == de.danoeh.antennapod.R.style.Theme_AntennaPod_Dark) { - textField.setTextColor(Resources.getSystem().getColor(android.R.color.white)); - } else { - textField.setTextColor(Resources.getSystem().getColor(android.R.color.black)); + @Override + public boolean onMenuItemActionCollapse(MenuItem item) { + if(searchResults != null) { + searchResults = null; + updateData(topList); + } + return true; + } + }); + } + + private void loadToplist() { + if (subscription != null) { + subscription.unsubscribe(); } + gridView.setVisibility(View.GONE); + txtvError.setVisibility(View.GONE); + butRetry.setVisibility(View.GONE); + txtvEmpty.setVisibility(View.GONE); + progressBar.setVisibility(View.VISIBLE); + subscription = rx.Observable.create((Observable.OnSubscribe<List<Podcast>>) subscriber -> { + String lang = Locale.getDefault().getLanguage(); + String url = "https://itunes.apple.com/" + lang + "/rss/toppodcasts/limit=25/explicit=true/json"; + OkHttpClient client = AntennapodHttpClient.getHttpClient(); + Request.Builder httpReq = new Request.Builder() + .url(url) + .header("User-Agent", ClientConfig.USER_AGENT); + List<Podcast> results = new ArrayList<>(); + try { + Response response = client.newCall(httpReq.build()).execute(); + if(!response.isSuccessful()) { + // toplist for language does not exist, fall back to united states + url = "https://itunes.apple.com/us/rss/toppodcasts/limit=25/explicit=true/json"; + httpReq = new Request.Builder() + .url(url) + .header("User-Agent", ClientConfig.USER_AGENT); + response = client.newCall(httpReq.build()).execute(); + } + if(response.isSuccessful()) { + String resultString = response.body().string(); + JSONObject result = new JSONObject(resultString); + JSONObject feed = result.getJSONObject("feed"); + JSONArray entries = feed.getJSONArray("entry"); - return view; + for(int i=0; i < entries.length(); i++) { + JSONObject json = entries.getJSONObject(i); + Podcast podcast = Podcast.fromToplist(json); + results.add(podcast); + } + } + else { + String prefix = getString(R.string.error_msg_prefix); + subscriber.onError(new IOException(prefix + response)); + } + } catch (IOException | JSONException e) { + subscriber.onError(e); + } + subscriber.onNext(results); + subscriber.onCompleted(); + }) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(podcasts -> { + progressBar.setVisibility(View.GONE); + topList = podcasts; + updateData(topList); + }, error -> { + Log.e(TAG, Log.getStackTraceString(error)); + progressBar.setVisibility(View.GONE); + txtvError.setText(error.toString()); + txtvError.setVisibility(View.VISIBLE); + butRetry.setOnClickListener(v -> loadToplist()); + butRetry.setVisibility(View.VISIBLE); + }); } private void search(String query) { if (subscription != null) { subscription.unsubscribe(); } + gridView.setVisibility(View.GONE); + txtvError.setVisibility(View.GONE); + butRetry.setVisibility(View.GONE); + txtvEmpty.setVisibility(View.GONE); + progressBar.setVisibility(View.VISIBLE); subscription = rx.Observable.create((Observable.OnSubscribe<List<Podcast>>) subscriber -> { String encodedQuery = null; try { @@ -181,15 +327,16 @@ public class ItunesSearchFragment extends Fragment { for (int i = 0; i < j.length(); i++) { JSONObject podcastJson = j.getJSONObject(i); - Podcast podcast = new Podcast(podcastJson); + Podcast podcast = Podcast.fromSearch(podcastJson); podcasts.add(podcast); } } else { - subscriber.onError(new IOException("Unexpected error: " + response)); + String prefix = getString(R.string.error_msg_prefix); + subscriber.onError(new IOException(prefix + response)); } } catch (IOException | JSONException e) { - Log.e(TAG, Log.getStackTraceString(e)); + subscriber.onError(e); } subscriber.onNext(podcasts); subscriber.onCompleted(); @@ -197,9 +344,15 @@ public class ItunesSearchFragment extends Fragment { .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(podcasts -> { + progressBar.setVisibility(View.GONE); updateData(podcasts); }, error -> { Log.e(TAG, Log.getStackTraceString(error)); + progressBar.setVisibility(View.GONE); + txtvError.setText(error.toString()); + txtvError.setVisibility(View.VISIBLE); + butRetry.setOnClickListener(v -> search(query)); + butRetry.setVisibility(View.VISIBLE); }); } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java index 2e779bf15..b3f6c3534 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java @@ -68,6 +68,7 @@ public class QueueFragment extends Fragment { public static final String TAG = "QueueFragment"; private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED | + EventDistributor.UNREAD_ITEMS_UPDATE | // sent when playback position is reset EventDistributor.PLAYER_STATUS_UPDATE; private TextView infoBar; @@ -567,6 +568,20 @@ public class QueueFragment extends Fragment { public LongList getQueueIds() { return queue != null ? LongList.of(FeedItemUtil.getIds(queue)) : new LongList(0); } + + @Override + public LongList getFavoritesIds() { + LongList favoritesIds = new LongList(); + if(queue == null) { + return favoritesIds; + } + for(FeedItem item : queue) { + if(item.isTagged(FeedItem.TAG_FAVORITE)) { + favoritesIds.add(item.getId()); + } + } + return favoritesIds; + } }; private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java index 513cf2760..dbd18163c 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java @@ -4,7 +4,7 @@ import android.content.Context; import android.os.Bundle; import android.support.v4.app.ListFragment; import android.support.v4.view.MenuItemCompat; -import android.support.v7.app.ActionBarActivity; +import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.SearchView; import android.util.Log; import android.view.Menu; @@ -116,7 +116,7 @@ public class SearchFragment extends ListFragment { final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding); lv.setPadding(0, vertPadding, 0, vertPadding); - ((ActionBarActivity) getActivity()).getSupportActionBar().setTitle(R.string.search_label); + ((AppCompatActivity) getActivity()).getSupportActionBar().setTitle(R.string.search_label); viewCreated = true; if (itemsLoaded) { onFragmentLoaded(); diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java index 06dbe4963..58fe8afbf 100644 --- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java @@ -57,22 +57,25 @@ public class FeedItemMenuHandler { * @param queueAccess Used for testing if the queue contains the selected item * @return Returns true if selectedItem is not null. */ - public static boolean onPrepareMenu(Context context, MenuInterface mi, FeedItem selectedItem, - boolean showExtendedMenu, LongList queueAccess) { + public static boolean onPrepareMenu(MenuInterface mi, + FeedItem selectedItem, + boolean showExtendedMenu, + LongList queueAccess, + LongList favorites) { if (selectedItem == null) { return false; } boolean hasMedia = selectedItem.getMedia() != null; - boolean isPlaying = hasMedia - && selectedItem.getState() == FeedItem.State.PLAYING; - - FeedItem.State state = selectedItem.getState(); + boolean isPlaying = hasMedia && selectedItem.getState() == FeedItem.State.PLAYING; if (!isPlaying) { mi.setItemVisibility(R.id.skip_episode_item, false); } - boolean isInQueue = selectedItem.isTagged(FeedItem.TAG_QUEUE); + boolean isInQueue = false; + if(queueAccess != null) { + isInQueue = queueAccess.contains(selectedItem.getId()); + } if(queueAccess == null || queueAccess.size() == 0 || queueAccess.get(0) == selectedItem.getId()) { mi.setItemVisibility(R.id.move_to_top_item, false); } @@ -123,7 +126,7 @@ public class FeedItemMenuHandler { mi.setItemVisibility(R.id.support_item, false); } - boolean isFavorite = selectedItem.isTagged(FeedItem.TAG_FAVORITE); + boolean isFavorite = favorites != null && favorites.contains(selectedItem.getId()); mi.setItemVisibility(R.id.add_to_favorites_item, !isFavorite); mi.setItemVisibility(R.id.remove_from_favorites_item, isFavorite); @@ -137,9 +140,13 @@ public class FeedItemMenuHandler { * @param excludeIds Menu item that should be excluded * @return true if selectedItem is not null. */ - public static boolean onPrepareMenu(Context context, MenuInterface mi, FeedItem selectedItem, - boolean showExtendedMenu, LongList queueAccess, int... excludeIds) { - boolean rc = onPrepareMenu(context, mi, selectedItem, showExtendedMenu, queueAccess); + public static boolean onPrepareMenu(MenuInterface mi, + FeedItem selectedItem, + boolean showExtendedMenu, + LongList queueAccess, + LongList favorites, + int... excludeIds) { + boolean rc = onPrepareMenu(mi, selectedItem, showExtendedMenu, queueAccess, favorites); if (rc && excludeIds != null) { for (int id : excludeIds) { mi.setItemVisibility(id, false); diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java index 2d6d4cac1..830e9d419 100644 --- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java @@ -68,8 +68,8 @@ public class FeedMenuHandler { case R.id.refresh_complete_item: DBTasks.refreshCompleteFeed(context, selectedFeed); break; - case R.id.hide_items: - showHideDialog(context, selectedFeed); + case R.id.filter_items: + showFilterDialog(context, selectedFeed); break; case R.id.mark_all_read_item: ConfirmationDialog conDialog = new ConfirmationDialog(context, @@ -110,14 +110,13 @@ public class FeedMenuHandler { return true; } - private static void showHideDialog(final Context context, final Feed feed) { - - final String[] items = context.getResources().getStringArray(R.array.episode_hide_options); - final String[] values = context.getResources().getStringArray(R.array.episode_hide_values); + private static void showFilterDialog(final Context context, final Feed feed) { + final String[] items = context.getResources().getStringArray(R.array.episode_filter_options); + final String[] values = context.getResources().getStringArray(R.array.episode_filter_values); final boolean[] checkedItems = new boolean[items.length]; - final Set<String> hidden = new HashSet<String>(Arrays.asList(feed.getItemFilter().getValues())); - Iterator<String> it = hidden.iterator(); + final Set<String> filter = new HashSet<>(Arrays.asList(feed.getItemFilter().getValues())); + Iterator<String> it = filter.iterator(); while(it.hasNext()) { // make sure we have no empty strings in the filter list if(TextUtils.isEmpty(it.next())) { @@ -126,23 +125,23 @@ public class FeedMenuHandler { } for(int i=0; i < values.length; i++) { String value = values[i]; - if(hidden.contains(value)) { + if(filter.contains(value)) { checkedItems[i] = true; } } AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(R.string.hide_episodes_title); + builder.setTitle(R.string.filter); builder.setMultiChoiceItems(items, checkedItems, (dialog, which, isChecked) -> { if (isChecked) { - hidden.add(values[which]); + filter.add(values[which]); } else { - hidden.remove(values[which]); + filter.remove(values[which]); } }); builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> { - feed.setHiddenItemProperties(hidden.toArray(new String[hidden.size()])); - DBWriter.setFeedItemsFilter(feed.getId(), hidden); + feed.setItemFilter(filter.toArray(new String[filter.size()])); + DBWriter.setFeedItemsFilter(feed.getId(), filter); }); builder.setNegativeButton(R.string.cancel_label, null); builder.create().show(); diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java index 083ac5202..785944768 100644 --- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java +++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java @@ -397,12 +397,11 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc ui.findPreference("prefSendCrashReport").setOnPreferenceClickListener(preference -> { Intent emailIntent = new Intent(Intent.ACTION_SEND); emailIntent.setType("text/plain"); - String to[] = { "Martin.Fietz@gmail.com" }; - emailIntent .putExtra(Intent.EXTRA_EMAIL, to); + emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{"Martin.Fietz@gmail.com"}); + emailIntent.putExtra(Intent.EXTRA_SUBJECT, "AntennaPod Crash Report"); + emailIntent.putExtra(Intent.EXTRA_TEXT, "Please describe what you were doing when the app crashed"); // the attachment - emailIntent .putExtra(Intent.EXTRA_STREAM, Uri.fromFile(CrashReportWriter.getFile())); - // the mail subject - emailIntent .putExtra(Intent.EXTRA_SUBJECT, "AntennaPod Crash Report"); + emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(CrashReportWriter.getFile())); String intentTitle = ui.getActivity().getString(R.string.send_email); ui.getActivity().startActivity(Intent.createChooser(emailIntent, intentTitle)); return true; diff --git a/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java b/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java index d61a189c2..323060f81 100644 --- a/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java +++ b/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java @@ -128,7 +128,7 @@ public class PlayerWidgetService extends Service { views.setTextViewText(R.id.txtvTitle, media.getEpisodeTitle()); - String progressString = getProgressString(media); + String progressString = getProgressString(); if (progressString != null) { views.setViewVisibility(R.id.txtvProgress, View.VISIBLE); views.setTextViewText(R.id.txtvProgress, progressString); @@ -181,9 +181,9 @@ public class PlayerWidgetService extends Service { return PendingIntent.getBroadcast(this, 0, startingIntent, 0); } - private String getProgressString(Playable media) { - int position = media.getPosition(); - int duration = media.getDuration(); + private String getProgressString() { + int position = playbackService.getCurrentPosition(); + int duration = playbackService.getDuration(); if (position > 0 && duration > 0) { return Converter.getDurationStringLong(position) + " / " + Converter.getDurationStringLong(duration); |