Decouple preferences

This commit is contained in:
ByteHamster 2022-04-25 22:45:47 +02:00
parent 20363ee41c
commit 069a2ca329
19 changed files with 75 additions and 410 deletions

View File

@ -10,6 +10,7 @@ import androidx.test.espresso.matcher.RootMatchers;
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithmFactory;
import org.awaitility.Awaitility;
import org.junit.Before;
import org.junit.Rule;
@ -397,7 +398,7 @@ public class PreferencesTest {
onView(withId(R.id.select_dialog_listview)).perform(swipeDown());
onView(withText(R.string.episode_cleanup_except_favorite_removal)).perform(click());
Awaitility.await().atMost(1000, MILLISECONDS)
.until(() -> UserPreferences.getEpisodeCleanupAlgorithm() instanceof ExceptFavoriteCleanupAlgorithm);
.until(() -> EpisodeCleanupAlgorithmFactory.build() instanceof ExceptFavoriteCleanupAlgorithm);
}
@Test
@ -408,7 +409,7 @@ public class PreferencesTest {
onView(withId(R.id.select_dialog_listview)).perform(swipeDown());
onView(withText(R.string.episode_cleanup_queue_removal)).perform(click());
Awaitility.await().atMost(1000, MILLISECONDS)
.until(() -> UserPreferences.getEpisodeCleanupAlgorithm() instanceof APQueueCleanupAlgorithm);
.until(() -> EpisodeCleanupAlgorithmFactory.build() instanceof APQueueCleanupAlgorithm);
}
@Test
@ -419,7 +420,7 @@ public class PreferencesTest {
onView(withId(R.id.select_dialog_listview)).perform(swipeUp());
onView(withText(R.string.episode_cleanup_never)).perform(click());
Awaitility.await().atMost(1000, MILLISECONDS)
.until(() -> UserPreferences.getEpisodeCleanupAlgorithm() instanceof APNullCleanupAlgorithm);
.until(() -> EpisodeCleanupAlgorithmFactory.build() instanceof APNullCleanupAlgorithm);
}
@Test
@ -431,7 +432,7 @@ public class PreferencesTest {
onView(withText(R.string.episode_cleanup_after_listening)).perform(click());
Awaitility.await().atMost(1000, MILLISECONDS)
.until(() -> {
EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
EpisodeCleanupAlgorithm alg = EpisodeCleanupAlgorithmFactory.build();
if (alg instanceof APCleanupAlgorithm) {
APCleanupAlgorithm cleanupAlg = (APCleanupAlgorithm) alg;
return cleanupAlg.getNumberOfHoursAfterPlayback() == 0;
@ -450,7 +451,7 @@ public class PreferencesTest {
onView(withText(search)).perform(click());
Awaitility.await().atMost(1000, MILLISECONDS)
.until(() -> {
EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
EpisodeCleanupAlgorithm alg = EpisodeCleanupAlgorithmFactory.build();
if (alg instanceof APCleanupAlgorithm) {
APCleanupAlgorithm cleanupAlg = (APCleanupAlgorithm) alg;
return cleanupAlg.getNumberOfHoursAfterPlayback() == 72; // 5 days

View File

@ -231,6 +231,7 @@ public class MainActivity extends CastEnabledActivity {
// for backward compatibility, we only change defaults for fresh installs
UserPreferences.setUpdateInterval(12);
AutoUpdateManager.restartUpdateAlarm(this);
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean(PREF_IS_FIRST_LAUNCH, false);

View File

@ -10,6 +10,7 @@ import android.widget.ArrayAdapter;
import androidx.appcompat.app.AlertDialog;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.databinding.FeedRefreshDialogBinding;
import org.apache.commons.lang3.ArrayUtils;
@ -70,6 +71,7 @@ public class FeedRefreshIntervalDialog {
builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
if (viewBinding.intervalRadioButton.isChecked()) {
UserPreferences.setUpdateInterval(INTERVAL_VALUES_HOURS[viewBinding.spinner.getSelectedItemPosition()]);
AutoUpdateManager.restartUpdateAlarm(context);
} else if (viewBinding.timeRadioButton.isChecked()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
UserPreferences.setUpdateTimeOfDay(viewBinding.timePicker.getHour(),
@ -78,8 +80,10 @@ public class FeedRefreshIntervalDialog {
UserPreferences.setUpdateTimeOfDay(viewBinding.timePicker.getCurrentHour(),
viewBinding.timePicker.getCurrentMinute());
}
AutoUpdateManager.restartUpdateAlarm(context);
} else if (viewBinding.disableRadioButton.isChecked()) {
UserPreferences.disableAutoUpdate(context);
UserPreferences.disableAutoUpdate();
AutoUpdateManager.disableAutoUpdate(context);
} else {
throw new IllegalStateException("Unexpected error.");
}

View File

@ -29,7 +29,7 @@ import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.core.service.download.ProxyConfig;
import de.danoeh.antennapod.model.download.ProxyConfig;
import io.reactivex.Completable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;

View File

@ -17,7 +17,7 @@ import java.util.Set;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.feed.SubscriptionsFilter;
import de.danoeh.antennapod.model.feed.SubscriptionsFilter;
import de.danoeh.antennapod.core.feed.SubscriptionsFilterGroup;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.ui.common.RecursiveRadioGroup;

View File

@ -14,7 +14,6 @@ import androidx.annotation.VisibleForTesting;
import androidx.core.app.NotificationCompat;
import androidx.preference.PreferenceManager;
import de.danoeh.antennapod.model.feed.FeedCounter;
import org.json.JSONArray;
import org.json.JSONException;
@ -32,16 +31,11 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.model.feed.FeedCounter;
import de.danoeh.antennapod.model.playback.MediaType;
import de.danoeh.antennapod.core.feed.SubscriptionsFilter;
import de.danoeh.antennapod.core.service.download.ProxyConfig;
import de.danoeh.antennapod.core.storage.APCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.ExceptFavoriteCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.APNullCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.APQueueCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm;
import de.danoeh.antennapod.model.feed.SubscriptionsFilter;
import de.danoeh.antennapod.model.download.ProxyConfig;
import de.danoeh.antennapod.model.feed.SortOrder;
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
/**
* Provides access to preferences set by the user in the settings screen. A
@ -692,33 +686,22 @@ public class UserPreferences {
.apply();
}
/**
* Sets the update interval value.
*/
public static void setUpdateInterval(long hours) {
prefs.edit()
.putString(PREF_UPDATE_INTERVAL, String.valueOf(hours))
.apply();
// when updating with an interval, we assume the user wants
// to update *now* and then every 'hours' interval thereafter.
AutoUpdateManager.restartUpdateAlarm(context);
}
/**
* Sets the update interval value.
*/
public static void setUpdateTimeOfDay(int hourOfDay, int minute) {
prefs.edit()
.putString(PREF_UPDATE_INTERVAL, hourOfDay + ":" + minute)
.apply();
AutoUpdateManager.restartUpdateAlarm(context);
}
public static void disableAutoUpdate(Context context) {
public static void disableAutoUpdate() {
prefs.edit()
.putString(PREF_UPDATE_INTERVAL, "0")
.apply();
AutoUpdateManager.disableAutoUpdate(context);
}
public static boolean gpodnetNotificationsEnabled() {
@ -835,22 +818,6 @@ public class UserPreferences {
.apply();
}
public static EpisodeCleanupAlgorithm getEpisodeCleanupAlgorithm() {
if (!isEnableAutodownload()) {
return new APNullCleanupAlgorithm();
}
int cleanupValue = getEpisodeCleanupValue();
if (cleanupValue == EPISODE_CLEANUP_EXCEPT_FAVORITE) {
return new ExceptFavoriteCleanupAlgorithm();
} else if (cleanupValue == EPISODE_CLEANUP_QUEUE) {
return new APQueueCleanupAlgorithm();
} else if (cleanupValue == EPISODE_CLEANUP_NULL) {
return new APNullCleanupAlgorithm();
} else {
return new APCleanupAlgorithm(cleanupValue);
}
}
public static int getEpisodeCleanupValue() {
return Integer.parseInt(prefs.getString(PREF_EPISODE_CLEANUP, "" + EPISODE_CLEANUP_NULL));
}

View File

@ -6,6 +6,7 @@ import androidx.annotation.NonNull;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.BasicAuthorizationInterceptor;
import de.danoeh.antennapod.core.service.UserAgentInterceptor;
import de.danoeh.antennapod.model.download.ProxyConfig;
import de.danoeh.antennapod.net.ssl.SslClientSetup;
import okhttp3.Cache;
import okhttp3.Credentials;

View File

@ -20,6 +20,7 @@ import androidx.core.app.ServiceCompat;
import androidx.core.content.ContextCompat;
import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.feed.LocalFeedUpdater;
import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithmFactory;
import de.danoeh.antennapod.model.download.DownloadStatus;
import org.apache.commons.io.FileUtils;
import org.greenrobot.eventbus.EventBus;
@ -455,7 +456,7 @@ public class DownloadService extends Service {
Log.d(TAG, "Received enqueue request. #requests=" + requests.size());
if (intent.getBooleanExtra(EXTRA_CLEANUP_MEDIA, false)) {
UserPreferences.getEpisodeCleanupAlgorithm().makeRoomForEpisodes(getApplicationContext(), requests.size());
EpisodeCleanupAlgorithmFactory.build().makeRoomForEpisodes(getApplicationContext(), requests.size());
}
for (DownloadRequest request : requests) {

View File

@ -20,15 +20,16 @@ import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.model.feed.FeedCounter;
import de.danoeh.antennapod.model.feed.FeedPreferences;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.storage.database.LongIntMap;
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
import de.danoeh.antennapod.storage.database.PodDBAdapter;
import java.util.Map;
public class NewEpisodesNotification {
private static final String TAG = "NewEpisodesNotification";
private static final String GROUP_KEY = "de.danoeh.antennapod.EPISODES";
private LongIntMap countersBefore;
private Map<Long, Integer> countersBefore;
public NewEpisodesNotification() {
}
@ -46,7 +47,7 @@ public class NewEpisodesNotification {
return;
}
int newEpisodesBefore = countersBefore.get(feed.getId());
int newEpisodesBefore = countersBefore.containsKey(feed.getId()) ? countersBefore.get(feed.getId()) : 0;
int newEpisodesAfter = getNewEpisodeCount(feed.getId());
Log.d(TAG, "New episodes before: " + newEpisodesBefore + ", after: " + newEpisodesAfter);
@ -130,7 +131,8 @@ public class NewEpisodesNotification {
private static int getNewEpisodeCount(long feedId) {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
int episodeCount = adapter.getFeedCounters(FeedCounter.SHOW_NEW, feedId).get(feedId);
Map<Long, Integer> counters = adapter.getFeedCounters(FeedCounter.SHOW_NEW, feedId);
int episodeCount = counters.containsKey(feedId) ? counters.get(feedId) : 0;
adapter.close();
return episodeCount;
}

View File

@ -76,7 +76,7 @@ public class AutomaticDownloadAlgorithm {
int autoDownloadableEpisodes = candidates.size();
int downloadedEpisodes = DBReader.getNumberOfDownloadedEpisodes();
int deletedEpisodes = UserPreferences.getEpisodeCleanupAlgorithm()
int deletedEpisodes = EpisodeCleanupAlgorithmFactory.build()
.makeRoomForEpisodes(context, autoDownloadableEpisodes);
boolean cacheIsUnlimited =
UserPreferences.getEpisodeCacheSize() == UserPreferences.getEpisodeCacheSizeUnlimited();

View File

@ -21,7 +21,7 @@ import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedItemFilter;
import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.model.feed.FeedPreferences;
import de.danoeh.antennapod.core.feed.SubscriptionsFilter;
import de.danoeh.antennapod.model.feed.SubscriptionsFilter;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.model.download.DownloadStatus;
import de.danoeh.antennapod.storage.database.PodDBAdapter;
@ -31,7 +31,6 @@ import de.danoeh.antennapod.storage.database.mapper.FeedCursorMapper;
import de.danoeh.antennapod.storage.database.mapper.FeedItemCursorMapper;
import de.danoeh.antennapod.storage.database.mapper.FeedMediaCursorMapper;
import de.danoeh.antennapod.storage.database.mapper.FeedPreferencesCursorMapper;
import de.danoeh.antennapod.storage.database.LongIntMap;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.comparator.DownloadStatusComparator;
import de.danoeh.antennapod.core.util.comparator.FeedItemPubdateComparator;
@ -881,7 +880,7 @@ public final class DBReader {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
final LongIntMap feedCounters = adapter.getFeedCounters(UserPreferences.getFeedCounterSetting());
final Map<Long, Integer> feedCounters = adapter.getFeedCounters(UserPreferences.getFeedCounterSetting());
SubscriptionsFilter subscriptionsFilter = UserPreferences.getSubscriptionsFilter();
List<Feed> feeds = subscriptionsFilter.filter(getFeedList(adapter), feedCounters);
@ -889,8 +888,8 @@ public final class DBReader {
int feedOrder = UserPreferences.getFeedOrder();
if (feedOrder == UserPreferences.FEED_ORDER_COUNTER) {
comparator = (lhs, rhs) -> {
long counterLhs = feedCounters.get(lhs.getId());
long counterRhs = feedCounters.get(rhs.getId());
long counterLhs = feedCounters.containsKey(lhs.getId()) ? feedCounters.get(lhs.getId()) : 0;
long counterRhs = feedCounters.containsKey(rhs.getId()) ? feedCounters.get(rhs.getId()) : 0;
if (counterLhs > counterRhs) {
// reverse natural order: podcast with most unplayed episodes first
return -1;
@ -913,11 +912,11 @@ public final class DBReader {
}
};
} else if (feedOrder == UserPreferences.FEED_ORDER_MOST_PLAYED) {
final LongIntMap playedCounters = adapter.getPlayedEpisodesCounters();
final Map<Long, Integer> playedCounters = adapter.getPlayedEpisodesCounters();
comparator = (lhs, rhs) -> {
long counterLhs = playedCounters.get(lhs.getId());
long counterRhs = playedCounters.get(rhs.getId());
long counterLhs = playedCounters.containsKey(lhs.getId()) ? playedCounters.get(lhs.getId()) : 0;
long counterRhs = playedCounters.containsKey(rhs.getId()) ? playedCounters.get(rhs.getId()) : 0;
if (counterLhs > counterRhs) {
// podcast with most played episodes first
return -1;
@ -945,8 +944,8 @@ public final class DBReader {
Map<String, NavDrawerData.TagDrawerItem> folders = new HashMap<>();
for (Feed feed : feeds) {
for (String tag : feed.getPreferences().getTags()) {
NavDrawerData.FeedDrawerItem drawerItem = new NavDrawerData.FeedDrawerItem(feed, feed.getId(),
feedCounters.get(feed.getId()));
int counter = feedCounters.containsKey(feed.getId()) ? feedCounters.get(feed.getId()) : 0;
NavDrawerData.FeedDrawerItem drawerItem = new NavDrawerData.FeedDrawerItem(feed, feed.getId(), counter);
if (FeedPreferences.TAG_ROOT.equals(tag)) {
items.add(drawerItem);
continue;
@ -967,7 +966,7 @@ public final class DBReader {
items.addAll(foldersSorted);
NavDrawerData result = new NavDrawerData(items, queueSize, numNewItems, numDownloadedItems,
feedCounters, UserPreferences.getEpisodeCleanupAlgorithm().getReclaimableItems());
feedCounters, EpisodeCleanupAlgorithmFactory.build().getReclaimableItems());
adapter.close();
return result;
}

View File

@ -228,7 +228,7 @@ public final class DBTasks {
* @param context Used for accessing the DB.
*/
public static void performAutoCleanup(final Context context) {
UserPreferences.getEpisodeCleanupAlgorithm().performCleanup(context);
EpisodeCleanupAlgorithmFactory.build().performCleanup(context);
}
private static Feed searchFeedByIdentifyingValueOrID(Feed feed) {

View File

@ -0,0 +1,22 @@
package de.danoeh.antennapod.core.storage;
import de.danoeh.antennapod.core.preferences.UserPreferences;
public abstract class EpisodeCleanupAlgorithmFactory {
public static EpisodeCleanupAlgorithm build() {
if (!UserPreferences.isEnableAutodownload()) {
return new APNullCleanupAlgorithm();
}
int cleanupValue = UserPreferences.getEpisodeCleanupValue();
switch (cleanupValue) {
case UserPreferences.EPISODE_CLEANUP_EXCEPT_FAVORITE:
return new ExceptFavoriteCleanupAlgorithm();
case UserPreferences.EPISODE_CLEANUP_QUEUE:
return new APQueueCleanupAlgorithm();
case UserPreferences.EPISODE_CLEANUP_NULL:
return new APNullCleanupAlgorithm();
default:
return new APCleanupAlgorithm(cleanupValue);
}
}
}

View File

@ -1,24 +1,24 @@
package de.danoeh.antennapod.core.storage;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.storage.database.LongIntMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class NavDrawerData {
public final List<DrawerItem> items;
public final int queueSize;
public final int numNewItems;
public final int numDownloadedItems;
public final LongIntMap feedCounters;
public final Map<Long, Integer> feedCounters;
public final int reclaimableSpace;
public NavDrawerData(List<DrawerItem> feeds,
int queueSize,
int numNewItems,
int numDownloadedItems,
LongIntMap feedIndicatorValues,
Map<Long, Integer> feedIndicatorValues,
int reclaimableSpace) {
this.items = feeds;
this.queueSize = queueSize;

View File

@ -1,68 +0,0 @@
package de.danoeh.antennapod.core.util;
import de.danoeh.antennapod.storage.database.LongIntMap;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class LongLongMapTest {
@Test
public void testEmptyMap() {
LongIntMap map = new LongIntMap();
assertEquals(0, map.size());
assertEquals("LongLongMap{}", map.toString());
assertEquals(0, map.get(42));
assertEquals(-1, map.get(42, -1));
assertFalse(map.delete(42));
assertEquals(-1, map.indexOfKey(42));
assertEquals(-1, map.indexOfValue(42));
assertEquals(1, map.hashCode());
}
@Test
public void testSingleElement() {
LongIntMap map = new LongIntMap();
map.put(17, 42);
assertEquals(1, map.size());
assertEquals("LongLongMap{17=42}", map.toString());
assertEquals(42, map.get(17));
assertEquals(42, map.get(17, -1));
assertEquals(0, map.indexOfKey(17));
assertEquals(0, map.indexOfValue(42));
assertTrue(map.delete(17));
}
@Test
public void testAddAndDelete() {
LongIntMap map = new LongIntMap();
for(int i=0; i < 100; i++) {
map.put(i * 17, i * 42);
}
assertEquals(100, map.size());
assertEquals(0, map.get(0));
assertEquals(42, map.get(17));
assertEquals(42, map.get(17, -1));
assertEquals(1, map.indexOfKey(17));
assertEquals(1, map.indexOfValue(42));
for(int i=0; i < 100; i++) {
assertTrue(map.delete(i * 17));
}
}
@Test
public void testOverwrite() {
LongIntMap map = new LongIntMap();
map.put(17, 42);
assertEquals(1, map.size());
assertEquals("LongLongMap{17=42}", map.toString());
assertEquals(42, map.get(17));
map.put(17, 23);
assertEquals(1, map.size());
assertEquals("LongLongMap{17=23}", map.toString());
assertEquals(23, map.get(17));
}
}

View File

@ -1,4 +1,4 @@
package de.danoeh.antennapod.core.service.download;
package de.danoeh.antennapod.model.download;
import androidx.annotation.Nullable;

View File

@ -1,13 +1,10 @@
package de.danoeh.antennapod.core.feed;
package de.danoeh.antennapod.model.feed;
import android.text.TextUtils;
import java.util.ArrayList;
import java.util.List;
import de.danoeh.antennapod.storage.database.LongIntMap;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.model.feed.FeedPreferences;
import java.util.Map;
public class SubscriptionsFilter {
private static final String divider = ",";
@ -69,7 +66,7 @@ public class SubscriptionsFilter {
/**
* Run a list of feed items through the filter.
*/
public List<Feed> filter(List<Feed> items, LongIntMap feedCounters) {
public List<Feed> filter(List<Feed> items, Map<Long, Integer> feedCounters) {
if (properties.length == 0) {
return items;
}
@ -104,7 +101,8 @@ public class SubscriptionsFilter {
if (showIfCounterGreaterZero) {
for (int i = result.size() - 1; i >= 0; i--) {
if (feedCounters.get(result.get(i).getId()) <= 0) {
if (!feedCounters.containsKey(result.get(i).getId())
|| feedCounters.get(result.get(i).getId()) <= 0) {
result.remove(i);
}
}

View File

@ -1,263 +0,0 @@
package de.danoeh.antennapod.storage.database;
import java.util.Arrays;
/**
* Fast and memory efficient long to long map
*/
public class LongIntMap {
private long[] keys;
private int[] values;
private int size;
/**
* Creates a new LongLongMap containing no mappings.
*/
public LongIntMap() {
this(10);
}
/**
* Creates a new SparseLongArray containing no mappings that will not
* require any additional memory allocation to store the specified
* number of mappings. If you supply an initial capacity of 0, the
* sparse array will be initialized with a light-weight representation
* not requiring any additional array allocations.
*/
public LongIntMap(int initialCapacity) {
if(initialCapacity < 0) {
throw new IllegalArgumentException("initial capacity must be 0 or higher");
}
keys = new long[initialCapacity];
values = new int[initialCapacity];
size = 0;
}
/**
* Increases size of array if needed
*/
private void growIfNeeded() {
if (size == keys.length) {
// Resize.
long[] newKeysArray = new long[size * 3 / 2 + 10];
int[] newValuesArray = new int[size * 3 / 2 + 10];
System.arraycopy(keys, 0, newKeysArray, 0, size);
System.arraycopy(values, 0, newValuesArray, 0, size);
keys = newKeysArray;
values = newValuesArray;
}
}
/**
* Gets the long mapped from the specified key, or <code>0</code>
* if no such mapping has been made.
*/
public int get(long key) {
return get(key, 0);
}
/**
* Gets the long mapped from the specified key, or the specified value
* if no such mapping has been made.
*/
public int get(long key, int valueIfKeyNotFound) {
int index = indexOfKey(key);
if(index >= 0) {
return values[index];
} else {
return valueIfKeyNotFound;
}
}
/**
* Removes the mapping from the specified key, if there was any.
*/
public boolean delete(long key) {
int index = indexOfKey(key);
if (index >= 0) {
removeAt(index);
return true;
} else {
return false;
}
}
/**
* Removes the mapping at the given index.
*/
private void removeAt(int index) {
System.arraycopy(keys, index + 1, keys, index, size - (index + 1));
System.arraycopy(values, index + 1, values, index, size - (index + 1));
size--;
}
/**
* Adds a mapping from the specified key to the specified value,
* replacing the previous mapping from the specified key if there
* was one.
*/
public void put(long key, int value) {
int index = indexOfKey(key);
if (index >= 0) {
values[index] = value;
} else {
growIfNeeded();
keys[size] = key;
values[size] = value;
size++;
}
}
/**
* Returns the number of key-value mappings that this SparseIntArray
* currently stores.
*/
public int size() {
return size;
}
/**
* Given an index in the range <code>0...size()-1</code>, returns
* the key from the <code>index</code>th key-value mapping that this
* SparseLongArray stores.
*
* <p>The keys corresponding to indices in ascending order are guaranteed to
* be in ascending order, e.g., <code>keyAt(0)</code> will return the
* smallest key and <code>keyAt(size()-1)</code> will return the largest
* key.</p>
*/
private long keyAt(int index) {
if (index >= size) {
throw new IndexOutOfBoundsException("n >= size()");
} else if(index < 0) {
throw new IndexOutOfBoundsException("n < 0");
}
return keys[index];
}
/**
* Given an index in the range <code>0...size()-1</code>, returns
* the value from the <code>index</code>th key-value mapping that this
* SparseLongArray stores.
*
* <p>The values corresponding to indices in ascending order are guaranteed
* to be associated with keys in ascending order, e.g.,
* <code>valueAt(0)</code> will return the value associated with the
* smallest key and <code>valueAt(size()-1)</code> will return the value
* associated with the largest key.</p>
*/
private int valueAt(int index) {
if (index >= size) {
throw new IndexOutOfBoundsException("n >= size()");
} else if(index < 0) {
throw new IndexOutOfBoundsException("n < 0");
}
return values[index];
}
/**
* Returns the index for which {@link #keyAt} would return the
* specified key, or a negative number if the specified
* key is not mapped.
*/
public int indexOfKey(long key) {
for(int i=0; i < size; i++) {
if(keys[i] == key) {
return i;
}
}
return -1;
}
/**
* Returns an index for which {@link #valueAt} would return the
* specified key, or a negative number if no keys map to the
* specified value.
* Beware that this is a linear search, unlike lookups by key,
* and that multiple keys can map to the same value and this will
* find only one of them.
*/
public int indexOfValue(long value) {
for (int i = 0; i < size; i++) {
if (values[i] == value) {
return i;
}
}
return -1;
}
/**
* Removes all key-value mappings from this SparseIntArray.
*/
public void clear() {
keys = new long[10];
values = new int[10];
size = 0;
}
/**
* Returns a copy of the values contained in this map.
*
* @return a copy of the values contained in this map
*/
public int[] values() {
return Arrays.copyOf(values, size);
}
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (! (other instanceof LongIntMap)) {
return false;
}
LongIntMap otherMap = (LongIntMap) other;
if (size != otherMap.size) {
return false;
}
for (int i = 0; i < size; i++) {
if (keys[i] != otherMap.keys[i] ||
values[i] != otherMap.values[i]) {
return false;
}
}
return true;
}
@Override
public int hashCode() {
int hashCode = 1;
for (int i = 0; i < size; i++) {
long value = values[i];
hashCode = 31 * hashCode + (int)(value ^ (value >>> 32));
}
return hashCode;
}
@Override
public String toString() {
if (size() <= 0) {
return "LongLongMap{}";
}
StringBuilder buffer = new StringBuilder(size * 28);
buffer.append("LongLongMap{");
for (int i=0; i < size; i++) {
if (i > 0) {
buffer.append(", ");
}
long key = keyAt(i);
buffer.append(key);
buffer.append('=');
long value = valueAt(i);
buffer.append(value);
}
buffer.append('}');
return buffer.toString();
}
}

View File

@ -1175,7 +1175,7 @@ public class PodDBAdapter {
return result;
}
public final LongIntMap getFeedCounters(FeedCounter setting, long... feedIds) {
public final Map<Long, Integer> getFeedCounters(FeedCounter setting, long... feedIds) {
String whereRead;
switch (setting) {
case SHOW_NEW_UNPLAYED_SUM:
@ -1194,12 +1194,12 @@ public class PodDBAdapter {
case SHOW_NONE:
// deliberate fall-through
default: // NONE
return new LongIntMap(0);
return new HashMap<>();
}
return conditionalFeedCounterRead(whereRead, feedIds);
}
private LongIntMap conditionalFeedCounterRead(String whereRead, long... feedIds) {
private Map<Long, Integer> conditionalFeedCounterRead(String whereRead, long... feedIds) {
String limitFeeds = "";
if (feedIds.length > 0) {
// work around TextUtils.join wanting only boxed items
@ -1222,7 +1222,7 @@ public class PodDBAdapter {
+ whereRead + " GROUP BY " + KEY_FEED;
Cursor c = db.rawQuery(query, null);
LongIntMap result = new LongIntMap(c.getCount());
Map<Long, Integer> result = new HashMap<>();
if (c.moveToFirst()) {
do {
long feedId = c.getLong(0);
@ -1234,7 +1234,7 @@ public class PodDBAdapter {
return result;
}
public final LongIntMap getPlayedEpisodesCounters(long... feedIds) {
public final Map<Long, Integer> getPlayedEpisodesCounters(long... feedIds) {
String whereRead = KEY_READ + "=" + FeedItem.PLAYED;
return conditionalFeedCounterRead(whereRead, feedIds);
}