From 1e7870542558c491ccf53ccc27ddd178bf441773 Mon Sep 17 00:00:00 2001 From: Kurt Aaholst Date: Fri, 27 Sep 2024 17:04:07 +0200 Subject: [PATCH] Refresh shortcuts --- .../java/uk/org/ngo/squeezer/Preferences.java | 23 +- .../itemlist/HomeMenuJiveItemView.java | 54 ++- .../ngo/squeezer/itemlist/JiveItemView.java | 26 +- .../model/CustomJiveItemHandling.java | 152 ++++--- .../uk/org/ngo/squeezer/model/JiveItem.java | 15 +- .../org/ngo/squeezer/service/CometClient.java | 18 +- .../ngo/squeezer/service/ConnectionState.java | 43 ++ .../squeezer/service/HomeMenuHandling.java | 102 ++--- .../ngo/squeezer/service/ISqueezeService.java | 6 +- .../ngo/squeezer/service/SlimDelegate.java | 28 +- .../ngo/squeezer/service/SqueezeService.java | 48 ++- .../service/event/LastscanChanged.java | 13 + .../org/ngo/squeezer/util/ReflectionTest.java | 383 +++++------------- 13 files changed, 406 insertions(+), 505 deletions(-) create mode 100644 Squeezer/src/main/java/uk/org/ngo/squeezer/service/event/LastscanChanged.java diff --git a/Squeezer/src/main/java/uk/org/ngo/squeezer/Preferences.java b/Squeezer/src/main/java/uk/org/ngo/squeezer/Preferences.java index 6a53a620e..1542caa87 100644 --- a/Squeezer/src/main/java/uk/org/ngo/squeezer/Preferences.java +++ b/Squeezer/src/main/java/uk/org/ngo/squeezer/Preferences.java @@ -770,15 +770,15 @@ public void setArchivedMenuItems(List list, Player player) { editor.apply(); } - public void saveShortcuts(Map map) { + public void saveShortcuts(List shortcuts) { + Map map = convertShortcuts(shortcuts); SharedPreferences.Editor editor = sharedPreferences.edit(); JSONObject json = new JSONObject(map); editor.putString(CUSTOM_SHORTCUTS, json.toString()); editor.apply(); } -// TODO If possible, remove this or CustomJiveItemHandling.convertShortcuts() - public Map convertShortcuts(List customShortcuts) { + private Map convertShortcuts(List customShortcuts) { Map map = new HashMap<>(); for (JiveItem item : customShortcuts) { map.put(item.getName(), item.getRecord()); @@ -790,28 +790,25 @@ public Map convertShortcuts(List customShortcuts) { * Return a map of names (keys) of shortcuts with value: Map which is a record * and can be used as such when generating JiveItems */ - public HashMap> restoreCustomShortcuts() { - HashMap> allShortcutsFromPref = new HashMap<>(); + public List> getCustomShortcuts() { + List> allShortcutsFromPref = new ArrayList<>(); String jsonString = sharedPreferences.getString(CUSTOM_SHORTCUTS, null); - if (TextUtils.isEmpty(jsonString)) { - return allShortcutsFromPref; - } + if (TextUtils.isEmpty(jsonString)) return allShortcutsFromPref; + try { // whole String to JSON, then extract name/record pairs JSONObject allShortcuts = new JSONObject(jsonString); Iterator keysItr = allShortcuts.keys(); while (keysItr.hasNext()) { String key = keysItr.next(); - JSON json = new JSON(); try { - Map recordFromPref = (Map) json.parse(allShortcuts.getString(key)); - allShortcutsFromPref.put(key, recordFromPref); + allShortcutsFromPref.add((Map) JSON.parse(allShortcuts.getString(key))); } catch (IllegalStateException e) { - Log.w(TAG, "Can't parse custom shortcut '" + key + "': " + e.getMessage()); + Log.w(TAG, "Can't parse custom shortcut '" + key + "'", e); } } } catch (JSONException e) { - e.printStackTrace(); + Log.w(TAG, "Exception reading shortcuts", e); } return allShortcutsFromPref; } diff --git a/Squeezer/src/main/java/uk/org/ngo/squeezer/itemlist/HomeMenuJiveItemView.java b/Squeezer/src/main/java/uk/org/ngo/squeezer/itemlist/HomeMenuJiveItemView.java index 5d0f892ba..7251b4a46 100644 --- a/Squeezer/src/main/java/uk/org/ngo/squeezer/itemlist/HomeMenuJiveItemView.java +++ b/Squeezer/src/main/java/uk/org/ngo/squeezer/itemlist/HomeMenuJiveItemView.java @@ -2,13 +2,12 @@ import android.view.View; -import uk.org.ngo.squeezer.Preferences; import uk.org.ngo.squeezer.R; -import uk.org.ngo.squeezer.Squeezer; import uk.org.ngo.squeezer.framework.ItemAdapter; -import uk.org.ngo.squeezer.model.CustomJiveItemHandling; import uk.org.ngo.squeezer.framework.ItemViewHolder; import uk.org.ngo.squeezer.model.JiveItem; +import uk.org.ngo.squeezer.service.HomeMenuHandling; +import uk.org.ngo.squeezer.service.ISqueezeService; import uk.org.ngo.squeezer.widget.UndoBarController; @@ -23,41 +22,35 @@ public class HomeMenuJiveItemView extends JiveItemView { public HomeMenuJiveItemView(HomeMenuActivity homeMenuActivity, View view, ItemAdapter, JiveItem> adapter) { super(homeMenuActivity, view); mItemAdapter = adapter; - if (mCustomJiveItemHandling == null) { - mCustomJiveItemHandling = new CustomJiveItemHandling(getActivity()); - } } @Override public void bindView(JiveItem item) { super.bindView(item); - final boolean isArchiveActive = Squeezer.getPreferences().getCustomizeHomeMenuMode() == Preferences.CustomizeHomeMenuMode.ARCHIVE; - final boolean isShortcutsActive = Squeezer.getPreferences().getCustomizeShortcutsMode() == Preferences.CustomizeShortcutsMode.ENABLED; + // archive DISABLED if (isArchiveActive) { - itemView.setOnLongClickListener(view -> setArchive(item, isShortcutsActive)); - } else { // archive DISABLED - if (isShortcutsActive) { - itemView.setOnLongClickListener(view -> setShortcuts(item)); - } else { // no archive and no shortcuts - itemView.setOnLongClickListener(null); - } + itemView.setOnLongClickListener(view -> setArchive(item)); + } else if (isShortcutsActive) { + itemView.setOnLongClickListener(view -> setShortcut(item)); + } else { // no archive and no shortcuts + itemView.setOnLongClickListener(null); } } - private boolean setArchive(JiveItem item, boolean isShortcutsActive) { + private boolean setArchive(JiveItem item) { if (!item.getId().equals(JiveItem.ARCHIVE.getId())) { // not the Archive node itself + ISqueezeService service = getActivity().requireService(); if (!item.getNode().equals(JiveItem.ARCHIVE.getId())) { // not INSIDE archive node - if (getActivity().requireService().isInArchive(item)) { + if (service.isInArchive(item)) { getActivity().showDisplayMessage(R.string.MENU_IS_SUBMENU_IN_ARCHIVE); return true; } - if (mCustomJiveItemHandling.isCustomShortcut(item)) { + if (service.getHomeMenuHandling().isCustomShortcut(item)) { if (isShortcutsActive) { - return removeShortcuts(item); - } else { - return true; // is shortcut but setting DISABLED, do nothing + removeShortcut(item); } + return true; // Don't show UndoBar for shortcuts } else { // is not a shortcut, remove the item and bring up UndoBar mItemAdapter.removeItem(getBindingAdapterPosition()); @@ -69,8 +62,8 @@ private boolean setArchive(JiveItem item, boolean isShortcutsActive) { UndoBarController.show(getActivity(), R.string.MENU_ITEM_MOVED, new UndoBarController.UndoListener() { @Override public void onUndo() { - getActivity().requireService().toggleArchiveItem(item); - getActivity().requireService().triggerHomeMenuEvent(); + service.toggleArchiveItem(item); + service.triggerHomeMenuEvent(); } @Override @@ -78,7 +71,7 @@ public void onDone() { } }); - if ((getActivity().requireService().toggleArchiveItem(item))) { + if ((service.toggleArchiveItem(item))) { // TODO: Do not instantly show the next screen or put UndoBar onto next screen HomeActivity.show(getActivity()); getActivity().showDisplayMessage(R.string.ARCHIVE_NODE_REMOVED); @@ -89,18 +82,19 @@ public void onDone() { return true; } - private boolean setShortcuts(JiveItem item) { - if (mCustomJiveItemHandling.isCustomShortcut(item)) { - return removeShortcuts(item); + private boolean setShortcut(JiveItem item) { + HomeMenuHandling homeMenuHandling = getActivity().requireService().getHomeMenuHandling(); + if (homeMenuHandling.isCustomShortcut(item)) { + removeShortcut(item); } return true; } - private boolean removeShortcuts(JiveItem item) { + private void removeShortcut(JiveItem item) { + HomeMenuHandling homeMenuHandling = getActivity().requireService().getHomeMenuHandling(); mItemAdapter.removeItem(getBindingAdapterPosition()); getActivity().showDisplayMessage(R.string.CUSTOM_SHORTCUT_REMOVED); getActivity().requireService().removeCustomShortcut(item); - mPreferences.saveShortcuts(mCustomJiveItemHandling.convertShortcuts()); - return true; // don't show UndoBar if Custom Shortcut + mPreferences.saveShortcuts(homeMenuHandling.getCustomShortcuts()); } } \ No newline at end of file diff --git a/Squeezer/src/main/java/uk/org/ngo/squeezer/itemlist/JiveItemView.java b/Squeezer/src/main/java/uk/org/ngo/squeezer/itemlist/JiveItemView.java index b85a482bb..3bcb6a759 100644 --- a/Squeezer/src/main/java/uk/org/ngo/squeezer/itemlist/JiveItemView.java +++ b/Squeezer/src/main/java/uk/org/ngo/squeezer/itemlist/JiveItemView.java @@ -39,6 +39,7 @@ import uk.org.ngo.squeezer.model.CustomJiveItemHandling; import uk.org.ngo.squeezer.model.JiveItem; import uk.org.ngo.squeezer.model.Window; +import uk.org.ngo.squeezer.service.HomeMenuHandling; public class JiveItemView extends ViewParamItemView { @@ -46,23 +47,15 @@ public class JiveItemView extends ViewParamItemView { private final ArtworkListLayout listLayout; Preferences mPreferences = Squeezer.getPreferences(); - final boolean isShortcutActive = mPreferences.getCustomizeShortcutsMode() == Preferences.CustomizeShortcutsMode.ENABLED; + final boolean isShortcutsActive = mPreferences.getCustomizeShortcutsMode() == Preferences.CustomizeShortcutsMode.ENABLED; final boolean isArchiveActive = mPreferences.getCustomizeHomeMenuMode() == Preferences.CustomizeHomeMenuMode.ARCHIVE; - /** - * Will also be used (and set) in HomeMenuJiveItemView. - */ - CustomJiveItemHandling mCustomJiveItemHandling = null; - JiveItemView(@NonNull JiveItemListActivity activity, @NonNull View view) { this(activity, activity.window.windowStyle, activity.getPreferredListLayout(), view); } JiveItemView(@NonNull JiveItemListActivity activity, Window.WindowStyle windowStyle, ArtworkListLayout preferredListLayout, @NonNull View view) { super(activity, view); - if (mCustomJiveItemHandling == null) { - mCustomJiveItemHandling = new CustomJiveItemHandling(activity); - } this.windowStyle = windowStyle; this.listLayout = listLayout(preferredListLayout, windowStyle); @@ -105,7 +98,7 @@ public void bindView(JiveItem item) { text2.setAlpha(getAlpha()); itemView.setOnClickListener(view -> onItemSelected()); - if ( isShortcutActive || isArchiveActive ) { + if ( isShortcutsActive || isArchiveActive ) { itemView.setOnLongClickListener(view -> putItemAsShortcut()); } else { itemView.setOnLongClickListener(null); @@ -130,15 +123,16 @@ public void bindView(JiveItem item) { */ private boolean putItemAsShortcut() { @StringRes int message = !isArchiveActive ? R.string.ITEM_CANNOT_BE_SHORTCUT : - isShortcutActive ? R.string.ITEM_CAN_NOT_BE_SHORTCUT_OR_ARCHIVED : R.string.ITEM_CANNOT_BE_ARCHIVED; + isShortcutsActive ? R.string.ITEM_CAN_NOT_BE_SHORTCUT_OR_ARCHIVED : R.string.ITEM_CANNOT_BE_ARCHIVED; - if (!mCustomJiveItemHandling.isShortcutable(item)) { + int shortCutWeight = CustomJiveItemHandling.shortcutWeight(item); + if (shortCutWeight == CustomJiveItemHandling.CUSTOM_SHORTCUT_WEIGHT_NOT_ALLOWED) { getActivity().showDisplayMessage(message); } else { - if (isShortcutActive) { - if (mCustomJiveItemHandling.triggerCustomShortcut(item)) { - mPreferences.saveShortcuts(mCustomJiveItemHandling.convertShortcuts()); -// TODO: check ok? + if (isShortcutsActive) { + HomeMenuHandling homeMenuHandling = getActivity().requireService().getHomeMenuHandling(); + if (homeMenuHandling.addShortcut(item, getActivity().parent, shortCutWeight)) { + mPreferences.saveShortcuts(homeMenuHandling.getCustomShortcuts()); getActivity().showDisplayMessage(R.string.ITEM_PUT_AS_SHORTCUT_ON_HOME_MENU); } else { getActivity().showDisplayMessage(R.string.ITEM_IS_ALREADY_A_SHORTCUT); diff --git a/Squeezer/src/main/java/uk/org/ngo/squeezer/model/CustomJiveItemHandling.java b/Squeezer/src/main/java/uk/org/ngo/squeezer/model/CustomJiveItemHandling.java index bbb46b433..11c994831 100644 --- a/Squeezer/src/main/java/uk/org/ngo/squeezer/model/CustomJiveItemHandling.java +++ b/Squeezer/src/main/java/uk/org/ngo/squeezer/model/CustomJiveItemHandling.java @@ -1,84 +1,124 @@ package uk.org.ngo.squeezer.model; -import java.util.HashMap; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; import java.util.Map; +import java.util.Optional; -import uk.org.ngo.squeezer.framework.ItemListActivity; -import uk.org.ngo.squeezer.service.HomeMenuHandling; -import uk.org.ngo.squeezer.service.ISqueezeService; +import uk.org.ngo.squeezer.itemlist.IServiceItemListCallback; +import uk.org.ngo.squeezer.service.SqueezeService; public class CustomJiveItemHandling { + private static final String TAG = CustomJiveItemHandling.class.getSimpleName(); + public static final int CUSTOM_SHORTCUT_WEIGHT_NOT_ALLOWED = -1; + public static final int CUSTOM_SHORTCUT_WEIGHT_MY_MUSIC = 2000; + private static final int CUSTOM_SHORTCUT_WEIGHT_APPS = 2010; + private static final int CUSTOM_SHORTCUT_WEIGHT_RADIO = 2020; - public int ADJUSTED_CUSTOM_SHORTCUT_WEIGHT = 2000; - final ItemListActivity mActivity; + public static int shortcutWeight(JiveItem item) { + // TODO add better check for fitting items + // TODO "All titles" is a name that comes up in several occations and will then not be updated + if ((item.nextWindow != null) || (item.goAction == null)) { + return CUSTOM_SHORTCUT_WEIGHT_NOT_ALLOWED; + } + return shortcutWeight(item.goAction.action); + } - public CustomJiveItemHandling(ItemListActivity activity) { - mActivity = activity; + public static void recoverShortcuts(SqueezeService service, List shortcuts) { + List albumShortcuts = new ArrayList<>(); + List artistShortcuts = new ArrayList<>(); + List genreShortcuts = new ArrayList<>(); + List trackShortcuts = new ArrayList<>(); + List folderShortcuts = new ArrayList<>(); + shortcuts.stream() + .filter(shortcut -> shortcutWeight(shortcut) == CUSTOM_SHORTCUT_WEIGHT_MY_MUSIC) + .forEach(shortcut -> { + Action itemAction = (shortcut.moreAction != null && shortcut.moreAction.action != null ? shortcut.moreAction : shortcut.goAction); + Action.JsonAction action = itemAction.action; + if (action.params.containsKey("folder_id") || "bmf".equals(shortcut.goAction.action.params.get("mode"))) folderShortcuts.add(shortcut); + else if (action.params.containsKey("track_id")) trackShortcuts.add(shortcut); + else if (action.params.containsKey("album_id")) albumShortcuts.add(shortcut); + else if (action.params.containsKey("artist_id")) artistShortcuts.add(shortcut); + else if (action.params.containsKey("genre_id")) genreShortcuts.add(shortcut); + }); + if (!(albumShortcuts.isEmpty() && trackShortcuts.isEmpty())) recoverItems(service, browseLibraryCommand("albums"), albumShortcuts, trackShortcuts); + if (!artistShortcuts.isEmpty()) recoverItems(service, browseLibraryCommand("artists"), artistShortcuts, List.of()); + if (!genreShortcuts.isEmpty()) recoverItems(service, browseLibraryCommand("genres"), genreShortcuts, List.of()); + if (!folderShortcuts.isEmpty()) recoverItems(service, browseLibraryCommand("bmf"), folderShortcuts, folderShortcuts); } - private ISqueezeService service() { - return mActivity.getService(); + private static void recoverItems(SqueezeService service, SlimCommand command, List mainShortcuts, List subShortCuts) { + service.requestItems(command, new RecoverReceiver(service, mainShortcuts, subShortCuts)); } - private HomeMenuHandling homeMenuHandling() { - return service().getDelegate().getHomeMenuHandling(); + private static SlimCommand browseLibraryCommand(String mode) { + return new SlimCommand() + .cmd("browselibrary", "items") + .param("mode", mode) + .param("menu", "1") + .param("useContextMenu", "1"); } - /** - * Send long pressed JiveItem - */ - public boolean triggerCustomShortcut(JiveItem item) { - item.appendWeight(ADJUSTED_CUSTOM_SHORTCUT_WEIGHT); - return homeMenuHandling().triggerCustomShortcut(item); + + private static int shortcutWeight(Action.JsonAction action) { + for (String s : action.cmd) { + if (allowMyMusic(s)) return CUSTOM_SHORTCUT_WEIGHT_MY_MUSIC; + if (allowApps(s)) return CUSTOM_SHORTCUT_WEIGHT_APPS; + if (allowRadio(s)) return CUSTOM_SHORTCUT_WEIGHT_RADIO; + } + return CUSTOM_SHORTCUT_WEIGHT_NOT_ALLOWED; } - public boolean isCustomShortcut(JiveItem item) { - return homeMenuHandling().customShortcuts.contains(item); + private static boolean allowMyMusic(String s) { + return s.equals("browselibrary"); } - public boolean isShortcutable(JiveItem item) { - // TODO add better check for fitting items - // TODO "All titles" is a name that comes up in several occations and will then not be updated - if ((item.nextWindow != null) || (item.goAction == null)) { - return false; - } - for (String s : item.goAction.action.cmd) { - if (allowMyMusic(s) || allowApps(s) || allowRadio(s)) { - return true; - } - } - return false; + private static boolean allowApps(String s) { + return s.equals("items"); } - private boolean allowMyMusic(String s) { - if (s.equals("browselibrary")) { - ADJUSTED_CUSTOM_SHORTCUT_WEIGHT = 2000; - return true; - } - return false; + private static boolean allowRadio(String s) { + return s.equals("play"); } - private boolean allowApps(String s) { - if (s.equals("items")) { - ADJUSTED_CUSTOM_SHORTCUT_WEIGHT = 2010; - return true; + private static class RecoverReceiver implements IServiceItemListCallback { + private final SqueezeService service; + private final List mainShortcuts; + private final List subShortCuts; + + public RecoverReceiver(SqueezeService service, List mainShortcuts, List subShortCuts) { + this.service = service; + this.mainShortcuts = mainShortcuts; + this.subShortCuts = subShortCuts; } - return false; - } - private boolean allowRadio(String s) { - if (s.equals("play")) { - ADJUSTED_CUSTOM_SHORTCUT_WEIGHT = 2020; - return true; + @Override + public void onItemsReceived(int count, int start, Map parameters, List items, Class dataType) { + if (!subShortCuts.isEmpty()) { + for (JiveItem item : items) { + if (item.goAction != null && item.goAction.action != null) { + String albumId = (String) item.goAction.action.params.get("album_id"); + if (albumId != null) recoverItems(service, browseLibraryCommand("tracks").param("album_id", albumId), subShortCuts, List.of()); + } + if (item.moreAction != null && item.moreAction.action != null) { + String folderId = (String) item.moreAction.action.params.get("folder_id"); + if (folderId != null) recoverItems(service, browseLibraryCommand("bmf").param("folder_id", folderId), subShortCuts, subShortCuts); + } + } + } + for (JiveItem shortcut : mainShortcuts) { + Optional found = items.stream().filter(item -> shortcut.getName().equals(item.getName())).findFirst(); + if (found.isPresent()) { + Log.i(TAG, "shortcut '" + shortcut.getName() + "': HIT, item=" + found.get()); + service.updateShortCut(shortcut, found.get().getRecord()); + } + } } - return false; - } - public Map convertShortcuts() { - Map map = new HashMap<>(); - for (JiveItem item : homeMenuHandling().customShortcuts) { - map.put(item.getName(), item.getRecord()); + @Override + public Object getClient() { + return service; } - return map; } } - diff --git a/Squeezer/src/main/java/uk/org/ngo/squeezer/model/JiveItem.java b/Squeezer/src/main/java/uk/org/ngo/squeezer/model/JiveItem.java index 89ba9582c..90a76e9b7 100644 --- a/Squeezer/src/main/java/uk/org/ngo/squeezer/model/JiveItem.java +++ b/Squeezer/src/main/java/uk/org/ngo/squeezer/model/JiveItem.java @@ -175,7 +175,7 @@ public Uri getIcon() { /** * @return Whether the song has downloadable artwork associated with it. */ - private boolean hasIconUri() { + public boolean hasIconUri() { return !getIcon().equals(Uri.EMPTY); } @@ -237,13 +237,6 @@ public Map getRecord() { return (Map) json.fromJSON(this.record); } - public void appendWeight(int weight) { - JSON json = new JSON(); - Map map = (Map) json.fromJSON(this.record); - map.put("weight", weight); - this.record = json.toJSON(map); - } - public JiveItem(Map record) { JSON json = new JSON(); this.record = json.toJSON(record); @@ -316,6 +309,7 @@ public JiveItem(Map record) { } public JiveItem(Parcel source) { + record = source.readString(); setId(source.readString()); name = source.readString(); text2 = source.readString(); @@ -356,6 +350,7 @@ public JiveItem(Parcel source) { @Override public void writeToParcel(Parcel dest, int flags) { + dest.writeString(record); dest.writeString(getId()); dest.writeString(name); dest.writeString(text2); @@ -391,10 +386,6 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeString(webLink.toString()); } - public void setWeight(int weight) { - this.weight = weight; - } - public boolean hasInput() { return hasInputField() || hasChoices(); } diff --git a/Squeezer/src/main/java/uk/org/ngo/squeezer/service/CometClient.java b/Squeezer/src/main/java/uk/org/ngo/squeezer/service/CometClient.java index c637d8db2..25098c752 100644 --- a/Squeezer/src/main/java/uk/org/ngo/squeezer/service/CometClient.java +++ b/Squeezer/src/main/java/uk/org/ngo/squeezer/service/CometClient.java @@ -75,7 +75,6 @@ import uk.org.ngo.squeezer.service.event.PlayerVolume; import uk.org.ngo.squeezer.service.event.RegisterSqueezeNetwork; import uk.org.ngo.squeezer.util.FluentHashMap; -import uk.org.ngo.squeezer.util.ImageFetcher; import uk.org.ngo.squeezer.util.Reflection; import uk.org.ngo.squeezer.util.SendWakeOnLan; @@ -214,6 +213,7 @@ public void startConnect(final SqueezeService service, boolean autoConnect) { cleanupBayeuxClient(); final Preferences.ServerAddress serverAddress = Squeezer.getPreferences().getServerAddress(); + mConnectionState.initLastScan(serverAddress.lastScan); final String username = serverAddress.userName; final String password = serverAddress.password; if (serverAddress.wakeOnLan) { @@ -381,18 +381,17 @@ private void parseServerStatus(ClientSessionChannel channel, Message message) { // We can't distinguish between no connected players and players not received // so we check the server version which is also set from server status - boolean firstTimePlayersReceived = (getConnectionState().getServerVersion() == null); + boolean firstTimePlayersReceived = (mConnectionState.getServerVersion() == null); - final Preferences preferences = Squeezer.getPreferences(); - final Preferences.ServerAddress serverAddress = preferences.getServerAddress(); long lastScan = Util.getLong(data, "lastscan"); - if (lastScan > 0 && lastScan != serverAddress.lastScan) { + if (mConnectionState.setLastScan(lastScan)) { + final Preferences preferences = Squeezer.getPreferences(); + final Preferences.ServerAddress serverAddress = preferences.getServerAddress(); preferences.saveLastScan(serverAddress, lastScan); - ImageFetcher.getInstance(Squeezer.getInstance()).clearCache(); } boolean rescan = Util.getInt(data, "rescan") != 0; - getConnectionState().setRescan( + mConnectionState.setRescan( rescan, Util.getString(data, "progressname"), Util.getString(data, "progressdone"), @@ -402,8 +401,8 @@ private void parseServerStatus(ClientSessionChannel channel, Message message) { mBackgroundHandler.sendEmptyMessageDelayed(MSG_REFRESH_SERVER_STATUS, 2000); } - getConnectionState().setMediaDirs(Util.getStringArray(data, ConnectionState.MEDIA_DIRS)); - getConnectionState().setServerVersion((String) data.get("version")); + mConnectionState.setMediaDirs(Util.getStringArray(data, ConnectionState.MEDIA_DIRS)); + mConnectionState.setServerVersion((String) data.get("version")); Object[] item_data = (Object[]) data.get("players_loop"); final HashMap players = new HashMap<>(); if (item_data != null) { @@ -651,7 +650,6 @@ public void onResponse(Player player, Request request, Message message) { parseMessage("playlist_tracks", "playlist_loop", message); break; } - parseMessage("titles_loop", message); } } diff --git a/Squeezer/src/main/java/uk/org/ngo/squeezer/service/ConnectionState.java b/Squeezer/src/main/java/uk/org/ngo/squeezer/service/ConnectionState.java index f31493d11..d48fe88ca 100644 --- a/Squeezer/src/main/java/uk/org/ngo/squeezer/service/ConnectionState.java +++ b/Squeezer/src/main/java/uk/org/ngo/squeezer/service/ConnectionState.java @@ -43,8 +43,10 @@ import uk.org.ngo.squeezer.service.event.ConnectionChanged; import uk.org.ngo.squeezer.service.event.DisplayEvent; import uk.org.ngo.squeezer.service.event.HandshakeComplete; +import uk.org.ngo.squeezer.service.event.LastscanChanged; import uk.org.ngo.squeezer.service.event.PlayersChanged; import uk.org.ngo.squeezer.service.event.RefreshEvent; +import uk.org.ngo.squeezer.util.ImageFetcher; public class ConnectionState { @@ -298,14 +300,55 @@ boolean canRehandshake() { } private volatile boolean rescan; + private volatile boolean rescanned; + private volatile long lastScan = 0; + private volatile long savedScan = 0; + + public void initLastScan(long lastScan) { + savedScan = lastScan; + this.lastScan = 0; + rescan = rescanned = false; + } + + /** + * Last scan time will change when browsing music folder + * So we only flush cache and rebuild shortcut after a rescan or if + * last scan time changed since last connection + * + * @param lastScan New last scanning time + * @return true if the new last scanning time is different from stored value. + */ + public boolean setLastScan(long lastScan) { + if (lastScan != 0 && lastScan != savedScan) { + Log.i(TAG, "setLastScan(" + lastScan + ") was " + savedScan); + if (rescanned || (this.lastScan == 0)) { + Log.i(TAG, "Flush/rebuild client side caches: " + lastScan); + rescanned = false; + ImageFetcher.getInstance(Squeezer.getInstance()).clearCache(); + mEventBus.post(new LastscanChanged(lastScan)); + } + this.lastScan = savedScan = lastScan; + return true; + } + this.lastScan = lastScan; + return false; + } + /** + * When a scanning status is received we notify subscribers of the progress. + * When the scanning is completed ask subscribers to refresh the contents from the server. + * Because last scanning time will change unnecessarily we store whether there have been a rescan + * in {@link #rescanned} + */ public void setRescan(boolean rescan, String progressName, String progressDone, String progressTotal) { if (rescan || rescan != this.rescan) { + Log.i(TAG, "setRescan(" + rescan + ")"); this.rescan = rescan; mEventBus.post(rescan ? new DisplayEvent(new DisplayMessage(formatScanningProgress(progressName, progressDone, progressTotal))) : new RefreshEvent() ); + if (rescan) rescanned = true; } } diff --git a/Squeezer/src/main/java/uk/org/ngo/squeezer/service/HomeMenuHandling.java b/Squeezer/src/main/java/uk/org/ngo/squeezer/service/HomeMenuHandling.java index b9bb8b53c..dc720bb85 100644 --- a/Squeezer/src/main/java/uk/org/ngo/squeezer/service/HomeMenuHandling.java +++ b/Squeezer/src/main/java/uk/org/ngo/squeezer/service/HomeMenuHandling.java @@ -11,17 +11,19 @@ import java.util.function.Function; import org.greenrobot.eventbus.EventBus; + import uk.org.ngo.squeezer.model.JiveItem; import uk.org.ngo.squeezer.model.MenuStatusMessage; import uk.org.ngo.squeezer.service.event.HomeMenuEvent; public class HomeMenuHandling { + private static final String CUSTOM_SHORTCUT_NODE = JiveItem.HOME.getId(); /** * Home menu tree as received from slimserver */ private final List homeMenu = new CopyOnWriteArrayList<>(); - public final CopyOnWriteArrayList customShortcuts = new CopyOnWriteArrayList<>(); + private final List customShortcuts = new CopyOnWriteArrayList<>(); public HomeMenuHandling(@NonNull EventBus eventBus) { mEventBus = eventBus; @@ -61,7 +63,7 @@ public void handleMenuStatusEvent(MenuStatusMessage event) { homeMenu.add(serverItem); } } - mEventBus.postSticky(new HomeMenuEvent(homeMenu)); + triggerHomeMenuEvent(); } public void triggerHomeMenuEvent() { @@ -82,7 +84,7 @@ List toggleArchiveItem(JiveItem toggledItem) { toggledItem.setNode(JiveItem.ARCHIVE.getId()); if (!homeMenu.contains(JiveItem.ARCHIVE)) { homeMenu.add(JiveItem.ARCHIVE); - mEventBus.postSticky(new HomeMenuEvent(homeMenu)); + triggerHomeMenuEvent(); } return getArchivedItems(); } @@ -135,81 +137,90 @@ private void addArchivedItems(List archivedItems) { } } - public void setHomeMenu(List archivedItems, Map> customShortcuts) { + public void setHomeMenu(List archivedItems) { homeMenu.remove(JiveItem.ARCHIVE); homeMenu.stream().forEach(item -> item.setNode(item.getOriginalNode())); - addArchivedItems(archivedItems); - loadShortcutItems(customShortcuts); - mEventBus.postSticky(new HomeMenuEvent(homeMenu)); + customizeHomeMenu(archivedItems); } - public void setHomeMenu(List items, List archivedItems, Map> customShortcuts) { - jiveMainNodes(items); + public void setHomeMenu(List items, List archivedItems) { homeMenu.clear(); homeMenu.addAll(items); + jiveMainNodes(); + customizeHomeMenu(archivedItems); + } + + private void customizeHomeMenu(List archivedItems) { addArchivedItems(archivedItems); - loadShortcutItems(customShortcuts); - mEventBus.postSticky(new HomeMenuEvent(homeMenu)); + homeMenu.addAll(customShortcuts); + triggerHomeMenuEvent(); } - private void jiveMainNodes(List homeMenu) { + private void jiveMainNodes() { addNode(JiveItem.EXTRAS, homeMenu); addNode(JiveItem.SETTINGS, homeMenu); addNode(JiveItem.ADVANCED_SETTINGS, homeMenu); } - void addNode(JiveItem jiveItem, List homeMenu) { + private void addNode(JiveItem jiveItem, List homeMenu) { if (!homeMenu.contains(jiveItem)) { jiveItem.setNode(jiveItem.getOriginalNode()); homeMenu.add(jiveItem); } } - private final static String CUSTOM_SHORTCUT_NODE = "home"; - - /** - * Load complete list of stored items from preferences. - * Use action the values on the initialized customNodes. - */ - public void loadShortcutItems(Map> map) { + public void setCustomShortcuts(List> shortcuts) { customShortcuts.clear(); - for (Map.Entry> pair : map.entrySet()) { - Map record = pair.getValue(); - JiveItem shortcut = new JiveItem(record); - shortcut.setName(pair.getKey()); - customShortcuts.add(setShortcut(shortcut)); - } - homeMenu.addAll(customShortcuts); + shortcuts.stream().forEach(shortcut -> customShortcuts.add(shortcut(shortcut))); } - public boolean triggerCustomShortcut(JiveItem itemToShortcut) { - return addShortcut(itemToShortcut); + public List getCustomShortcuts() { + return customShortcuts; } - private boolean addShortcut(JiveItem item) { - if (!shortcutAlreadyAdded(item)) { - JiveItem template = new JiveItem(item.getRecord()); -// TODO template.setIcon - customShortcuts.add(setShortcut(template)); - homeMenu.add(template); - } else { - return false; - } + public boolean isCustomShortcut(JiveItem item) { + return customShortcuts.contains(item); + } + + public boolean addShortcut(JiveItem item, JiveItem parent, int shortcutWeight) { + if (shortcutAlreadyAdded(item)) return false; + addShortcut(item.getRecord(), parent, shortcutWeight); return true; } + public List updateShortcut(JiveItem item, Map record) { + removeCustomShortcut(item); + addShortcut(record, item, item.getWeight()); + triggerHomeMenuEvent(); + return customShortcuts; + } + + private void addShortcut(Map record, JiveItem parent, int shortcutWeight) { + record.put("weight", shortcutWeight); + JiveItem template = shortcut(record); + if (!template.hasIcon() && parent != null && parent.hasIcon()) { + if (parent.hasIconUri()) { + record.put("icon", parent.getIcon().toString()); + } else { + record.put("id", parent.getId()); + } + template = shortcut(record); + } + customShortcuts.add(template); + homeMenu.add(template); + } + private boolean shortcutAlreadyAdded(JiveItem itemToShortcut) { for (JiveItem item : customShortcuts) { - if (item.getName().equals(itemToShortcut.getName())) { - return true; - } + if (item.getName().equals(itemToShortcut.getName())) return true; } return false; } - private JiveItem setShortcut(JiveItem item) { + private JiveItem shortcut(Map shortcut) { + JiveItem item = new JiveItem(shortcut); item.setNode(CUSTOM_SHORTCUT_NODE); - item.setId("customShortcut_" + customShortcuts.size()); + if (item.getId() == null) item.setId("customShortcut_" + customShortcuts.size()); return item; } @@ -219,9 +230,6 @@ public void removeCustomShortcut(JiveItem item) { } public void removeAllShortcuts() { - for (JiveItem item : customShortcuts) { - customShortcuts.remove(item); - homeMenu.remove(item); - } + for (JiveItem item : customShortcuts) removeCustomShortcut(item); } } diff --git a/Squeezer/src/main/java/uk/org/ngo/squeezer/service/ISqueezeService.java b/Squeezer/src/main/java/uk/org/ngo/squeezer/service/ISqueezeService.java index 036622e91..07f450390 100644 --- a/Squeezer/src/main/java/uk/org/ngo/squeezer/service/ISqueezeService.java +++ b/Squeezer/src/main/java/uk/org/ngo/squeezer/service/ISqueezeService.java @@ -235,11 +235,7 @@ public interface ISqueezeService { */ void triggerHomeMenuEvent(); - /** - * Get mDelegate - * @return - */ - SlimDelegate getDelegate(); + HomeMenuHandling getHomeMenuHandling(); /** * Remove the item after it was long pressed on the home menu screen diff --git a/Squeezer/src/main/java/uk/org/ngo/squeezer/service/SlimDelegate.java b/Squeezer/src/main/java/uk/org/ngo/squeezer/service/SlimDelegate.java index 8d3312be8..9fd9ab0c7 100644 --- a/Squeezer/src/main/java/uk/org/ngo/squeezer/service/SlimDelegate.java +++ b/Squeezer/src/main/java/uk/org/ngo/squeezer/service/SlimDelegate.java @@ -16,8 +16,6 @@ package uk.org.ngo.squeezer.service; -import android.util.Log; - import androidx.annotation.NonNull; import java.util.List; @@ -31,7 +29,7 @@ import uk.org.ngo.squeezer.model.PlayerState; import uk.org.ngo.squeezer.model.SlimCommand; -public class SlimDelegate { +class SlimDelegate { @NonNull private final SlimClient mClient; @@ -146,14 +144,6 @@ public Set getVolumeSyncGroup(boolean groupVolume) { return mClient.getConnectionState().getVolume(groupVolume); } - void setHomeMenu(List archivedItems, Map> customShortcuts) { - mClient.getConnectionState().getHomeMenuHandling().setHomeMenu(archivedItems, customShortcuts); - } - - void setHomeMenu(List items, List archivedItems, Map> customShortcuts) { - mClient.getConnectionState().getHomeMenuHandling().setHomeMenu(items, archivedItems, customShortcuts); - } - public String getUsername() { return mClient.getUsername(); } @@ -170,26 +160,10 @@ String[] getMediaDirs() { return mClient.getConnectionState().getMediaDirs(); } - List toggleArchiveItem(JiveItem item) { - return mClient.getConnectionState().getHomeMenuHandling().toggleArchiveItem(item); - } - - public boolean isInArchive(JiveItem item) { - return mClient.getConnectionState().getHomeMenuHandling().isInArchive(item); - } - - public void triggerHomeMenuEvent() { - mClient.getConnectionState().getHomeMenuHandling().triggerHomeMenuEvent(); - } - public HomeMenuHandling getHomeMenuHandling() { return mClient.getConnectionState().getHomeMenuHandling(); } - public void removeCustomShortcut(JiveItem item) { - mClient.getConnectionState().getHomeMenuHandling().removeCustomShortcut(item); - } - public int addItems(String folderID, Set set) { Player player = mClient.getConnectionState().getActivePlayer(); return mClient.getConnectionState().getRandomPlay(player).addItems(folderID, set); diff --git a/Squeezer/src/main/java/uk/org/ngo/squeezer/service/SqueezeService.java b/Squeezer/src/main/java/uk/org/ngo/squeezer/service/SqueezeService.java index f8586ed46..69107bbec 100644 --- a/Squeezer/src/main/java/uk/org/ngo/squeezer/service/SqueezeService.java +++ b/Squeezer/src/main/java/uk/org/ngo/squeezer/service/SqueezeService.java @@ -74,6 +74,7 @@ import uk.org.ngo.squeezer.Util; import uk.org.ngo.squeezer.download.DownloadDatabase; import uk.org.ngo.squeezer.model.Action; +import uk.org.ngo.squeezer.model.CustomJiveItemHandling; import uk.org.ngo.squeezer.model.JiveItem; import uk.org.ngo.squeezer.model.MusicFolderItem; import uk.org.ngo.squeezer.model.SlimCommand; @@ -87,6 +88,7 @@ import uk.org.ngo.squeezer.service.event.ActivePlayerChanged; import uk.org.ngo.squeezer.service.event.ConnectionChanged; import uk.org.ngo.squeezer.service.event.HandshakeComplete; +import uk.org.ngo.squeezer.service.event.LastscanChanged; import uk.org.ngo.squeezer.service.event.MusicChanged; import uk.org.ngo.squeezer.service.event.PlayStatusChanged; import uk.org.ngo.squeezer.service.event.PlayerStateChanged; @@ -136,6 +138,7 @@ public class SqueezeService extends Service { private volatile boolean foreGround; private final SlimDelegate mDelegate = new SlimDelegate(mEventBus); + private final HomeMenuHandling homeMenuHandling = mDelegate.getHomeMenuHandling(); private final RandomPlayDelegate randomPlayDelegate = new RandomPlayDelegate(mDelegate); @@ -211,7 +214,10 @@ public void onCreate() { NotificationManagerCompat nm = NotificationManagerCompat.from(this); nm.cancel(PLAYBACKSERVICE_STATUS); - Squeezer.getPreferences(this::cachePreferences); + Squeezer.getPreferences(preferences -> { + cachePreferences(preferences); + homeMenuHandling.setCustomShortcuts(preferences.getCustomShortcuts()); + }); WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE); this.wifiLock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "Squeezer_WifiLock"); @@ -348,7 +354,6 @@ private void moveCurrentPlaylist(Player from, Player to) { } class HomeMenuReceiver implements IServiceItemListCallback { - private final List homeMenu = new ArrayList<>(); @Override @@ -361,8 +366,7 @@ public void onItemsReceived(int count, int start, Map parameters if ((useArchive) && (mDelegate.getActivePlayer() != null)) { archivedMenuItems = preferences.getArchivedMenuItems(mDelegate.getActivePlayer()); } - Map> customShortcuts = preferences.restoreCustomShortcuts(); - mDelegate.setHomeMenu(homeMenu, archivedMenuItems, customShortcuts); + homeMenuHandling.setHomeMenu(homeMenu, archivedMenuItems); } } @@ -372,6 +376,14 @@ public Object getClient() { } } + public void requestItems(SlimCommand command, IServiceItemListCallback callback) { + mDelegate.requestAllItems(callback).params(command.params).cmd(command.cmd()).exec(); + } + + public void updateShortCut(JiveItem item, Map record) { + List shortcuts = homeMenuHandling.updateShortcut(item, record); + Squeezer.getPreferences().saveShortcuts(shortcuts); + } private void requestPlayerData() { Player activePlayer = mDelegate.getActivePlayer(); @@ -820,6 +832,11 @@ public void onEvent(PlayersChanged event) { } } + @Subscribe(priority = 1) + public void onEvent(LastscanChanged event) { + CustomJiveItemHandling.recoverShortcuts(this, homeMenuHandling.getCustomShortcuts()); + } + /** * @return The player that should be chosen as the (new) active player. This is either the * last active player (if known), the first player the server knows about if there are @@ -859,7 +876,7 @@ public void onItemsReceived(int count, int start, Map parameters @Override public Object getClient() { - return this; + return SqueezeService.this; } }; @@ -882,7 +899,7 @@ public void onItemsReceived(int count, int start, Map parameters @Override public Object getClient() { - return this; + return SqueezeService.this; } }; @@ -1396,12 +1413,11 @@ public void preferenceChanged(Preferences preferences, String key) { if ((useArchive) && (getActivePlayer() != null)) { archivedMenuItems = preferences.getArchivedMenuItems(getActivePlayer()); } - Map> customShortcuts = preferences.restoreCustomShortcuts(); - mDelegate.setHomeMenu(archivedMenuItems, customShortcuts); + homeMenuHandling.setHomeMenu(archivedMenuItems); } else if (Preferences.KEY_CUSTOMIZE_SHORTCUT_MODE.equals(key)) { if (preferences.getCustomizeShortcutsMode() == Preferences.CustomizeShortcutsMode.DISABLED) { - mDelegate.getHomeMenuHandling().removeAllShortcuts(); - preferences.saveShortcuts(preferences.convertShortcuts(mDelegate.getHomeMenuHandling().customShortcuts)); // TODO check for simplification + homeMenuHandling.removeAllShortcuts(); + preferences.saveShortcuts(homeMenuHandling.getCustomShortcuts()); } } else if (Preferences.KEY_ACTION_ON_INCOMING_CALL.equals(key)) { if (preferences.getActionOnIncomingCall() != Preferences.IncomingCallAction.NONE) { @@ -1562,28 +1578,28 @@ public Boolean randomPlayFolder(JiveItem item) { } public boolean toggleArchiveItem(JiveItem item) { - List menu = mDelegate.toggleArchiveItem(item); + List menu = homeMenuHandling.toggleArchiveItem(item); Squeezer.getPreferences().setArchivedMenuItems(menu, getActivePlayer()); return menu.isEmpty(); } @Override public boolean isInArchive(JiveItem item) { - return mDelegate.isInArchive(item); + return homeMenuHandling.isInArchive(item); } public void triggerHomeMenuEvent() { - mDelegate.triggerHomeMenuEvent(); + homeMenuHandling.triggerHomeMenuEvent(); } @Override - public SlimDelegate getDelegate() { - return mDelegate; + public HomeMenuHandling getHomeMenuHandling() { + return homeMenuHandling; } @Override public void removeCustomShortcut(JiveItem item) { - mDelegate.removeCustomShortcut(item); + homeMenuHandling.removeCustomShortcut(item); } } diff --git a/Squeezer/src/main/java/uk/org/ngo/squeezer/service/event/LastscanChanged.java b/Squeezer/src/main/java/uk/org/ngo/squeezer/service/event/LastscanChanged.java new file mode 100644 index 000000000..a622d4db7 --- /dev/null +++ b/Squeezer/src/main/java/uk/org/ngo/squeezer/service/event/LastscanChanged.java @@ -0,0 +1,13 @@ +package uk.org.ngo.squeezer.service.event; + +/** + * Event sent when the timestamp of the last scan finished for the currently connected server + * has changed + */ +public class LastscanChanged { + public long lastScan; + + public LastscanChanged(long lastScan) { + this.lastScan = lastScan; + } +} diff --git a/Squeezer/src/test/java/uk/org/ngo/squeezer/util/ReflectionTest.java b/Squeezer/src/test/java/uk/org/ngo/squeezer/util/ReflectionTest.java index 84fab628a..7db6ec0db 100644 --- a/Squeezer/src/test/java/uk/org/ngo/squeezer/util/ReflectionTest.java +++ b/Squeezer/src/test/java/uk/org/ngo/squeezer/util/ReflectionTest.java @@ -17,249 +17,98 @@ public class ReflectionTest extends TestCase { - class Item { + class Item {} + class Item1 extends Item {} + class Item2 extends Item {} - } - - class Item1 extends Item { - - } - - class Item2 extends Item { - - } - - class GroupItem extends Item { - - } - - class GroupItem1 extends GroupItem { - - } - - class GroupItem2 extends GroupItem { - - } - - - class A { - - } - - class B1 extends A { - - } - - class B2 extends A { - - } - - class C extends A { - - } - - class D1 extends C { + class GroupItem extends Item {} + class GroupItem1 extends GroupItem {} + class GroupItem2 extends GroupItem {} - } - - class D2 extends C { - - } - - - class AA { - - } - - class BB extends AA { - - } - - - interface I { - - } - class AI implements I { + class A {} + class B1 extends A {} + class B2 extends A {} + class C extends A {} + class D1 extends C {} + class D2 extends C {} - } - - class BI1 extends AI { - - } - - class BI2 extends AI { - - } - - class BIG1 extends AI { + class AA {} + class BB extends AA {} - } - - class BIG2 extends AI { - - } - - class CIG extends AI { - - } + interface I {} + class AI implements I {} + class BI1 extends AI {} + class BI2 extends AI {} + class BIG1 extends AI {} + class BIG2 extends AI {} + class CIG extends AI {} + class CIG1 extends CIG {} + class CIG2 extends CIG {} - class CIG1 extends CIG { - - } - - class CIG2 extends CIG { - - } - - - class StrangeExtend extends A { - - } + class StrangeExtend extends A {} // Resolves to Item2 for StrangeExtend and Item1 for A - class Item1ToItem2 extends StrangeExtend { - - } - - - interface _I { - - } + class Item1ToItem2 extends StrangeExtend {} - interface II extends _I { + interface _I {} + interface II extends _I {} + class AII implements II {} + class BII implements II {} + class CII extends BII {} + class BAII extends AII {} + class BIII implements II, I {} + class AIII implements II, I {} - } - - class AII implements II { - - } - - class BII implements II { - - } - - class CII extends BII { + interface __I {} + class BAIII extends AIII implements __I {} - } - - class BAII extends AII { - - } - - class BIII implements II, I { - - } + class SwapOrder1 extends AA {} + class SwapOrder2 implements II {} + class SwapOrder3 extends AII {} - class AIII implements II, I { - - } - - interface __I { - - } - - class BAIII extends AIII implements __I { - - } - - class SwapOrder1 extends AA { - - } - - class SwapOrder2 implements II { - - } - - class SwapOrder3 extends AII { - - } - - class Activity { - - } - - class Activity1 extends Activity { - - } - - class SwitchTypeI implements I { - - } - - class SwitchTypeAI extends AI { + class Activity { } + class SwitchTypeI implements I {} + class SwitchTypeAI extends AI {} + interface FunctionInterface { + int get(); } public void testGenericTypeResolver() { - assertTypesEquals(new Type[]{Item1.class}, - Reflection.genericTypeResolver(new B1().getClass(), A.class)); - assertTypesEquals(new Type[]{Item1.class}, - Reflection.genericTypeResolver(B1.class, A.class)); - assertTypesEquals(new Type[]{Item2.class}, - Reflection.genericTypeResolver(new B2().getClass(), A.class)); - assertTypesEquals(new Type[]{Item2.class}, - Reflection.genericTypeResolver(B2.class, A.class)); - - assertTypesEquals(new Type[]{GroupItem1.class}, - Reflection.genericTypeResolver(D1.class, A.class)); - assertTypesEquals(new Type[]{GroupItem2.class}, - Reflection.genericTypeResolver(D2.class, A.class)); - assertTypesEquals(new Type[]{GroupItem1.class}, - Reflection.genericTypeResolver(new D1().getClass(), C.class)); - assertTypesEquals(new Type[]{GroupItem2.class}, - Reflection.genericTypeResolver(new D2().getClass(), C.class)); - - assertTypesEquals(new Type[]{Item1.class, Item2.class}, - Reflection.genericTypeResolver(BII.class, II.class)); - assertTypesEquals(new Type[]{Item1.class}, - Reflection.genericTypeResolver(BII.class, _I.class)); - assertTypesEquals(new Type[]{Item1.class, Item2.class}, - Reflection.genericTypeResolver(CII.class, II.class)); - assertTypesEquals(new Type[]{Item1.class}, - Reflection.genericTypeResolver(CII.class, _I.class)); - assertTypesEquals(new Type[]{Item1.class, Item2.class}, - Reflection.genericTypeResolver(BAII.class, II.class)); - assertTypesEquals(new Type[]{Item1.class, Item2.class}, - Reflection.genericTypeResolver(BAII.class, AII.class)); - assertTypesEquals(new Type[]{Item1.class}, - Reflection.genericTypeResolver(BAII.class, _I.class)); - assertTypesEquals(new Type[]{Item1.class, Item2.class}, - Reflection.genericTypeResolver(BIII.class, II.class)); - assertTypesEquals(new Type[]{Item2.class}, - Reflection.genericTypeResolver(BIII.class, I.class)); - assertTypesEquals(new Type[]{Item1.class}, - Reflection.genericTypeResolver(BIII.class, _I.class)); - assertTypesEquals(new Type[]{Item1.class, GroupItem1.class}, - Reflection.genericTypeResolver(BAIII.class, II.class)); - assertTypesEquals(new Type[]{Item1.class, GroupItem1.class}, - Reflection.genericTypeResolver(BAIII.class, AIII.class)); - assertTypesEquals(new Type[]{GroupItem1.class}, - Reflection.genericTypeResolver(BAIII.class, I.class)); - assertTypesEquals(new Type[]{Item1.class}, - Reflection.genericTypeResolver(BAIII.class, _I.class)); - assertTypesEquals(new Type[]{GroupItem2.class}, - Reflection.genericTypeResolver(BAIII.class, __I.class)); - - assertTypesEquals(new Type[]{Item2.class, Item1.class}, - Reflection.genericTypeResolver(new SwapOrder1() { - }.getClass(), AA.class)); - assertTypesEquals(new Type[]{Item2.class, Item1.class}, - Reflection.genericTypeResolver(new SwapOrder2() { - }.getClass(), II.class)); - assertTypesEquals(new Type[]{Item2.class, Item1.class}, - Reflection.genericTypeResolver(new SwapOrder3() { - }.getClass(), II.class)); - assertTypesEquals(new Type[]{Item2.class, Item1.class}, - Reflection.genericTypeResolver(new SwapOrder3() { - }.getClass(), AII.class)); - - assertTypesEquals(new Type[]{Item1.class}, - Reflection.genericTypeResolver(new SwitchTypeI() { - }.getClass(), I.class)); - assertTypesEquals(new Type[]{Item1.class}, - Reflection.genericTypeResolver(new SwitchTypeAI() { - }.getClass(), I.class)); + assertTypesEquals(new Type[]{Item1.class}, Reflection.genericTypeResolver(new B1().getClass(), A.class)); + assertTypesEquals(new Type[]{Item1.class}, Reflection.genericTypeResolver(B1.class, A.class)); + assertTypesEquals(new Type[]{Item2.class}, Reflection.genericTypeResolver(new B2().getClass(), A.class)); + assertTypesEquals(new Type[]{Item2.class}, Reflection.genericTypeResolver(B2.class, A.class)); + + assertTypesEquals(new Type[]{GroupItem1.class}, Reflection.genericTypeResolver(D1.class, A.class)); + assertTypesEquals(new Type[]{GroupItem2.class}, Reflection.genericTypeResolver(D2.class, A.class)); + assertTypesEquals(new Type[]{GroupItem1.class}, Reflection.genericTypeResolver(new D1().getClass(), C.class)); + assertTypesEquals(new Type[]{GroupItem2.class}, Reflection.genericTypeResolver(new D2().getClass(), C.class)); + + assertTypesEquals(new Type[]{Item1.class, Item2.class}, Reflection.genericTypeResolver(BII.class, II.class)); + assertTypesEquals(new Type[]{Item1.class}, Reflection.genericTypeResolver(BII.class, _I.class)); + assertTypesEquals(new Type[]{Item1.class, Item2.class}, Reflection.genericTypeResolver(CII.class, II.class)); + assertTypesEquals(new Type[]{Item1.class}, Reflection.genericTypeResolver(CII.class, _I.class)); + assertTypesEquals(new Type[]{Item1.class, Item2.class}, Reflection.genericTypeResolver(BAII.class, II.class)); + assertTypesEquals(new Type[]{Item1.class, Item2.class}, Reflection.genericTypeResolver(BAII.class, AII.class)); + assertTypesEquals(new Type[]{Item1.class}, Reflection.genericTypeResolver(BAII.class, _I.class)); + assertTypesEquals(new Type[]{Item1.class, Item2.class}, Reflection.genericTypeResolver(BIII.class, II.class)); + assertTypesEquals(new Type[]{Item2.class}, Reflection.genericTypeResolver(BIII.class, I.class)); + assertTypesEquals(new Type[]{Item1.class}, Reflection.genericTypeResolver(BIII.class, _I.class)); + assertTypesEquals(new Type[]{Item1.class, GroupItem1.class}, Reflection.genericTypeResolver(BAIII.class, II.class)); + assertTypesEquals(new Type[]{Item1.class, GroupItem1.class}, Reflection.genericTypeResolver(BAIII.class, AIII.class)); + assertTypesEquals(new Type[]{GroupItem1.class}, Reflection.genericTypeResolver(BAIII.class, I.class)); + assertTypesEquals(new Type[]{Item1.class}, Reflection.genericTypeResolver(BAIII.class, _I.class)); + assertTypesEquals(new Type[]{GroupItem2.class}, Reflection.genericTypeResolver(BAIII.class, __I.class)); + + assertTypesEquals(new Type[]{Item2.class, Item1.class}, Reflection.genericTypeResolver(new SwapOrder1() {}.getClass(), AA.class)); + assertTypesEquals(new Type[]{Item2.class, Item1.class}, Reflection.genericTypeResolver(new SwapOrder2() {}.getClass(), II.class)); + assertTypesEquals(new Type[]{Item2.class, Item1.class}, Reflection.genericTypeResolver(new SwapOrder3() {}.getClass(), II.class)); + assertTypesEquals(new Type[]{Item2.class, Item1.class}, Reflection.genericTypeResolver(new SwapOrder3() {}.getClass(), AII.class)); + + assertTypesEquals(new Type[]{Item1.class}, Reflection.genericTypeResolver(new SwitchTypeI() {}.getClass(), I.class)); + assertTypesEquals(new Type[]{Item1.class}, Reflection.genericTypeResolver(new SwitchTypeAI() {}.getClass(), I.class)); } private void assertTypesEquals(Type[] expected, Type[] actual) { @@ -281,18 +130,14 @@ public void testGetGenericClass() { assertEquals(Item2.class, Reflection.getGenericClass(new B2().getClass(), A.class, 0)); assertEquals(Item2.class, Reflection.getGenericClass(B2.class, A.class, 0)); - assertEquals(GroupItem1.class, - Reflection.getGenericClass(new D1().getClass(), A.class, 0)); + assertEquals(GroupItem1.class, Reflection.getGenericClass(new D1().getClass(), A.class, 0)); assertEquals(GroupItem1.class, Reflection.getGenericClass(D1.class, A.class, 0)); - assertEquals(GroupItem2.class, - Reflection.getGenericClass(new D2().getClass(), A.class, 0)); + assertEquals(GroupItem2.class, Reflection.getGenericClass(new D2().getClass(), A.class, 0)); assertEquals(GroupItem2.class, Reflection.getGenericClass(D2.class, A.class, 0)); - assertEquals(GroupItem1.class, - Reflection.getGenericClass(new D1().getClass(), C.class, 0)); + assertEquals(GroupItem1.class, Reflection.getGenericClass(new D1().getClass(), C.class, 0)); assertEquals(GroupItem1.class, Reflection.getGenericClass(D1.class, C.class, 0)); - assertEquals(GroupItem2.class, - Reflection.getGenericClass(new D2().getClass(), C.class, 0)); + assertEquals(GroupItem2.class, Reflection.getGenericClass(new D2().getClass(), C.class, 0)); assertEquals(GroupItem2.class, Reflection.getGenericClass(D2.class, C.class, 0)); assertEquals(Item1.class, Reflection.getGenericClass(BB.class, AA.class, 0)); @@ -314,16 +159,22 @@ public void testGetGenericClass() { assertEquals(GroupItem2.class, Reflection.getGenericClass(CIG2.class, CIG.class, 0)); assertEquals(Item1.class, Reflection.getGenericClass(Item1ToItem2.class, A.class, 0)); - assertEquals(Item2.class, - Reflection.getGenericClass(Item1ToItem2.class, StrangeExtend.class, 0)); + assertEquals(Item2.class, Reflection.getGenericClass(Item1ToItem2.class, StrangeExtend.class, 0)); assertEquals(Item1.class, Reflection.getGenericClass(BB.class, AA.class, 0)); assertEquals(Item2.class, Reflection.getGenericClass(BB.class, AA.class, 1)); - assertEquals(Item1.class, Reflection.getGenericClass(new SwitchTypeI() { - }.getClass(), I.class, 0)); - assertEquals(Item1.class, Reflection.getGenericClass(new SwitchTypeAI() { - }.getClass(), I.class, 0)); + assertEquals(Item1.class, Reflection.getGenericClass(new SwitchTypeI() {}.getClass(), I.class, 0)); + assertEquals(Item1.class, Reflection.getGenericClass(new SwitchTypeAI() {}.getClass(), I.class, 0)); + + // Lambda doesn't resolve, but anonymous implementation does + assertEquals(null, Reflection.getGenericClass(((FunctionInterface) () -> 0).getClass(), FunctionInterface.class, 0)); + assertEquals(Item.class, Reflection.getGenericClass(new FunctionInterface(){ + @Override + public int get() { + return 0; + } + }.getClass(), FunctionInterface.class, 0)); } public void testResolveGenericCollections() { @@ -343,48 +194,34 @@ public void testResolveGenericCollections() { private static final long serialVersionUID = 1L; }; - assertEquals(Item1.class, - Reflection.getGenericClass(itemList.getClass(), Collection.class, 0)); + assertEquals(Item1.class, Reflection.getGenericClass(itemList.getClass(), Collection.class, 0)); assertEquals(Item1.class, Reflection.getGenericClass(itemList.getClass(), List.class, 0)); - assertEquals(Item1.class, - Reflection.getGenericClass(itemList.getClass(), AbstractCollection.class, 0)); - assertEquals(Item1.class, - Reflection.getGenericClass(itemList.getClass(), AbstractList.class, 0)); + assertEquals(Item1.class, Reflection.getGenericClass(itemList.getClass(), AbstractCollection.class, 0)); + assertEquals(Item1.class, Reflection.getGenericClass(itemList.getClass(), AbstractList.class, 0)); - assertEquals(Item1.class, - Reflection.getGenericClass(itemSet.getClass(), Collection.class, 0)); + assertEquals(Item1.class, Reflection.getGenericClass(itemSet.getClass(), Collection.class, 0)); assertEquals(Item1.class, Reflection.getGenericClass(itemSet.getClass(), Set.class, 0)); - assertEquals(Item1.class, - Reflection.getGenericClass(itemSet.getClass(), AbstractCollection.class, 0)); - assertEquals(Item1.class, - Reflection.getGenericClass(itemSet.getClass(), AbstractSet.class, 0)); + assertEquals(Item1.class, Reflection.getGenericClass(itemSet.getClass(), AbstractCollection.class, 0)); + assertEquals(Item1.class, Reflection.getGenericClass(itemSet.getClass(), AbstractSet.class, 0)); assertEquals(String.class, Reflection.getGenericClass(itemMap.getClass(), Map.class, 0)); assertEquals(Item1.class, Reflection.getGenericClass(itemMap.getClass(), Map.class, 1)); - assertEquals(String.class, - Reflection.getGenericClass(itemMap.getClass(), AbstractMap.class, 0)); - assertEquals(Item1.class, - Reflection.getGenericClass(itemMap.getClass(), AbstractMap.class, 1)); + assertEquals(String.class, Reflection.getGenericClass(itemMap.getClass(), AbstractMap.class, 0)); + assertEquals(Item1.class, Reflection.getGenericClass(itemMap.getClass(), AbstractMap.class, 1)); - assertEquals(Integer.class, - Reflection.getGenericClass(intList.getClass(), Collection.class, 0)); + assertEquals(Integer.class, Reflection.getGenericClass(intList.getClass(), Collection.class, 0)); assertEquals(Integer.class, Reflection.getGenericClass(intList.getClass(), List.class, 0)); - assertEquals(Integer.class, - Reflection.getGenericClass(intList.getClass(), AbstractCollection.class, 0)); - assertEquals(Integer.class, - Reflection.getGenericClass(intList.getClass(), AbstractList.class, 0)); + assertEquals(Integer.class, Reflection.getGenericClass(intList.getClass(), AbstractCollection.class, 0)); + assertEquals(Integer.class, Reflection.getGenericClass(intList.getClass(), AbstractList.class, 0)); - assertEquals(Integer.class, - Reflection.getGenericClass(intSet.getClass(), Collection.class, 0)); + assertEquals(Integer.class, Reflection.getGenericClass(intSet.getClass(), Collection.class, 0)); assertEquals(Integer.class, Reflection.getGenericClass(intSet.getClass(), Set.class, 0)); - assertEquals(Integer.class, - Reflection.getGenericClass(intSet.getClass(), AbstractCollection.class, 0)); - assertEquals(Integer.class, - Reflection.getGenericClass(intSet.getClass(), AbstractSet.class, 0)); - - // Unsolvable - assertEquals(null, - Reflection.getGenericClass(new ArrayList().getClass(), List.class, 0)); + assertEquals(Integer.class, Reflection.getGenericClass(intSet.getClass(), AbstractCollection.class, 0)); + assertEquals(Integer.class, Reflection.getGenericClass(intSet.getClass(), AbstractSet.class, 0)); + + // Generic variables doesn't resolve, but subclassing the generic interface does + assertEquals(null, Reflection.getGenericClass(new ArrayList().getClass(), List.class, 0)); + assertEquals(Integer.class, Reflection.getGenericClass(new ArrayList(){}.getClass(), List.class, 0)); } }