There’s a good chance that any developers who read these updates will be chomping at the bit to learn more about the plugins I mentioned a few posts back. To help these guys out (or until 0.3 is released, tease them) I figured I’d write a few posts on how one develops plugins for Twaddle.
First of all: Where does a developer place their plugins for Twaddle to pick them up? With 0.3 there’ll be a “Plugin” directory in the main Twaddle application directory. Developers should install their plugins into a subdirectory of the Plugins directory. Eg: \Program Files\Twaddle\Plugins\MichaelsCoolPlugin\. Any linked assemblies, resources, etc, should also sit within this directory. The only other point to mention here is that if you want your plugins to be loaded the filename must contain the string .Plugin. (Note the period at the start and end) as well as .dll. This way if the directory \Program Files\Twaddle\Plugins\MichaelsCoolPlugin\ contains the files Michael.Resources.dll, Michael.Plugin.dll and ThirdParty.Library.dll Twaddle would only attempt to load plugins from Michael.Plugin.dll. This helps to ensure start times for Twaddle are low.
Once Twaddle has found your assembly it will look through all of the public, non-abstract, non-interface types in your assembly and look at the interfaces they implement. There are several different interfaces you can implement which control the style of plugin you’re writing. The same class can implement multiple interfaces and there can be multiple plugins in the one assembly. Note that if your class does implement several plugin interfaces then one instance will be created for each plugin interface. So if you need to share data between the instances you’ll have to handle that yourself.
All of these interfaces (as well as many others Twaddle gives you) are defined in the Twaddle.Interface assembly (which I’ll distribute) and your plugin will need to link to. In addition all of the different interfaces are marked as implementing the Twaddle.Interface.IPlugin interface. This interface is an informational interface that tells Twaddle about your plugin (some of which is then displayed to the end user). The definition of IPlugin is as follows:
public interface IPlugin {
/// <summary>
/// A Unique identifier that will remain consistent regardless of locale, build, etc.
/// </summary>
string Id { get; }
/// <summary>
/// The short name for this plugin
/// <example>Public Timeline</example>
/// </summary>
string Name { get; }
/// <summary>
/// A longer descriptor of the plugin
/// <example>The Public Timeline plugin updates and displays the current user's timline</example>
/// </summary>
string Description { get; }
IPluginConfiguration Configuration(IAppSettings settings);
/// <summary>
/// A rough indication of the amount of network data this plugin uses
/// </summary>
UsageAmount Data { get; }
/// <summary>
/// A rough indication of the amount of memory this plugin uses
/// </summary>
UsageAmount Memory { get; }
/// <summary>
/// A rough indication of the amount of processor this plugin uses
/// </summary>
UsageAmount CPU { get; }
/// <summary>
/// A rough indication of the amount of battery this plugin uses
/// </summary>
UsageAmount Battery { get; }
/// <summary>
/// A rough indication of the amount of disk space this plugin uses
/// </summary>
UsageAmount Storage { get; }
};
The Id property should be a unique value for your plugin as it’s used to store user settings. A good candidate would be a pre-generated guid constant (ie return “21EC2020-3AEA-1069-A2DD-08002B30309D” instead of doing return Guid.NewGuid().ToString() or the namespace of your plugin (“Michael.CoolPlugin.SomeSnazzyEventResponderPlugin”. The Name property is displayed to the user when configuring your plugin whilst the Description property is used to tell the user a little bit more about your plugin.
The UsageAmount returning properties (Storage, Battery, etc) are intended as a rough guide to the User so they can make an informed decision as to if they want to leave your plugin enabled or not. The values are None, Inconsequential, Low, Medium, and High. As an example a video uploading plugin would likely have a High Storage and Data value (as they save and upload video files). A picture uploading plugin would probably be Medium Storage and Medium Data. A plugin that pings your server once an hour might have a Data value of Inconsequential. Whilst one that has no network connectivity would be None.
I’m going to skip the Configuration() method because it would comprise its whole own post. But if your plugin has no configuration at all you can return null from this method.
Of course these IPlugins don’t describe much on their own. The interfaces you’d actually be implementing are ITaskPlugin, IFormControlPlugin, IEventResponderPlugin, IMenuPlugin, IPendingItemProcessorPlugin,IPostFormControlPlugin and ISendMessageFormControlPlugin. The final two may get merged before 0.3 (I haven’t decided yet). Hopefully the name of these interfaces should give you some indication of what they do, but I’ll give more details in a future post.
Before I finish up here there is one more interface a plugin developer will need to be familiar with. IDependencyResolver. This is defined as:
public interface IDependencyResolver {
TDependency Resolve<TDependency>() where TDependency : IDependency;
TDependency Resolve<TDependency>(string name) where TDependency : IDependency;
}
IDependency is a marker interface used to give compile-time checking over what interfaces defined in Twaddle.Interface are obtained through the IDependencyResolver. This is because some are implemented by the developer and others are passed into methods that different plugins use. All plugin types are given an IDependencyResolver at instantiation and they should maintain a reference to this object. The instance given is different between each plugin instance (as there’s different types returned for some of the dependencies, or instance-specific information returned).
Lets say that you’re implementing a new screen in Twaddle and want to get a list of actions applicable to a tweet (something that other developers can provide by creating IMenuPlugin implementations). How would you do this? Well, there’s an interface IMenuItemFactory which can take a tweet as an argument and return a list of MenuItem’s for you to display in your interface. Sample code might look like:
private void DisplayMenuItems(IDependencyResolver resolver, IStatus selectedTweet) {
IMenuItemFactory factory = resolver.Resolve<IMenuItemFactory>();
ICollection<MenuItem> menu = factory.Items(selectedTweet);
// Now add the items in menu to your on screen menu, or some such.
}
I think that should be enough of a brief introduction for now – I’ll start writing an posts describing each of the plugin interfaces in turn. I’ve put a poll up for you to vote on which you’re most interested in learning more about.