View on GitHub

Mechanoid

Rapid Development Tools for Android™

In a nutshell Mechanoid is an Eclipse Plugin sporting a set of code generators for Android™ driven by DSL's (Domain Specific Languages) with full Eclipse Editor Support for the rapid development of Android applications.

The following building blocks currently make up the high-level feature set of Mechanoid.

Mechanoid DB

Use the Sqlite language itself to generate Android content providers and the ActiveRecord pattern with rich editor support.

Mechanoid Net

Generate REST over JSON service clients with this simple language to describe JSON Objects and REST methods.

Mechanoid Ops

A Framework to create and run decoupled background operations as described in Virgil Dobjanschi's Google IO REST 2010 presentation.

Mechanoid Prefs

A simple DSL to generate thin wrappers for strongly typed access to shared preferences.

Learn More

Scroll down to learn more about each component or see the Getting Started Guide for details on how to setup an Android project with Mechanoid.

Mechanoid DB

Mechanoid DB is a powerful tool with fully blown editor supporting the Sqlite language itself as the driver to generate top quality Android Sqlite-backed Content Providers.

Define your database

With Mechanoid DB you define your database in a mechdb file with a full featured Eclipse editor supporting syntax-highlighting, code completion and more.

			package com.robotoworks.examples.recipes.content
			
			database RecipesDB {
				migration {
					create table recipes (
						_id integer primary key autoincrement,
						title text,
						description text
					);
				}
			}
			

Use the generated API

After saving the mechdb file you can start using the generated code.

You can insert data easily

		        Recipes.newBuilder()
		        .setTitle("Omelette")
		        .setDescription("Wonderful omelette with cheese")
		        .insert();			
			

Or insert with ActiveRecord if you prefer?

				RecipesRecord record = new RecipesRecord();
		        record.setTitle("Ragu");
		        record.setDescription("Serve with pasta");
		        record.save(); 
        	

SQuery for your CursorLoader's

        	CursorLoader loader = 
        		SQuery.newQuery().createSupportLoader(
                    Recipes.CONTENT_URI,
                    new String[] { 
                            Recipes._ID, 
                            Recipes.TITLE,
                            Recipes.DESCRIPTION 
                    });
        	

Learn More

Mechanoid DB supports schema migrations first class and almost all of the sqlite syntax including views, joins, triggers and more, you just need to define the sql and Mechanoid will generate the code!

To find out more read the Mechanoid DB Documentation.

Mechanoid Net

Mechanoid Net is a JSON over REST client generator for Android, backed by a powerful Eclipse editor

Define your service client

Define your REST client with a language that represents the HTTP Protocol

			client RecipesServiceClient "http://www.robotoworks.com/example" {
				get getRecipeList /recipes {
					params
						limit:int,
						query:String
					response Recipe[]
				}
				
				get getRecipe /recipe/id:int {
					response Recipe[]
				}
				
				post addRecipe /recipes {
			    	response {
			    		recipe_id:int
			    	}
					body Recipe
				}
				
				delete deleteRecipe /recipe/id:int {
					response boolean
				}
			}			
        	

And describe your JSON entities

Describe your JSON graph with entities

			entity generate Recipe {
				recipe_id:int,
				title:String,
				description:String
			}		
        	

Then use the generated API

Use the generated client API to access your REST service

	        RecipesServiceClient client = new RecipesServiceClient();
	        
	        // Get a list of recipes
	        Response<GetRecipeListResult> recipeListResponse = 
	        		client.getRecipeList();
	
	        // Get a recipe
	        Response<GetRecipeResult> recipeResponse = 
	        		client.getRecipe(new GetRecipeRequest(123));
	        
	        // Add a recipe
	        Recipe recipe = new Recipe();
	        recipe.setTitle("Ragu");
	        recipe.setDescription("Great with Pasta");
	        
	        Response<AddRecipeResult> addRecipeResponse = 
	        		client.addRecipe(new AddRecipeRequest(recipe));
	        
	        // Delete a recipe
	        Response<DeleteRecipeResult> deleteRecipeResponse = 
	        		client.deleteRecipe(new DeleteRecipeRequest(123));	
        	

Learn More

Mechanoid Net is a powerful tool to describe your JSON over REST service clients, it can save you hours of time writing parsing logic and fixing issues freeing yourself from boiler-plate problems.

To find out more read the Mechanoid Net Documentation.

Mechanoid Ops

Mechanoid Ops is a framework for running background operations that are decoupled from the UI, first described by Virgil Dobjanschi in his GOOGLE IO REST 2010 Presentation.

In Mechanoid we call this the Operation Service Pattern, we describe our operations using a simple DSL, for each operation a class stub is generated in which we can do our background work and return the result to listeners.

Define your operations

We use a simple DSL to define our operations in a *.mechnet file

		package com.robotoworks.examples.recipes.ops
		
		service Recipes {
			operation getRecipes()
			operation getRecipe(long id)
			operation addRecipe(String title, String description)
			operation deleteRecipe(long id)
		}
	    

Implement your operation stubs

For each operation a java class stub is generated, we can then, for instance, make network requests...

		public class AddRecipeOperation extends AbstractAddRecipeOperation {
			@Override
			protected OperationResult onExecute(Args args) {
				
				// Operation arguments are available in Args, ie:-
				String title = args.title;
				String description = args.description;
				
				// Using a Mechanoid Net generated rest client...
				Recipe recipe = new Recipe();
				recipe.setTitle(title);
				recipe.setDescription(description);
				
				RecipesServiceClient client = new RecipesServiceClient();
				
				int newRecipeId = 0;
				
				try {
					Response<AddRecipeResult> response 
						= client.addRecipe(new AddRecipeRequest(recipe));
					
					response.checkResponseCodeOk();
					
					AddRecipeResult result = response.parse();
					
					newRecipeId = result.getRecipeId();
					
				} catch (ServiceException e) {
					// In the event of an error we can 
					// return the Exception as an error result
					return OperationResult.error(e);
				}
				
				// If everything is good we return an ok result,
				return OperationResult.ok(bundle);
			}
		}        
	    

Execute your operations asynchronously

For each defined service, a service bridge is generated which we use to execute operations

		// Bind a callback listener
		Ops.bindListener(listener);
		
		// Create an operation intent using the generated static helper method
		Intent intent = AddRecipeOperation.newIntent("Ragu", "Great with Pasta");
		
		// Execute
		int addRecipeOperationId = Ops.execute(intent);
		
        

Listen and act on operations completing

Implement an OperationServiceListener to handle the callback

		private OperationServiceListener listener 
			= new OperationServiceListener() {
			
			@Override
			public void onOperationComplete(int id, OperationResult result) {
				
				if(id == addRecipeOperationId) {
					if(result.isOk()) {
						// TODO: Do something
					} else {
						
						Throwable error = result.getError();
						
						// TODO: Deal with the error
					}
				}
			}
		};
		

Check to see if operations are currently pending

We can use the generated service bridge to check if an operation is currently pending completion with a given operation id.

		boolean isPending = Ops.isOperationPending(addRecipeOperationId);
		

Alternatively, use an OperationManager in your UI

Managing callbacks for operation ids and checking operations can be cumbersome in UI, especially with orientation changes, instead we can use a manager and provide our own unique identifiers to identify specific operations.

		public class AddRecipesActivity extends FragmentActivity {
		
			// Define your own ID
		    private static final int OP_ADD_RECIPES = 123;
		    
			private SupportOperationManager mOperationManager;
		    
		    @Override
		    protected void onCreate(Bundle savedInstanceState) {
		        super.onCreate(savedInstanceState);
		        
		        // Create an instance of manager for your bridge
		        mOperationManager = SupportOperationManager.create(this, mCallbacks);
		        
		        // Execute your operation using the manager
		        Intent intent = AddRecipeOperation.newIntent("Ragu", "Great with Pasta");
		        mOperationManager.execute(intent, OP_ADD_RECIPES, false);
		    }
		    
		    // Implement the callbacks
		    private OperationManagerCallbacks mCallbacks = 
		    		new OperationManagerCallbacks() {		
		    	@Override
				public void onOperationComplete(int id,
						OperationResult result, boolean fromCache) {
					if(id == OP_ADD_RECIPES) {
						if(result.isOk()) {
							// TODO Do something
						} else {
							Throwable error = result.getError();
							
							// TODO Do something with the error
						}
					}
				}
			};
		}
	   

Learn More

Mechanoid Ops provides a framework for executing asynchronous operations, and takes the pain away from handling background operations in the Android UI Lifecycle.

To find out more read the Mechanoid Ops Documentation.

Mechanoid Prefs

Mechanoid Prefs is a simple DSL to describe and generated Android Shared Preferences wrappers.

Define your shared preferences

Preferences are defined in a *.mechprefs file, with Eclipse Editor support.

	       	preferences User {
				name:String = "Anonymous"
				age:int
				cash_balance:float
				likes_ragu:boolean
			}
      	   

Access your generated preferences wrapper

We can now access preferences using the generated code.

	         // Get an instance of the generated wrapper
	        UserPreferences prefs = UserPreferences.getInstance();
	        
	        // Easily access preferences
	        int age = prefs.getAge();
	        boolean likesRagu = prefs.getLikesRagu();
	        float cashBalance = prefs.getCashBalance();
	        
	        // Easily save prefences
	        prefs.edit()
	        	.putAge(37)
	        	.putLikesRagu(true)
	        	.putCashBalance(-10000)
	        	.commit();
	        
	        // And the same flexibility...
	        
	        // Still access properties
	        String ageKey = UserPreferences.Keys.AGE;
	        
	        // Still Register listeners
	        prefs.registerOnSharedPreferenceChangeListener(listener);
	        
	        // Access underlying preferences
	        SharedPreferences realPreferences = prefs.getSharedPreferences();     	   
      	   

Learn More

Although very simple, Mechanoid Prefs makes access to your preferences look pretty but offers the same flexibility as the underlying SharedPreferences.

To find out more read the Mechanoid Prefs Documentation.

Get Started

Visit the Mechanoid Documentation to get started.