Exemplo n.º 1
0
        protected override async Task PrepareAsync()
        {
            await base.PrepareAsync().ConfigureAwait(false);

            AbstractItemsScreenData.PlayableItemCreatorDelegate picd = mi => new GameItem(mi)
            {
                Command = new MethodDelegateCommand(() => ServiceRegistration.Get <IGameLauncher>().LaunchGame(mi))
            };
            _genericPlayableItemCreatorDelegate = picd;

            _defaultScreen    = new GamesShowItemsScreenData(picd);
            _availableScreens = new List <AbstractScreenData>
            {
                _defaultScreen,
                new GameFilterByPlatformScreenData(),
                new GameFilterByYearScreenData(),
                new GameFilterByGenreScreenData(),
                new GameFilterByDeveloperScreenData()
            };

            _defaultSorting    = new SortByTitle();
            _availableSortings = new List <Sorting>
            {
                _defaultSorting,
                new SortByYear(),
                new SortByRatingDesc()
            };

            var optionalMias = new Guid[]
            {
                GoodMergeAspect.ASPECT_ID
            }.Union(MediaNavigationModel.GetMediaSkinOptionalMIATypes(MediaNavigationMode));

            _customRootViewSpecification = new MediaLibraryQueryViewSpecification(_viewName, null, _necessaryMias, optionalMias, true);
        }
Exemplo n.º 2
0
        public void InitMediaNavigation(out string mediaNavigationMode, out NavigationData navigationData)
        {
            IEnumerable <Guid> skinDependentOptionalMIATypeIDs = MediaNavigationModel.GetMediaSkinOptionalMIATypes(MediaNavigationMode);

            AbstractItemsScreenData.PlayableItemCreatorDelegate picd = mi => new VideoItem(mi)
            {
                Command = new MethodDelegateCommand(() => PlayItemsModel.CheckQueryPlayAction(mi))
            };
            ViewSpecification rootViewSpecification = new MediaLibraryQueryViewSpecification(Consts.RES_VIDEOS_VIEW_NAME,
                                                                                             null, Consts.NECESSARY_VIDEO_MIAS, skinDependentOptionalMIATypeIDs, true)
            {
                MaxNumItems = Consts.MAX_NUM_ITEMS_VISIBLE
            };
            AbstractScreenData filterByGenre = new VideosFilterByGenreScreenData();
            ICollection <AbstractScreenData> availableScreens = new List <AbstractScreenData>
            {
                new VideosShowItemsScreenData(picd),
                new VideosFilterByLanguageScreenData(),
                new VideosFilterByActorScreenData(),
                filterByGenre,
                // C# doesn't like it to have an assignment inside a collection initializer
                new VideosFilterByYearScreenData(),
                new VideosFilterBySystemScreenData(),
                new VideosSimpleSearchScreenData(picd),
            };

            Sorting.Sorting sortByTitle = new SortByTitle();
            ICollection <Sorting.Sorting> availableSortings = new List <Sorting.Sorting>
            {
                sortByTitle,
                new SortByYear(),
                new VideoSortByFirstGenre(),
                new VideoSortByDuration(),
                new VideoSortByDirector(),
                new VideoSortByFirstActor(),
                new VideoSortBySize(),
                new VideoSortByAspectRatio(),
                new SortBySystem(),
            };

            navigationData = new NavigationData(null, Consts.RES_VIDEOS_VIEW_NAME, MediaNavigationRootState,
                                                MediaNavigationRootState, rootViewSpecification, filterByGenre, availableScreens, sortByTitle)
            {
                AvailableSortings = availableSortings
            };

            mediaNavigationMode = MediaNavigationMode;
        }
        protected void InitializeSearch(ViewSpecification baseViewSpecification)
        {
            _baseViewSpecification = baseViewSpecification as MediaLibraryQueryViewSpecification;
            if (_baseViewSpecification == null)
            {
                return;
            }
            if (_simpleSearchTextProperty == null)
            {
                _simpleSearchTextProperty = new WProperty(typeof(string), string.Empty);
                _simpleSearchTextProperty.Attach(OnSimpleSearchTextChanged);
            }
            SimpleSearchText = string.Empty;

            // Initialize data manually which would have been initialized by AbstractItemsScreenData.UpdateMediaItems else
            IsItemsValid = true;
            IsItemsEmpty = false;
            TooManyItems = false;
            NumItemsStr  = "-";
            NumItems     = 0;
            lock (_syncObj)
                _view = null;
            _items = new ItemsList();
        }
Exemplo n.º 4
0
        /// <summary>
        /// Updates the GUI data for a filter values selection screen which reflects the available filter values for
        /// the current base view specification of our <see cref="AbstractScreenData._navigationData"/>.
        /// </summary>
        protected void ReloadFilterValuesList(bool createNewList)
        {
            MediaLibraryQueryViewSpecification currentVS = _navigationData.BaseViewSpecification as MediaLibraryQueryViewSpecification;

            if (currentVS == null)
            { // Should never happen
                ServiceRegistration.Get <ILogger>().Error("FilterScreenData: Wrong type of media library view '{0}'", _navigationData.BaseViewSpecification);
                return;
            }
            // Control other threads reentering this method
            lock (_syncObj)
            {
                if (_buildingList)
                { // Another thread is already building the items list - mark it as dirty and let the other thread
                  // rebuild it.
                    _listDirty = true;
                    return;
                }
                // Mark the list as being built
                _buildingList = true;
                _listDirty    = false;
            }
            try
            {
                ItemsList items;
                if (createNewList)
                {
                    items = new ItemsList();
                }
                else
                {
                    items = _items;
                    items.Clear();
                }

                try
                {
                    Display_ListBeingBuilt();
                    bool grouping = true;
                    ICollection <FilterValue> fv = _clusterFilter == null?
                                                   _filterCriterion.GroupValues(currentVS.NecessaryMIATypeIds, _clusterFilter, currentVS.Filter) : null;

                    if (fv == null || fv.Count <= Consts.MAX_NUM_ITEMS_VISIBLE)
                    {
                        fv       = _filterCriterion.GetAvailableValues(currentVS.NecessaryMIATypeIds, _clusterFilter, currentVS.Filter);
                        grouping = false;
                    }
                    if (fv.Count > Consts.MAX_NUM_ITEMS_VISIBLE)
                    {
                        Display_TooManyItems(fv.Count);
                    }
                    else
                    {
                        lock (_syncObj)
                            if (_listDirty)
                            {
                                goto RebuildView;
                            }
                        int totalNumItems = 0;

                        // Build collection of available (filter/display) screens which will remain in the next view - that is all currently
                        // available screens without the screen which equals this current screen. But we cannot simply remove "this"
                        // from the collection, because "this" could be a derived screen (in case our base screen showed groups).
                        // So we need an equality criterion when the screen to be removed is equal to this screen in terms of its
                        // filter criterion. But with the given data, we actually cannot derive that equality.
                        // So we simply use the MenuItemLabel, which should be the same in this and the base screen of the same filter.
                        foreach (FilterValue filterValue in fv)
                        {
                            string  filterTitle                      = filterValue.Title;
                            IFilter selectAttributeFilter            = filterValue.SelectAttributeFilter;
                            MediaLibraryQueryViewSpecification subVS = currentVS.CreateSubViewSpecification(filterTitle, filterValue.Filter);
                            T filterValueItem = new T
                            {
                                SimpleTitle = filterTitle,
                                NumItems    = filterValue.NumItems,
                                Command     = grouping ?
                                              new MethodDelegateCommand(() => NavigateToGroup(subVS, selectAttributeFilter)) :
                                              new MethodDelegateCommand(() => NavigateToSubView(subVS))
                            };
                            items.Add(filterValueItem);
                            if (filterValue.NumItems.HasValue)
                            {
                                totalNumItems += filterValue.NumItems.Value;
                            }
                        }
                        Display_Normal(items.Count, totalNumItems == 0 ? new int?() : totalNumItems);
                    }
                }
                catch (Exception e)
                {
                    ServiceRegistration.Get <ILogger>().Warn("AbstractFiltersScreenData: Error creating filter values list", e);
                    Display_ItemsInvalid();
                }
RebuildView:
                if (_listDirty)
                {
                    lock (_syncObj)
                        _buildingList = false;
                    ReloadFilterValuesList(createNewList);
                }
                else
                {
                    _items = items;
                    _items.FireChange();
                }
            }
            finally
            {
                lock (_syncObj)
                    _buildingList = false;
            }
        }
        /// <summary>
        /// Updates the GUI data for a filter values selection screen which reflects the available filter values for
        /// the current base view specification of our <see cref="AbstractScreenData._navigationData"/>.
        /// </summary>
        protected async Task ReloadFilterValuesList(bool createNewList)
        {
            MediaLibraryQueryViewSpecification currentVS = _navigationData.BaseViewSpecification as MediaLibraryQueryViewSpecification;

            if (currentVS == null)
            { // Should never happen
                ServiceRegistration.Get <ILogger>().Error("FilterScreenData: Wrong type of media library view '{0}'", _navigationData.BaseViewSpecification);
                return;
            }
            // Control other threads reentering this method
            lock (_syncObj)
            {
                if (_buildingList)
                { // Another thread is already building the items list - mark it as dirty and let the other thread
                  // rebuild it.
                    _listDirty = true;
                    return;
                }
                // Mark the list as being built
                _buildingList = true;
                _listDirty    = false;
            }
            try
            {
                ItemsList items;
                if (createNewList)
                {
                    items = new ItemsList();
                }
                else
                {
                    items = _items;
                    items.Clear();
                }

                try
                {
                    Display_ListBeingBuilt();
                    IFilter            filter        = currentVS.FilterTree.BuildFilter(_filterPath);
                    ICollection <Guid> necessaryMIAs = _necessaryFilteredMIATypeIds ?? currentVS.NecessaryMIATypeIds;

                    // If the number of values to create exceeds MAX_NUM_ITEMS_VISIBLE we need to try and group the items.
                    // We request all values first, rather than groups, on the assumption that most of the time the limit
                    // shouldn't be reached given that we are filtering the values.
                    bool grouping = false;
                    ICollection <FilterValue> fv = await _filterCriterion.GetAvailableValuesAsync(necessaryMIAs, _clusterFilter, filter).ConfigureAwait(false);

                    if (fv.Count > Consts.MAX_NUM_ITEMS_VISIBLE && _clusterFilter == null)
                    {
                        ICollection <FilterValue> groupValues = await _filterCriterion.GroupValuesAsync(necessaryMIAs, _clusterFilter, filter).ConfigureAwait(false);

                        if (groupValues != null && groupValues.Count > 0)
                        {
                            fv       = groupValues;
                            grouping = true;
                        }
                    }
                    if (fv.Count > Consts.MAX_NUM_ITEMS_VISIBLE)
                    {
                        Display_TooManyItems(fv.Count);
                    }
                    else
                    {
                        bool dirty;
                        lock (_syncObj)
                            dirty = _listDirty;
                        if (dirty)
                        {
                            UpdateOrRebuildView(items, createNewList);
                            return;
                        }

                        _sortable = true;
                        int totalNumItems           = 0;
                        List <FilterItem> itemsList = new List <FilterItem>();
                        // Build collection of available (filter/display) screens which will remain in the next view - that is all currently
                        // available screens without the screen which equals this current screen. But we cannot simply remove "this"
                        // from the collection, because "this" could be a derived screen (in case our base screen showed groups).
                        // So we need an equality criterion when the screen to be removed is equal to this screen in terms of its
                        // filter criterion. But with the given data, we actually cannot derive that equality.
                        // So we simply use the MenuItemLabel, which should be the same in this and the base screen of the same filter.
                        foreach (FilterValue f in fv)
                        {
                            //Used for enclosure
                            FilterValue filterValue = f;
                            _sortable &= filterValue.Item != null;
                            string  filterTitle                      = filterValue.Title;
                            IFilter selectAttributeFilter            = filterValue.SelectAttributeFilter;
                            MediaLibraryQueryViewSpecification subVS = currentVS.CreateSubViewSpecification(filterTitle,
                                                                                                            FilterTreePath.Combine(_filterPath, filterValue.RelativeFilterPath), filterValue.Filter, filterValue.LinkedId);
                            T filterValueItem = new T
                            {
                                // Support non-playable MediaItems (i.e. Series, Seasons)
                                MediaItem   = filterValue.Item,
                                SimpleTitle = filterTitle,
                                NumItems    = filterValue.NumItems,
                                Id          = filterValue.Id,
                                Command     = new MethodDelegateCommand(() =>
                                {
                                    if (grouping)
                                    {
                                        NavigateToGroup(subVS, selectAttributeFilter);
                                    }
                                    else
                                    {
                                        NavigateToSubView(subVS);
                                    }
                                }),
                                View = subVS.BuildView()
                            };
                            itemsList.Add(filterValueItem);
                            if (filterValue.NumItems.HasValue)
                            {
                                totalNumItems += filterValue.NumItems.Value;
                            }
                        }
                        if (_sortable)
                        {
                            Sorting.Sorting sorting = CurrentSorting;
                            if (sorting != null)
                            {
                                itemsList.Sort((i1, i2) => sorting.Compare(i1.MediaItem, i2.MediaItem));
                            }
                        }
                        // Derived classes can implement special initial selection handling here,
                        // e.g. the first unwatched episode could be selected from a list of episodes
                        SetSelectedItem(itemsList);
                        CollectionUtils.AddAll(items, itemsList);
                        Display_Normal(items.Count, totalNumItems == 0 ? new int?() : totalNumItems);
                    }
                }
                catch (Exception e)
                {
                    ServiceRegistration.Get <ILogger>().Warn("AbstractFiltersScreenData: Error creating filter values list", e);
                    Display_ItemsInvalid();
                }
                UpdateOrRebuildView(items, createNewList);
            }
            finally
            {
                lock (_syncObj)
                    _buildingList = false;
            }
        }
        /// <summary>
        /// Updates the GUI data for a filter values selection screen which reflects the available filter values for
        /// the current base view specification of our <see cref="AbstractScreenData._navigationData"/>.
        /// </summary>
        protected void ReloadFilterValuesList(bool createNewList)
        {
            MediaLibraryQueryViewSpecification currentVS = _navigationData.BaseViewSpecification as MediaLibraryQueryViewSpecification;

            if (currentVS == null)
            { // Should never happen
                ServiceRegistration.Get <ILogger>().Error("FilterScreenData: Wrong type of media library view '{0}'", _navigationData.BaseViewSpecification);
                return;
            }
            // Control other threads reentering this method
            lock (_syncObj)
            {
                if (_buildingList)
                { // Another thread is already building the items list - mark it as dirty and let the other thread
                  // rebuild it.
                    _listDirty = true;
                    return;
                }
                // Mark the list as being built
                _buildingList = true;
                _listDirty    = false;
            }
            try
            {
                ItemsList items;
                if (createNewList)
                {
                    items = new ItemsList();
                }
                else
                {
                    items = _items;
                    items.Clear();
                }

                try
                {
                    Display_ListBeingBuilt();
                    bool grouping = true;
                    //If currentVS is the base view it's possible that it has a filter that is incompatible with _filterCriterion.
                    //This is the case if a plugin has added a base filter to exclude certain items, e.g. TV excludes recordings
                    //and the new filter filters by a different media type, e.g. series'. Ignore the base filter in this case.
                    IFilter currentFilter = (_navigationData.Parent != null && CanFilter(_navigationData.Parent.CurrentScreenData)) ||
                                            currentVS.CanCombineFilters(_filteredMias) ? currentVS.Filter : null;
                    ICollection <FilterValue> fv = _clusterFilter == null?
                                                   _filterCriterion.GroupValues(currentVS.NecessaryMIATypeIds, _clusterFilter, currentFilter) : null;

                    if (fv == null || fv.Count <= Consts.MAX_NUM_ITEMS_VISIBLE)
                    {
                        fv       = _filterCriterion.GetAvailableValues(currentVS.NecessaryMIATypeIds, _clusterFilter, currentFilter);
                        grouping = false;
                    }
                    if (fv.Count > Consts.MAX_NUM_ITEMS_VISIBLE)
                    {
                        Display_TooManyItems(fv.Count);
                    }
                    else
                    {
                        bool dirty;
                        lock (_syncObj)
                            dirty = _listDirty;
                        if (dirty)
                        {
                            UpdateOrRebuildView(items, createNewList);
                            return;
                        }

                        _sortable = true;
                        int totalNumItems           = 0;
                        List <FilterItem> itemsList = new List <FilterItem>();
                        // Build collection of available (filter/display) screens which will remain in the next view - that is all currently
                        // available screens without the screen which equals this current screen. But we cannot simply remove "this"
                        // from the collection, because "this" could be a derived screen (in case our base screen showed groups).
                        // So we need an equality criterion when the screen to be removed is equal to this screen in terms of its
                        // filter criterion. But with the given data, we actually cannot derive that equality.
                        // So we simply use the MenuItemLabel, which should be the same in this and the base screen of the same filter.
                        foreach (FilterValue filterValue in fv)
                        {
                            _sortable &= filterValue.Item != null;
                            string  filterTitle                      = filterValue.Title;
                            IFilter selectAttributeFilter            = filterValue.SelectAttributeFilter;
                            MediaLibraryQueryViewSpecification subVS = currentVS.CreateSubViewSpecification(filterTitle, filterValue.Filter, _filteredMias);
                            T filterValueItem = new T
                            {
                                // Support non-playable MediaItems (i.e. Series, Seasons)
                                MediaItem   = filterValue.Item,
                                SimpleTitle = filterTitle,
                                NumItems    = filterValue.NumItems,
                                Id          = filterValue.Id,
                                Command     = grouping ?
                                              new MethodDelegateCommand(() => NavigateToGroup(subVS, selectAttributeFilter)) :
                                              new MethodDelegateCommand(() => NavigateToSubView(subVS))
                            };
                            itemsList.Add(filterValueItem);
                            if (filterValue.NumItems.HasValue)
                            {
                                totalNumItems += filterValue.NumItems.Value;
                            }
                        }
                        if (_sortable)
                        {
                            Sorting.Sorting sorting = CurrentSorting;
                            if (sorting != null)
                            {
                                itemsList.Sort((i1, i2) => sorting.Compare(i1.MediaItem, i2.MediaItem));
                            }
                        }
                        CollectionUtils.AddAll(items, itemsList);
                        Display_Normal(items.Count, totalNumItems == 0 ? new int?() : totalNumItems);
                    }
                }
                catch (Exception e)
                {
                    ServiceRegistration.Get <ILogger>().Warn("AbstractFiltersScreenData: Error creating filter values list", e);
                    Display_ItemsInvalid();
                }
                UpdateOrRebuildView(items, createNewList);
            }
            finally
            {
                lock (_syncObj)
                    _buildingList = false;
            }
        }
        /// <summary>
        /// Returns context variables to be set for the given workflow state id.
        /// </summary>
        /// <param name="workflowStateId">Workflow state which determines the root media navigation state.</param>
        /// <returns>Mapping of context variable keys to values.</returns>
        protected static IDictionary <string, object> PrepareRootState(Guid workflowStateId)
        {
            IDictionary <string, object> result = new Dictionary <string, object>();
            // The initial state ID determines the media model "part" to initialize: Browse local media, browse media library, audio, videos or images.
            // The media model part determines the media navigation mode and the view contents to be set.
            NavigationData      navigationData;
            MediaNavigationMode mode;

            if (workflowStateId == Consts.WF_STATE_ID_AUDIO_NAVIGATION_ROOT)
            {
                mode = MediaNavigationMode.Audio;
                IEnumerable <Guid> skinDependentOptionalMIATypeIDs       = GetMediaSkinOptionalMIATypes(mode);
                AbstractItemsScreenData.PlayableItemCreatorDelegate picd = mi => new AudioItem(mi)
                {
                    Command = new MethodDelegateCommand(() => PlayItemsModel.CheckQueryPlayAction(mi))
                };
                ViewSpecification rootViewSpecification = new MediaLibraryQueryViewSpecification(Consts.RES_AUDIO_VIEW_NAME,
                                                                                                 null, Consts.NECESSARY_AUDIO_MIAS, skinDependentOptionalMIATypeIDs, true)
                {
                    MaxNumItems = Consts.MAX_NUM_ITEMS_VISIBLE
                };
                AbstractScreenData filterByAlbum = new AudioFilterByAlbumScreenData();
                ICollection <AbstractScreenData> availableScreens = new List <AbstractScreenData>
                {
                    new AudioShowItemsScreenData(picd),
                    new AudioFilterByArtistScreenData(),
                    filterByAlbum, // C# doesn't like it to have an assignment inside a collection initializer
                    new AudioFilterByGenreScreenData(),
                    new AudioFilterByDecadeScreenData(),
                    new AudioFilterBySystemScreenData(),
                    new AudioSimpleSearchScreenData(picd),
                };
                Sorting.Sorting sortByAlbumTrack = new AudioSortByAlbumTrack();
                ICollection <Sorting.Sorting> availableSortings = new List <Sorting.Sorting>
                {
                    sortByAlbumTrack,
                    new SortByTitle(),
                    new AudioSortByFirstGenre(),
                    new AudioSortByFirstArtist(),
                    new AudioSortByAlbum(),
                    new AudioSortByTrack(),
                    new SortByYear(),
                    new SortBySystem(),
                };
                navigationData = new NavigationData(null, Consts.RES_AUDIO_VIEW_NAME, workflowStateId,
                                                    workflowStateId, rootViewSpecification, filterByAlbum, availableScreens, sortByAlbumTrack)
                {
                    AvailableSortings = availableSortings
                };
            }
            else if (workflowStateId == Consts.WF_STATE_ID_VIDEOS_NAVIGATION_ROOT)
            {
                mode = MediaNavigationMode.Videos;
                IEnumerable <Guid> skinDependentOptionalMIATypeIDs       = GetMediaSkinOptionalMIATypes(mode);
                AbstractItemsScreenData.PlayableItemCreatorDelegate picd = mi => new VideoItem(mi)
                {
                    Command = new MethodDelegateCommand(() => PlayItemsModel.CheckQueryPlayAction(mi))
                };
                ViewSpecification rootViewSpecification = new MediaLibraryQueryViewSpecification(Consts.RES_VIDEOS_VIEW_NAME,
                                                                                                 null, Consts.NECESSARY_VIDEO_MIAS, skinDependentOptionalMIATypeIDs, true)
                {
                    MaxNumItems = Consts.MAX_NUM_ITEMS_VISIBLE
                };
                AbstractScreenData filterByGenre = new VideosFilterByGenreScreenData();
                ICollection <AbstractScreenData> availableScreens = new List <AbstractScreenData>
                {
                    new VideosShowItemsScreenData(picd),
                    new VideosFilterByLanguageScreenData(),
                    new VideosFilterByActorScreenData(),
                    filterByGenre, // C# doesn't like it to have an assignment inside a collection initializer
                    new VideosFilterByYearScreenData(),
                    new VideosFilterBySystemScreenData(),
                    new VideosSimpleSearchScreenData(picd),
                };
                Sorting.Sorting sortByTitle = new SortByTitle();
                ICollection <Sorting.Sorting> availableSortings = new List <Sorting.Sorting>
                {
                    sortByTitle,
                    new SortByYear(),
                    new VideoSortByFirstGenre(),
                    new VideoSortByDuration(),
                    new VideoSortByDirector(),
                    new VideoSortByFirstActor(),
                    new VideoSortBySize(),
                    new VideoSortByAspectRatio(),
                    new SortBySystem(),
                };
                navigationData = new NavigationData(null, Consts.RES_VIDEOS_VIEW_NAME, workflowStateId,
                                                    workflowStateId, rootViewSpecification, filterByGenre, availableScreens, sortByTitle)
                {
                    AvailableSortings = availableSortings
                };
            }
            else if (workflowStateId == Consts.WF_STATE_ID_SERIES_NAVIGATION_ROOT)
            {
                mode = MediaNavigationMode.Videos;
                IEnumerable <Guid> skinDependentOptionalMIATypeIDs       = GetMediaSkinOptionalMIATypes(mode);
                AbstractItemsScreenData.PlayableItemCreatorDelegate picd = mi => new SeriesItem(mi)
                {
                    Command = new MethodDelegateCommand(() => PlayItemsModel.CheckQueryPlayAction(mi))
                };
                ViewSpecification rootViewSpecification = new MediaLibraryQueryViewSpecification(Consts.RES_SERIES_VIEW_NAME,
                                                                                                 null, Consts.NECESSARY_SERIES_MIAS, skinDependentOptionalMIATypeIDs, true)
                {
                    MaxNumItems = Consts.MAX_NUM_ITEMS_VISIBLE
                };
                AbstractScreenData filterBySeries = new SeriesFilterByNameScreenData();
                ICollection <AbstractScreenData> availableScreens = new List <AbstractScreenData>
                {
                    new SeriesShowItemsScreenData(picd),
                    filterBySeries, // C# doesn't like it to have an assignment inside a collection initializer
                    new VideosFilterByLanguageScreenData(),
                    new SeriesFilterBySeasonScreenData(),
                    new VideosFilterByGenreScreenData(),
                    new VideosSimpleSearchScreenData(picd),
                };
                Sorting.Sorting sortByEpisode = new SeriesSortByEpisode();
                ICollection <Sorting.Sorting> availableSortings = new List <Sorting.Sorting>
                {
                    sortByEpisode,
                    new SortByTitle(),
                    new SortByDate(),
                    new SortBySystem(),
                };
                navigationData = new NavigationData(null, Consts.RES_SERIES_VIEW_NAME, workflowStateId,
                                                    workflowStateId, rootViewSpecification, filterBySeries, availableScreens, sortByEpisode)
                {
                    AvailableSortings = availableSortings
                };
            }
            else if (workflowStateId == Consts.WF_STATE_ID_MOVIES_NAVIGATION_ROOT)
            {
                mode = MediaNavigationMode.Movies;
                IEnumerable <Guid> skinDependentOptionalMIATypeIDs       = GetMediaSkinOptionalMIATypes(mode);
                AbstractItemsScreenData.PlayableItemCreatorDelegate picd = mi => new MovieItem(mi)
                {
                    Command = new MethodDelegateCommand(() => PlayItemsModel.CheckQueryPlayAction(mi))
                };
                ViewSpecification rootViewSpecification = new MediaLibraryQueryViewSpecification(Consts.RES_MOVIES_VIEW_NAME,
                                                                                                 null, Consts.NECESSARY_MOVIES_MIAS, skinDependentOptionalMIATypeIDs, true)
                {
                    MaxNumItems = Consts.MAX_NUM_ITEMS_VISIBLE
                };
                AbstractScreenData filterByGenre = new VideosFilterByGenreScreenData();
                ICollection <AbstractScreenData> availableScreens = new List <AbstractScreenData>
                {
                    new MoviesShowItemsScreenData(picd),
                    new VideosFilterByActorScreenData(),
                    filterByGenre, // C# doesn't like it to have an assignment inside a collection initializer
                    new VideosFilterByYearScreenData(),
                    new VideosFilterBySystemScreenData(),
                    new VideosSimpleSearchScreenData(picd),
                };
                Sorting.Sorting sortByTitle = new SortByTitle();
                ICollection <Sorting.Sorting> availableSortings = new List <Sorting.Sorting>
                {
                    sortByTitle,
                    new SortByYear(),
                    new VideoSortByFirstGenre(),
                    new VideoSortByDuration(),
                    new VideoSortByDirector(),
                    new VideoSortByFirstActor(),
                    new VideoSortBySize(),
                    new VideoSortByAspectRatio(),
                    new SortBySystem(),
                };

                navigationData = new NavigationData(null, Consts.RES_MOVIES_VIEW_NAME, workflowStateId,
                                                    workflowStateId, rootViewSpecification, filterByGenre, availableScreens, sortByTitle)
                {
                    AvailableSortings = availableSortings
                };
            }
            else if (workflowStateId == Consts.WF_STATE_ID_IMAGES_NAVIGATION_ROOT)
            {
                mode = MediaNavigationMode.Images;
                IEnumerable <Guid> skinDependentOptionalMIATypeIDs       = GetMediaSkinOptionalMIATypes(mode);
                AbstractItemsScreenData.PlayableItemCreatorDelegate picd = mi => new ImageItem(mi)
                {
                    Command = new MethodDelegateCommand(() => PlayItemsModel.CheckQueryPlayAction(mi))
                };
                ViewSpecification rootViewSpecification = new MediaLibraryQueryViewSpecification(Consts.RES_IMAGES_VIEW_NAME,
                                                                                                 null, Consts.NECESSARY_IMAGE_MIAS, skinDependentOptionalMIATypeIDs, true)
                {
                    MaxNumItems = Consts.MAX_NUM_ITEMS_VISIBLE
                };
                AbstractScreenData filterByYear = new ImagesFilterByYearScreenData();
                ICollection <AbstractScreenData> availableScreens = new List <AbstractScreenData>
                {
                    new ImagesShowItemsScreenData(picd),
                    filterByYear, // C# doesn't like it to have an assignment inside a collection initializer
                    new ImagesFilterBySizeScreenData(),
                    new ImagesFilterBySystemScreenData(),
                    new ImagesSimpleSearchScreenData(picd),
                };
                Sorting.Sorting sortByYear = new SortByYear();
                ICollection <Sorting.Sorting> availableSortings = new List <Sorting.Sorting>
                {
                    new SortByYear(),
                    new SortByTitle(),
                    new ImageSortBySize(),
                    new SortBySystem(),
                };
                navigationData = new NavigationData(null, Consts.RES_IMAGES_VIEW_NAME, workflowStateId,
                                                    workflowStateId, rootViewSpecification, filterByYear, availableScreens, sortByYear)
                {
                    AvailableSortings = availableSortings
                };
            }
            else
            {
                // If we were called with a supported root state, we should be either in state WF_STATE_ID_LOCAL_MEDIA_NAVIGATION_ROOT
                // or WF_STATE_ID_MEDIA_BROWSE_NAVIGATION_ROOT here
                if (workflowStateId != Consts.WF_STATE_ID_LOCAL_MEDIA_NAVIGATION_ROOT &&
                    workflowStateId != Consts.WF_STATE_ID_BROWSE_MEDIA_NAVIGATION_ROOT)
                {
                    // Error case: We cannot handle the given state
                    ServiceRegistration.Get <ILogger>().Warn("MediaNavigationModel: Unknown root workflow state with ID '{0}', initializing local media navigation", workflowStateId);
                    // We simply use the local media mode as fallback for this case, so we go on
                    workflowStateId = Consts.WF_STATE_ID_LOCAL_MEDIA_NAVIGATION_ROOT;
                }
                mode = workflowStateId == Consts.WF_STATE_ID_LOCAL_MEDIA_NAVIGATION_ROOT ? MediaNavigationMode.BrowseLocalMedia :
                       MediaNavigationMode.BrowseMediaLibrary;
                IEnumerable <Guid> skinDependentOptionalMIATypeIDs       = GetMediaSkinOptionalMIATypes(mode);
                AbstractItemsScreenData.PlayableItemCreatorDelegate picd = mi =>
                {
                    if (mi.Aspects.ContainsKey(AudioAspect.ASPECT_ID))
                    {
                        return new AudioItem(mi)
                               {
                                   Command = new MethodDelegateCommand(() => PlayItemsModel.CheckQueryPlayAction(mi))
                               }
                    }
                    ;
                    if (mi.Aspects.ContainsKey(VideoAspect.ASPECT_ID))
                    {
                        return new VideoItem(mi)
                               {
                                   Command = new MethodDelegateCommand(() => PlayItemsModel.CheckQueryPlayAction(mi))
                               }
                    }
                    ;
                    if (mi.Aspects.ContainsKey(ImageAspect.ASPECT_ID))
                    {
                        return new ImageItem(mi)
                               {
                                   Command = new MethodDelegateCommand(() => PlayItemsModel.CheckQueryPlayAction(mi))
                               }
                    }
                    ;
                    return(null);
                };
                IEnumerable <Guid> necessaryMIATypeIDs = new Guid[]
                {
                    ProviderResourceAspect.ASPECT_ID,
                    MediaAspect.ASPECT_ID,
                };
                IEnumerable <Guid> optionalMIATypeIDs = new Guid[]
                {
                    AudioAspect.ASPECT_ID,
                    VideoAspect.ASPECT_ID,
                    ImageAspect.ASPECT_ID,
                }.Union(skinDependentOptionalMIATypeIDs);
                string viewName = workflowStateId == Consts.WF_STATE_ID_LOCAL_MEDIA_NAVIGATION_ROOT ?
                                  Consts.RES_LOCAL_MEDIA_ROOT_VIEW_NAME : Consts.RES_BROWSE_MEDIA_ROOT_VIEW_NAME;
                ViewSpecification rootViewSpecification = workflowStateId == Consts.WF_STATE_ID_LOCAL_MEDIA_NAVIGATION_ROOT ?
                                                          new AddedRemovableMediaViewSpecificationFacade(new LocalMediaRootProxyViewSpecification(viewName, necessaryMIATypeIDs, optionalMIATypeIDs)) :
                                                          new AddedRemovableMediaViewSpecificationFacade(new BrowseMediaRootProxyViewSpecification(viewName, necessaryMIATypeIDs, optionalMIATypeIDs));
                // Dynamic screens remain null - browse media states don't provide dynamic filters
                AbstractScreenData screenData = workflowStateId == Consts.WF_STATE_ID_LOCAL_MEDIA_NAVIGATION_ROOT ?
                                                (AbstractScreenData) new LocalMediaNavigationScreenData(picd) : new BrowseMediaNavigationScreenData(picd);
                Sorting.Sorting browseDefaultSorting            = new BrowseDefaultSorting();
                ICollection <Sorting.Sorting> availableSortings = new List <Sorting.Sorting>
                {
                    browseDefaultSorting,
                    new SortByTitle(),
                    new SortByDate(),
                    // We could offer sortings here which are specific for one media item type but which will cope with all three item types (and sort items of the three types in a defined order)
                };
                navigationData = new NavigationData(null, viewName, workflowStateId,
                                                    workflowStateId, rootViewSpecification, screenData, null, browseDefaultSorting)
                {
                    AvailableSortings = availableSortings
                };
            }
            result.Add(Consts.KEY_NAVIGATION_MODE, mode);
            result.Add(Consts.KEY_NAVIGATION_DATA, navigationData);
            return(result);
        }