From 05c81e67c15be7d9623128aea21c2de02ac3047c Mon Sep 17 00:00:00 2001 From: Kurt Aaholst Date: Fri, 27 Sep 2024 17:04:07 +0200 Subject: [PATCH] Refresh shortcuts - WIP --- .../java/uk/org/ngo/squeezer/Preferences.java | 24 +- .../itemlist/HomeMenuJiveItemView.java | 54 ++- .../ngo/squeezer/itemlist/JiveItemView.java | 27 +- .../java/uk/org/ngo/squeezer/model/Album.java | 29 ++ .../model/CustomJiveItemHandling.java | 191 ++++++--- .../uk/org/ngo/squeezer/model/JiveItem.java | 19 +- .../org/ngo/squeezer/service/CometClient.java | 9 + .../squeezer/service/HomeMenuHandling.java | 72 ++-- .../ngo/squeezer/service/ISqueezeService.java | 6 +- .../ngo/squeezer/service/SlimDelegate.java | 28 +- .../ngo/squeezer/service/SqueezeService.java | 49 ++- .../org/ngo/squeezer/util/ReflectionTest.java | 383 +++++------------- 12 files changed, 420 insertions(+), 471 deletions(-) create mode 100644 Squeezer/src/main/java/uk/org/ngo/squeezer/model/Album.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..8bfe475ba 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,26 @@ 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); + Map recordFromPref = (Map) JSON.parse(allShortcuts.getString(key)); + allShortcutsFromPref.add(new JiveItem(recordFromPref)); } 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..e92ae6deb 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,17 @@ 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(); + long lastScan = mPreferences.getServerAddress().lastScan; + if (homeMenuHandling.addShortcut(item, getActivity().parent, shortCutWeight, lastScan)) { + 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/Album.java b/Squeezer/src/main/java/uk/org/ngo/squeezer/model/Album.java new file mode 100644 index 000000000..f1dab497c --- /dev/null +++ b/Squeezer/src/main/java/uk/org/ngo/squeezer/model/Album.java @@ -0,0 +1,29 @@ +package uk.org.ngo.squeezer.model; + +import android.net.Uri; + +import androidx.annotation.NonNull; + +import java.util.Map; + +import uk.org.ngo.squeezer.Util; + +public class Album { + + public String id; + public String name; + + + public Album(Map record) { + id = Util.getString(record, "id"); + name = Util.getString(record, "album"); + } + + @Override + public String toString() { + return "Album{" + + "id='" + id + '\'' + + ", name='" + name + '\'' + + '}'; + } +} \ No newline at end of file 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..7edf4f8cf 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,173 @@ package uk.org.ngo.squeezer.model; -import java.util.HashMap; +import android.util.Log; + +import java.util.List; import java.util.Map; -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.Util; +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 recoverShortcut(SqueezeService service, JiveItem shortcut) { + int shortcutWeight = shortcutWeight(shortcut); + if (shortcutWeight == CUSTOM_SHORTCUT_WEIGHT_MY_MUSIC) { + Action itemAction = (shortcut.moreAction != null && shortcut.moreAction.action != null ? shortcut.moreAction : shortcut.goAction); + Action.JsonAction action = itemAction.action; + if (action.params.containsKey("track_id")) { + SlimCommand command = new SlimCommand().cmd("titles").param("tags", JiveItem.SONG_TAGS).param("search", shortcut.getName()); + Log.i(TAG, "shortcut '" + shortcut.getName() + "': recoverAction=" + command); + service.requestItems(command, new SongRecoverReceiver(service, shortcut)); + } else + if (action.params.containsKey("album_id")) { + SlimCommand command = new SlimCommand().cmd("albums").param("search", shortcut.getName()); + Log.i(TAG, "shortcut '" + shortcut.getName() + "': recoverAction=" + command); + service.requestItems(command, new AlbumRecoverReceiver(service, shortcut)); + } + } } - private ISqueezeService service() { - return mActivity.getService(); + public 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; } - private HomeMenuHandling homeMenuHandling() { - return service().getDelegate().getHomeMenuHandling(); + private static boolean allowMyMusic(String s) { + return s.equals("browselibrary"); } - /** - * Send long pressed JiveItem - */ - public boolean triggerCustomShortcut(JiveItem item) { - item.appendWeight(ADJUSTED_CUSTOM_SHORTCUT_WEIGHT); - return homeMenuHandling().triggerCustomShortcut(item); + + private static boolean allowApps(String s) { + return s.equals("items"); } - public boolean isCustomShortcut(JiveItem item) { - return homeMenuHandling().customShortcuts.contains(item); + private static boolean allowRadio(String s) { + return s.equals("play"); } - 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; + private static class SongRecoverReceiver implements IServiceItemListCallback { + private final SqueezeService service; + private final JiveItem shortcut; + + public SongRecoverReceiver(SqueezeService service, JiveItem shortcut) { + this.service = service; + this.shortcut = shortcut; } - for (String s : item.goAction.action.cmd) { - if (allowMyMusic(s) || allowApps(s) || allowRadio(s)) { - return true; + + @Override + public void onItemsReceived(int count, int start, Map parameters, List items, Class dataType) { + for (Song item : items) { + if (shortcut.getName().equals(item.title)) { + Log.i(TAG, "shortcut '" + shortcut.getName() + "': HIT, item=" + item); + Map record = shortcut.getRecord(); + updateActions(record, "track_id", item.id); + updateActions(record, "album_id", item.albumId); + service.updateShortCut(shortcut, record); + break; + } } } - return false; - } - private boolean allowMyMusic(String s) { - if (s.equals("browselibrary")) { - ADJUSTED_CUSTOM_SHORTCUT_WEIGHT = 2000; - return true; + @Override + public Object getClient() { + return service; } - return false; } - private boolean allowApps(String s) { - if (s.equals("items")) { - ADJUSTED_CUSTOM_SHORTCUT_WEIGHT = 2010; - return true; + private static class AlbumRecoverReceiver implements IServiceItemListCallback { + private final SqueezeService service; + private final JiveItem shortcut; + + public AlbumRecoverReceiver(SqueezeService service, JiveItem shortcut) { + this.service = service; + this.shortcut = shortcut; + } + + @Override + public void onItemsReceived(int count, int start, Map parameters, List items, Class dataType) { + for (Album item : items) { + if (shortcut.getName().equals(item.name)) { + Log.i(TAG, "shortcut '" + shortcut.getName() + "': HIT, item=" + item); + Map record = shortcut.getRecord(); + updateActions(record, "album_id", item.id); + service.updateShortCut(shortcut, record); + break; + } + } + } + + @Override + public Object getClient() { + return service; } - return false; } - private boolean allowRadio(String s) { - if (s.equals("play")) { - ADJUSTED_CUSTOM_SHORTCUT_WEIGHT = 2020; - return true; + private static void updateActions(Map record, String property, String id) { + Map baseRecord = Util.getRecord(record, "base"); + Map baseActions = (baseRecord != null ? Util.getRecord(baseRecord, "actions") : null); + Map itemActions = Util.getRecord(record, "actions"); + + updateActions(record, property, id, baseActions); + if (itemActions != null) Log.i(TAG, "itemActions: " + itemActions.keySet()); + updateActions(record, property, id, itemActions); + } + + private static void updateActions(Map record, String property, String id, Map actionsRecord) { + if (actionsRecord != null) { + for (Object object : actionsRecord.values()) { + if (object instanceof Map) { + Map actionRecord = (Map) object; + + Object[] choices = (Object[]) actionRecord.get("choices"); + if (choices != null) { + Log.i(TAG, "choices: " + choices); + for (Object choice : choices) { + actionRecord = (Map) choice; + updateParams(property, id, actionRecord); + } + } else { + updateParams(property, id, actionRecord); + } + + String itemsParams = (String) actionRecord.get("itemsParams"); + if (itemsParams != null) { + Log.i(TAG, "itemsParams: " + itemsParams); + updateParams(property, id, record, itemsParams); + } + } + } } - return false; } - public Map convertShortcuts() { - Map map = new HashMap<>(); - for (JiveItem item : homeMenuHandling().customShortcuts) { - map.put(item.getName(), item.getRecord()); + private static void updateParams(String property, String id, Map actionRecord) { + updateParams(property, id, actionRecord, "params"); + } + + private static void updateParams(String property, String id, Map actionRecord, String paramsProperty) { + Map params = Util.getRecord(actionRecord, paramsProperty); + if (params != null) { + Log.i(TAG, property + " modify params: " + params); + if (params.containsKey(property)) params.put(property, id); + Log.i(TAG, property + " -> : " + params); } - 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..f65066b1f 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 @@ -114,6 +114,7 @@ private static Map record(String id, String node, String text, i private String record; + public int lastScan; @NonNull private String name = ""; public String text2; @NonNull public String textkey = ""; @@ -175,7 +176,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,16 +238,10 @@ 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); + lastScan = getInt(record, "lastscan"); setId(getString(record, record.containsKey("cmd") ? "cmd" : "id")); splitItemText(getStringOrEmpty(record, record.containsKey("name") ? "name" : "text")); textkey = getStringOrEmpty(record, "textkey"); @@ -316,6 +311,8 @@ public JiveItem(Map record) { } public JiveItem(Parcel source) { + record = source.readString(); + lastScan = source.readInt(); setId(source.readString()); name = source.readString(); text2 = source.readString(); @@ -356,6 +353,8 @@ public JiveItem(Parcel source) { @Override public void writeToParcel(Parcel dest, int flags) { + dest.writeString(record); + dest.writeInt(lastScan); dest.writeString(getId()); dest.writeString(name); dest.writeString(text2); @@ -391,10 +390,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..e2c203d4d 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 @@ -55,6 +55,7 @@ import uk.org.ngo.squeezer.Preferences; import uk.org.ngo.squeezer.Squeezer; import uk.org.ngo.squeezer.Util; +import uk.org.ngo.squeezer.model.Album; import uk.org.ngo.squeezer.model.AlertWindow; import uk.org.ngo.squeezer.model.DisplayMessage; import uk.org.ngo.squeezer.itemlist.IServiceItemListCallback; @@ -156,6 +157,7 @@ class CometClient extends BaseClient { new AlarmsListener(), new AlarmPlaylistsListener(), new SongListener(), + new AlbumListener(), new MusicFolderListener(), new JiveItemListener() ); @@ -655,6 +657,13 @@ public void onResponse(Player player, Request request, Message message) { } } + private class AlbumListener extends ItemListener { + @Override + public void onResponse(Player player, Request request, Message message) { + parseMessage("albums_loop", message); + } + } + private class MusicFolderListener extends ItemListener { @Override public void onResponse(Player player, Request request, Message message) { 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..7dc887215 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; @@ -135,20 +137,22 @@ private void addArchivedItems(List archivedItems) { } } - public void setHomeMenu(List archivedItems, Map> customShortcuts) { + public void setHomeMenu(List archivedItems, List customShortcuts) { homeMenu.remove(JiveItem.ARCHIVE); homeMenu.stream().forEach(item -> item.setNode(item.getOriginalNode())); - addArchivedItems(archivedItems); - loadShortcutItems(customShortcuts); - mEventBus.postSticky(new HomeMenuEvent(homeMenu)); + customizeHomeMenu(archivedItems, customShortcuts); } - public void setHomeMenu(List items, List archivedItems, Map> customShortcuts) { + public void setHomeMenu(List items, List archivedItems, List customShortcuts) { jiveMainNodes(items); homeMenu.clear(); homeMenu.addAll(items); + customizeHomeMenu(archivedItems, customShortcuts); + } + + private void customizeHomeMenu(List archivedItems, List customShortcuts) { addArchivedItems(archivedItems); - loadShortcutItems(customShortcuts); + setCustomShortcuts(customShortcuts); mEventBus.postSticky(new HomeMenuEvent(homeMenu)); } @@ -165,31 +169,35 @@ void addNode(JiveItem jiveItem, List homeMenu) { } } - private final static String CUSTOM_SHORTCUT_NODE = "home"; + public List getCustomShortcuts() { + return customShortcuts; + } - /** - * Load complete list of stored items from preferences. - * Use action the values on the initialized customNodes. - */ - public void loadShortcutItems(Map> map) { + /** Set the supplied list of item as shortcuts on the home menu. */ + private 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)); - } + for (JiveItem shortcut : shortcuts) customShortcuts.add(setShortcut(shortcut)); homeMenu.addAll(customShortcuts); } - public boolean triggerCustomShortcut(JiveItem itemToShortcut) { - return addShortcut(itemToShortcut); + public boolean isCustomShortcut(JiveItem item) { + return customShortcuts.contains(item); } - private boolean addShortcut(JiveItem item) { + public boolean addShortcut(JiveItem item, JiveItem parent, int shortcutWeight, long lastScan) { if (!shortcutAlreadyAdded(item)) { - JiveItem template = new JiveItem(item.getRecord()); -// TODO template.setIcon + Map record = item.getRecord(); + record.put("weight", shortcutWeight); + record.put("lastscan", lastScan); + JiveItem template = new JiveItem(record); + if (!template.hasIcon() && parent != null && parent.hasIcon()) { + if (parent.hasIconUri()) { + record.put("icon", parent.getIcon().toString()); + } else { + record.put("id", parent.getId()); + } + template = new JiveItem(record); + } customShortcuts.add(setShortcut(template)); homeMenu.add(template); } else { @@ -209,7 +217,7 @@ private boolean shortcutAlreadyAdded(JiveItem itemToShortcut) { private JiveItem setShortcut(JiveItem item) { item.setNode(CUSTOM_SHORTCUT_NODE); - item.setId("customShortcut_" + customShortcuts.size()); + if (item.getId() == null) item.setId("customShortcut_" + customShortcuts.size()); return item; } @@ -219,9 +227,15 @@ public void removeCustomShortcut(JiveItem item) { } public void removeAllShortcuts() { - for (JiveItem item : customShortcuts) { - customShortcuts.remove(item); - homeMenu.remove(item); - } + for (JiveItem item : customShortcuts) removeCustomShortcut(item); + } + + public List updateCustomShortcut(JiveItem item, Map record, long lastScan) { + removeCustomShortcut(item); + record.put("lastscan", lastScan); + JiveItem template = new JiveItem(record); + customShortcuts.add(template); + homeMenu.add(template); + return customShortcuts; } } 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..15ab014ea 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; @@ -136,6 +137,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); @@ -348,7 +350,6 @@ private void moveCurrentPlaylist(Player from, Player to) { } class HomeMenuReceiver implements IServiceItemListCallback { - private final List homeMenu = new ArrayList<>(); @Override @@ -361,8 +362,17 @@ 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, preferences.getCustomShortcuts()); + long lastScan = preferences.getServerAddress().lastScan; + if (lastScan > 0) { + List shortcuts = homeMenuHandling.getCustomShortcuts(); + for (int i = 0; i < shortcuts.size(); i++) { + JiveItem shortcut = shortcuts.get(i); + if (lastScan != shortcut.lastScan) { + CustomJiveItemHandling.recoverShortcut(SqueezeService.this, shortcut); + } + } + } } } @@ -372,6 +382,15 @@ 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) { + Preferences preferences = Squeezer.getPreferences(); + List shortcuts = homeMenuHandling.updateCustomShortcut(item, record, preferences.getServerAddress().lastScan); + preferences.saveShortcuts(shortcuts); + } private void requestPlayerData() { Player activePlayer = mDelegate.getActivePlayer(); @@ -859,7 +878,7 @@ public void onItemsReceived(int count, int start, Map parameters @Override public Object getClient() { - return this; + return SqueezeService.this; } }; @@ -882,7 +901,7 @@ public void onItemsReceived(int count, int start, Map parameters @Override public Object getClient() { - return this; + return SqueezeService.this; } }; @@ -1396,12 +1415,12 @@ public void preferenceChanged(Preferences preferences, String key) { if ((useArchive) && (getActivePlayer() != null)) { archivedMenuItems = preferences.getArchivedMenuItems(getActivePlayer()); } - Map> customShortcuts = preferences.restoreCustomShortcuts(); - mDelegate.setHomeMenu(archivedMenuItems, customShortcuts); + List customShortcuts = preferences.getCustomShortcuts(); + homeMenuHandling.setHomeMenu(archivedMenuItems, customShortcuts); } 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 +1581,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/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)); } }