diff options
Diffstat (limited to 'app/src/main/java/de/danoeh')
46 files changed, 1493 insertions, 639 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/AppConfig.java b/app/src/main/java/de/danoeh/antennapod/AppConfig.java deleted file mode 100644 index 24f13d4a3..000000000 --- a/app/src/main/java/de/danoeh/antennapod/AppConfig.java +++ /dev/null @@ -1,7 +0,0 @@ -package de.danoeh.antennapod; - -public final class AppConfig { - /** Should be used when setting User-Agent header for HTTP-requests. */ - public final static String USER_AGENT = "AntennaPod/0.9.9.4"; - -} diff --git a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java index 451094909..f47fe3944 100644 --- a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java +++ b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java @@ -1,12 +1,14 @@ package de.danoeh.antennapod; import android.app.Application; +import android.content.Intent; import android.content.res.Configuration; -import de.danoeh.antennapod.core.asynctask.PicassoProvider; import de.danoeh.antennapod.core.feed.EventDistributor; import de.danoeh.antennapod.core.preferences.PlaybackPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.core.service.FeedMediaSizeService; +import de.danoeh.antennapod.core.util.NetworkUtils; import de.danoeh.antennapod.spa.SPAUtil; /** Main application class. */ @@ -37,12 +39,15 @@ public class PodcastApp extends Application { singleton = this; LOGICAL_DENSITY = getResources().getDisplayMetrics().density; - PicassoProvider.setupPicassoInstance(this); - UserPreferences.createInstance(this); - PlaybackPreferences.createInstance(this); + UpdateManager.init(this); + UserPreferences.init(this); + PlaybackPreferences.init(this); + NetworkUtils.init(this); EventDistributor.getInstance(); SPAUtil.sendSPAppsQueryFeedsIntent(this); + + startService(new Intent(this, FeedMediaSizeService.class)); } public static float getLogicalDensity() { diff --git a/app/src/main/java/de/danoeh/antennapod/UpdateManager.java b/app/src/main/java/de/danoeh/antennapod/UpdateManager.java new file mode 100644 index 000000000..2f6bb1b50 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/UpdateManager.java @@ -0,0 +1,88 @@ +package de.danoeh.antennapod; + + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.util.Log; + +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.storage.DBReader; +import de.danoeh.antennapod.core.storage.DBWriter; + +/* + * This class's job is do perform maintenance tasks whenever the app has been updated + */ +public class UpdateManager { + + public static final String TAG = UpdateManager.class.getSimpleName(); + + private static final String PREF_NAME = "app_version"; + private static final String KEY_VERSION_CODE = "version_code"; + + private static int currentVersionCode; + + private static Context context; + private static SharedPreferences prefs; + + public static void init(Context context) { + UpdateManager.context = context; + prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); + PackageManager pm = context.getPackageManager(); + try { + PackageInfo info = pm.getPackageInfo(context.getPackageName(), 0); + currentVersionCode = info.versionCode; + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Failed to obtain package info for package name: " + context.getPackageName(), e); + currentVersionCode = 0; + return; + } + final int oldVersionCode = getStoredVersionCode(); + Log.d(TAG, "old: " + oldVersionCode + ", current: " + currentVersionCode); + if(oldVersionCode < currentVersionCode) { + onUpgrade(oldVersionCode, currentVersionCode); + setCurrentVersionCode(); + } + } + + public static int getStoredVersionCode() { + return prefs.getInt(KEY_VERSION_CODE, -1); + } + + public static void setCurrentVersionCode() { + prefs.edit().putInt(KEY_VERSION_CODE, currentVersionCode).apply(); + } + + private static void onUpgrade(final int oldVersionCode, final int newVersionCode) { + if(oldVersionCode < 1030099) { + // delete the now obsolete image cache + // from now on, Glide will handle caching images + new Thread() { + public void run() { + List<Feed> feeds = DBReader.getFeedList(context); + for (Feed podcast : feeds) { + List<FeedItem> episodes = DBReader.getFeedItemList(context, podcast); + for (FeedItem episode : episodes) { + FeedImage image = episode.getImage(); + if (image != null && image.isDownloaded() && image.getFile_url() != null) { + File imageFile = new File(image.getFile_url()); + if (imageFile.exists()) { + imageFile.delete(); + } + image.setFile_url(null); // calls setDownloaded(false) + DBWriter.setFeedImage(context, image); + } + } + } + } + }.start(); + } + } + +} 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 b59f6ba35..294acd427 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java @@ -1,5 +1,6 @@ package de.danoeh.antennapod.activity; +import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; @@ -13,6 +14,8 @@ import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.widget.Toolbar; import android.util.Log; +import android.view.ContextMenu; +import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; @@ -24,21 +27,26 @@ import android.widget.ImageButton; import android.widget.ListView; import android.widget.TextView; -import com.squareup.picasso.Picasso; +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.engine.DiskCacheStrategy; import org.apache.commons.lang3.StringUtils; import de.danoeh.antennapod.R; import de.danoeh.antennapod.adapter.ChapterListAdapter; 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.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.storage.DBReader; +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; @@ -430,6 +438,7 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc drawerLayout.closeDrawer(navDrawer); } }); + registerForContextMenu(navList); drawerToggle.syncState(); findViewById(R.id.nav_settings).setOnClickListener(new View.OnClickListener() { @@ -549,9 +558,13 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc } txtvTitle.setText(media.getEpisodeTitle()); getSupportActionBar().setTitle(""); - Picasso.with(this) + Glide.with(this) .load(media.getImageUri()) - .fit() + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate() .into(butShowCover); setNavButtonVisibility(); @@ -634,6 +647,65 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc } } + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + if(v.getId() != R.id.nav_list) { + return; + } + AdapterView.AdapterContextMenuInfo adapterInfo = (AdapterView.AdapterContextMenuInfo) menuInfo; + int position = adapterInfo.position; + if(position < navAdapter.getSubscriptionOffset()) { + return; + } + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.nav_feed_context, menu); + 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! + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); + if(menuInfo.targetView.getParent() instanceof ListView == false + || ((ListView)menuInfo.targetView.getParent()).getId() != R.id.nav_list) { + return false; + } + int position = menuInfo.position; + Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset()); + switch(item.getItemId()) { + case R.id.mark_all_seen_item: + DBWriter.markFeedSeen(this, feed.getId()); + return true; + case R.id.mark_all_read_item: + DBWriter.markFeedRead(this, feed.getId()); + return true; + case R.id.remove_item: + final FeedRemover remover = new FeedRemover(this, feed) { + @Override + protected void onPostExecute(Void result) { + super.onPostExecute(result); + } + }; + ConfirmationDialog conDialog = new ConfirmationDialog(this, + R.string.remove_feed_label, + R.string.feed_delete_confirmation_msg) { + @Override + public void onConfirmButtonPressed( + DialogInterface dialog) { + dialog.dismiss(); + remover.executeAsync(); + } + }; + conDialog.createNewDialog().show(); + return true; + default: + return super.onContextItemSelected(item); + } + } + + private DBReader.NavDrawerData navDrawerData; private AsyncTask<Void, Void, DBReader.NavDrawerData> loadTask; @@ -708,8 +780,8 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc } @Override - public int getNumberOfUnreadFeedItems(long feedId) { - return (navDrawerData != null) ? navDrawerData.numUnreadFeedItems.get(feedId) : 0; + public int getFeedCounter(long feedId) { + return navDrawerData != null ? navDrawerData.feedCounters.get(feedId) : 0; } }; } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java index 287ae3568..0993ed6d6 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java @@ -17,7 +17,8 @@ import android.widget.ListView; import android.widget.Spinner; import android.widget.TextView; -import com.squareup.picasso.Picasso; +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.engine.DiskCacheStrategy; import org.apache.commons.lang3.StringUtils; import org.jsoup.Jsoup; @@ -36,6 +37,7 @@ import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator; import de.danoeh.antennapod.core.feed.EventDistributor; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.FeedItem; +import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DownloadRequestException; import de.danoeh.antennapod.core.storage.DownloadRequester; @@ -124,9 +126,13 @@ public class DefaultOnlineFeedViewActivity extends OnlineFeedViewActivity { subscribeButton = (Button) header.findViewById(R.id.butSubscribe); if (feed.getImage() != null && StringUtils.isNotBlank(feed.getImage().getDownload_url())) { - Picasso.with(this) + Glide.with(this) .load(feed.getImage().getDownload_url()) - .fit() + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate() .into(cover); } 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 5f76a20a8..9cdaf2fc6 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java @@ -2,6 +2,9 @@ package de.danoeh.antennapod.activity; import android.content.ClipData; import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; @@ -12,24 +15,31 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; 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.Spinner; import android.widget.TextView; import android.widget.Toast; +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.joanzapata.android.iconify.Iconify; -import com.squareup.picasso.Picasso; 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.FeedPreferences; +import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.storage.DownloadRequestException; +import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.LangUtils; import de.danoeh.antennapod.menuhandler.FeedMenuHandler; @@ -38,6 +48,7 @@ import de.danoeh.antennapod.menuhandler.FeedMenuHandler; */ public class FeedInfoActivity extends ActionBarActivity { private static final String TAG = "FeedInfoActivity"; + private boolean autoDeleteChanged = false; public static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId"; @@ -52,6 +63,7 @@ public class FeedInfoActivity extends ActionBarActivity { private EditText etxtUsername; private EditText etxtPassword; private CheckBox cbxAutoDownload; + private Spinner spnAutoDelete; private final View.OnClickListener copyUrlToClipboard = new View.OnClickListener() { @Override @@ -89,6 +101,7 @@ public class FeedInfoActivity extends ActionBarActivity { txtvAuthor = (TextView) findViewById(R.id.txtvAuthor); txtvUrl = (TextView) findViewById(R.id.txtvUrl); cbxAutoDownload = (CheckBox) findViewById(R.id.cbxAutoDownload); + spnAutoDelete = (Spinner) findViewById(R.id.spnAutoDelete); etxtUsername = (EditText) findViewById(R.id.etxtUsername); etxtPassword = (EditText) findViewById(R.id.etxtPassword); @@ -108,13 +121,18 @@ public class FeedInfoActivity extends ActionBarActivity { Log.d(TAG, "Language is " + feed.getLanguage()); Log.d(TAG, "Author is " + feed.getAuthor()); Log.d(TAG, "URL is " + feed.getDownload_url()); + FeedPreferences prefs = feed.getPreferences(); imgvCover.post(new Runnable() { @Override public void run() { - Picasso.with(FeedInfoActivity.this) + Glide.with(FeedInfoActivity.this) .load(feed.getImageUri()) - .fit() + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate() .into(imgvCover); } }); @@ -133,17 +151,49 @@ public class FeedInfoActivity extends ActionBarActivity { Iconify.addIcons(txtvUrl); cbxAutoDownload.setEnabled(UserPreferences.isEnableAutodownload()); - cbxAutoDownload.setChecked(feed.getPreferences().getAutoDownload()); + 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(); } }); + spnAutoDelete.setOnItemSelectedListener(new OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { + FeedPreferences.AutoDeleteAction auto_delete_action; + switch (parent.getSelectedItemPosition()) { + case 0: + auto_delete_action = FeedPreferences.AutoDeleteAction.GLOBAL; + break; + + case 1: + auto_delete_action = FeedPreferences.AutoDeleteAction.YES; + break; + + case 2: + auto_delete_action = FeedPreferences.AutoDeleteAction.NO; + break; + + default: // TODO - add exceptions here + return; + } + feed.getPreferences().setAutoDeleteAction(auto_delete_action);// p + autoDeleteChanged = true; + } + @Override + public void onNothingSelected(AdapterView<?> parent) { + // Another interface callback + } + }); + spnAutoDelete.setSelection(prefs.getAutoDeleteAction().ordinal()); - etxtUsername.setText(feed.getPreferences().getUsername()); - etxtPassword.setText(feed.getPreferences().getPassword()); + etxtUsername.setText(prefs.getUsername()); + etxtPassword.setText(prefs.getPassword()); etxtUsername.addTextChangedListener(authTextWatcher); etxtPassword.addTextChangedListener(authTextWatcher); @@ -181,13 +231,18 @@ public class FeedInfoActivity extends ActionBarActivity { @Override protected void onPause() { super.onPause(); - if (feed != null && authInfoChanged) { - Log.d(TAG, "Auth info changed, saving credentials"); + if (feed != null) { FeedPreferences prefs = feed.getPreferences(); - prefs.setUsername(etxtUsername.getText().toString()); - prefs.setPassword(etxtPassword.getText().toString()); - DBWriter.setFeedPreferences(this, prefs); + if (authInfoChanged) { + Log.d(TAG, "Auth info changed, saving credentials"); + prefs.setUsername(etxtUsername.getText().toString()); + prefs.setPassword(etxtPassword.getText().toString()); + } + if (authInfoChanged || autoDeleteChanged) { + DBWriter.setFeedPreferences(this, prefs); + } authInfoChanged = false; + autoDeleteChanged = false; } } @@ -205,7 +260,8 @@ public class FeedInfoActivity extends ActionBarActivity { menu.findItem(R.id.support_item).setVisible( feed != null && feed.getPaymentLink() != null); menu.findItem(R.id.share_link_item).setVisible(feed != null && feed.getLink() != null); - menu.findItem(R.id.visit_website_item).setVisible(feed != null && feed.getLink() != null); + menu.findItem(R.id.visit_website_item).setVisible(feed != null && feed.getLink() != null && + IntentUtils.isCallable(this, new Intent(Intent.ACTION_VIEW, Uri.parse(feed.getLink())))); return true; } @@ -226,4 +282,25 @@ public class FeedInfoActivity extends ActionBarActivity { return super.onOptionsItemSelected(item); } } + + private class ApplyToEpisodesDialog extends ConfirmationDialog { + + private final Feed feed; + private final boolean autoDownload; + + public ApplyToEpisodesDialog(Context context, Feed feed, boolean autoDownload) { + super(context, R.string.auto_download_apply_to_items_title, + R.string.auto_download_apply_to_items_message); + this.feed = feed; + this.autoDownload = autoDownload; + setPositiveText(R.string.yes); + setNegativeText(R.string.no); + } + + @Override + public void onConfirmButtonPressed(DialogInterface dialog) { + DBWriter.setFeedsItemsAutoDownload(context, feed, autoDownload); + } + } + } 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 254749cd6..7eee1558b 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java @@ -1,6 +1,7 @@ package de.danoeh.antennapod.activity; import android.app.AlertDialog; +import android.app.ProgressDialog; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; @@ -19,6 +20,8 @@ import android.support.v7.app.ActionBarActivity; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.widget.Toolbar; import android.util.Log; +import android.view.ContextMenu; +import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; @@ -31,11 +34,15 @@ import java.util.List; import de.danoeh.antennapod.R; 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.event.ProgressEvent; import de.danoeh.antennapod.core.feed.EventDistributor; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.QueueEvent; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.util.StorageUtils; import de.danoeh.antennapod.fragment.AddFeedFragment; import de.danoeh.antennapod.fragment.AllEpisodesFragment; @@ -92,9 +99,10 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity private ActionBarDrawerToggle drawerToggle; - private CharSequence drawerTitle; private CharSequence currentTitle; + private ProgressDialog pd; + @Override public void onCreate(Bundle savedInstanceState) { setTheme(UserPreferences.getNoTitleTheme()); @@ -107,7 +115,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity setSupportActionBar(toolbar); getSupportActionBar().setElevation(3.0f); - drawerTitle = currentTitle = getTitle(); + currentTitle = getTitle(); drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); navList = (ListView) findViewById(R.id.nav_list); @@ -136,6 +144,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity navList.setAdapter(navAdapter); navList.setOnItemClickListener(navListClickListener); navList.setOnItemLongClickListener(newListLongClickListener); + registerForContextMenu(navList); navAdapter.registerDataSetObserver(new DataSetObserver() { @Override @@ -161,9 +170,9 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity String lastFragment = getLastNavFragment(); if(ArrayUtils.contains(NAV_DRAWER_TAGS, lastFragment)) { loadFragment(lastFragment, null); + } else { + loadFeedFragmentById(Integer.valueOf(lastFragment), null); } - // else: lastFragment contains feed id - drawer data is not loaded yet, - // so loading is postponed until then } externalPlayerFragment = new ExternalPlayerFragment(); transaction.replace(R.id.playerFragment, externalPlayerFragment); @@ -173,6 +182,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity } private void saveLastNavFragment(String tag) { + Log.d(TAG, "saveLastNavFragment(tag: " + tag +")"); SharedPreferences prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE); SharedPreferences.Editor edit = prefs.edit(); if(tag != null) { @@ -185,7 +195,9 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity private String getLastNavFragment() { SharedPreferences prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE); - return prefs.getString(PREF_LAST_FRAGMENT_TAG, QueueFragment.TAG); + String lastFragment = prefs.getString(PREF_LAST_FRAGMENT_TAG, QueueFragment.TAG); + Log.d(TAG, "getLastNavFragment() -> " + lastFragment); + return lastFragment; } private void checkFirstLaunch() { @@ -251,6 +263,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity } public void loadFragment(int index, Bundle args) { + Log.d(TAG, "loadFragment(index: " + index + ", args: " + args +")"); if (index < navAdapter.getSubscriptionOffset()) { String tag = navAdapter.getTags().get(index); loadFragment(tag, args); @@ -261,7 +274,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity } public void loadFragment(final String tag, Bundle args) { - Log.d(TAG, "loadFragment(\"" + tag + "\", " + args + ")"); + Log.d(TAG, "loadFragment(tag: " + tag + ", args: " + args + ")"); Fragment fragment = null; switch (tag) { case QueueFragment.TAG: @@ -297,32 +310,20 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity return; } Feed feed = itemAccess.getItem(relPos); - long feedId = feed.getId(); + loadFeedFragmentById(feed.getId(), args); + } + + public void loadFeedFragmentById(long feedId, Bundle args) { Fragment fragment = ItemlistFragment.newInstance(feedId); if(args != null) { fragment.setArguments(args); } - saveLastNavFragment(String.valueOf(feed.getId())); + saveLastNavFragment(String.valueOf(feedId)); currentTitle = ""; getSupportActionBar().setTitle(currentTitle); loadFragment(fragment); } - public void loadFeedFragmentById(long feedId) { - if (navDrawerData != null) { - int relPos = -1; - List<Feed> feeds = navDrawerData.feeds; - for (int i = 0; relPos < 0 && i < feeds.size(); i++) { - if (feeds.get(i).getId() == feedId) { - relPos = i; - } - } - if(relPos >= 0) { - loadFeedFragmentByPosition(relPos, null); - } - } - } - private void loadFragment(Fragment fragment) { FragmentManager fragmentManager = getSupportFragmentManager(); // clear back stack @@ -362,15 +363,19 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity } private int getSelectedNavListIndex() { - String lastFragment = getLastNavFragment(); - int tagIndex = navAdapter.getTags().indexOf(lastFragment); + String currentFragment = getLastNavFragment(); + if(currentFragment == null) { + // should not happen, but better safe than sorry + return -1; + } + int tagIndex = navAdapter.getTags().indexOf(currentFragment); if(tagIndex >= 0) { return tagIndex; - } else if(ArrayUtils.contains(NAV_DRAWER_TAGS, lastFragment)) { + } else if(ArrayUtils.contains(NAV_DRAWER_TAGS, currentFragment)) { // the fragment was just hidden return -1; } else { // last fragment was not a list, but a feed - long feedId = Long.parseLong(lastFragment); + long feedId = Long.parseLong(currentFragment); if (navDrawerData != null) { List<Feed> feeds = navDrawerData.feeds; for (int i = 0; i < feeds.size(); i++) { @@ -465,6 +470,9 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity cancelLoadTask(); EventDistributor.getInstance().unregister(contentUpdate); EventBus.getDefault().unregister(this); + if(pd != null) { + pd.dismiss(); + } } @Override @@ -481,6 +489,67 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity } } + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + if(v.getId() != R.id.nav_list) { + return; + } + AdapterView.AdapterContextMenuInfo adapterInfo = (AdapterView.AdapterContextMenuInfo) menuInfo; + int position = adapterInfo.position; + if(position < navAdapter.getSubscriptionOffset()) { + return; + } + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.nav_feed_context, menu); + 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! + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); + if(menuInfo.targetView.getParent() instanceof ListView == false + || ((ListView)menuInfo.targetView.getParent()).getId() != R.id.nav_list) { + return false; + } + final int position = menuInfo.position; + Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset()); + switch(item.getItemId()) { + case R.id.mark_all_seen_item: + DBWriter.markFeedSeen(this, feed.getId()); + return true; + case R.id.mark_all_read_item: + DBWriter.markFeedRead(this, feed.getId()); + return true; + case R.id.remove_item: + final FeedRemover remover = new FeedRemover(this, feed) { + @Override + protected void onPostExecute(Void result) { + super.onPostExecute(result); + if(getSelectedNavListIndex() == position) { + loadFragment(NewEpisodesFragment.TAG, null); + } + } + }; + ConfirmationDialog conDialog = new ConfirmationDialog(this, + R.string.remove_feed_label, + R.string.feed_delete_confirmation_msg) { + @Override + public void onConfirmButtonPressed( + DialogInterface dialog) { + dialog.dismiss(); + remover.executeAsync(); + } + }; + conDialog.createNewDialog().show(); + return true; + default: + return super.onContextItemSelected(item); + } + } + private DBReader.NavDrawerData navDrawerData; private AsyncTask<Void, Void, DBReader.NavDrawerData> loadTask; private int selectedNavListIndex = 0; @@ -520,8 +589,8 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity } @Override - public int getNumberOfUnreadFeedItems(long feedId) { - return (navDrawerData != null) ? navDrawerData.numUnreadFeedItems.get(feedId) : 0; + public int getFeedCounter(long feedId) { + return navDrawerData != null ? navDrawerData.feedCounters.get(feedId) : 0; } }; @@ -542,13 +611,6 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity navDrawerData = result; navAdapter.notifyDataSetChanged(); - String lastFragment = getLastNavFragment(); - if(!ArrayUtils.contains(NAV_DRAWER_TAGS, lastFragment)) { - long feedId = Long.valueOf(lastFragment); - loadFeedFragmentById(feedId); - saveLastNavFragment(null); - } - if (handleIntent) { handleNavIntent(); } @@ -568,6 +630,24 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity loadData(); } + public void onEventMainThread(ProgressEvent event) { + Log.d(TAG, "onEvent(" + event + ")"); + switch(event.action) { + case START: + pd = new ProgressDialog(this); + pd.setMessage(event.message); + pd.setIndeterminate(true); + pd.setCancelable(false); + pd.show(); + break; + case END: + if(pd != null) { + pd.dismiss(); + } + break; + } + } + private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { @Override 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 0cd388b9d..b83e9bc15 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java @@ -278,6 +278,7 @@ public abstract class MediaplayerActivity extends ActionBarActivity startActivity(intent); return true; } else if (media != null) { + FeedItem feedItem = ((FeedMedia) media).getItem(); switch (item.getItemId()) { case R.id.disable_sleeptimer_item: if (controller.serviceAvailable()) { @@ -333,12 +334,20 @@ public abstract class MediaplayerActivity extends ActionBarActivity break; case R.id.support_item: if (media instanceof FeedMedia) { - FeedItem feedItem = ((FeedMedia) media).getItem(); DBTasks.flattrItemIfLoggedIn(this, feedItem); } break; case R.id.share_link_item: - ShareUtils.shareLink(this, media.getWebsiteLink()); + ShareUtils.shareFeedItemLink(this, feedItem); + break; + case R.id.share_download_url_item: + ShareUtils.shareFeedItemDownloadLink(this, feedItem); + break; + case R.id.share_link_with_position_item: + ShareUtils.shareFeedItemLink(this, feedItem, true); + break; + case R.id.share_download_url_with_position_item: + ShareUtils.shareFeedItemDownloadLink(this, feedItem, true); break; case R.id.skip_episode_item: sendBroadcast(new Intent( @@ -471,7 +480,7 @@ public abstract class MediaplayerActivity extends ActionBarActivity checked = i; } choices[i] = String.valueOf(values[i]) + " " - + getString(R.string.time_unit_seconds); + + getString(R.string.time_seconds); } choice = values[checked]; AlertDialog.Builder builder = new AlertDialog.Builder(MediaplayerActivity.this); @@ -519,7 +528,7 @@ public abstract class MediaplayerActivity extends ActionBarActivity checked = i; } choices[i] = String.valueOf(values[i]) + " " - + getString(R.string.time_unit_seconds); + + getString(R.string.time_seconds); } choice = values[checked]; AlertDialog.Builder builder = new AlertDialog.Builder(MediaplayerActivity.this); diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java index 2b1b13ae6..3ab384012 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java @@ -180,7 +180,7 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity { url = URLChecker.prepareURL(url); feed = new Feed(url, new Date(0)); if (username != null && password != null) { - feed.setPreferences(new FeedPreferences(0, false, username, password)); + feed.setPreferences(new FeedPreferences(0, false, FeedPreferences.AutoDeleteAction.GLOBAL, username, password)); } String fileUrl = new File(getExternalCacheDir(), FileNameGenerator.generateFileName(feed.getDownload_url())).toString(); diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java index feb3989bc..2ec987d1a 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java @@ -2,7 +2,6 @@ package de.danoeh.antennapod.activity; import android.content.ActivityNotFoundException; import android.content.Intent; -import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.Bundle; import android.util.Log; @@ -18,11 +17,10 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStreamReader; import java.io.Reader; -import java.util.List; -import de.danoeh.antennapod.BuildConfig; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.LangUtils; import de.danoeh.antennapod.core.util.StorageUtils; @@ -72,38 +70,33 @@ public class OpmlImportFromPathActivity extends OpmlImportBaseActivity { int nextOption = 1; intentPickAction = new Intent(Intent.ACTION_PICK); intentPickAction.setData(Uri.parse("file://")); - List<ResolveInfo> intentActivities = getPackageManager() - .queryIntentActivities(intentPickAction, CHOOSE_OPML_FILE); - if(intentActivities.size() == 0) { - intentPickAction.setData(null); - intentActivities = getPackageManager() - .queryIntentActivities(intentPickAction, CHOOSE_OPML_FILE); - if(intentActivities.size() == 0) { - txtvHeaderExplanation1.setVisibility(View.GONE); - txtvExplanation1.setVisibility(View.GONE); - findViewById(R.id.divider1).setVisibility(View.GONE); - butChooseFilesystem.setVisibility(View.GONE); - } + + if(false == IntentUtils.isCallable(getApplicationContext(), intentPickAction)) { + intentPickAction.setData(null); + if(false == IntentUtils.isCallable(getApplicationContext(), intentPickAction)) { + txtvHeaderExplanation1.setVisibility(View.GONE); + txtvExplanation1.setVisibility(View.GONE); + findViewById(R.id.divider1).setVisibility(View.GONE); + butChooseFilesystem.setVisibility(View.GONE); } + } if(txtvExplanation1.getVisibility() == View.VISIBLE) { - txtvHeaderExplanation1.setText("Option " + nextOption); - nextOption++; - } + txtvHeaderExplanation1.setText("Option " + nextOption); + nextOption++; + } intentGetContentAction = new Intent(Intent.ACTION_GET_CONTENT); intentGetContentAction.addCategory(Intent.CATEGORY_OPENABLE); intentGetContentAction.setType("*/*"); - intentActivities = getPackageManager() - .queryIntentActivities(intentGetContentAction, CHOOSE_OPML_FILE); - if(intentActivities.size() == 0) { - txtvHeaderExplanation2.setVisibility(View.GONE); - txtvExplanation2.setVisibility(View.GONE); - findViewById(R.id.divider2).setVisibility(View.GONE); - butChooseExternal.setVisibility(View.GONE); - } else { - txtvHeaderExplanation2.setText("Option " + nextOption); - nextOption++; - } + if(false == IntentUtils.isCallable(getApplicationContext(), intentGetContentAction)) { + txtvHeaderExplanation2.setVisibility(View.GONE); + txtvExplanation2.setVisibility(View.GONE); + findViewById(R.id.divider2).setVisibility(View.GONE); + butChooseExternal.setVisibility(View.GONE); + } else { + txtvHeaderExplanation2.setText("Option " + nextOption); + nextOption++; + } txtvHeaderExplanation3.setText("Option " + nextOption); } @@ -137,7 +130,7 @@ public class OpmlImportFromPathActivity extends OpmlImportBaseActivity { try { mReader = new InputStreamReader(new FileInputStream(file), LangUtils.UTF_8); - if (BuildConfig.DEBUG) Log.d(TAG, "Parsing " + file.toString()); + Log.d(TAG, "Parsing " + file.toString()); startImport(mReader); } catch (FileNotFoundException e) { Log.d(TAG, "File not found which really should be there"); diff --git a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java index 3802de2a6..94970d833 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java @@ -60,10 +60,13 @@ public class PreferenceActivity extends ActionBarActivity { root.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); setContentView(root); - prefFragment = new MainFragment(); - getFragmentManager().beginTransaction().replace(R.id.content, prefFragment).commit(); + // we need to create the PreferenceController before the MainFragment + // since the MainFragment depends on the preferenceController already being created preferenceController = new PreferenceController(preferenceUI); + + prefFragment = new MainFragment(); + getFragmentManager().beginTransaction().replace(R.id.content, prefFragment).commit(); } @Override diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java b/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java index 8e347a819..aca2b359a 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java @@ -88,7 +88,7 @@ public class ActionButtonUtils { butSecondary.setContentDescription(context.getString(labels[0])); } } else { - if (item.isRead()) { + if (item.isPlayed()) { butSecondary.setVisibility(View.INVISIBLE); } else { butSecondary.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java b/app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java index e22b31361..d3ee08546 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java @@ -36,18 +36,19 @@ public class AdapterUtils { || state == FeedItem.State.IN_PROGRESS) { if (media.getDuration() > 0) { episodeProgress.setVisibility(View.VISIBLE); - episodeProgress - .setProgress((int) (((double) media + episodeProgress.setProgress((int) (((double) media .getPosition()) / media.getDuration() * 100)); - txtvPos.setText(Converter - .getDurationStringLong(media.getDuration() + txtvPos.setText(Converter.getDurationStringLong(media.getDuration() - media.getPosition())); } } else if (!media.isDownloaded()) { - txtvPos.setText(Converter.byteToString(media.getSize())); + if(media.getSize() > 0) { + txtvPos.setText(Converter.byteToString(media.getSize())); + } else { + txtvPos.setText(""); + } } else { - txtvPos.setText(Converter.getDurationStringLong(media - .getDuration())); + txtvPos.setText(Converter.getDurationStringLong(media.getDuration())); } } } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesListAdapter.java index d96326053..4b6ee8f91 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesListAdapter.java @@ -1,6 +1,8 @@ package de.danoeh.antennapod.adapter; import android.content.Context; +import android.graphics.drawable.Drawable; +import android.net.Uri; import android.text.format.DateUtils; import android.view.LayoutInflater; import android.view.View; @@ -11,11 +13,18 @@ import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; -import com.squareup.picasso.Picasso; +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.bumptech.glide.load.resource.drawable.GlideDrawable; +import com.bumptech.glide.request.animation.GlideAnimation; +import com.bumptech.glide.request.target.GlideDrawableImageViewTarget; + +import java.lang.ref.WeakReference; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; +import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.util.Converter; @@ -72,6 +81,7 @@ public class AllEpisodesListAdapter extends BaseAdapter { .getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.new_episodes_listitem, parent, false); + holder.placeholder = (TextView) convertView.findViewById(R.id.txtvPlaceholder); holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); holder.pubDate = (TextView) convertView .findViewById(R.id.txtvPublished); @@ -82,16 +92,18 @@ public class AllEpisodesListAdapter extends BaseAdapter { .findViewById(R.id.imgvInPlaylist); holder.progress = (ProgressBar) convertView .findViewById(R.id.pbar_progress); - holder.imageView = (ImageView) convertView.findViewById(R.id.imgvImage); + holder.cover = (ImageView) convertView.findViewById(R.id.imgvCover); holder.txtvDuration = (TextView) convertView.findViewById(R.id.txtvDuration); convertView.setTag(holder); } else { holder = (Holder) convertView.getTag(); } + holder.placeholder.setVisibility(View.VISIBLE); + holder.placeholder.setText(item.getFeed().getTitle()); holder.title.setText(item.getTitle()); holder.pubDate.setText(DateUtils.formatDateTime(context, item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL)); - if (showOnlyNewEpisodes || item.isRead() || false == itemAccess.isNew(item)) { + if (showOnlyNewEpisodes || false == item.isNew()) { holder.statusUnread.setVisibility(View.INVISIBLE); } else { holder.statusUnread.setVisibility(View.VISIBLE); @@ -141,14 +153,54 @@ public class AllEpisodesListAdapter extends BaseAdapter { holder.butSecondary.setTag(item); holder.butSecondary.setOnClickListener(secondaryActionListener); - Picasso.with(context) + Glide.with(context) .load(item.getImageUri()) - .fit() - .into(holder.imageView); + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate() + .into(new CoverTarget(item.getFeed().getImageUri(), holder.placeholder, holder.cover)); return convertView; } + private class CoverTarget extends GlideDrawableImageViewTarget { + + private final WeakReference<Uri> fallback; + private final WeakReference<TextView> placeholder; + private final WeakReference<ImageView> cover; + + public CoverTarget(Uri fallbackUri, TextView txtvPlaceholder, ImageView imgvCover) { + super(imgvCover); + fallback = new WeakReference<>(fallbackUri); + placeholder = new WeakReference<>(txtvPlaceholder); + cover = new WeakReference<>(imgvCover); + } + + @Override + public void onLoadFailed(Exception e, Drawable errorDrawable) { + Uri fallbackUri = fallback.get(); + TextView txtvPlaceholder = placeholder.get(); + ImageView imgvCover = cover.get(); + if(fallbackUri != null && txtvPlaceholder != null && imgvCover != null) { + Glide.with(context) + .load(fallbackUri) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate() + .into(new CoverTarget(null, txtvPlaceholder, imgvCover)); + } + } + + @Override + public void onResourceReady(GlideDrawable drawable, GlideAnimation anim) { + super.onResourceReady(drawable, anim); + TextView txtvPlaceholder = placeholder.get(); + if(txtvPlaceholder != null) { + txtvPlaceholder.setVisibility(View.INVISIBLE); + } + } + } + private View.OnClickListener secondaryActionListener = new View.OnClickListener() { @Override public void onClick(View v) { @@ -159,11 +211,12 @@ public class AllEpisodesListAdapter extends BaseAdapter { static class Holder { + TextView placeholder; TextView title; TextView pubDate; View statusUnread; ImageView queueStatus; - ImageView imageView; + ImageView cover; ProgressBar progress; TextView txtvDuration; ImageButton butSecondary; @@ -179,7 +232,5 @@ public class AllEpisodesListAdapter extends BaseAdapter { boolean isInQueue(FeedItem item); - boolean isNew(FeedItem item); - } } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java b/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java index 223fe17f6..c3486f2f2 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java @@ -12,11 +12,8 @@ import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator; 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.gpoddernet.model.GpodnetEpisodeAction; -import de.danoeh.antennapod.core.preferences.GpodnetPreferences; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DBWriter; @@ -61,7 +58,7 @@ public class DefaultActionButtonCallback implements ActionButtonCallback { boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media); if (!isDownloading && !media.isDownloaded()) { LongList queueIds = DBReader.getQueueIDList(context); - if (NetworkUtils.isDownloadAllowed(context) || userAllowedMobileDownloads()) { + if (NetworkUtils.isDownloadAllowed() || userAllowedMobileDownloads()) { try { DBTasks.downloadFeedItems(context, item); Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show(); @@ -95,21 +92,8 @@ public class DefaultActionButtonCallback implements ActionButtonCallback { } } } else { - if (!item.isRead()) { + if (!item.isPlayed()) { DBWriter.markItemRead(context, item, true, true); - - if(GpodnetPreferences.loggedIn()) { - // gpodder: send played action - FeedMedia media = item.getMedia(); - GpodnetEpisodeAction action = new GpodnetEpisodeAction.Builder(item, GpodnetEpisodeAction.Action.PLAY) - .currentDeviceId() - .currentTimestamp() - .started(media.getDuration() / 1000) - .position(media.getDuration() / 1000) - .total(media.getDuration() / 1000) - .build(); - GpodnetPreferences.enqueueEpisodeAction(action); - } } } } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java index f29cfdf2f..0eb15da8c 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java @@ -124,11 +124,15 @@ public class DownloadLogAdapter extends BaseAdapter { ButtonHolder holder = (ButtonHolder) v.getTag(); if(holder.typeId == Feed.FEEDFILETYPE_FEED) { Feed feed = DBReader.getFeed(context, holder.id); - feed.setLastUpdate(new Date(0)); // force refresh - try { - DBTasks.refreshFeed(context, feed); - } catch (DownloadRequestException e) { - e.printStackTrace(); + if (feed != null) { + feed.setLastUpdate(new Date(0)); // force refresh + try { + DBTasks.refreshFeed(context, feed); + } catch (DownloadRequestException e) { + e.printStackTrace(); + } + } else { + Log.wtf(TAG, "Could not find feed for feed id: " + holder.id); } } else if(holder.typeId == FeedMedia.FEEDFILETYPE_FEEDMEDIA) { FeedMedia media = DBReader.getFeedMedia(context, holder.id); diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java index 15e0a7a33..2b1eccea5 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java @@ -10,10 +10,12 @@ import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; -import com.squareup.picasso.Picasso; +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.engine.DiskCacheStrategy; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.feed.FeedItem; +import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.util.Converter; /** @@ -88,9 +90,13 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter { holder.butSecondary.setOnClickListener(secondaryActionListener); - Picasso.with(context) + Glide.with(context) .load(item.getImageUri()) - .fit() + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate() .into(holder.imageView); return convertView; diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java index b39e23d42..56e2bb1bd 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java @@ -33,13 +33,17 @@ public class FeedItemlistAdapter extends BaseAdapter { private final Context context; private boolean showFeedtitle; private int selectedItemIndex; + /** true if played items should be made partially transparent */ + private boolean makePlayedItemsTransparent; private final ActionButtonUtils actionButtonUtils; public static final int SELECTION_NONE = -1; public FeedItemlistAdapter(Context context, ItemAccess itemAccess, - ActionButtonCallback callback, boolean showFeedtitle) { + ActionButtonCallback callback, + boolean showFeedtitle, + boolean makePlayedItemsTransparent) { super(); this.callback = callback; this.context = context; @@ -47,6 +51,7 @@ public class FeedItemlistAdapter extends BaseAdapter { this.showFeedtitle = showFeedtitle; this.selectedItemIndex = SELECTION_NONE; this.actionButtonUtils = new ActionButtonUtils(context); + this.makePlayedItemsTransparent = makePlayedItemsTransparent; } @Override @@ -106,18 +111,18 @@ public class FeedItemlistAdapter extends BaseAdapter { StringBuilder buffer = new StringBuilder(item.getTitle()); if (showFeedtitle) { - buffer.append("("); + buffer.append(" ("); buffer.append(item.getFeed().getTitle()); buffer.append(")"); } holder.title.setText(buffer.toString()); - if(false == item.isRead() && itemAccess.isNew(item)) { + if(item.isNew()) { holder.statusUnread.setVisibility(View.VISIBLE); } else { holder.statusUnread.setVisibility(View.INVISIBLE); } - if(item.isRead()) { + if(item.isPlayed() && makePlayedItemsTransparent) { ViewHelper.setAlpha(convertView, 0.5f); } else { ViewHelper.setAlpha(convertView, 1.0f); @@ -180,7 +185,6 @@ public class FeedItemlistAdapter extends BaseAdapter { convertView.setVisibility(View.GONE); } return convertView; - } private final OnClickListener butActionListener = new OnClickListener() { @@ -221,8 +225,6 @@ public class FeedItemlistAdapter extends BaseAdapter { FeedItem getItem(int position); - boolean isNew(FeedItem item); - } } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java index 0d2d5cfa0..3c91cbbbb 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java @@ -15,7 +15,8 @@ import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; -import com.squareup.picasso.Picasso; +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.engine.DiskCacheStrategy; import org.apache.commons.lang3.ArrayUtils; @@ -27,6 +28,7 @@ import java.util.List; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.core.feed.Feed; +import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.fragment.AddFeedFragment; import de.danoeh.antennapod.fragment.AllEpisodesFragment; @@ -259,9 +261,13 @@ public class NavListAdapter extends BaseAdapter holder = (FeedHolder) convertView.getTag(); } - Picasso.with(context) + Glide.with(context) .load(feed.getImageUri()) - .fit() + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate() .into(holder.image); holder.title.setText(feed.getTitle()); @@ -276,10 +282,10 @@ public class NavListAdapter extends BaseAdapter p.addRule(RelativeLayout.LEFT_OF, R.id.txtvCount); holder.failure.setVisibility(View.GONE); } - int feedUnreadItems = itemAccess.getNumberOfUnreadFeedItems(feed.getId()); - if(feedUnreadItems > 0) { + int counter = itemAccess.getFeedCounter(feed.getId()); + if(counter > 0) { holder.count.setVisibility(View.VISIBLE); - holder.count.setText(String.valueOf(feedUnreadItems)); + holder.count.setText(String.valueOf(counter)); holder.count.setTypeface(holder.title.getTypeface()); } else { holder.count.setVisibility(View.GONE); @@ -306,7 +312,7 @@ public class NavListAdapter extends BaseAdapter int getSelectedItemIndex(); int getQueueSize(); int getNumberOfNewItems(); - int getNumberOfUnreadFeedItems(long feedId); + int getFeedCounter(long feedId); } } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java index bba5a00a9..60c125fda 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java @@ -1,7 +1,10 @@ package de.danoeh.antennapod.adapter; import android.content.Context; +import android.graphics.drawable.Drawable; +import android.net.Uri; import android.text.format.DateUtils; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -11,11 +14,18 @@ import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; -import com.squareup.picasso.Picasso; +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.bumptech.glide.load.resource.drawable.GlideDrawable; +import com.bumptech.glide.request.animation.GlideAnimation; +import com.bumptech.glide.request.target.GlideDrawableImageViewTarget; + +import java.lang.ref.WeakReference; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; +import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.util.Converter; @@ -25,6 +35,7 @@ import de.danoeh.antennapod.core.util.Converter; */ public class QueueListAdapter extends BaseAdapter { + private static final String TAG = QueueListAdapter.class.getSimpleName(); private final Context context; private final ItemAccess itemAccess; @@ -76,7 +87,8 @@ public class QueueListAdapter extends BaseAdapter { convertView = inflater.inflate(R.layout.queue_listitem, parent, false); holder.dragHandle = (ImageView) convertView.findViewById(R.id.drag_handle); - holder.imageView = (ImageView) convertView.findViewById(R.id.imgvImage); + holder.placeholder = (TextView) convertView.findViewById(R.id.txtvPlaceholder); + holder.cover = (ImageView) convertView.findViewById(R.id.imgvCover); holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); holder.pubDate = (TextView) convertView.findViewById(R.id.txtvPubDate); holder.progressLeft = (TextView) convertView.findViewById(R.id.txtvProgressLeft); @@ -86,7 +98,6 @@ public class QueueListAdapter extends BaseAdapter { .findViewById(R.id.butSecondaryAction); holder.progress = (ProgressBar) convertView .findViewById(R.id.progressBar); - holder.imageView = (ImageView) convertView.findViewById(R.id.imgvImage); convertView.setTag(holder); } else { holder = (Holder) convertView.getTag(); @@ -98,10 +109,11 @@ public class QueueListAdapter extends BaseAdapter { holder.dragHandle.setVisibility(View.VISIBLE); } + holder.placeholder.setText(item.getFeed().getTitle()); + holder.title.setText(item.getTitle()); FeedMedia media = item.getMedia(); - holder.title.setText(item.getTitle()); String pubDate = DateUtils.formatDateTime(context, item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL); holder.pubDate.setText(pubDate.replace(" ", "\n")); @@ -129,7 +141,11 @@ public class QueueListAdapter extends BaseAdapter { holder.progressRight.setText(Converter.getDurationStringLong(media.getDuration())); } } else { - holder.progressLeft.setText(Converter.byteToString(media.getSize())); + if(media.getSize() > 0) { + holder.progressLeft.setText(Converter.byteToString(media.getSize())); + } else { + holder.progressLeft.setText(""); + } holder.progressRight.setText(Converter.getDurationStringLong(media.getDuration())); holder.progress.setVisibility(View.GONE); } @@ -140,14 +156,54 @@ public class QueueListAdapter extends BaseAdapter { holder.butSecondary.setTag(item); holder.butSecondary.setOnClickListener(secondaryActionListener); - Picasso.with(context) + Glide.with(context) .load(item.getImageUri()) - .fit() - .into(holder.imageView); + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate() + .into(new CoverTarget(item.getFeed().getImageUri(), holder.placeholder, holder.cover)); return convertView; } + private class CoverTarget extends GlideDrawableImageViewTarget { + + private final WeakReference<Uri> fallback; + private final WeakReference<TextView> placeholder; + private final WeakReference<ImageView> cover; + + public CoverTarget(Uri fallbackUri, TextView txtvPlaceholder, ImageView imgvCover) { + super(imgvCover); + fallback = new WeakReference<>(fallbackUri); + placeholder = new WeakReference<>(txtvPlaceholder); + cover = new WeakReference<>(imgvCover); + } + + @Override + public void onLoadFailed(Exception e, Drawable errorDrawable) { + Uri fallbackUri = fallback.get(); + TextView txtvPlaceholder = placeholder.get(); + ImageView imgvCover = cover.get(); + if(fallbackUri != null && txtvPlaceholder != null && imgvCover != null) { + Glide.with(context) + .load(fallbackUri) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate() + .into(new CoverTarget(null, txtvPlaceholder, imgvCover)); + } + } + + @Override + public void onResourceReady(GlideDrawable drawable, GlideAnimation anim) { + super.onResourceReady(drawable, anim); + TextView txtvPlaceholder = placeholder.get(); + if(txtvPlaceholder != null) { + txtvPlaceholder.setVisibility(View.INVISIBLE); + } + } + } + private View.OnClickListener secondaryActionListener = new View.OnClickListener() { @Override public void onClick(View v) { @@ -156,10 +212,10 @@ public class QueueListAdapter extends BaseAdapter { } }; - static class Holder { ImageView dragHandle; - ImageView imageView; + ImageView cover; + TextView placeholder; TextView title; TextView pubDate; TextView progressLeft; diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java index cedce7903..83f5dcb4d 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java @@ -8,13 +8,15 @@ import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; -import com.squareup.picasso.Picasso; +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.engine.DiskCacheStrategy; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.FeedComponent; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.SearchResult; +import de.danoeh.antennapod.core.glide.ApGlideSettings; /** * List adapter for search activity. @@ -73,9 +75,13 @@ public class SearchlistAdapter extends BaseAdapter { holder.title.setText(feed.getTitle()); holder.subtitle.setVisibility(View.GONE); - Picasso.with(context) + Glide.with(context) .load(feed.getImageUri()) - .fit() + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate() .into(holder.cover); } else if (component.getClass() == FeedItem.class) { @@ -86,9 +92,13 @@ public class SearchlistAdapter extends BaseAdapter { holder.subtitle.setText(result.getSubtitle()); } - Picasso.with(context) + Glide.with(context) .load(item.getFeed().getImageUri()) - .fit() + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate() .into(holder.cover); } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java index b85709c5e..743f9fc86 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java @@ -8,13 +8,15 @@ import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.TextView; -import com.squareup.picasso.Picasso; +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.engine.DiskCacheStrategy; import org.apache.commons.lang3.StringUtils; import java.util.List; import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.gpoddernet.model.GpodnetPodcast; /** @@ -49,9 +51,13 @@ public class PodcastListAdapter extends ArrayAdapter<GpodnetPodcast> { } if (StringUtils.isNotBlank(podcast.getLogoUrl())) { - Picasso.with(convertView.getContext()) + Glide.with(convertView.getContext()) .load(podcast.getLogoUrl()) - .fit() + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate() .into(holder.image); } 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 4fc2838b7..08ffdd197 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 @@ -1,23 +1,18 @@ package de.danoeh.antennapod.adapter.itunes; import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.os.AsyncTask; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.TextView; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.DefaultHttpClient; +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.engine.DiskCacheStrategy; + import org.json.JSONException; import org.json.JSONObject; -import java.io.IOException; import java.util.List; import de.danoeh.antennapod.R; @@ -46,55 +41,6 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> { this.context = context; } - /** - * Updates the given ImageView with the image in the given Podcast's imageUrl - */ - class FetchImageTask extends AsyncTask<Void,Void,Bitmap>{ - /** - * Current podcast - */ - private final Podcast podcast; - - /** - * ImageView to be updated - */ - private final ImageView imageView; - - /** - * Constructor - * - * @param podcast Podcast that has the image - * @param imageView UI image to be updated - */ - FetchImageTask(Podcast podcast, ImageView imageView){ - this.podcast = podcast; - this.imageView = imageView; - } - - //Get the image from the url - @Override - protected Bitmap doInBackground(Void... params) { - HttpClient client = new DefaultHttpClient(); - HttpGet get = new HttpGet(podcast.imageUrl); - try { - HttpResponse response = client.execute(get); - return BitmapFactory.decodeStream(response.getEntity().getContent()); - } catch (IOException e) { - e.printStackTrace(); - } - return null; - } - - //Set the background image for the podcast - @Override - protected void onPostExecute(Bitmap img) { - super.onPostExecute(img); - if(img!=null) { - imageView.setImageBitmap(img); - } - } - } - @Override public View getView(int position, View convertView, ViewGroup parent) { //Current podcast @@ -121,7 +67,13 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> { viewHolder.titleView.setText(podcast.title); //Update the empty imageView with the image from the feed - new FetchImageTask(podcast,viewHolder.coverView).execute(); + Glide.with(context) + .load(podcast.imageUrl) + .placeholder(R.color.light_gray) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .fitCenter() + .dontAnimate() + .into(viewHolder.coverView); //Feed the grid view return view; diff --git a/app/src/main/java/de/danoeh/antennapod/config/ApplicationCallbacksImpl.java b/app/src/main/java/de/danoeh/antennapod/config/ApplicationCallbacksImpl.java index 4d9be5d78..008aacfa5 100644 --- a/app/src/main/java/de/danoeh/antennapod/config/ApplicationCallbacksImpl.java +++ b/app/src/main/java/de/danoeh/antennapod/config/ApplicationCallbacksImpl.java @@ -8,7 +8,6 @@ import android.content.Intent; import de.danoeh.antennapod.PodcastApp; import de.danoeh.antennapod.activity.StorageErrorActivity; import de.danoeh.antennapod.core.ApplicationCallbacks; -import de.danoeh.antennapod.core.preferences.UserPreferences; public class ApplicationCallbacksImpl implements ApplicationCallbacks { @@ -22,8 +21,4 @@ public class ApplicationCallbacksImpl implements ApplicationCallbacks { return new Intent(context, StorageErrorActivity.class); } - @Override - public void setUpdateInterval(long updateInterval) { - UserPreferences.restartUpdateAlarm(updateInterval, updateInterval); - } } diff --git a/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java b/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java index 10666aa36..932b9d22f 100644 --- a/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java +++ b/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java @@ -14,7 +14,6 @@ public class ClientConfigurator { ClientConfig.downloadServiceCallbacks = new DownloadServiceCallbacksImpl(); ClientConfig.gpodnetCallbacks = new GpodnetCallbacksImpl(); ClientConfig.playbackServiceCallbacks = new PlaybackServiceCallbacksImpl(); - ClientConfig.storageCallbacks = new StorageCallbacksImpl(); ClientConfig.flattrCallbacks = new FlattrCallbacksImpl(); ClientConfig.dbTasksCallbacks = new DBTasksCallbacksImpl(); } diff --git a/app/src/main/java/de/danoeh/antennapod/config/StorageCallbacksImpl.java b/app/src/main/java/de/danoeh/antennapod/config/StorageCallbacksImpl.java deleted file mode 100644 index 943e05690..000000000 --- a/app/src/main/java/de/danoeh/antennapod/config/StorageCallbacksImpl.java +++ /dev/null @@ -1,153 +0,0 @@ -package de.danoeh.antennapod.config; - - -import android.content.ContentValues; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.util.Log; - -import de.danoeh.antennapod.core.StorageCallbacks; -import de.danoeh.antennapod.core.storage.PodDBAdapter; - -public class StorageCallbacksImpl implements StorageCallbacks { - - @Override - public int getDatabaseVersion() { - return 15; - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - Log.w("DBAdapter", "Upgrading from version " + oldVersion + " to " - + newVersion + "."); - if (oldVersion <= 1) { - db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + " ADD COLUMN " - + PodDBAdapter.KEY_TYPE + " TEXT"); - } - if (oldVersion <= 2) { - db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS - + " ADD COLUMN " + PodDBAdapter.KEY_LINK + " TEXT"); - } - if (oldVersion <= 3) { - db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS - + " ADD COLUMN " + PodDBAdapter.KEY_ITEM_IDENTIFIER + " TEXT"); - } - if (oldVersion <= 4) { - db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + " ADD COLUMN " - + PodDBAdapter.KEY_FEED_IDENTIFIER + " TEXT"); - } - if (oldVersion <= 5) { - db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_DOWNLOAD_LOG - + " ADD COLUMN " + PodDBAdapter.KEY_REASON_DETAILED + " TEXT"); - db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_DOWNLOAD_LOG - + " ADD COLUMN " + PodDBAdapter.KEY_DOWNLOADSTATUS_TITLE + " TEXT"); - } - if (oldVersion <= 6) { - db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS - + " ADD COLUMN " + PodDBAdapter.KEY_CHAPTER_TYPE + " INTEGER"); - } - if (oldVersion <= 7) { - db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA - + " ADD COLUMN " + PodDBAdapter.KEY_PLAYBACK_COMPLETION_DATE - + " INTEGER"); - } - if (oldVersion <= 8) { - final int KEY_ID_POSITION = 0; - final int KEY_MEDIA_POSITION = 1; - - // Add feeditem column to feedmedia table - db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA - + " ADD COLUMN " + PodDBAdapter.KEY_FEEDITEM - + " INTEGER"); - Cursor feeditemCursor = db.query(PodDBAdapter.TABLE_NAME_FEED_ITEMS, - new String[]{PodDBAdapter.KEY_ID, PodDBAdapter.KEY_MEDIA}, "? > 0", - new String[]{PodDBAdapter.KEY_MEDIA}, null, null, null); - if (feeditemCursor.moveToFirst()) { - db.beginTransaction(); - ContentValues contentValues = new ContentValues(); - do { - long mediaId = feeditemCursor.getLong(KEY_MEDIA_POSITION); - contentValues.put(PodDBAdapter.KEY_FEEDITEM, feeditemCursor.getLong(KEY_ID_POSITION)); - db.update(PodDBAdapter.TABLE_NAME_FEED_MEDIA, contentValues, PodDBAdapter.KEY_ID + "=?", new String[]{String.valueOf(mediaId)}); - contentValues.clear(); - } while (feeditemCursor.moveToNext()); - db.setTransactionSuccessful(); - db.endTransaction(); - } - feeditemCursor.close(); - } - if (oldVersion <= 9) { - db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS - + " ADD COLUMN " + PodDBAdapter.KEY_AUTO_DOWNLOAD - + " INTEGER DEFAULT 1"); - } - if (oldVersion <= 10) { - db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS - + " ADD COLUMN " + PodDBAdapter.KEY_FLATTR_STATUS - + " INTEGER"); - db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS - + " ADD COLUMN " + PodDBAdapter.KEY_FLATTR_STATUS - + " INTEGER"); - db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA - + " ADD COLUMN " + PodDBAdapter.KEY_PLAYED_DURATION - + " INTEGER"); - } - if (oldVersion <= 11) { - db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS - + " ADD COLUMN " + PodDBAdapter.KEY_USERNAME - + " TEXT"); - db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS - + " ADD COLUMN " + PodDBAdapter.KEY_PASSWORD - + " TEXT"); - db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS - + " ADD COLUMN " + PodDBAdapter.KEY_IMAGE - + " INTEGER"); - } - if (oldVersion <= 12) { - db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS - + " ADD COLUMN " + PodDBAdapter.KEY_IS_PAGED + " INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS - + " ADD COLUMN " + PodDBAdapter.KEY_NEXT_PAGE_LINK + " TEXT"); - } - if (oldVersion <= 13) { - // remove duplicate rows in "Chapters" table that were created because of a bug. - db.execSQL(String.format("DELETE FROM %s WHERE %s NOT IN " + - "(SELECT MIN(%s) as %s FROM %s GROUP BY %s,%s,%s,%s,%s)", - PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS, - PodDBAdapter.KEY_ID, - PodDBAdapter.KEY_ID, - PodDBAdapter.KEY_ID, - PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS, - PodDBAdapter.KEY_TITLE, - PodDBAdapter.KEY_START, - PodDBAdapter.KEY_FEEDITEM, - PodDBAdapter.KEY_LINK, - PodDBAdapter.KEY_CHAPTER_TYPE)); - } - if(oldVersion <= 14) { - - db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS - + " ADD COLUMN " + PodDBAdapter.KEY_AUTO_DOWNLOAD + " INTEGER"); - db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS - + " SET " + PodDBAdapter.KEY_AUTO_DOWNLOAD + " = " - + "(SELECT " + PodDBAdapter.KEY_AUTO_DOWNLOAD - + " FROM " + PodDBAdapter.TABLE_NAME_FEEDS - + " WHERE " + PodDBAdapter.TABLE_NAME_FEEDS + "." + PodDBAdapter.KEY_ID - + " = " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + PodDBAdapter.KEY_FEED + ")"); - - db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS - + " ADD COLUMN " + PodDBAdapter.KEY_HIDE + " TEXT"); - - - db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS - + " ADD COLUMN " + PodDBAdapter.KEY_LAST_UPDATE_FAILED + " INTEGER DEFAULT 0"); - - // create indexes - db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDITEMS_FEED); - db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDITEMS_IMAGE); - db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDMEDIA_FEEDITEM); - db.execSQL(PodDBAdapter.CREATE_INDEX_QUEUE_FEEDITEM); - db.execSQL(PodDBAdapter.CREATE_INDEX_SIMPLECHAPTERS_FEEDITEM); - } - } -} diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java new file mode 100644 index 000000000..8a4a4efbf --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java @@ -0,0 +1,414 @@ +package de.danoeh.antennapod.dialog; + +import android.content.res.TypedArray; +import android.graphics.Color; +import android.os.Bundle; +import android.support.v4.app.ActivityCompat; +import android.support.v4.app.Fragment; +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.ArrayAdapter; +import android.widget.Button; +import android.widget.ListView; +import android.widget.Toast; + +import com.joanzapata.android.iconify.IconDrawable; +import com.joanzapata.android.iconify.Iconify; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator; +import de.danoeh.antennapod.core.feed.FeedItem; +import de.danoeh.antennapod.core.storage.DBTasks; +import de.danoeh.antennapod.core.storage.DBWriter; +import de.danoeh.antennapod.core.storage.DownloadRequestException; +import de.danoeh.antennapod.core.util.LongList; + +public class EpisodesApplyActionFragment extends Fragment { + + public String TAG = "EpisodeActionFragment"; + + private ListView mListView; + private ArrayAdapter<String> mAdapter; + + private Button btnAddToQueue; + private Button btnMarkAsPlayed; + private Button btnMarkAsUnplayed; + private Button btnDownload; + private Button btnDelete; + + private final Map<Long,FeedItem> idMap; + private final List<FeedItem> episodes; + private final List<String> titles = new ArrayList(); + private final LongList checkedIds = new LongList(); + + private MenuItem mSelectToggle; + + private int textColor; + + public EpisodesApplyActionFragment(List<FeedItem> episodes) { + this.episodes = episodes; + this.idMap = new HashMap<>(episodes.size()); + for(FeedItem episode : episodes) { + this.idMap.put(episode.getId(), episode); + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.episodes_apply_action_fragment, container, false); + + mListView = (ListView) view.findViewById(android.R.id.list); + mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); + mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + public void onItemClick(AdapterView<?> ListView, View view, int position, long rowId) { + long id = episodes.get(position).getId(); + if (checkedIds.contains(id)) { + checkedIds.remove(id); + } else { + checkedIds.add(id); + } + refreshCheckboxes(); + } + }); + + for(FeedItem episode : episodes) { + titles.add(episode.getTitle()); + } + + mAdapter = new ArrayAdapter<>(getActivity(), + android.R.layout.simple_list_item_multiple_choice, titles); + mListView.setAdapter(mAdapter); + checkAll(); + + btnAddToQueue = (Button) view.findViewById(R.id.btnAddToQueue); + btnAddToQueue.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + queueChecked(); + } + }); + btnMarkAsPlayed = (Button) view.findViewById(R.id.btnMarkAsPlayed); + btnMarkAsPlayed.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + markedCheckedPlayed(); + } + }); + btnMarkAsUnplayed = (Button) view.findViewById(R.id.btnMarkAsUnplayed); + btnMarkAsUnplayed.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + markedCheckedUnplayed(); + } + }); + btnDownload = (Button) view.findViewById(R.id.btnDownload); + btnDownload.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + downloadChecked(); + } + }); + btnDelete = (Button) view.findViewById(R.id.btnDelete); + btnDelete.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + deleteChecked(); + } + }); + + return view; + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.episodes_apply_action_options, menu); + + int[] attrs = { android.R.attr.textColor }; + TypedArray ta = getActivity().obtainStyledAttributes(attrs); + textColor = ta.getColor(0, Color.GRAY); + ta.recycle(); + + menu.findItem(R.id.sort).setIcon(new IconDrawable(getActivity(), + Iconify.IconValue.fa_sort).color(textColor).actionBarSize()); + + mSelectToggle = menu.findItem(R.id.select_toggle); + mSelectToggle.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + if (checkedIds.size() == episodes.size()) { + checkNone(); + } else { + checkAll(); + } + return true; + } + }); + + menu.findItem(R.id.select_options).setIcon(new IconDrawable(getActivity(), + Iconify.IconValue.fa_caret_down).color(textColor).actionBarSize()); + } + + @Override + public void onPrepareOptionsMenu (Menu menu) { + Iconify.IconValue iVal; + if(checkedIds.size() == episodes.size()) { + iVal = Iconify.IconValue.fa_check_square_o; + } else if(checkedIds.size() == 0) { + iVal = Iconify.IconValue.fa_square_o; + } else { + iVal = Iconify.IconValue.fa_minus_square_o; + } + mSelectToggle.setIcon(new IconDrawable(getActivity(), iVal).color(textColor).actionBarSize()); + + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int resId = 0; + switch(item.getItemId()) { + case R.id.select_options: + return true; + case R.id.check_all: + checkAll(); + resId = R.string.selected_all_label; + break; + case R.id.check_none: + checkNone(); + resId = R.string.deselected_all_label; + break; + case R.id.check_played: + checkPlayed(true); + resId = R.string.selected_played_label; + break; + case R.id.check_unplayed: + checkPlayed(false); + resId = R.string.selected_unplayed_label; + break; + case R.id.check_downloaded: + checkDownloaded(true); + resId = R.string.selected_downloaded_label; + break; + case R.id.check_not_downloaded: + checkDownloaded(false); + resId = R.string.selected_not_downloaded_label; + break; + case R.id.sort_title_a_z: + sortByTitle(false); + return true; + case R.id.sort_title_z_a: + sortByTitle(true); + return true; + case R.id.sort_date_new_old: + sortByDate(true); + return true; + case R.id.sort_date_old_new: + sortByDate(false); + return true; + case R.id.sort_duration_long_short: + sortByDuration(true); + return true; + case R.id.sort_duration_short_long: + sortByDuration(false); + return true; + } + if(resId != 0) { + Toast.makeText(getActivity(), resId, Toast.LENGTH_SHORT).show(); + return true; + } else { + return false; + } + } + + private void sortByTitle(final boolean reverse) { + Collections.sort(episodes, new Comparator<FeedItem>() { + @Override + public int compare(FeedItem lhs, FeedItem rhs) { + if (reverse) { + return -1 * lhs.getTitle().compareTo(rhs.getTitle()); + } else { + return lhs.getTitle().compareTo(rhs.getTitle()); + } + } + }); + refreshTitles(); + refreshCheckboxes(); + } + + private void sortByDate(final boolean reverse) { + Collections.sort(episodes, new Comparator<FeedItem>() { + @Override + public int compare(FeedItem lhs, FeedItem rhs) { + if (lhs.getPubDate() == null) { + return -1; + } else if (rhs.getPubDate() == null) { + return 1; + } + int code = lhs.getPubDate().compareTo(rhs.getPubDate()); + if (reverse) { + return -1 * code; + } else { + return code; + } + } + }); + refreshTitles(); + refreshCheckboxes(); + } + + private void sortByDuration(final boolean reverse) { + Collections.sort(episodes, new Comparator<FeedItem>() { + @Override + public int compare(FeedItem lhs, FeedItem rhs) { + int ordering; + if (false == lhs.hasMedia()) { + ordering = 1; + } else if (false == rhs.hasMedia()) { + ordering = -1; + } else { + ordering = lhs.getMedia().getDuration() - rhs.getMedia().getDuration(); + } + if(reverse) { + return -1 * ordering; + } else { + return ordering; + } + } + }); + refreshTitles(); + refreshCheckboxes(); + } + + private void checkAll() { + for (FeedItem episode : episodes) { + if(false == checkedIds.contains(episode.getId())) { + checkedIds.add(episode.getId()); + } + } + refreshCheckboxes(); + } + + private void checkNone() { + checkedIds.clear(); + refreshCheckboxes(); + } + + private void checkPlayed(boolean isPlayed) { + for (FeedItem episode : episodes) { + if(episode.isPlayed() == isPlayed) { + if(!checkedIds.contains(episode.getId())) { + checkedIds.add(episode.getId()); + } + } else { + if(checkedIds.contains(episode.getId())) { + checkedIds.remove(episode.getId()); + } + } + } + refreshCheckboxes(); + } + + private void checkDownloaded(boolean isDownloaded) { + for (FeedItem episode : episodes) { + if(episode.hasMedia() && episode.getMedia().isDownloaded() == isDownloaded) { + if(!checkedIds.contains(episode.getId())) { + checkedIds.add(episode.getId()); + } + } else { + if(checkedIds.contains(episode.getId())) { + checkedIds.remove(episode.getId()); + } + } + } + refreshCheckboxes(); + } + + private void refreshTitles() { + titles.clear(); + for(FeedItem episode : episodes) { + titles.add(episode.getTitle()); + } + mAdapter.notifyDataSetChanged(); + } + + private void refreshCheckboxes() { + for (int i = 0; i < episodes.size(); i++) { + FeedItem episode = episodes.get(i); + boolean checked = checkedIds.contains(episode.getId()); + mListView.setItemChecked(i, checked); + } + ActivityCompat.invalidateOptionsMenu(EpisodesApplyActionFragment.this.getActivity()); + } + + private void queueChecked() { + LongList orderedIds = new LongList(); + for(FeedItem episode : episodes) { + if(checkedIds.contains(episode.getId())) { + orderedIds.add((episode.getId())); + } + } + DBWriter.addQueueItem(getActivity(), false, orderedIds.toArray()); + close(); + } + + private void markedCheckedPlayed() { + DBWriter.markItemRead(getActivity(), true, checkedIds.toArray()); + close(); + } + + private void markedCheckedUnplayed() { + DBWriter.markItemRead(getActivity(), false, checkedIds.toArray()); + close(); + } + + private void downloadChecked() { + // download the check episodes in the same order as they are currently displayed + List<FeedItem> toDownload = new ArrayList<FeedItem>(checkedIds.size()); + for(FeedItem episode : episodes) { + if(checkedIds.contains(episode.getId())) { + toDownload.add(episode); + } + } + try { + DBTasks.downloadFeedItems(getActivity(), toDownload.toArray(new FeedItem[0])); + } catch (DownloadRequestException e) { + e.printStackTrace(); + DownloadRequestErrorDialogCreator.newRequestErrorDialog(getActivity(), e.getMessage()); + } + close(); + } + + private void deleteChecked() { + for(long id : checkedIds.toArray()) { + FeedItem episode = idMap.get(id); + if(episode.hasMedia()) { + DBWriter.deleteFeedMediaOfItem(getActivity(), episode.getMedia().getId()); + } + } + close(); + } + + private void close() { + getActivity().getSupportFragmentManager().popBackStack(); + } + +} diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/TimeDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/TimeDialog.java index 6561d501e..5c4d4c430 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/TimeDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/TimeDialog.java @@ -39,9 +39,9 @@ public abstract class TimeDialog extends Dialog { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); - String[] spinnerContent = new String[]{context.getString(R.string.time_unit_seconds), - context.getString(R.string.time_unit_minutes), - context.getString(R.string.time_unit_hours)}; + String[] spinnerContent = new String[]{context.getString(R.string.time_seconds), + context.getString(R.string.time_minutes), + context.getString(R.string.time_hours)}; setContentView(R.layout.time_dialog); etxtTime = (EditText) findViewById(R.id.etxtTime); 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 ff5485251..b4c4f1822 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java @@ -30,8 +30,8 @@ import java.util.concurrent.atomic.AtomicReference; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; -import de.danoeh.antennapod.adapter.DefaultActionButtonCallback; import de.danoeh.antennapod.adapter.AllEpisodesListAdapter; +import de.danoeh.antennapod.adapter.DefaultActionButtonCallback; import de.danoeh.antennapod.core.asynctask.DownloadObserver; import de.danoeh.antennapod.core.dialog.ConfirmationDialog; import de.danoeh.antennapod.core.feed.EventDistributor; @@ -72,10 +72,10 @@ public class AllEpisodesFragment extends Fragment { private TextView txtvEmpty; private ProgressBar progLoading; private ContextMenu contextMenu; + private AdapterView.AdapterContextMenuInfo lastMenuInfo = null; private List<FeedItem> episodes; private LongList queuedItemsIds; - private LongList newItemsIds; private List<Downloader> downloaderList; private boolean itemsLoaded = false; @@ -341,12 +341,16 @@ public class AllEpisodesFragment extends Fragment { } contextMenu = menu; - FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, queuedItemsIds); + lastMenuInfo = (AdapterView.AdapterContextMenuInfo) menuInfo; + FeedItemMenuHandler.onPrepareMenu(getActivity(), contextMenuInterface, item, true, queuedItemsIds); } @Override public boolean onContextItemSelected(MenuItem item) { AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); + if(menuInfo == null) { + menuInfo = lastMenuInfo; + } FeedItem selectedItem = itemAccess.getItem(menuInfo.position); if (selectedItem == null) { @@ -380,14 +384,7 @@ public class AllEpisodesFragment extends Fragment { private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() { @Override - public void onContentChanged() { - if (listAdapter != null) { - listAdapter.notifyDataSetChanged(); - } - } - - @Override - public void onDownloadDataAvailable(List<Downloader> downloaderList) { + public void onContentChanged(List<Downloader> downloaderList) { AllEpisodesFragment.this.downloaderList = downloaderList; if (listAdapter != null) { listAdapter.notifyDataSetChanged(); @@ -434,18 +431,6 @@ public class AllEpisodesFragment extends Fragment { return false; } } - - @Override - public boolean isNew(FeedItem item) { - if (itemsLoaded) { - // should actually never be called in NewEpisodesFragment, but better safe than sorry - return showOnlyNewEpisodes || newItemsIds.contains(item.getId()); - } else { - return false; - } - } - - }; private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { @@ -510,8 +495,7 @@ public class AllEpisodesFragment extends Fragment { } else { return new Object[]{ DBReader.getRecentlyPublishedEpisodes(context, RECENT_EPISODES_LIMIT), - DBReader.getQueueIDList(context), - DBReader.getNewItemIds(context) + DBReader.getQueueIDList(context) }; } } else { @@ -528,7 +512,6 @@ public class AllEpisodesFragment extends Fragment { if (lists != null) { episodes = (List<FeedItem>) lists[0]; queuedItemsIds = (LongList) lists[1]; - newItemsIds = (LongList) lists[2]; itemsLoaded = true; if (viewsCreated && activity.get() != null) { onFragmentLoaded(); 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 3076f8136..a1667cce0 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java @@ -10,12 +10,14 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; -import com.squareup.picasso.Picasso; +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; /** @@ -80,8 +82,12 @@ public class CoverFragment extends Fragment implements public void run() { Context c = getActivity(); if (c != null) { - Picasso.with(c) + 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); } } 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 fdb128f03..634c3c546 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java @@ -11,9 +11,11 @@ import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; -import com.squareup.picasso.Picasso; +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.engine.DiskCacheStrategy; import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.util.Converter; import de.danoeh.antennapod.core.util.playback.Playable; @@ -55,7 +57,7 @@ public class ExternalPlayerFragment extends Fragment { public void onClick(View v) { Log.d(TAG, "layoutInfo was clicked"); - if (controller.getMedia() != null) { + if (controller != null && controller.getMedia() != null) { startActivity(PlaybackService.getPlayerActivityIntent( getActivity(), controller.getMedia())); } @@ -192,14 +194,18 @@ public class ExternalPlayerFragment extends Fragment { private boolean loadMediaInfo() { Log.d(TAG, "Loading media info"); - if (controller.serviceAvailable()) { + if (controller != null && controller.serviceAvailable()) { Playable media = controller.getMedia(); if (media != null) { txtvTitle.setText(media.getEpisodeTitle()); - Picasso.with(getActivity()) + Glide.with(getActivity()) .load(media.getImageUri()) - .fit() + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate() .into(imgvCover); fragmentLayout.setVisibility(View.VISIBLE); 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 a7c6d62e6..9693e6886 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java @@ -25,12 +25,12 @@ import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.Toast; -import de.danoeh.antennapod.BuildConfig; import de.danoeh.antennapod.R; 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.util.Converter; +import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.ShareUtils; import de.danoeh.antennapod.core.util.ShownotesProvider; import de.danoeh.antennapod.core.util.playback.Playable; @@ -104,8 +104,7 @@ public class ItemDescriptionFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - if (BuildConfig.DEBUG) - Log.d(TAG, "Creating view"); + Log.d(TAG, "Creating view"); webvDescription = new WebView(getActivity()); if (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) { if (Build.VERSION.SDK_INT >= 11 @@ -141,8 +140,7 @@ public class ItemDescriptionFragment extends Fragment { @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); - if (BuildConfig.DEBUG) - Log.d(TAG, "Page finished"); + Log.d(TAG, "Page finished"); // Restoring the scroll position might not always work view.postDelayed(new Runnable() { @@ -163,15 +161,13 @@ public class ItemDescriptionFragment extends Fragment { @Override public void onAttach(Activity activity) { super.onAttach(activity); - if (BuildConfig.DEBUG) - Log.d(TAG, "Fragment attached"); + Log.d(TAG, "Fragment attached"); } @Override public void onDetach() { super.onDetach(); - if (BuildConfig.DEBUG) - Log.d(TAG, "Fragment detached"); + Log.d(TAG, "Fragment detached"); if (webViewLoader != null) { webViewLoader.cancel(true); } @@ -180,8 +176,7 @@ public class ItemDescriptionFragment extends Fragment { @Override public void onDestroy() { super.onDestroy(); - if (BuildConfig.DEBUG) - Log.d(TAG, "Fragment destroyed"); + Log.d(TAG, "Fragment destroyed"); if (webViewLoader != null) { webViewLoader.cancel(true); } @@ -195,8 +190,7 @@ public class ItemDescriptionFragment extends Fragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (BuildConfig.DEBUG) - Log.d(TAG, "Creating fragment"); + Log.d(TAG, "Creating fragment"); Bundle args = getArguments(); saveState = args.getBoolean(ARG_SAVE_STATE, false); highlightTimecodes = args.getBoolean(ARG_HIGHLIGHT_TIMECODES, false); @@ -258,11 +252,7 @@ public class ItemDescriptionFragment extends Fragment { WebView.HitTestResult r = webvDescription.getHitTestResult(); if (r != null && r.getType() == WebView.HitTestResult.SRC_ANCHOR_TYPE) { - if (BuildConfig.DEBUG) - Log.d(TAG, - "Link of webview was long-pressed. Extra: " - + r.getExtra() - ); + Log.d(TAG, "Link of webview was long-pressed. Extra: " + r.getExtra()); selectedURL = r.getExtra(); webvDescription.showContextMenu(); return true; @@ -281,8 +271,10 @@ public class ItemDescriptionFragment extends Fragment { switch (item.getItemId()) { case R.id.open_in_browser_item: Uri uri = Uri.parse(selectedURL); - getActivity() - .startActivity(new Intent(Intent.ACTION_VIEW, uri)); + final Intent intent = new Intent(Intent.ACTION_VIEW, uri); + if(IntentUtils.isCallable(getActivity(), intent)) { + getActivity().startActivity(intent); + } break; case R.id.share_url_item: ShareUtils.shareLink(getActivity(), selectedURL); @@ -331,8 +323,12 @@ public class ItemDescriptionFragment extends Fragment { R.string.go_to_position_label); menu.setHeaderTitle(Converter.getDurationStringLong(Timeline.getTimecodeLinkTime(selectedURL))); } else { - menu.add(Menu.NONE, R.id.open_in_browser_item, Menu.NONE, - R.string.open_in_browser_label); + Uri uri = Uri.parse(selectedURL); + final Intent intent = new Intent(Intent.ACTION_VIEW, uri); + if(IntentUtils.isCallable(getActivity(), intent)) { + menu.add(Menu.NONE, R.id.open_in_browser_item, Menu.NONE, + R.string.open_in_browser_label); + } menu.add(Menu.NONE, R.id.copy_url_item, Menu.NONE, R.string.copy_url_label); menu.add(Menu.NONE, R.id.share_url_item, Menu.NONE, @@ -358,8 +354,7 @@ public class ItemDescriptionFragment extends Fragment { // /webvDescription.loadData(url, "text/html", "utf-8"); webvDescription.loadDataWithBaseURL(null, data, "text/html", "utf-8", "about:blank"); - if (BuildConfig.DEBUG) - Log.d(TAG, "Webview loaded"); + Log.d(TAG, "Webview loaded"); webViewLoader = null; } @@ -370,8 +365,7 @@ public class ItemDescriptionFragment extends Fragment { @Override protected Void doInBackground(Void... params) { - if (BuildConfig.DEBUG) - Log.d(TAG, "Loading Webview"); + Log.d(TAG, "Loading Webview"); try { Activity activity = getActivity(); if (activity != null) { @@ -397,24 +391,17 @@ public class ItemDescriptionFragment extends Fragment { private void savePreference() { if (saveState) { - if (BuildConfig.DEBUG) - Log.d(TAG, "Saving preferences"); + Log.d(TAG, "Saving preferences"); SharedPreferences prefs = getActivity().getSharedPreferences(PREF, Activity.MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); if (media != null && webvDescription != null) { - if (BuildConfig.DEBUG) - Log.d(TAG, - "Saving scroll position: " - + webvDescription.getScrollY() - ); + Log.d(TAG, "Saving scroll position: " + webvDescription.getScrollY()); editor.putInt(PREF_SCROLL_Y, webvDescription.getScrollY()); editor.putString(PREF_PLAYABLE_ID, media.getIdentifier() .toString()); } else { - if (BuildConfig.DEBUG) - Log.d(TAG, - "savePreferences was called while media or webview was null"); + Log.d(TAG, "savePreferences was called while media or webview was null"); editor.putInt(PREF_SCROLL_Y, -1); editor.putString(PREF_PLAYABLE_ID, ""); } @@ -424,8 +411,7 @@ public class ItemDescriptionFragment extends Fragment { private boolean restoreFromPreference() { if (saveState) { - if (BuildConfig.DEBUG) - Log.d(TAG, "Restoring from preferences"); + Log.d(TAG, "Restoring from preferences"); Activity activity = getActivity(); if (activity != null) { SharedPreferences prefs = activity.getSharedPreferences( @@ -435,8 +421,7 @@ public class ItemDescriptionFragment extends Fragment { if (scrollY != -1 && media != null && id.equals(media.getIdentifier().toString()) && webvDescription != null) { - if (BuildConfig.DEBUG) - Log.d(TAG, "Restored scroll Position: " + scrollY); + Log.d(TAG, "Restored scroll Position: " + scrollY); webvDescription.scrollTo(webvDescription.getScrollX(), scrollY); return true; 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 51a1e2252..4edb7f36f 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java @@ -1,7 +1,8 @@ package de.danoeh.antennapod.fragment; import android.annotation.TargetApi; -import android.content.ActivityNotFoundException; +import android.content.ClipData; +import android.content.Context; import android.content.Intent; import android.content.res.TypedArray; import android.net.Uri; @@ -18,7 +19,9 @@ import android.support.v7.widget.Toolbar; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Log; +import android.view.ContextMenu; import android.view.LayoutInflater; +import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; @@ -32,7 +35,8 @@ import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; -import com.squareup.picasso.Picasso; +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.engine.DiskCacheStrategy; import java.util.List; @@ -45,6 +49,7 @@ import de.danoeh.antennapod.core.feed.EventDistributor; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.QueueEvent; +import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.download.Downloader; import de.danoeh.antennapod.core.storage.DBReader; @@ -53,7 +58,9 @@ 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.Converter; +import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.LongList; +import de.danoeh.antennapod.core.util.ShareUtils; import de.danoeh.antennapod.core.util.playback.Timeline; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.greenrobot.event.EventBus; @@ -107,6 +114,11 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba private ImageButton butMore; private PopupMenu popupMenu; + /** + * URL that was selected via long-press. + */ + private String selectedURL; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -194,20 +206,19 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba webvDescription.getSettings().setLayoutAlgorithm( WebSettings.LayoutAlgorithm.NARROW_COLUMNS); webvDescription.getSettings().setLoadWithOverviewMode(true); + webvDescription.setOnLongClickListener(webViewLongClickListener); webvDescription.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - try { + if(IntentUtils.isCallable(getActivity(), intent)) { startActivity(intent); - } catch (ActivityNotFoundException e) { - e.printStackTrace(); - return true; } return true; } }); + registerForContextMenu(webvDescription); imgvCover = (ImageView) header.findViewById(R.id.imgvCover); progbarDownload = (ProgressBar) header.findViewById(R.id.progbarDownload); @@ -272,10 +283,10 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba popupMenu.getMenu().clear(); popupMenu.inflate(R.menu.feeditem_options); if (item.hasMedia()) { - FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, queue); + FeedItemMenuHandler.onPrepareMenu(getActivity(), popupMenuInterface, item, true, queue); } else { // these are already available via button1 and button2 - FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, queue, + FeedItemMenuHandler.onPrepareMenu(getActivity(), popupMenuInterface, item, true, queue, R.id.mark_read_item, R.id.visit_website_item); } popupMenu.show(); @@ -313,7 +324,7 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba private void onFragmentLoaded() { - progbarLoading.setVisibility(View.GONE); + progbarLoading.setVisibility(View.INVISIBLE); if (webviewData != null) { webvDescription.loadDataWithBaseURL(null, webviewData, "text/html", "utf-8", "about:blank"); @@ -327,10 +338,16 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba txtvTitle.setText(item.getTitle()); txtvPublished.setText(DateUtils.formatDateTime(getActivity(), item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL)); - Picasso.with(getActivity()).load(item.getImageUri()) - .fit() + Glide.with(getActivity()) + .load(item.getImageUri()) + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate() .into(imgvCover); - progbarDownload.setVisibility(View.GONE); + + progbarDownload.setVisibility(View.INVISIBLE); if (item.hasMedia() && downloaderList != null) { for (Downloader downloader : downloaderList) { if (downloader.getDownloadRequest().getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA @@ -346,7 +363,7 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba TypedArray drawables = getActivity().obtainStyledAttributes(new int[]{R.attr.navigation_accept, R.attr.location_web_site}); - if (!item.isRead()) { + if (!item.isPlayed()) { butAction1.setCompoundDrawablesWithIntrinsicBounds(drawables.getDrawable(0), null, null, null); butAction1.setText(getActivity().getString(R.string.mark_read_label)); butAction1.setVisibility(View.VISIBLE); @@ -398,6 +415,83 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba getLoaderManager().restartLoader(0, null, ItemFragment.this); } + private View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() { + + @Override + public boolean onLongClick(View v) { + WebView.HitTestResult r = webvDescription.getHitTestResult(); + if (r != null + && r.getType() == WebView.HitTestResult.SRC_ANCHOR_TYPE) { + Log.d(TAG, "Link of webview was long-pressed. Extra: " + r.getExtra()); + selectedURL = r.getExtra(); + webvDescription.showContextMenu(); + return true; + } + selectedURL = null; + return false; + } + }; + + @Override + public boolean onContextItemSelected(MenuItem item) { + boolean handled = selectedURL != null; + if (selectedURL != null) { + switch (item.getItemId()) { + case R.id.open_in_browser_item: + Uri uri = Uri.parse(selectedURL); + final Intent intent = new Intent(Intent.ACTION_VIEW, uri); + if(IntentUtils.isCallable(getActivity(), intent)) { + getActivity().startActivity(intent); + } + break; + case R.id.share_url_item: + ShareUtils.shareLink(getActivity(), selectedURL); + break; + case R.id.copy_url_item: + if (android.os.Build.VERSION.SDK_INT >= 11) { + ClipData clipData = ClipData.newPlainText(selectedURL, + selectedURL); + android.content.ClipboardManager cm = (android.content.ClipboardManager) getActivity() + .getSystemService(Context.CLIPBOARD_SERVICE); + cm.setPrimaryClip(clipData); + } else { + android.text.ClipboardManager cm = (android.text.ClipboardManager) getActivity() + .getSystemService(Context.CLIPBOARD_SERVICE); + cm.setText(selectedURL); + } + Toast t = Toast.makeText(getActivity(), + R.string.copied_url_msg, Toast.LENGTH_SHORT); + t.show(); + break; + default: + handled = false; + break; + + } + selectedURL = null; + } + return handled; + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, + ContextMenu.ContextMenuInfo menuInfo) { + if (selectedURL != null) { + super.onCreateContextMenu(menu, v, menuInfo); + Uri uri = Uri.parse(selectedURL); + final Intent intent = new Intent(Intent.ACTION_VIEW, uri); + if(IntentUtils.isCallable(getActivity(), intent)) { + menu.add(Menu.NONE, R.id.open_in_browser_item, Menu.NONE, + R.string.open_in_browser_label); + } + menu.add(Menu.NONE, R.id.copy_url_item, Menu.NONE, + R.string.copy_url_label); + menu.add(Menu.NONE, R.id.share_url_item, Menu.NONE, + R.string.share_url_label); + menu.setHeaderTitle(selectedURL); + } + } + @Override public Loader<Pair<FeedItem,LongList>> onCreateLoader(int id, Bundle args) { return new DBTaskLoader<Pair<FeedItem,LongList>>(getActivity()) { @@ -445,14 +539,7 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba private final DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() { @Override - public void onContentChanged() { - if (itemsLoaded && getActivity() != null) { - updateAppearance(); - } - } - - @Override - public void onDownloadDataAvailable(List<Downloader> downloaderList) { + public void onContentChanged(List<Downloader> downloaderList) { ItemFragment.this.downloaderList = downloaderList; if (itemsLoaded && getActivity() != null) { updateAppearance(); 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 a9cbe8291..63ebf234e 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java @@ -4,13 +4,16 @@ import android.annotation.SuppressLint; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.LightingColorFilter; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.support.v4.app.Fragment; import android.support.v4.app.ListFragment; import android.support.v4.view.MenuItemCompat; - import android.support.v7.app.ActionBarActivity; import android.support.v7.widget.SearchView; import android.util.Log; @@ -29,8 +32,10 @@ import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.TextView; +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.joanzapata.android.iconify.IconDrawable; import com.joanzapata.android.iconify.Iconify; -import com.squareup.picasso.Picasso; import org.apache.commons.lang3.Validate; @@ -43,7 +48,6 @@ import de.danoeh.antennapod.adapter.DefaultActionButtonCallback; import de.danoeh.antennapod.adapter.FeedItemlistAdapter; import de.danoeh.antennapod.core.asynctask.DownloadObserver; import de.danoeh.antennapod.core.asynctask.FeedRemover; -import de.danoeh.antennapod.core.asynctask.PicassoProvider; import de.danoeh.antennapod.core.dialog.ConfirmationDialog; import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator; import de.danoeh.antennapod.core.feed.EventDistributor; @@ -53,6 +57,8 @@ import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedItemFilter; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.QueueEvent; +import de.danoeh.antennapod.core.glide.ApGlideSettings; +import de.danoeh.antennapod.core.glide.FastBlurTransformation; import de.danoeh.antennapod.core.service.download.DownloadService; import de.danoeh.antennapod.core.service.download.Downloader; import de.danoeh.antennapod.core.storage.DBReader; @@ -61,6 +67,7 @@ import de.danoeh.antennapod.core.storage.DownloadRequestException; import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.gui.MoreContentListFooterUtil; +import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.menuhandler.FeedMenuHandler; import de.danoeh.antennapod.menuhandler.MenuItemUtils; @@ -83,12 +90,11 @@ public class ItemlistFragment extends ListFragment { protected FeedItemlistAdapter adapter; private ContextMenu contextMenu; + private AdapterView.AdapterContextMenuInfo lastMenuInfo = null; private long feedID; private Feed feed; private LongList queuedItemsIds; - private LongList newItemsIds; - private boolean itemsLoaded = false; private boolean viewsCreated = false; @@ -155,6 +161,7 @@ public class ItemlistFragment extends ListFragment { @Override public void onResume() { super.onResume(); + Log.d(TAG, "onResume()"); updateProgressBarVisibility(); startItemLoader(); } @@ -217,6 +224,18 @@ public class ItemlistFragment extends ListFragment { return false; } }); + if(feed == null || feed.getLink() == null) { + menu.findItem(R.id.share_link_item).setVisible(false); + menu.findItem(R.id.visit_website_item).setVisible(false); + } + int[] attrs = { android.R.attr.textColor }; + TypedArray ta = getActivity().obtainStyledAttributes(attrs); + int textColor = ta.getColor(0, Color.GRAY); + ta.recycle(); + + menu.findItem(R.id.episode_actions).setIcon(new IconDrawable(getActivity(), + Iconify.IconValue.fa_gears).color(textColor).actionBarSize()); + isUpdatingFeed = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker); } } @@ -234,6 +253,10 @@ public class ItemlistFragment extends ListFragment { try { if (!FeedMenuHandler.onOptionsItemClicked(getActivity(), item, feed)) { switch (item.getItemId()) { + case R.id.episode_actions: + Fragment fragment = new EpisodesApplyActionFragment(feed.getItems()); + ((MainActivity)getActivity()).loadChildFragment(fragment); + return true; case R.id.remove_item: final FeedRemover remover = new FeedRemover( getActivity(), feed) { @@ -302,12 +325,16 @@ public class ItemlistFragment extends ListFragment { } contextMenu = menu; - FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, queuedItemsIds); + lastMenuInfo = (AdapterView.AdapterContextMenuInfo) menuInfo; + FeedItemMenuHandler.onPrepareMenu(getActivity(), contextMenuInterface, item, true, queuedItemsIds); } @Override public boolean onContextItemSelected(MenuItem item) { AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); + if(menuInfo == null) { + menuInfo = lastMenuInfo; + } // because of addHeaderView(), positions are increased by 1! FeedItem selectedItem = itemAccess.getItem(menuInfo.position-1); @@ -396,12 +423,15 @@ public class ItemlistFragment extends ListFragment { private boolean insideOnFragmentLoaded = false; private void onFragmentLoaded() { + if(!isVisible()) { + return; + } insideOnFragmentLoaded = true; if (adapter == null) { setListAdapter(null); setupHeaderView(); setupFooterView(); - adapter = new FeedItemlistAdapter(getActivity(), itemAccess, new DefaultActionButtonCallback(getActivity()), false); + adapter = new FeedItemlistAdapter(getActivity(), itemAccess, new DefaultActionButtonCallback(getActivity()), false, true); setListAdapter(adapter); downloadObserver = new DownloadObserver(getActivity(), new Handler(), downloadObserverCallback); downloadObserver.onResume(); @@ -421,6 +451,10 @@ public class ItemlistFragment extends ListFragment { } private void refreshHeaderView() { + if (getListView() == null || feed == null) { + Log.e(TAG, "Unable to setup listview: listView = null or feed = null"); + return; + } if(feed.hasLastUpdateFailed()) { txtvFailure.setVisibility(View.VISIBLE); } else { @@ -448,14 +482,7 @@ public class ItemlistFragment extends ListFragment { private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() { @Override - public void onContentChanged() { - if (adapter != null) { - adapter.notifyDataSetChanged(); - } - } - - @Override - public void onDownloadDataAvailable(List<Downloader> downloaderList) { + public void onContentChanged(List<Downloader> downloaderList) { ItemlistFragment.this.downloaderList = downloaderList; if (adapter != null) { adapter.notifyDataSetChanged(); @@ -485,17 +512,26 @@ public class ItemlistFragment extends ListFragment { txtvTitle.setText(feed.getTitle()); txtvAuthor.setText(feed.getAuthor()); - Picasso.with(getActivity()) + + // https://github.com/bumptech/glide/issues/529 + imgvBackground.setColorFilter(new LightingColorFilter(0xff828282, 0x000000)); + + Glide.with(getActivity()) .load(feed.getImageUri()) .placeholder(R.color.image_readability_tint) .error(R.color.image_readability_tint) - .transform(PicassoProvider.blurTransformation) - .resize(PicassoProvider.BLUR_IMAGE_SIZE, PicassoProvider.BLUR_IMAGE_SIZE) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .transform(new FastBlurTransformation(getActivity())) + .dontAnimate() .into(imgvBackground); - Picasso.with(getActivity()) + Glide.with(getActivity()) .load(feed.getImageUri()) - .fit() + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate() .into(imgvCover); butShowInfo.setOnClickListener(new View.OnClickListener() { @@ -558,11 +594,6 @@ public class ItemlistFragment extends ListFragment { } @Override - public boolean isNew(FeedItem item) { - return (newItemsIds != null) && newItemsIds.contains(item.getId()); - } - - @Override public int getItemDownloadProgressPercent(FeedItem item) { if (downloaderList != null) { for (Downloader downloader : downloaderList) { @@ -599,13 +630,12 @@ public class ItemlistFragment extends ListFragment { Context context = getActivity(); if (context != null) { Feed feed = DBReader.getFeed(context, feedID); - if(feed.getItemFilter() != null) { + if(feed != null && feed.getItemFilter() != null) { FeedItemFilter filter = feed.getItemFilter(); feed.setItems(filter.filter(context, feed.getItems())); } LongList queuedItemsIds = DBReader.getQueueIDList(context); - LongList newItemsIds = DBReader.getNewItemIds(context); - return new Object[] { feed, queuedItemsIds, newItemsIds }; + return new Object[] { feed, queuedItemsIds }; } else { return null; } @@ -617,7 +647,6 @@ public class ItemlistFragment extends ListFragment { if (res != null) { feed = (Feed) res[0]; queuedItemsIds = (LongList) res[1]; - newItemsIds = res[2] == null ? null : (LongList) res[2]; itemsLoaded = true; if (viewsCreated) { onFragmentLoaded(); 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 16789d694..edd4da7fe 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java @@ -22,6 +22,8 @@ import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; @@ -31,7 +33,7 @@ import de.danoeh.antennapod.activity.OnlineFeedViewActivity; import de.danoeh.antennapod.adapter.itunes.ItunesAdapter; import de.danoeh.antennapod.core.preferences.UserPreferences; -import static de.danoeh.antennapod.adapter.itunes.ItunesAdapter.*; +import static de.danoeh.antennapod.adapter.itunes.ItunesAdapter.Podcast; //Searches iTunes store for given string and displays results in a list public class ItunesSearchFragment extends Fragment { @@ -160,8 +162,18 @@ public class ItunesSearchFragment extends Fragment { * * @param query Search string */ - public SearchTask(String query){ - this.query = query; + public SearchTask(String query) { + String encodedQuery = null; + try { + encodedQuery = URLEncoder.encode(query, "UTF-8"); + } catch(UnsupportedEncodingException e) { + // this won't ever be thrown + } + if(encodedQuery != null) { + this.query = encodedQuery; + } else { + this.query = query; // failsafe + } } //Get the podcast data diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java index 4bce3c7ba..9a25674b6 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java @@ -72,7 +72,7 @@ public class NewEpisodesFragment extends AllEpisodesFragment { Log.d(TAG, "remove(" + which + ")"); stopItemLoader(); FeedItem item = (FeedItem) listView.getAdapter().getItem(which); - DBWriter.markItemRead(getActivity(), item.getId(), true); + DBWriter.markItemRead(getActivity(), true, item.getId()); undoBarController.showUndoBar(false, getString(R.string.marked_as_read_label), new FeedItemUndoToken(item, which) @@ -88,7 +88,7 @@ public class NewEpisodesFragment extends AllEpisodesFragment { public void onUndo(FeedItemUndoToken token) { if (token != null) { long itemId = token.getFeedItemId(); - DBWriter.markItemRead(context, itemId, false); + DBWriter.markItemRead(context, false, itemId); } } @Override @@ -97,7 +97,7 @@ public class NewEpisodesFragment extends AllEpisodesFragment { long itemId = token.getFeedItemId(); FeedItem item = DBReader.getFeedItem(context, itemId); FeedMedia media = item.getMedia(); - if(media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) { + if(media != null && media.hasAlmostEnded() && item.getFeed().getPreferences().getCurrentAutoDelete()) { DBWriter.deleteFeedMediaOfItem(context, media.getId()); } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java index 9099829d8..b094133d3 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java @@ -192,7 +192,10 @@ public class PlaybackHistoryFragment extends ListFragment { private void onFragmentLoaded() { if (adapter == null) { - adapter = new FeedItemlistAdapter(getActivity(), itemAccess, new DefaultActionButtonCallback(activity.get()), true); + // played items shoudln't be transparent for this fragment since, *all* items + // in this fragment will, by definition, be played. So it serves no purpose and can make + // it harder to read. + adapter = new FeedItemlistAdapter(getActivity(), itemAccess, new DefaultActionButtonCallback(activity.get()), true, false); setListAdapter(adapter); downloadObserver = new DownloadObserver(activity.get(), new Handler(), downloadObserverCallback); downloadObserver.onResume(); @@ -204,14 +207,7 @@ public class PlaybackHistoryFragment extends ListFragment { private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() { @Override - public void onContentChanged() { - if (adapter != null) { - adapter.notifyDataSetChanged(); - } - } - - @Override - public void onDownloadDataAvailable(List<Downloader> downloaderList) { + public void onContentChanged(List<Downloader> downloaderList) { PlaybackHistoryFragment.this.downloaderList = downloaderList; if (adapter != null) { adapter.notifyDataSetChanged(); @@ -226,11 +222,6 @@ public class PlaybackHistoryFragment extends ListFragment { } @Override - public boolean isNew(FeedItem item) { - return false; - } - - @Override public int getItemDownloadProgressPercent(FeedItem item) { if (downloaderList != null) { for (Downloader downloader : downloaderList) { 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 d82c7b8f7..24c9fc425 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java @@ -47,6 +47,7 @@ import de.danoeh.antennapod.core.storage.DBTasks; 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.Converter; import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.QueueSorter; import de.danoeh.antennapod.core.util.gui.FeedItemUndoToken; @@ -66,12 +67,14 @@ public class QueueFragment extends Fragment { EventDistributor.DOWNLOAD_QUEUED | EventDistributor.PLAYER_STATUS_UPDATE; + private TextView infoBar; private DragSortListView listView; private QueueListAdapter listAdapter; private TextView txtvEmpty; private ProgressBar progLoading; private ContextMenu contextMenu; + private AdapterView.AdapterContextMenuInfo lastMenuInfo = null; private UndoBarController<FeedItemUndoToken> undoBarController; @@ -325,16 +328,20 @@ public class QueueFragment extends Fragment { } contextMenu = menu; + lastMenuInfo = (AdapterView.AdapterContextMenuInfo) menuInfo; LongList queueIds = new LongList(queue.size()); for(FeedItem queueItem : queue) { queueIds.add(queueItem.getId()); } - FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, queueIds); + FeedItemMenuHandler.onPrepareMenu(getActivity(), contextMenuInterface, item, true, queueIds); } @Override public boolean onContextItemSelected(MenuItem item) { AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); + if(menuInfo == null) { + menuInfo = lastMenuInfo; + } FeedItem selectedItem = itemAccess.getItem(menuInfo.position); if (selectedItem == null) { @@ -358,6 +365,7 @@ public class QueueFragment extends Fragment { ((MainActivity) getActivity()).getSupportActionBar().setTitle(R.string.queue_label); View root = inflater.inflate(R.layout.queue_fragment, container, false); + infoBar = (TextView) root.findViewById(R.id.info_bar); listView = (DragSortListView) root.findViewById(android.R.id.list); txtvEmpty = (TextView) root.findViewById(android.R.id.empty); progLoading = (ProgressBar) root.findViewById(R.id.progLoading); @@ -426,7 +434,7 @@ public class QueueFragment extends Fragment { long itemId = token.getFeedItemId(); FeedItem item = DBReader.getFeedItem(context, itemId); FeedMedia media = item.getMedia(); - if(media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) { + if(media != null && media.hasAlmostEnded() && item.getFeed().getPreferences().getCurrentAutoDelete()) { DBWriter.deleteFeedMediaOfItem(context, media.getId()); } } @@ -464,18 +472,25 @@ public class QueueFragment extends Fragment { // we need to refresh the options menu because it sometimes // needs data that may have just been loaded. getActivity().supportInvalidateOptionsMenu(); - } - private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() { - @Override - public void onContentChanged() { - if (listAdapter != null && !blockDownloadObserverUpdate) { - listAdapter.notifyDataSetChanged(); + // refresh information bar + String info = queue.size() + getString(R.string.episodes_suffix); + if(queue.size() > 0) { + long duration = 0; + for(FeedItem item : queue) { + if(item.getMedia() != null) { + duration += item.getMedia().getDuration(); + } } + info += " \u2022 "; + info += Converter.getDurationStringLocalized(getActivity(), duration); } + infoBar.setText(info); + } + private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() { @Override - public void onDownloadDataAvailable(List<Downloader> downloaderList) { + public void onContentChanged(List<Downloader> downloaderList) { QueueFragment.this.downloaderList = downloaderList; if (listAdapter != null && !blockDownloadObserverUpdate) { listAdapter.notifyDataSetChanged(); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java index b1b61f74b..eb4d18328 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java @@ -3,6 +3,7 @@ package de.danoeh.antennapod.fragment; import android.os.Bundle; import android.os.Handler; import android.support.v4.app.ListFragment; +import android.util.Log; import android.view.View; import android.widget.ListView; import android.widget.Toast; @@ -24,7 +25,7 @@ import de.danoeh.antennapod.core.storage.DownloadRequester; * Displays all running downloads and provides actions to cancel them */ public class RunningDownloadsFragment extends ListFragment { - private static final String TAG = "RunningDownloadsFragment"; + private static final String TAG = "RunningDownloadsFrag"; private DownloadObserver downloadObserver; private List<Downloader> downloaderList; @@ -53,12 +54,8 @@ public class RunningDownloadsFragment extends ListFragment { downloadObserver = new DownloadObserver(getActivity(), new Handler(), new DownloadObserver.Callback() { @Override - public void onContentChanged() { - downloadlistAdapter.notifyDataSetChanged(); - } - - @Override - public void onDownloadDataAvailable(List<Downloader> downloaderList) { + public void onContentChanged(List<Downloader> downloaderList) { + Log.d(TAG, "onContentChanged: downloaderList.size() == " + downloaderList.size()); RunningDownloadsFragment.this.downloaderList = downloaderList; downloadlistAdapter.notifyDataSetChanged(); } 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 fc6225409..975493ce9 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java @@ -120,7 +120,7 @@ public class SearchFragment extends ListFragment { SearchResult result = (SearchResult) l.getAdapter().getItem(position); FeedComponent comp = result.getComponent(); if (comp.getClass() == Feed.class) { - ((MainActivity) getActivity()).loadFeedFragmentById(comp.getId()); + ((MainActivity) getActivity()).loadFeedFragmentById(comp.getId(), null); } else { if (comp.getClass() == FeedItem.class) { FeedItem item = (FeedItem) comp; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java index 6139a4901..623c6faa7 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java @@ -62,7 +62,10 @@ public abstract class PodcastListFragment extends Fragment { @Override public boolean onQueryTextSubmit(String s) { sv.clearFocus(); - ((MainActivity) getActivity()).loadChildFragment(SearchListFragment.newInstance(s)); + MainActivity activity = (MainActivity)getActivity(); + if (activity != null) { + activity.loadChildFragment(SearchListFragment.newInstance(s)); + } return true; } 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 fe1a09149..3e2fdf24f 100644 --- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java @@ -4,6 +4,7 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; import android.util.Log; +import android.widget.Toast; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.feed.FeedItem; @@ -16,7 +17,7 @@ import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.storage.DBTasks; 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.IntentUtils; import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.ShareUtils; @@ -56,7 +57,7 @@ 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(MenuInterface mi, FeedItem selectedItem, + public static boolean onPrepareMenu(Context context, MenuInterface mi, FeedItem selectedItem, boolean showExtendedMenu, LongList queueAccess) { if (selectedItem == null) { return false; @@ -84,8 +85,19 @@ public class FeedItemMenuHandler { if (!(!isInQueue && selectedItem.getMedia() != null)) { mi.setItemVisibility(R.id.add_to_queue_item, false); } + if (!showExtendedMenu || selectedItem.getLink() == null) { + mi.setItemVisibility(R.id.visit_website_item, false); mi.setItemVisibility(R.id.share_link_item, false); + mi.setItemVisibility(R.id.share_link_with_position_item, false); + } + if (!showExtendedMenu || !hasMedia || selectedItem.getMedia().getDownload_url() == null) { + mi.setItemVisibility(R.id.share_download_url_item, false); + mi.setItemVisibility(R.id.share_download_url_with_position_item, false); + } + if(false == hasMedia || selectedItem.getMedia().getPosition() <= 0) { + mi.setItemVisibility(R.id.share_link_with_position_item, false); + mi.setItemVisibility(R.id.share_download_url_with_position_item, false); } if (!(state == FeedItem.State.UNREAD || state == FeedItem.State.IN_PROGRESS)) { @@ -108,10 +120,6 @@ public class FeedItemMenuHandler { mi.setItemVisibility(R.id.deactivate_auto_download, false); } - if (!showExtendedMenu || selectedItem.getLink() == null) { - mi.setItemVisibility(R.id.visit_website_item, false); - } - if (selectedItem.getPaymentLink() == null || !selectedItem.getFlattrStatus().flattrable()) { mi.setItemVisibility(R.id.support_item, false); } @@ -125,21 +133,19 @@ public class FeedItemMenuHandler { * @param excludeIds Menu item that should be excluded * @return true if selectedItem is not null. */ - public static boolean onPrepareMenu(MenuInterface mi, - FeedItem selectedItem, boolean showExtendedMenu, LongList queueAccess, int... excludeIds) { - boolean rc = onPrepareMenu(mi, selectedItem, showExtendedMenu, queueAccess); + public static boolean onPrepareMenu(Context context, MenuInterface mi, FeedItem selectedItem, + boolean showExtendedMenu, LongList queueAccess, int... excludeIds) { + boolean rc = onPrepareMenu(context, mi, selectedItem, showExtendedMenu, queueAccess); if (rc && excludeIds != null) { for (int id : excludeIds) { mi.setItemVisibility(id, false); } } - return rc; } public static boolean onMenuItemClicked(Context context, int menuItemId, FeedItem selectedItem) throws DownloadRequestException { - DownloadRequester requester = DownloadRequester.getInstance(); switch (menuItemId) { case R.id.skip_episode_item: context.sendBroadcast(new Intent(PlaybackService.ACTION_SKIP_CURRENT_EPISODE)); @@ -148,22 +154,25 @@ public class FeedItemMenuHandler { DBWriter.deleteFeedMediaOfItem(context, selectedItem.getMedia().getId()); break; case R.id.mark_read_item: - selectedItem.setRead(true); + selectedItem.setPlayed(true); DBWriter.markItemRead(context, selectedItem, true, false); if(GpodnetPreferences.loggedIn()) { FeedMedia media = selectedItem.getMedia(); - GpodnetEpisodeAction actionPlay = new GpodnetEpisodeAction.Builder(selectedItem, Action.PLAY) - .currentDeviceId() - .currentTimestamp() - .started(media.getDuration() / 1000) - .position(media.getDuration() / 1000) - .total(media.getDuration() / 1000) - .build(); - GpodnetPreferences.enqueueEpisodeAction(actionPlay); + // not all items have media, Gpodder only cares about those that do + if (media != null) { + GpodnetEpisodeAction actionPlay = new GpodnetEpisodeAction.Builder(selectedItem, Action.PLAY) + .currentDeviceId() + .currentTimestamp() + .started(media.getDuration() / 1000) + .position(media.getDuration() / 1000) + .total(media.getDuration() / 1000) + .build(); + GpodnetPreferences.enqueueEpisodeAction(actionPlay); + } } break; case R.id.mark_unread_item: - selectedItem.setRead(false); + selectedItem.setPlayed(false); DBWriter.markItemRead(context, selectedItem, false, false); if(GpodnetPreferences.loggedIn()) { GpodnetEpisodeAction actionNew = new GpodnetEpisodeAction.Builder(selectedItem, Action.NEW) @@ -198,7 +207,13 @@ public class FeedItemMenuHandler { break; case R.id.visit_website_item: Uri uri = Uri.parse(selectedItem.getLink()); - context.startActivity(new Intent(Intent.ACTION_VIEW, uri)); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + if(IntentUtils.isCallable(context, intent)) { + context.startActivity(intent); + } else { + Toast.makeText(context, context.getString(R.string.download_error_malformed_url), + Toast.LENGTH_SHORT); + } break; case R.id.support_item: DBTasks.flattrItemIfLoggedIn(context, selectedItem); @@ -206,6 +221,15 @@ public class FeedItemMenuHandler { case R.id.share_link_item: ShareUtils.shareFeedItemLink(context, selectedItem); break; + case R.id.share_download_url_item: + ShareUtils.shareFeedItemDownloadLink(context, selectedItem); + break; + case R.id.share_link_with_position_item: + ShareUtils.shareFeedItemLink(context, selectedItem, true); + break; + case R.id.share_download_url_with_position_item: + ShareUtils.shareFeedItemDownloadLink(context, selectedItem, true); + break; default: Log.d(TAG, "Unknown menuItemId: " + menuItemId); return 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 7bd8fedc9..3df59724d 100644 --- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java @@ -9,6 +9,7 @@ import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import android.widget.Toast; import java.util.ArrayList; import java.util.Arrays; @@ -20,6 +21,7 @@ import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.storage.DownloadRequestException; +import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.ShareUtils; /** @@ -39,11 +41,11 @@ public class FeedMenuHandler { } Log.d(TAG, "Preparing options menu"); - menu.findItem(R.id.mark_all_read_item).setVisible(selectedFeed.hasNewItems()); - if (selectedFeed.getPaymentLink() != null && selectedFeed.getFlattrStatus().flattrable()) + if (selectedFeed.getPaymentLink() != null && selectedFeed.getFlattrStatus().flattrable()) { menu.findItem(R.id.support_item).setVisible(true); - else + } else { menu.findItem(R.id.support_item).setVisible(false); + } menu.findItem(R.id.refresh_complete_item).setVisible(selectedFeed.isPaged()); @@ -83,7 +85,13 @@ public class FeedMenuHandler { break; case R.id.visit_website_item: Uri uri = Uri.parse(selectedFeed.getLink()); - context.startActivity(new Intent(Intent.ACTION_VIEW, uri)); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + if(IntentUtils.isCallable(context, intent)) { + context.startActivity(intent); + } else { + Toast.makeText(context, context.getString(R.string.download_error_malformed_url), + Toast.LENGTH_SHORT); + } break; case R.id.support_item: DBTasks.flattrFeedIfLoggedIn(context, selectedFeed); @@ -91,7 +99,7 @@ public class FeedMenuHandler { case R.id.share_link_item: ShareUtils.shareFeedlink(context, selectedFeed); break; - case R.id.share_source_item: + case R.id.share_download_url_item: ShareUtils.shareFeedDownloadLink(context, selectedFeed); break; default: 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 f387b7524..39edfe582 100644 --- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java +++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java @@ -2,6 +2,7 @@ package de.danoeh.antennapod.preferences; import android.app.Activity; import android.app.AlertDialog; +import android.app.TimePickerDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -16,8 +17,10 @@ import android.preference.Preference; import android.preference.PreferenceScreen; import android.text.Editable; import android.text.TextWatcher; +import android.text.format.DateFormat; import android.util.Log; import android.widget.EditText; +import android.widget.TimePicker; import android.widget.Toast; import java.io.File; @@ -33,12 +36,9 @@ import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.activity.PreferenceActivity; import de.danoeh.antennapod.activity.PreferenceActivityGingerbread; import de.danoeh.antennapod.asynctask.OpmlExportWorker; -import de.danoeh.antennapod.core.asynctask.FlattrClickWorker; import de.danoeh.antennapod.core.preferences.GpodnetPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences; -import de.danoeh.antennapod.core.util.flattr.FlattrStatus; import de.danoeh.antennapod.core.util.flattr.FlattrUtils; -import de.danoeh.antennapod.core.util.flattr.SimpleFlattrThing; import de.danoeh.antennapod.dialog.AuthenticationDialog; import de.danoeh.antennapod.dialog.AutoFlattrPreferenceDialog; import de.danoeh.antennapod.dialog.GpodnetSetHostnameDialog; @@ -49,7 +49,6 @@ import de.danoeh.antennapod.dialog.VariableSpeedDialog; */ public class PreferenceController { private static final String TAG = "PreferenceController"; - public static final String PREF_FLATTR_THIS_APP = "prefFlattrThisApp"; public static final String PREF_FLATTR_SETTINGS = "prefFlattrSettings"; public static final String PREF_FLATTR_AUTH = "pref_flattr_authenticate"; public static final String PREF_FLATTR_REVOKE = "prefRevokeAccess"; @@ -105,23 +104,6 @@ public class PreferenceController { ); } - ui.findPreference(PreferenceController.PREF_FLATTR_THIS_APP).setOnPreferenceClickListener( - new Preference.OnPreferenceClickListener() { - - @Override - public boolean onPreferenceClick(Preference preference) { - new FlattrClickWorker(activity, - new SimpleFlattrThing(activity.getString(R.string.app_name), - FlattrUtils.APP_URL, - new FlattrStatus(FlattrStatus.STATUS_QUEUE) - ) - ).executeAsync(); - - return true; - } - } - ); - ui.findPreference(PreferenceController.PREF_FLATTR_REVOKE).setOnPreferenceClickListener( new Preference.OnPreferenceClickListener() { @@ -200,6 +182,15 @@ public class PreferenceController { } }); + ui.findPreference(UserPreferences.PREF_UPDATE_INTERVAL) + .setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + showUpdateIntervalTimePreferencesDialog(); + return true; + } + }); + ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL) .setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override @@ -345,14 +336,33 @@ public class PreferenceController { @Override public void onConfirmed(boolean autoFlattrEnabled, float autoFlattrValue) { - UserPreferences.setAutoFlattrSettings(activity, autoFlattrEnabled, autoFlattrValue); + UserPreferences.setAutoFlattrSettings(autoFlattrEnabled, autoFlattrValue); checkItemVisibility(); } }); return true; } }); - buildUpdateIntervalPreference(); + ui.findPreference(UserPreferences.PREF_IMAGE_CACHE_SIZE) + .setOnPreferenceChangeListener( + new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object o) { + if (o instanceof String) { + int newValue = Integer.valueOf((String) o) * 1024 * 1024; + if(newValue != UserPreferences.getImageCacheSize()) { + AlertDialog.Builder dialog = new AlertDialog.Builder(ui.getActivity()); + dialog.setTitle(android.R.string.dialog_alert_title); + dialog.setMessage(R.string.pref_restart_required); + dialog.setPositiveButton(android.R.string.ok, null); + dialog.show(); + } + return true; + } + return false; + } + } + ); buildSmartMarkAsPlayedPreference(); buildAutodownloadSelectedNetworsPreference(); setSelectedNetworksEnabled(UserPreferences @@ -385,12 +395,8 @@ public class PreferenceController { ui.findPreference(PreferenceController.PREF_GPODNET_HOSTNAME).setSummary(GpodnetPreferences.getHostname()); } - private void buildUpdateIntervalPreference() { + private String[] getUpdateIntervalEntries(final String[] values) { final Resources res = ui.getActivity().getResources(); - - ListPreference pref = (ListPreference) ui.findPreference(UserPreferences.PREF_UPDATE_INTERVAL); - String[] values = res.getStringArray( - R.array.update_intervall_values); String[] entries = new String[values.length]; for (int x = 0; x < values.length; x++) { Integer v = Integer.parseInt(values[x]); @@ -399,19 +405,15 @@ public class PreferenceController { entries[x] = res.getString(R.string.pref_update_interval_hours_manual); break; case 1: - entries[x] = v - + " " - + res.getString(R.string.pref_update_interval_hours_singular); + entries[x] = v + " " + res.getString(R.string.pref_update_interval_hours_singular); break; default: - entries[x] = v + " " - + res.getString(R.string.pref_update_interval_hours_plural); + entries[x] = v + " " + res.getString(R.string.pref_update_interval_hours_plural); break; } } - pref.setEntries(entries); - + return entries; } private void buildSmartMarkAsPlayedPreference() { @@ -426,7 +428,7 @@ public class PreferenceController { entries[x] = res.getString(R.string.pref_smart_mark_as_played_disabled); } else { Integer v = Integer.parseInt(values[x]); - entries[x] = v + " " + res.getString(R.string.time_unit_seconds); + entries[x] = res.getQuantityString(R.plurals.time_seconds_quantified, v); } } pref.setEntries(entries); @@ -529,9 +531,7 @@ public class PreferenceController { } UserPreferences.setAutodownloadSelectedNetworks( - activity, prefValuesList - .toArray(new String[prefValuesList - .size()]) + prefValuesList.toArray(new String[prefValuesList.size()]) ); return true; } else { @@ -606,7 +606,63 @@ public class PreferenceController { builder.create().show(); } + private void showUpdateIntervalTimePreferencesDialog() { + final Context context = ui.getActivity(); + + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(R.string.pref_autoUpdateIntervallOrTime_title); + builder.setMessage(R.string.pref_autoUpdateIntervallOrTime_message); + builder.setNegativeButton(R.string.pref_autoUpdateIntervallOrTime_Disable, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + UserPreferences.setUpdateInterval(0); + } + }); + builder.setNeutralButton(R.string.pref_autoUpdateIntervallOrTime_Interval, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_Interval)); + final String[] values = context.getResources().getStringArray(R.array.update_intervall_values); + final String[] entries = getUpdateIntervalEntries(values); + builder.setSingleChoiceItems(entries, -1, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + int hours = Integer.valueOf(values[which]); + UserPreferences.setUpdateInterval(hours); + dialog.dismiss(); + } + }); + builder.setNegativeButton(context.getString(R.string.cancel_label), null); + builder.show(); + } + }); + builder.setPositiveButton(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + int hourOfDay = 7, minute = 0; + int[] updateTime = UserPreferences.getUpdateTimeOfDay(); + if (updateTime.length == 2) { + hourOfDay = updateTime[0]; + minute = updateTime[1]; + } + TimePickerDialog timePickerDialog = new TimePickerDialog(context, new TimePickerDialog.OnTimeSetListener() { + @Override + public void onTimeSet(TimePicker view, int hourOfDay, int minute) { + if (view.getTag() == null) { // onTimeSet() may get called twice! + view.setTag("TAGGED"); + UserPreferences.setUpdateTimeOfDay(hourOfDay, minute); + } + } + }, hourOfDay, minute, DateFormat.is24HourFormat(context)); + timePickerDialog.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay)); + timePickerDialog.show(); + } + } + ); + builder.show(); + } public static interface PreferenceUI { diff --git a/app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java b/app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java index f55a7603f..2615ec5c8 100644 --- a/app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java +++ b/app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java @@ -9,24 +9,20 @@ import android.util.Log; import org.apache.commons.lang3.StringUtils; -import de.danoeh.antennapod.core.BuildConfig; import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.util.NetworkUtils; public class ConnectivityActionReceiver extends BroadcastReceiver { - private static final String TAG = "ConnectivityActionReceiver"; + private static final String TAG = "ConnectivityActionRecvr"; @Override public void onReceive(final Context context, Intent intent) { if (StringUtils.equals(intent.getAction(), ConnectivityManager.CONNECTIVITY_ACTION)) { - if (BuildConfig.DEBUG) - Log.d(TAG, "Received intent"); + Log.d(TAG, "Received intent"); - if (NetworkUtils.autodownloadNetworkAvailable(context)) { - if (BuildConfig.DEBUG) - Log.d(TAG, - "auto-dl network available, starting auto-download"); + if (NetworkUtils.autodownloadNetworkAvailable()) { + Log.d(TAG, "auto-dl network available, starting auto-download"); DBTasks.autodownloadUndownloadedItems(context); } else { // if new network is Wi-Fi, finish ongoing downloads, // otherwise cancel all downloads @@ -34,12 +30,9 @@ public class ConnectivityActionReceiver extends BroadcastReceiver { .getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo ni = cm.getActiveNetworkInfo(); if (ni == null || ni.getType() != ConnectivityManager.TYPE_WIFI) { - if (BuildConfig.DEBUG) - Log.i(TAG, - "Device is no longer connected to Wi-Fi. Cancelling ongoing downloads"); + Log.i(TAG, "Device is no longer connected to Wi-Fi. Cancelling ongoing downloads"); DownloadRequester.getInstance().cancelAllDownloads(context); } - } } } 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 1fe9e2cf9..990b3bd54 100644 --- a/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java +++ b/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java @@ -62,7 +62,7 @@ public class PlayerWidgetService extends Service { DBWriter.markItemRead(this, item, true, false); DBWriter.removeQueueItem(this, item, false); DBWriter.addItemToPlaybackHistory(this, media); - if (UserPreferences.isAutoDelete()) { + if (item.getFeed().getPreferences().getCurrentAutoDelete()) { Log.d(TAG, "Delete " + media.toString()); DBWriter.deleteFeedMediaOfItem(this, media.getId()); } |