Plugin: Shutdown Manager (or call it whatever you want ;) ) - A blog to develop a MP2 plugin (1 Viewer)

chefkoch

Retired Team Member
  • Premium Supporter
  • October 5, 2004
    3,129
    1,634
    Dresden / Munich / Maastricht
    Home Country
    Germany Germany
    Hi,

    I am missing an essential feature in MP2: I want to choose different power actions from a menu to shutdown the pc, supend or restart it. Close and Minimize MP2 etc.

    I already wrote down some ideas and specification in wiki: http://wiki.team-mediaportal.com/index.php?title=2_MEDIAPORTAL_2/Contribute/Development/Tasks_&_Todos/Shutdown_menu

    The branch which will be used for the development can be found on GIT: https://github.com/MediaPortal/MediaPortal-2/tree/FEAT_ShutdownManager

    After some time now, I decided to try to develop it on my own. There are a few different things which needs to be implemented:
    1. The name of the plugin ;) Does not matter at this stage, and will be re-defined during the code review after everything else is done.
    2. The GUI dialog for showing the user the shutdown / power actions which will be executed immediately.
    3. The GUI dialog for showing the user the shutdown / power actions which will be executed delayed. (Sleeptimer feature)
    4. A settings UI to:
      - reorder items of dialog (2.) containing the immediate executed actions
      - hide/show item of dialog (2.) containing the immediate executed actions
    5. The implementation to actually perform the power actions.
    6. Make the dialogs or the power actions available in GUI and via keybindings.
    I will post more ideas, progress updates, problems and their fixes within this thread.

    Please remember that these are the steps I worked myself into the topic. It is not best practice. If there are things which I improve later I try to link to the better practice from the worse one.

    Table of Contents:
    • Conclusions & ToDo tasks in general (independent from this plugin/implementation):
    • Documentation of the default available styles/ controls....
    • Documentation of the available Commands/ properties?
    • Where does DataContext apply??? i.e.
      <ControlTemplate x:Key="Contents_Template" DataContext="{Binding Source={StaticResource Model}}">
      on ControlTemplate does not work, but
      <StackPanel DataContext="{Binding Source={StaticResource Model}}">
      on StackPanel does.
    • How to build and use ItemLists? Which data type are the items of? derived from ListItem? ...
    • What is ListItem AdditionalProperties for?
    • How to find defintions of styles? where to look for, i.e. EditPlaylistListViewStyle
     
    Last edited:

    chefkoch

    Retired Team Member
  • Premium Supporter
  • October 5, 2004
    3,129
    1,634
    Dresden / Munich / Maastricht
    Home Country
    Germany Germany
    At the beginning the project for the plugin needs to be set up in the MP2-Client.sln.

    The basic directory structure has been created using:
    • ShutdownManager\plugin.xml: contains all required plugin definitions.
    • ShutdownManager\Language: contains the english language file. Translations to other languages will be done at Transifex, but won't be added to that development-folder.
    • ShutdownManager\Models: contains the code for the dialogs, in other words there we will define the information which are available for the skins.
    • ShutdownManager\Skin\default: contains the skin files for the dialogs. We only need to provide these default files and the plugin can be used immediately by all other skins, without the need to write or copy any skin files to those.
    Let's start with the plugin.xml.
    <RegisterLocation="/Models">
    <Model Id="25F16911-ED0D-4439-9858-5E69C970C037"
    Name="ShutdownDialogModel"
    ClassName="MediaPortal.Plugins.ShutdownManager.Models.ShutdownDialogModel"/>
    <Model Id="D5513721-92D8-4E45-B988-2C4DBF055B0F"
    Name="ShutdownTimerDialogModel"
    ClassName="MediaPortal.Plugins.ShutdownManager.Models.ShutdownTimerDialogModel"/>
    </Register>
    This registers our models to MP2 so that skins can access their public properties or execute public methods, or the models could be referenced by workflow states. (follows next)

    <Register Location="/Workflow/States">
    <DialogState Id="BBFA7DB7-5055-48D5-A904-0F0C79849369"
    Name="ShutdownManager"
    DialogScreen="dialogShutdown"
    DisplayLabel="[ShutdownManager.ShutdownDialogStateDisplayLabel]"
    WorkflowModel="25F16911-ED0D-4439-9858-5E69C970C037"/>
    <!-- ShutdownManager Menu Model -->
    <DialogState Id="90FB6BC8-6038-4261-A00F-53774ED11B1A"
    Name="dialogShutdownTimer"
    DialogScreen="dialogShutdownTimer"
    DisplayLabel="[ShutdownManager.ShutdownTimerDialogStateDisplayLabel]"
    WorkflowModel="D5513721-92D8-4E45-B988-2C4DBF055B0F"/>
    </Register>
    This creates 2 workflow states as dialogs. Here the dialogs are referencing to the models, which we defined before.

    To make the dialogs accessible from the skin, I added 2 new menu entries to the home menu for the beginning, by adding a skin file ShutdownManager/Skin/default/workflow/shutdown-actions.xml.
    <MenuActions>
    <PushNavigationTransition Id="72480EE1-64D7-4BD5-943C-22C6DE2D4A9D"
    Name="Home->ShutdownMain"
    DisplayCategory="b-Shutdown"
    SortOrder="a"
    SourceStates="7F702D9C-F2DD-42da-9ED8-0BA92F07787F"
    TargetState="BBFA7DB7-5055-48D5-A904-0F0C79849369"
    DisplayTitle="[Shutdown.MenuItem]"/>
    </MenuActions>
    <MenuActions>
    <PushNavigationTransition Id="A80EC846-279E-4A71-9AB2-C73612D4B6EA"
    Name="Home->ShutdownTimerMain"
    DisplayCategory="b-ShutdownTimer"
    SortOrder="a"
    SourceStates="7F702D9C-F2DD-42da-9ED8-0BA92F07787F"
    TargetState="90FB6BC8-6038-4261-A00F-53774ED11B1A"
    DisplayTitle="[ShutdownTimer.MenuItem]"/>
    </MenuActions>

    More information a detailed discription of workflows, models, states etc can be found in wiki.

    The result so far is:
    menuitem.png
     
    Last edited:

    chefkoch

    Retired Team Member
  • Premium Supporter
  • October 5, 2004
    3,129
    1,634
    Dresden / Munich / Maastricht
    Home Country
    Germany Germany
    Now that we defined the DialogStates dialogShutdown and dialogShutdownTimer the appropriate files in ShutdownManager/Skin/default/screens needs to be added.

    The first dialog model is created as ShutdownManager/Models/ShutdownDialogModel.cs. For testing purposes the executed shutdown action is only written to log files instead of i.e. shutting down the PC for real.
    namespace MediaPortal.Plugins.ShutdownManager.Models
    {
    /// <summary>
    /// Workflow model for the weather setup.
    /// </summary>
    public class ShutdownDialogModel : IWorkflowModel​
    {​
    private ItemsList _customTimerActions ;​
    public const string SHUTDOWN_DIALOG_MODEL_ID_STR = "25F16911-ED0D-4439-9858-5E69C970C037";​
    public void Shutdown()​
    {​
    ServiceRegistration.Get<ILogger>().Debug("ShutdownManager: Shutdown Action has been executed");​
    }​
    public void Suspend()​
    {​
    ServiceRegistration.Get<ILogger>().Debug("ShutdownManager: Suspend Action has been executed");​
    }​
    public void Hibernate()​
    {​
    ServiceRegistration.Get<ILogger>().Debug("ShutdownManager: Hibernate Action has been executed");​
    }
    ...

    Almost every public method of each available model in MP2 can be referenced and executed as a command by almost every screen. In ShutdownManager/Skin/default/screens/dialogShutdown.xaml only the ShutdownDialogModel is referenced through it's Id
    <Modelx:Key="Model"Id="25F16911-ED0D-4439-9858-5E69C970C037"/>
    The model can be referenced inside this screen file now by the Key "Model". So a Key is kind of a placeholder.

    The dialog is inherited from screens\master_dialog_bare.xaml through:
    <Include
    Source="screens\master_dialog_bare.xaml">

    Additional to Close button which is already available because of master_dialog_bare.xaml, each shutdown action is added as a button to the dialog. For grouping a StackPanelis used. The datacontext for the StackPanel is set to Model which is 25F16911-ED0D-4439-9858-5E69C970C037 in the end.

    <StackPanelDataContext="{Binding Source={StaticResource Model}}">
    <Button Content="Suspend"
    Command="{Command Suspend}" />
    <Button Content="Hibernate"
    Command="{Command Hibernate}" />
    <Button Content="Shutdown"
    Command="{Command Shutdown}" />
    <Button Content="Define custom shutdown timer">
    <Button.Command>
    <CommandList>
    <Command Source="{Service ScreenManager}"
    Path="CloseTopmostDialog" />
    <Command Source="{Service ScreenManager}"
    Path="ShowDialog"
    Parameters="dialogShutdownTimer" />
    </CommandList>
    </Button.Command>
    </Button>
    <Button Content="Restart"
    Command="{Command Restart" />
    <Button Content="Minimize MediaPortal"
    Command="{Command MinimizeMP" />
    <Button Content="Restart MediaPortal"
    Command="{Command RestartMP" />
    <Button Content="Close MediaPortal"
    Command="{Command CloseMP " />
    </StackPanel>

    dialog.png


    Clicking on these buttons writes the action to the log:
    log.png
     
    Last edited:

    chefkoch

    Retired Team Member
  • Premium Supporter
  • October 5, 2004
    3,129
    1,634
    Dresden / Munich / Maastricht
    Home Country
    Germany Germany
    Use constants for references in code to
    • workflow states,
    • localization resource identifier and
    • GUI keys
    I added a new class "General\Consts.cs". There all kind of constant values, especially strings, are being defined. These constants can be reused directly within all code of the ShutdownManager project.

    The workflow states are defined in the plugin.xml file. To reuse them in code directly they are added to the Consts class. The ID is defined as text and redefined as GUID to be reused in code immediately.
    Workflow states in plugin\Consts.cs
    public const string STR_WF_STATE_ID_SHUTDOWN_MENU = "BBFA7DB7-5055-48D5-A904-0F0C79849369";
    public const string STR_WF_STATE_ID_SHUTDOWN_TIMER = "90FB6BC8-6038-4261-A00F-53774ED11B1A";
    public const string STR_WF_STATE_ID_SHUTDOWN_CONFIGURATION = "F499DC76-2BCE-4126-AF4E-7FEB9DB88E80";

    public static readonly Guid WF_STATE_ID_SHUTDOWN_MENU = newGuid(STR_WF_STATE_ID_SHUTDOWN_MENU);
    public static readonly Guid WF_STATE_ID_SHUTDOWN_TIMER = newGuid(STR_WF_STATE_ID_SHUTDOWN_TIMER);
    public static readonly Guid WF_STATE_ID_SHUTDOWN_CONFIGURATION = newGuid(STR_WF_STATE_ID_SHUTDOWN_CONFIGURATION);
    Workflow states in plugin.xml
    <Register Location="/Workflow/States">
    <!-- Shutdown Dialog -->
    <DialogState Id="BBFA7DB7-5055-48D5-A904-0F0C79849369"
    Name="ShutdownManager"
    DialogScreen="dialogShutdown"
    DisplayLabel="[ShutdownManager.ShutdownDialogStateDisplayLabel]"
    WorkflowModel="25F16911-ED0D-4439-9858-5E69C970C037"/>
    <!-- ShutdownTimer Dialog -->
    <DialogState Id="90FB6BC8-6038-4261-A00F-53774ED11B1A"
    Name="dialogShutdownTimer"
    DialogScreen="dialogShutdownTimer"
    DisplayLabel="[ShutdownManager.ShutdownTimerDialogStateDisplayLabel]"
    WorkflowModel="D5513721-92D8-4E45-B988-2C4DBF055B0F"/>
    <!-- ShutdownConfiguration Dialog -->
    <DialogState Id="F499DC76-2BCE-4126-AF4E-7FEB9DB88E80"
    Name="dialogShutdownConfiguration"
    DialogScreen="dialogShutdownConfiguration"
    DisplayLabel="[ShutdownManager.ShutdownTimerDialogStateDisplayLabel]"
    WorkflowModel="869C15FC-AF55-4003-BF0D-F5AF7B6D0B3B"/>
    </Register>

    You might remember that we also defined the Workflow model IDs within the plugin.xml. A definition inside Consts.cs is not needed, because these Model IDs are defined in the models directly as a constant. Examples:
    ShutdownDialogModel (string GUID), ShutdownTimerDialogModel (string GUID), ShutdownConfigurationDialogModel (string GUID)

    The localization resource identifiers (LRI) can be reused in code to fill properties and list items etc with localized code.
    In the example I use another helper method for setting the list item text depending on the ShutdownAction.
    The LRI in Consts can refer to those which are in the plugin's own language file or even to those which are provided by others.
    Localization resource identifies in plugin\Consts.cs
    // Localization resource identifiers
    public const string RES_SHUTDOWN_TIMER_SETUP_MENU_ITEM = "[ShutdownMenu.TimerSetup]";
    public const string RES_SHUTDOWN_TIMER_CANCEL_MENU_ITEM = "[ShutdownMenu.TimerCancel]";
    public const string RES_SYSTEM_HIBERNATE_MENU_ITEM = "[ShutdownMenu.Hibernate]";
    public const string RES_SYSTEM_SHUTDOWN_MENU_ITEM = "[ShutdownMenu.Shutdown]";
    public const string RES_SYSTEM_SUSPEND_MENU_ITEM = "[ShutdownMenu.Suspend]";
    public const string RES_SYSTEM_RESTART_MENU_ITEM = "[ShutdownMenu.Restart]";
    public const string RES_SYSTEM_LOGOFF_MENU_ITEM = "[ShutdownMenu.Logoff]";
    public const string RES_MEDIAPORTAL_MINIMIZE_MENU_ITEM = "[ShutdownMenu.MinimizeMP]";
    public const string RES_MEDIAPORTAL_RESTART_MENU_ITEM = "[ShutdownMenu.RestartMP]";
    public const string RES_MEDIAPORTAL_SHUTDOWN_MENU_ITEM="[ShutdownMenu.ShutdownMP]";
    Localization resource identifies in strings_en.xml
    <resources>
    <string name="Settings.General.System.ShutdownDialog">Shutdown menu</string>
    <string name="Settings.General.System.ShutdownDialog.Help">Configure visibility and position of shutdown menu items</string>
    <string name="ShutdownMenu.Title">Shutdown menu</string>
    <string name="ShutdownMenu.Timer.Title">Timer menu</string>
    <string name="ShutdownMenu.Configuration.Title">Configure shutdown items</string>
    <string name="ShutdownMenu.TimerSetup">Define custom shutdown timer...</string>
    <string name="ShutdownMenu.TimerCancel">Cancel custom shutdown timer...</string>
    <string name="ShutdownMenu.Hibernate">Hibernate</string>
    <string name="ShutdownMenu.Shutdown">Shutdown</string>
    <string name="ShutdownMenu.Suspend">Suspend</string>
    <string name="ShutdownMenu.Restart">Restart</string>
    <string name="ShutdownMenu.Logoff">Logoff</string>
    <string name="ShutdownMenu.MinimizeMP">Minimize MediaPortal</string>
    <string name="ShutdownMenu.RestartMP">Restart MediaPortal</string>
    <string name="ShutdownMenu.ShutdownMP">Close MediaPortal</string>
    </resources>

    The accessor keys for GUI communication in

    Accessor keys in General\Consts.cs
    // Accessor keys for GUI communication
    public const string KEY_NAME = "Name";
    public const string KEY_INDEX = "Sort-Index";
    public const string KEY_IS_CHECKED = "IsChecked";
    public const string KEY_IS_DOWN_BUTTON_FOCUSED = "IsDownButtonFocused";
    public const string KEY_IS_UP_BUTTON_FOCUSED = "IsUpButtonFocused";
    Usage of accessor keys in plugin code
    item.AdditionalProperties[Consts.KEY_IS_CHECKED] = si.Enabled;
    item.AdditionalProperties[Consts.KEY_IS_DOWN_BUTTON_FOCUSED] = i == _focusedDownButton;
    item.AdditionalProperties[Consts.KEY_IS_UP_BUTTON_FOCUSED] = i == _focusedUpButton;
    item.AdditionalProperties[Consts.KEY_INDEX] = i;
    Usage of accessor keys in skin files
    <CheckBox x:Name="ShutdownActionMarker"
    Grid.Column="0"
    Grid.Row="0"
    Margin="2,1,0,1"
    IsChecked="{Binding Path=AdditionalProperties[IsChecked],Mode=TwoWay}" />
    <Button x:Name="MoveUpButton"
    Grid.Column="1"
    Grid.Row="0"
    Margin="3"
    Width="30"
    Height="30"
    VerticalAlignment="Center"
    HorizontalAlignment="Center"
    Style="{ThemeResource ArrowButtonUpStyle}"
    Context="{Binding}"
    Command="{DynamicResource ResourceKey=MoveUp_Command}"
    SetFocus="{Binding Path=AdditionalProperties[IsUpButtonFocused],Mode=OneTime}" />
    <Button x:Name="MoveDownButton"
    Grid.Column="2"
    Grid.Row="0"
    Margin="3"
    Width="30"
    Height="30"
    VerticalAlignment="Center"
    HorizontalAlignment="Center"
    Style="{ThemeResource ArrowButtonDownStyle}"
    Context="{Binding}"
    Command="{DynamicResource ResourceKey=MoveDown_Command}"
    SetFocus="{Binding Path=AdditionalProperties[IsDownButtonFocused],Mode=OneTime}" />

    More infos about filling an itemlist from code and using it within skin as well as infos about settings handling will follow.
     
    Last edited:

    chefkoch

    Retired Team Member
  • Premium Supporter
  • October 5, 2004
    3,129
    1,634
    Dresden / Munich / Maastricht
    Home Country
    Germany Germany
    Settings and Configuration Management in MediaPortal 2
    Please also read wiki for more information about settings, configuration and their difference.

    Loading/Saving settings from/to xml files is done by the SettingsManager.
    A plugin only needs to provide it's own class and all's properties are then automatically (de-)serialized from/to the xml files. The plugin itself does not need to care about any Serialization or file handling stuff.

    The ShutdownSettings contains three public properties:
    [Setting(SettingScope.User, 60)]
    public int? LastCustomShutdownTime { get; set; }

    [Setting(SettingScope.User, ShutdownAction.Suspend)]
    public ShutdownAction? LastCustomShutdownAction { get; set; }

    [Setting(SettingScope.User, null)]
    public List<ShutdownItem> ShutdownItemList
    {
    get
    {
    if (_shutdownItemList == null)
    CreateDefaultShutdownMenu();
    return _shutdownItemList;
    }
    set
    {
    _shutdownItemList = value;
    }
    }
    For each setting
    • the scope, whether it is a MP2 global or a 'per-user' setting and
    • a default value will be defined.
    Loading the settingsis simply done by:
    ShutdownSettings settings = ServiceRegistration.Get<ISettingsManager>().Load<ShutdownSettings>();
    After settings are loaded, all it's properties can be accessed as usual.

    Saving the settings is similar to loading them:
    ISettingsManager settingsManager = ServiceRegistration.Get<ISettingsManager>();
    ShutdownSettings settings = settingsManager.Load<ShutdownSettings>();
    // Apply new shutdown item list
    settings.ShutdownItemList = ShutdownItemList;

    settingsManager.Save(settings);

    Up to this point it is already possible to load/save various settings from/to xml files, without writing any line of code regarding paths, file names, or xml file handling.

    Even if the settings management is already working, nothing has been changed from the user point of view. To change settings the configuration management is needed. Like explained in the first post, the user should be able to configure the order and visibility of the shutdown items.
    To achieve this a CustomConfigSetting is needed. For using other settings, like YesNo (Boolean), paths, Single- or MutlipleSelections please take a look at the wiki and other existing plugins.
    Example: Videoplayer\DefaultGeometry: registration of ConfigSetting in plugin.xml with reference to DefaultGeometry class, definition of DefaultGeometry class as SingleSelectionSetting, DefaultGeometry in PlayerSettings
    Compared i.e. to the DefaultGeometry setting, which is only a SingleSelection out of a list and already has the overloads Load() and Save(), is our CustomConfigSetting ShutdownDialogSetting empty.
    namespace MediaPortal.Plugins.ShutdownManager.Settings.Configuration.General
    {
    public class ShutdownDialogSetting : CustomConfigSetting
    {
    }
    }

    Within the plugin.xml
    <Register Location="/Models">
    <Model Id="869C15FC-AF55-4003-BF0D-F5AF7B6D0B3B"
    Name="ShutdownConfigurationModel"
    ClassName="MediaPortal.Plugins.ShutdownManager.Models.ShutdownConfigurationModel"/>
    </Register>
    <Register Location="/Workflow/States">
    <!-- ShutdownConfiguration Dialog -->
    <DialogState Id="F499DC76-2BCE-4126-AF4E-7FEB9DB88E80"
    Name="dialogShutdownConfiguration"
    DialogScreen="dialogShutdownConfiguration"
    DisplayLabel="[ShutdownManager.ShutdownConfigurationDialogStateDisplayLabel]"
    WorkflowModel="869C15FC-AF55-4003-BF0D-F5AF7B6D0B3B"/>
    </Register>
    • a setting ShutdownDialogSetting is being registered as CustomConfigSetting with reference to the dialogShutdownConfiguration workflow state
    <!-- Register ShutdownConfiguration at '/General/System' group -->
    <Register Location="/Configuration/Settings/General/System">
    <CustomConfigSetting
    Id="ShutdownDialog"
    Text="[Settings.General.System.ShutdownDialog]"
    HelpText="[Settings.General.System.ShutdownDialog.Help]"
    ClassName="MediaPortal.Plugins.ShutdownManager.Settings.Configuration.General.ShutdownDialogSetting"
    AdditionalData="WorkflowState=F499DC76-2BCE-4126-AF4E-7FEB9DB88E80,ConfAppPanel=..."/>
    </Register>

    Creating the GUI for the CustomConfigSetting
    At next a new class ShutdownItem with it's properties Action (type: ShutdownAction) and Enabled (type: bool) has been implemented.
    The ShutdownSettings class got the property ShutdownItemsList (type: List<ShutdownItems>)
    Now it is already possible to save and load the order and the visibility of the shutdown items.

    Let's make it available to GUI through the new ShutdownConfigurationModel.
    It loads and saves the list of shutdown items and provides public properties and methods for usage within the GUI.
    Within skin skin files it is possible to trigger:
    Additionally an ItemList of available shutdown actions is provided. Each item of that ItemList has AddtionalProperties. The currently the important one here is: IsChecked(in code the constant KEY_IS_CHECKED is being used, see previous post for infos about the constans and their usage)
    Let's design the screen files (skin file) for the dialogShutdownConfiguration:
    The Layout could be improved and slimmed down in future. If I'll do that I'll make a separated post and link to it from here.
    What we've got here:
    51-config.png


    As you can see you can freely define any screen like you want it to in MP2. You even could build the list horizontal instead of vertical and move everything from left to right etc, without any changes to plugin code itself, because the required properties and methods are already populated.

    Screens of the other dialogs:
    52-menu.png 53-timer.png

    The other properties of the ShutdownSettings are not populated to the user, but managed by the ShutdownTimerModel: Load, Save

    Next task should be the collection of infos about the remaining playtime of the current media and the current active playlist, their conditional visibility in dialogShutdownTimer and some reworks, cleanups & imrovements in code and skin files.
     
    Last edited:

    chefkoch

    Retired Team Member
  • Premium Supporter
  • October 5, 2004
    3,129
    1,634
    Dresden / Munich / Maastricht
    Home Country
    Germany Germany

    chefkoch

    Retired Team Member
  • Premium Supporter
  • October 5, 2004
    3,129
    1,634
    Dresden / Munich / Maastricht
    Home Country
    Germany Germany
    Ok to finish this thread and add latest information:

    The ShutdownManager has been implemented as a plugin called: "SystemStateMenu"
    For documentation take a look at the wiki page: http://wiki.team-mediaportal.com/2_MEDIAPORTAL_2/Reference/SystemStateMenu
    There are also links included to the source code and other related components.

    The "sleep timer" feature is not included within "SystemStateMenu", yet.
    For documentation of it's design concept take a look at the it's wiki page: http://wiki.team-mediaportal.com/2_MEDIAPORTAL_2/Contribute/zz_Design_Concepts/Sleeptimer
     

    Users who are viewing this thread

    Top Bottom