Social Icons

Featured Posts

среда, 1 мая 2013 г.

Drawing charts with wxWidgets. Part I - Introduction.

Introduction.

I developed wxFreeChart to provide wxWidgets the flexible and comprehensive framework for building charting applications with support of the various chart types, with various data sources, and control of the most visual representation attributes (fonts, axes configuration, titles, background, etc). wxFreeChart is using model-controller-view pattern. It’s design allows to easily integrate various data sources (files, sensors, calculated by application, database, received by network, etc). Data source is abstract, e.g. it’s no sense for framework of how data received. Rendering system enables application developer to make many different chart types with the same approach. Almost every aspect of visual representation can be controlled, this allow you, for example, to make as many axes, as you need, change their colors and fonts, set markers and their appearance, set gradient backgrounds, change legend, chart title and it’s font and color. wxFreeChart is like a set of many building blocks, that developer configure and links them together. Project has wxWidgets license. You can use wxFreeChart in your applications (even commercial) for free.

Supported chart types.

wxFreeChart supports the following chart types:
  1. Line XY charts.
  2. Histogram XY charts.
  3. Area XY charts.
  4. Bar charts.
  5. Stacked bar charts.
  6. Layered bar charts.
  7. OHLC bars financial charts.
  8. OHLC candlesticks financial charts.
  9. Bubble charts.
  10. Gantt charts.
Features.
  1. Markers.
  2. Crosshair.
  3. Dynamic charts.
  4. Unlimited amount of axes. 
  5. Gradient backgrounds and bars.
  6. Legend.
  7. Bars and candlestick colors can be controlled by various conditions (e.g. indicators, etc).
  8. Zoom and pan for charts.
wxFreeChart design.

Main concepts are:
  1.  Dataset.
  2. Renderer.
  3. Axis.
  4. Plot.
  5. Chart.         
  6. wxChartPanel.
Dataset.

Dataset is a data access interface. It's a model in Model-Controller-View pattern. It doesn't hold data by itself, it only defines data interface. There are different implementations (they are described below). You can write your own Dataset implementation, if noone provided classes fits your needs.

Datasets can be following types:

  1. XYDataset. Provides data for charts, based on XY coordinates.
  2. CategoryDataset. For charts with numeric/string information on one axis, and string information on another, and pie plots.
  3. OHLCDataset. For financial quote charts.
  4. XYZDataset. For bubble charts.

Renderer. 

Renderer is object, that performs data drawing. For one dataset type, can be many renderer, for example: XYDataset can be rendered as lines, histogram or area, OHLCDataset can be drawn as bars or candlesticks.

Renderers can be following types:

  1. XYRenderer. For drawing XY data.
  2. BarRenderer. For drawing bars: normal, stacked, layered.
  3. OHLCRenderer. For drawing financial quote data.
  4. XYZDataset. For drawing XYZ data.
Axis. 

Axis is an object, that performs data scaling and axis rendering.
  1. NumberAxis. For displaying numeric labels, and scaling numeric data.
  2. CategoryAxis. For string labels.
  3. DateAxis. For date/time labels.
  4. CompDateAxis. Interval axis for date/time labels.

Plot. 

Plot is an object, that connects axes, datasets, visual objects (markers, crosshairs). It contains title, background, etc.

Chart.

Chart is an object, that contains title, plot.

Other objects.
  1. Markers.
  2. Area draws.
  3. Legend.
  4. AxisShare.
  5. Multiplot.
Markers. 

Markers are used to mark data values and/or intervals. Markers are connected with dataset. There are following marker types:

  1. Point marker. Marks single point value.
  2. Line marker. Uses to mark the specified value as line.
  3. Range marker. Used to mark the specified range of values.

Area draws. 

Area draws are objects, that performs drawing background (plot, chart), bars in bars plots, legend symbols, etc. There two area draw types: fill and gradient.

Legend. 

Legend draws legend symbols and serie names. It has position within plot, font to draw labels, etc. Legend can be vertical or horizontal.

AxisShare. 

AxisShare is helper object, that makes it possible to create combined axis plots.

Multiplot. 

Multiplot is a special kind of plot, that contains many subplots. It's used to 

Summary.

In this part of article, i introduced wxFreeChart design. In the next parts i will provide you an example of how to create a simple chart, more detailed explanation for wxFreeChart objects, dataset-renderers configurations for each chart type.

Links.



вторник, 26 февраля 2013 г.

Remote operations on Drupal objects. Technical details.

In the previous article Drupal with Android integration: make posts and upload photos. i wrote the introduction about to integrate Drupal with Android. In this article i'll explain the communication technical details. If you need some guidlines, about how xml-rpc is implemented in Drupal or you need the information about the remote methods it exposes, this article will be useful for you. If you need to develop an app, that will create, edit or delete pages, files, comments, etc. This article will give you information about how to make it.

Xml-rpc background.
Xml-rpc is a lightweight remote procedure calls protocol. It's not require methods description as, for example, with SOAP. Xml-rpc server exposes a set of remote methods, with input arguments and return value types. It support the following data types:
  • int integer value
  • double double-precision floating point value
  • boolean boolean value
  • string string value
  • dateTime date/time value
  • base64 base64 encoded binary data
  • array array of values
  • struct structure, where each member has name and value (similar to associative arrays)
int, double, boolean, string, dateTime and base64 are primitive types. array and struct are complex types, consisting of other values.

Examples:
struct node.create(struct node)
Method takes one input argument as structure, and returns structure value.

struct user.login(string, string)
Method takes two string input arguments, and returns structure value.

Drupal exposes a set of methods through xml-rpc.
Drupal xml-rpc servers are implemented in modules, in hook_xmlrpc (or by defining service resources, but this method is not in scope of this article). There are built-in methods, and you can create your own.

Most of the operations on Drupal objects, that are accessible through xml-rpc, are CRUD (create-read-update-delete). For example, for node operations are: node.create, node.retrieve, node.update, node.delete, node.index. The same is for applied for the most Drupal objects: comments, users, files, taxonomy terms. Only replace node with appropriate object type in method name. Most of the objects are identified by it's integer id: nid (node identifier), cid (comment identifier), fid (file identifier), and so on. retrieve, update and delete methods takes object id as first argument.

Authorization.
Authorization must be performed before calling the methods, that require an authorized user. Most of the methods, described in this article, must be called by an authorized user.

struct user.login(string username, string password)
Method to perform user login. It takes username and password arguments, and returns structure containing session params (sessid and session_name) and nested structure with information about user (email, uid, etc).
If you need to perform remote actions that require authorization, you must call this method first. You need to store sessid and session_name returned values, as they are identify openned user session. This values will be sent as cookie for all subsequent remote calls.

user.logout()
Perform logout. This method must be called, when to more actions will be performed, to close Drupal session.

Users operations.
User information is passed in structure with the following members:
  • name - user name
  • mail  - email address
  • pass - plain text unencrypted password
These properties can be passed, but are optional:
  • status - 0 for blocked, otherwise will be active by default
  • notify - 1 to notify user of new account, will not notify by default
Roles can be passed in a roles property, which is an associative array formatted with '' => '', not including the authenticated user role, which is given by default.

This structure is passed to create, register and update methods as input argument.
Users are identified by integer value - uid (user identifier).

struct user.create(struct account)
Create the new user, specified by account structure. This method may be called only by authorized users with 'administer users' permission.

struct user.retrieve(int uid)
Retrieves a user account information. User is specified by uid (user identifier). Returned structure contains user account information.

struct user.update(int uid, struct account)
Update the existing user, specified by uid (user identifier). New user's data is specified by accout input argument. Calling user must have 'administer users' permission.

boolean user.delete(int uid)
Removes the user, specified by uid. Returns true, if user is successfully deleted, false - overwise.

array user.index(int page, string fields, array parameters, int pagesize)
Get list of users. All arguments are optional. This method can split result to pages. page argument specifies the page number to return, fields - what fields to be returned, parameters - is the parameters used to build SQL WHERE clause, pagesize - specifies size of the page. Returned value is an array of structures with requested users. If method is called without arguments, it will return all users.
For example: you have 100 users, setting pagesize = 20 and page = 2, will return users 40-60.

struct user.register(struct account)
Register a new user. This method may be called by only unauthorized user (no user.login call before it). User's data is passed in account argument. The difference between register and create methods, is that register is called by an anonymous user, while create can by called only by user with 'administer users' permission.

Node operations.
Node operations are CRUD (create-read-update-delete). Node is identified by nid (node identifier), and node information is passed and returned in structure with the following members:
  • type - node type (page, story, etc)
  • title - node title
  • body - node body
  • nid - node identifier (returned by retrieve, create, update methods)
struct node.create(struct node)
Creates a new node. It takes structure containing information about node to create. Method returns structure with created node information. Input and output structure differs in that, output is assigned nid (node identifier).

struct node.retrieve(string nid)
Retrieves the node content. Input argument is nid (node identifier). Returned value is structure with node information.

struct node.update(string nid, struct node)
Updates an existing node. Input arguments are nid and structure with node information (node type, nid, title, body, etc). Method returns structure with updated node information.

boolean node.delete(string nid)
Remove the node, specified by nid (node identifier). Returns true, if node successfully deleted, false - overwise.

array node.index(int page, string fields, array parameters, int pagesize)
Get list of nodes. All arguments are optional. This method can split result to pages. page argument specifies the page number to return, fields - what fields to be returned, parameters - is the parameters used to build SQL WHERE clause, pagesize - specifies size of the page. Returned value is an array of structures with requested nodes. If method is called without arguments, it will return all nodes.

For example: you have 100 nodes, setting pagesize = 20 and page = 2, will return nodes 40-60.

array node.files(int nid, int file_contents)
This method returns files associated with a node. Node is specified by nid (node identifier). file_contents argument specifies, whether or not to return file contents. Returned value is an array of structures with file information (see File manipulation section for details, about file structures).

array node.comments(int nid, int count, int offset)
Get node comments. Node specified by nid argument. count specifies comments count to return, offset specifies index of the first comment to return. Method returns an array of structures with comments information (see Comments operations section for details, about comment structure). To call this method comment module must be enabled.

Comments operations.
Comment operations are use the same CRUD approach, as other objects. Comment information is passed in structure with the following members:
  • author - name of the author of comment
  • date - date of the comment. Value 'now' 
  • subject - the subject of the comment
  • comment_body
  • nid - node identifier
  • pid - parent comment id
  • format - comment format
struct comment.create(struct comment)
Create a new comment. Comment data specified by by comment argument. Method returns created comment information.

struct comment.retrieve(int cid)
Returns the comment, specified by cid (comment identifier). Method returns struct with comment information.

struct comment.update(int cid, struct comment)
Update an existing comment. Comment specified by cid argument, new comment data by comment argument. Method returns updated comment information.

boolean comment.delete(int cid)
Delete the comment, specified by cid (comment identifier). Returns true if comment is successfully removed, false - overwise.

array comment.index(int page, string fields, array parameters, int pagesize)

Get list of comments. All arguments are optional. This method can split result to pages. page argument specifies the page number to return, fields - what fields to be returned, parameters - is the parameters used to build SQL WHERE clause, pagesize - specifies size of the page. Returned value is an array of structures with requested comments. If method is called without arguments, it will return all comments.

For example: you have 100 comments, setting pagesize = 20 and page = 2, will return comments 40-60.


int comment.countAll(int nid)
Method returns the number of comments for the given node. Node is specified by nid (node identifier).

int comment.countNew(int nid, int since)
Method returns the number of new comments on a given node since a given timestamp. nid argument specifies the node, since specifies timestamp since that to return comments.

File operations.
File operations use the same CRUD approach, as other objects. Files identified by integer fids (file identifiers), and file described by the structure with the following members:
  • filename - file name
  • filepath - file path
  • file - file data encoded as base64
struct file.create(struct file)
Create a new file. file input argument is structure, describing file. Method returns structure with created file information. Input and output structure differs in that, output has assigned fid (file identifier).

struct file.retrieve(int fid, int file_contents)
Retrieve the file, specified by fid (file identifier). file_contents argument specifies, whether or not to return file content. Structure returned by method, contains file description.

boolean file.delete(int fid)
Remove the file, specified by fid (file identifier). Returns true if file is successfully deleted, false - overwise.

array file.index(int page, string fields, array parameters, int pagesize)

Get list of files. All arguments are optional. This method can split result to pages. page argument specifies the page number to return, fields - what fields to be returned, parameters - is the parameters used to build SQL WHERE clause, pagesize - specifies size of the page. Returned value is an array of structures with requested files. If method is called without arguments, it will return all files.

For example: you have 100 files, setting pagesize = 20 and page = 2, will return files 40-60.


System calls.
System calls are intended to get server capabilities, list of method it exposes, method signatures, gettting, setting and deleting system varibles.

array system.methodSignature(string method)
Returns requested method signature. Method is specified by method argument, which is method name. Returned value is an array of strings, which are names of types of method arguments.

struct system.getCapabilities()
Returns server capabilities, as members of struct. Member names are names of capabilities. Member values are structures with specUrl and specVersion members. Example of capabilities are: xmlrpc, system.multicall, introspection, json-rpc.

array system.listMethods()
Enumerates the methods implemented by xml-rpc server. Method not takes input arguments. It returns an array of strings, each of which is the name of a method implemented by server.

string system.methodHelp(string method)
Returns method's help string. Method name is specified by method argument, which is the string with requested method name.

struct system.connect()
Returns the details of current logged in user and session (sessid and session_name). The returned structure is the same as with user.login method.

string system.get_variable(string name, string default)
Returns the value of a system variable using variable_get().

system.set_variable(string name, string value)
Sets the value of a system variable using variable_set().

system.del_variable(string name)
Deletes a system variable using variable_del().

Summary.
I described, the principles of xml-rpc,  how it works with Drupal and what actions can be performed with Drupal using remote methods. There are also blog, taxonomy and other methods, but they are not in scope of this article. Also, there are other remote procedure protocols, supported by services module.

Please, send me your comments and suggestions.

Links.
Xml-rpc specification: http://xmlrpc.scripting.com/spec.html
Drupal services module: http://drupal.org/project/services

суббота, 16 февраля 2013 г.

Drupal with Android integration: make posts and upload photos. Part II - Android.

This is part II of article about Drupal and Android integration. 
In the Part I, i wrote about principles of communication and how to implement Drupal module.
In this part of article i'll describe how to implement Android application.

3. Android application

We finished with Drupal configuration and server-side code, and now we create Android client application.  It will be an app first asking user to enter username and password and press "Ok" button. If login is succeeded, app show user an activity, where he can enter page title and body, make photo, and post it to the server.

XML-RPC is not supported on Android out-of-box, but there is a good library from Redstone. (http://xmlrpc.sourceforge.net/). Redstone's license is commercial friendly, so you can use this library in your commercial applications for free. I modified it to work it properly on Android. And you can download code with my modifications (link is given in the end of article).

Redstone XML-RPC contains many classes, but to start using it, you need to know a few of them. 
XmlRpcClient. acts as XML-RPC client, sending requests to server. It takes url of endpoint to connect to. For example: http://yoursite.com/?q=android

XmlRpcStruct is XML-RPC named values structure, where each member has it's name and value. Similar to MapMany XML-RPC method arguments and return values are passed in this structure.

All Drupal conversations will be implemented in DrupalConnect class. This class will contain methods for authentication, logout, post page, delete page and upload photo. For photo uploads, i created http client. It's more lightweight, than standard Android clients, and supports upload progress reporting. You can find it in my source code for Android app. DrupalConnect is made as singlton pattern, because there is only one connection per app, and it must be available from many places.

Define urls. Please, replace URLBASE value with your Drupal website address.
 /** Base url of Drupal installation */
 final static private String URLBASE = "http://192.168.1.9/drupal";
 
 /** XML-RPC url */
 final static private String XMLRPC = URLBASE + "/?q=androidrpc";
 /** Photoupload url */
 final static private String PHOTOUPLOAD = URLBASE + "/?q=photoupload";


Now, implement login method

 /**
  * Perform authentication.
  * @param username user name
  * @param password password
  * @throws IOException
  * @throws XmlRpcException
  * @throws XmlRpcFault
  */
 public void login(String username, String password) throws IOException, XmlRpcException, XmlRpcFault {
  if (isAuthenticated())
   logout();

  XmlRpcClient xmlrpc = new XmlRpcClient(XMLRPC, false);

  XmlRpcStruct res = (XmlRpcStruct) xmlrpc.invoke("user.login", new Object[] { username, password });

  sessid = res.getString("sessid");
  session_name = res.getString("session_name");
 }


First, we check if already authenticated, and if so, perform logout. 
Next, create XmlRpcClient. First argument is endpoint url, second - whether to stream messages. Message streaming not compatible with all server-side implementations, so set it to false.
The actual remote method invocation performed by xmlrpc.invoke. First argument is a method name, and second is array of arguments - if this case, it's username and password. invoke method can have many return types. In the case of user.login method, it will be XmlRpcStruct structure containing info about session and logged in user (email, uid, etc).

Drupal session defined by the two members of returned structure: sessid and session_name. Save them to be used in all subsequent remote calls, to identify our session.
As we defined login method, we also must define logout.


        /**
  * Close session.
  * @throws MalformedURLException
  * @throws XmlRpcException
  * @throws XmlRpcFault
  */
 public void logout() throws MalformedURLException, XmlRpcException, XmlRpcFault {
  if (!isAuthenticated())
   return ;
     
  try {
   // create xml-rpc client
   XmlRpcClient xmlrpc = new XmlRpcClient(XMLRPC, false);
   // set session cookie  
   xmlrpc.setRequestProperty("Cookie", getSessionCookieString());

   // remote call
   xmlrpc.invoke("user.logout", new Object[] { });
  }
  catch (Exception ex) {
   ex.printStackTrace();
  }
  
  sessid = null;
  session_name = null;
 }



First, check if we are authenticated. If no, just return. Next, create XmlRpcClient. Please, take a look at setRequestProperty method call. This method sets additional http header that will be sent with XML-RPC request. We set Cookie identifying Drupal session.

We created login and logout methods. And now we make postPage method. 

 /**
  * Posts page.
  * @param title page title
  * @param body page body
  * @return page node identifier
  * @throws IOException
  * @throws XmlRpcException
  * @throws XmlRpcFault
  */
 @SuppressWarnings("unchecked")
 public int postPage(String title, String body) throws IOException, XmlRpcException, XmlRpcFault {
  // check if user is authenticated
  if (!isAuthenticated()) {
   throw new IllegalStateException("Session is not open.");
  }

  // create xml-rpc client
  XmlRpcClient xmlrpc = new XmlRpcClient(XMLRPC, false);
  // set session cookie
  xmlrpc.setRequestProperty("Cookie", getSessionCookieString());
  
  // set page values
  XmlRpcStruct params = new XmlRpcStruct();
  params.put("type", "page");
  params.put("title", title);
  params.put("body", body);
  
  // remote call
  XmlRpcStruct res = (XmlRpcStruct) xmlrpc.invoke("node.create", new Object[] { params });

  // get page nid and return it
  return Integer.parseInt(res.get("nid").toString());
 }


And again, first check if user is authenticated. Then create XML-RPC client and setup session cookie. Next, create structure defining node params. type = "page", title and body. And perform the remote call. Remote call returns structure, where we need "nid" member - it's page node identifier, that will be used, for example, to remove page or update it.
Create deletePage method.


 /**
  * Delete page.
  * @param nid page node identifier
  * @throws IOException
  * @throws XmlRpcException
  * @throws XmlRpcFault
  */
 public boolean deletePage(int nid) throws IOException, XmlRpcException, XmlRpcFault {
  // check if user is authenticated
  if (!isAuthenticated()) {
   throw new IllegalStateException("Session is not open.");
  }

  // create xml-rpc client
  XmlRpcClient xmlrpc = new XmlRpcClient(XMLRPC, false);
  // set session cookie
  xmlrpc.setRequestProperty("Cookie", getSessionCookieString());

  // page params: nid
  XmlRpcStruct params = new XmlRpcStruct();
  params.put("nid", ""+nid);

  // node.delete return boolean indicating, whether node is removed or not
  return (Boolean) xmlrpc.invoke("node.delete", new Object[] { params });
 }



Method start is the same, as previous. Remote method params is structure, that contains only page nid. And return type of remote method is boolean, indicating, whether page is deleted or not. 
Now, create uploadPhoto method, for photo uploads.


        /**
  * Perform photo upload.
  * @param photoParams photo params (such as file, file name, page node identifier)
  * @param listener listener to receive send progress notifications
  * @throws IOException if an error is occurred
  */
 public void uploadPhoto(PhotoParams params, HttpProgressListener listener) {
  // check if user is authenticated
  if (!isAuthenticated()) {
   throw new IllegalStateException("Session is not open.");
  }
  
  HttpMultipartClient httpClient = new HttpMultipartClient(PHOTOUPLOAD, listener);
  httpClient.addHeader("Cookie", getSessionCookieString());
  
  httpClient.addField("form_id", "photoupload_upload_file");
  httpClient.addField("op", "Upload");
  
  httpClient.addField("nid", ""+params.nid);
  httpClient.addFile("files[image]", "image/jpeg", params.fileName, params.file);

  httpClient.post();
 }



It's all done with DrupalConnect class.

Now, create login activity. It will contain two EditText controls for login and password, remember check box, used to remember last succesful login data, login and cancel buttons.






















In LoginActivity class, first define UI control variables.


 // UI controls
 private EditText editUsername;
 private EditText editPassword;
 private CheckBox checkRemember;
 private Button buttonOk;
 private Button buttonCancel;



In onCreate method, get UI controls, setup button handlers.


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        // get UI controls
        editUsername = (EditText) findViewById(R.id.editUsername);
        editPassword = (EditText) findViewById(R.id.editPassword);
        checkRemember = (CheckBox) findViewById(R.id.checkRemember);

        buttonOk = (Button) findViewById(R.id.buttonOk);
        buttonOk.setOnClickListener(new OnClickListener() {
  @Override
  public void onClick(View v) {
   onButtonOk();
  }
 });

        buttonCancel = (Button) findViewById(R.id.buttonCancel);
        buttonCancel.setOnClickListener(new OnClickListener() {
  @Override
  public void onClick(View v) {
   onButtonCancel();
  }
 });

        PrefsHelper.setup(this, "AndroidDrupal");
        loadAuthData();
    }



Now, define "Ok" button handler. 


/**
 * Called to button "ok" click.
 */
public void onButtonOk() {
 final String username = editUsername.getText().toString();
 final String password = editPassword.getText().toString();
  
 // string buffer for errors
 StringBuilder errors = null;
  
 // check user name
 if (username.trim().length() == 0 || !username.matches("\\w+")) {
  if (errors == null)
   errors = new StringBuilder();
  errors.append("Invalid user name\n");
 }
  
 // check password
 if (password.trim().length() == 0 || !password.matches("\\w+")) {
  if (errors == null)
   errors = new StringBuilder();
  errors.append("Invalid password\n");
 } 
  
 // 
 if (errors != null) {
  GUIHelper.showError(this, errors.toString());
  return ;
 }
 
 // show login progress dialog
 final ProgressDialog dlg = ProgressDialog.show(this, "Logging in", "Logging in. Please wait.", true, true);
  
 // create an async task to perform login
 (new AsyncTask() {
  @Override
  protected String doInBackground(Void... params) {
   try {
    DrupalConnect.getInstance().login(username, password);
    return "OK";
   }
   catch (Exception ex) {
    return ex.getMessage();
   }
  }

  @Override
  protected void onPostExecute(String result) {
   dlg.dismiss();
   
   if (result.equals("OK")) {
    saveAuthData();
     
    // switch to MainActivity
    Intent intent = new Intent(LoginActivity.this, PostActivity.class);
    startActivity(intent);
    finish();
   }
   else {
    GUIHelper.showError(LoginActivity.this, "Login is failed. " + result);
   }
  }
 }).execute();
    }


First, it checks username and password, and if it's error - show error message and return. Next, create ProgressDialog, showing "Logging in. Please wait.". And then, create AsyncTask to perform login. In it's doInBackground we call our singleton DrupalConnect class method login. In onPostExecute method (called after doInBackground is complete), we check the result. If all is ok, switch to the next activity. If there is an error, show the message describing what's wrong.

We created our first activity for user login. And how create the main application activity, call it PostActivity. It will be simple activity with two EditText controls for title and body, and three buttons: make photo, post and exit. User will enter title and body, and if he wants to also attach a photo, he clicks "Make photo" button. When all done, he clicks "Post" button, to make post and upload photo (if he made it).























Define UI control varibles.


       // UI controls
       private EditText editTitle;
       private EditText editBody;
       private Button buttonMakePhoto;
       private Button buttonPost;
       private Button buttonExit;


Get UI controls and setup button handlers in onCreate method.


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_post);

      

        // get UI controls
        editTitle = (EditText) findViewById(R.id.editTitle);
        editBody = (EditText) findViewById(R.id.editBody);
        buttonMakePhoto = (Button) findViewById(R.id.buttonMakePhoto);
        buttonMakePhoto.setOnClickListener(new OnClickListener() {
  @Override
  public void onClick(View v) {
   makePhoto();
  }
 });
     

        buttonPost = (Button) findViewById(R.id.buttonPost);
        buttonPost.setOnClickListener(new OnClickListener() {
  @Override
  public void onClick(View v) {
   postPage();
  }
 });


        buttonExit = (Button) findViewById(R.id.buttonExit);
        buttonExit.setOnClickListener(new OnClickListener() {
  @Override
  public void onClick(View v) {
   exit();
  }
 });
    }



Create "Make photo" button handler.


@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_post);
        
        // get UI controls
        editTitle = (EditText) findViewById(R.id.editTitle);
        editBody = (EditText) findViewById(R.id.editBody);
        buttonMakePhoto = (Button) findViewById(R.id.buttonMakePhoto);
        buttonMakePhoto.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View v) {
    makePhoto();
   }
  });
        
        buttonPost = (Button) findViewById(R.id.buttonPost);
        buttonPost.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View v) {
    postPage();
   }
  });
        
        buttonExit = (Button) findViewById(R.id.buttonExit);
        buttonExit.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View v) {
    exit();
   }
  });
    }



It calls standard Android's image capture activity to capture image from camera. Now, we need to override onActivityResult in PostActivity to get capture result. It will be called after camera activity is closed.


@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_post);
        
        // get UI controls
        editTitle = (EditText) findViewById(R.id.editTitle);
        editBody = (EditText) findViewById(R.id.editBody);
        buttonMakePhoto = (Button) findViewById(R.id.buttonMakePhoto);
        buttonMakePhoto.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View v) {
    makePhoto();
   }
  });
        
        buttonPost = (Button) findViewById(R.id.buttonPost);
        buttonPost.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View v) {
    postPage();
   }
  });
        
        buttonExit = (Button) findViewById(R.id.buttonExit);
        buttonExit.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View v) {
    exit();
   }
  });
    }



Next, implement "Post" button handler.  


    private void postPage() {
     // check if posting is in progress
     if (isPostInProgress) {
      return ;
     }

     isPostInProgress = true;
    
     // get page title and body
     final String title = editTitle.getText().toString();
     final String body = editBody.getText().toString();

     // show progress dialog
     final ProgressDialog progressDialog = ProgressDialog.show(this, "Posting", "Posting. Please, wait.", true, false);
          

     // start async task for posting to Drupal
     (new AsyncTask() {
      Exception e;

  @Override
  protected Boolean doInBackground(Void... params) {
   try {
    nid = DrupalConnect.getInstance().postPage(title, body);
    return true;
   }
   catch (Exception e) {
    this.e = e;
    return false;
   }
  }

  @Override
  protected void onPostExecute(Boolean result) {
   super.onPostExecute(result);

   progressDialog.dismiss();

   if (result) {
    // if user made photo - upload it
    // if not - report that post succeeded
    if (CameraHelper.photoFile != null) {
     uploadPhoto();
    }
    else {
     GUIHelper.showMessage(PostActivity.this, "Post succeeded.", "Message");
     isPostInProgress = false;
    }
   }
   else {
    GUIHelper.showError(PostActivity.this, "Post is failed. "+e.getMessage());
    isPostInProgress = false;
   }
  }
     }).execute();
 }



It gets page title and body from EditText controls, then shows progress dialog, with "Posting. Please, wait." message. And then, creates AsyncTask for performing call to Drupal. Please, look at onPostExecute method of AsyncTask, it hides progress dialog and checks for post result. If post if succeeded, but there is also photo made by an user, it calls uploadPhoto method. Code for this method is given below.  


    private void uploadPhoto() {
     // create progress dialog
     final ProgressDialog progressDialog = new ProgressDialog(this);
     progressDialog.setTitle("Uploading photo");
     progressDialog.setMessage("Uploading photo");
     progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
     progressDialog.setIndeterminate(false);
     progressDialog.setCancelable(false);
     progressDialog.show();
     
     // create async task to upload photo
     (new AsyncTask() {
      Exception e;

  @Override
  protected Boolean doInBackground(Void... params) {
          PhotoParams photoParams = new PhotoParams();
   photoParams.nid = nid;
   photoParams.file = CameraHelper.photoFile;
   photoParams.fileName = CameraHelper.photoFile.getName();

   DrupalConnect.getInstance().uploadPhoto(photoParams, new HttpProgressListener() {
    @Override
    public void sendStarted(int total) {
     publishProgress(0, total);
    }

    @Override
    public void sendProgress(int uploaded, int total){
     publishProgress(uploaded, total);
    }

    @Override
    public void sendError(Exception ex) {
     e = ex;
    }
    
    @Override
    public void sendDone() {
    }
   });
   return null;
  }

  @Override
  protected void onPostExecute(Boolean result) {
   super.onPostExecute(result);

   progressDialog.dismiss();

   // check if exception is occurred during upload
   if (e == null) {
    // delete photo file
    // it must be deleted when upload is succeeded

    deletePhoto();

    GUIHelper.showError(PostActivity.this, "Post and upload are succeeded.");
   }
   else {
    GUIHelper.showError(PostActivity.this, "Upload is failed. "+e.getMessage());
    // delete page
    deletePage();
   }
   isPostInProgress = false;
  }

  @Override
  protected void onProgressUpdate(Integer... values) {
   super.onProgressUpdate(values);

          int sent = values[0];
   int total = values[1];

   // if this is the first call, set progress dialog max value
   if (sent == 0) {
    progressDialog.setMax(total);
   }
   progressDialog.setProgress(values[0]);
  }
     }).execute();
    }



First, it show progress dialog with "Uploading photo" message, and starts AsyncTask for the actual upload. It create PhotoParams structure with photo file, file name, and page node identifier (the page to which a photo be attached). Calls DrupalConnect's uploadPhoto method, passing it PhotoParams and http progress listener arguments. Listener reports http upload progress. It's sendProgress method calls AsyncTask's publishProgress to update progress dialog's progress. The actual progress dialog update is performed in onProgressUpdate method of our async task. When upload is done, onPostExecute hides progress dialog, and checks for result. If all is ok, it show  "Post and upload are succeeded." message. But, if there is an error, we must delete page as it's already created on Drupal's side, but our transaction is failed. To perform this, call method deletePage of PostActivity. The code for deletePage is given below.  


    private void deletePage() {
     // check if there is page
     if (nid == 0) {
      return ;
     }
     (new AsyncTask() {
   @Override
   protected Boolean doInBackground(Void... params) {
    try {
     DrupalConnect.getInstance().deletePage(nid);
     nid = 0;
    }
    catch (Exception e) {
     e.printStackTrace();
    }
    return null;
   }
  }).execute();
    }



And again, we create the AsyncTask to perform Drupal remote call. We pass page nid (stored from last postPage call) to DrupalConnect's deletePage method. And finally, we need to implement handlers for "Exit" button. It will perform logout and close PostActivity.


    private void exit() {
     (new AsyncTask() {
   @Override
   protected Boolean doInBackground(Void... params) {
    try {
     DrupalConnect.getInstance().logout();
    }
    catch (Exception e) {
     e.printStackTrace();
    }
    return null;
   }

   @Override
   protected void onPostExecute(Boolean result) {
    super.onPostExecute(result);

    PostActivity.this.finish();
   }
     }).execute();
    }



And again, conversation with DrupalConnect is made in AsyncTask.

Summary.
You can see, how easy it's to connect Drupal and Android using XML-RPC. I shown only how to create and delete pages and upload photos, it's also possible to manipulate other Drupal objects (comments, files, users, etc), register new users and more. This approach is applicable to other web CMSes, that supports XML-RPC, and to other mobile platforms.

 Please, send me your comments and suggestions.

About author.
I'm software developer with more than 12 years of programming experience. My primary interests are: systems architecture and design, web, network, mobile programming, Unix systems (Linux/FreeBSD), system programming and reverse engineering. I like all new cutting edge technologies, and was very excited by Android platform. Currently, i'm working on several projects, related to web, mobile and desktop integration.

You can download source code for this article here: https://github.com/BItGriff/AndroidDrupal/archive/master.zip

Drupal with Android integration: make posts and upload photos. Part I - Drupal.


Drupal is powerful and popular web CMS, with many useful features and hundreds of different modules that can extend core functionality. Android is powerful, popular and very fast growing mobile platform. It's leading smartphone and tablets platform. In this article i'll show you how to integrate Drupal with Android powered mobile devices, to authorize on site and to perform some actions. 

If you are interested in development mobile applications, that can backup or store files to a website, upload photos, make posts, administer site, or perform other remote actions - this article is for you. Here you can find guidelines and principles description of how mobile and web integration can be made. 

Example in this article will be a practical task: post pages and upload photos from Android device to Drupal website. In example, i show how to use it with Drupal 6, but all is also applicable to Drupal 7 (only minor modifications of Drupal module are required). This article show how to integrate Drupal with Android OS. If you need another platform, it can be useful for you too, as it describes principles and issues of communication. 

How it will be done?

Our task is to perform actions on Drupal website, and we need a method to communicate with it.
We will use XML-RPC protocol for communication between Android and Drupal. It's lightweight remote procedure call protocol, and you don't need any additional knowledge, as with SOAP. Also, it's not require  interface definitions, as with SOAP.

To use it, we need services module (http://drupal.org/project/services).

1. Services module

This module allows to create services within Drupal modules. A service exposes a set of methods, that can be called remotely by third party applications. Each method has defined name, parameters and return type. In our case they will be called from Android-powered mobile device.


Services module supports many communication protocols. REST, XMLRPC, JSON, JSON-RPC, SOAP, AMF are supported. 


In this example we will use XMLRPC.

2. Configuring services module

Services module is not included in Drupal core. So, you need to download it from http://drupal.org/project/services. Unpack it into your Drupal website modules subdirectory (sites/all/modules), and enable it in modules administration page.

If all is fine, you will see it in modules admin page.




Next, you need to define the service endpoint. Open the services admin page of your site, and add "android" endpoint:



Then click "Add" link:


And then, enter endpoint's parameters:  machine-readable name, server type and endpoint path. 
Session authentication must be checked. If it's unchecked, all rpc calls will be executed as anonymous user.
Example is shown below:


Next, open resources tab. And select resouces, that will be available through this endpoint.
Resources are nodes, comments, files, users, etc. You need to enable user login and logout, and node create and delete. You can enable more resouces. For example: to allow users registration from mobile, to add/remove comments and etc. But, it's good practice to allow only that's really needed. (For example, if you use captcha for users registration to block bots, enabling user.register method through services will allow them to avoid captcha).

Now, services module is configured.

3. Drupal module

The purpose of our module is to get uploads from Android devices and attach them to pages. 
We call our module "photoupload". Create subdirectory photoupload in sites/all/modules directory of Drupal. Now we create photoupload.infoSet up mandatory fields:


name = Photo upload
description = Module that manages photo uploads from Android devices

core = 6.x
version = "6.x-1.0"
project = "photoupload"

Now, create module file photoupload.module

Begin with php opening tag.
 <?php 
First, our module will define "upload photos" permission. Only users with this permission are allowed to upload photos.


function photoupload_perm() 
{
 return array('upload photos');
}


Next, we need to define menu item for uploads. Call it "photoupload". Menu item is needed to handle urls, so it will be hidden (callback). Upload is made through form, so define page callback to be drupal_get_form, page arguments as array containing name of function to create form. Access arguments is array containing permission, required for access to menu item. In this case, it will an array with "upload photos" element.


function photoupload_menu() 

{
 $items = array();

 // mobile photo upload
 $items['photoupload'] = array(
  'title' => 'Mobile photo upload',
  'description' => 'Upload photo',
  // setup page callback to return form
  'page callback' => 'drupal_get_form',
  // form function
  'page arguments' => array('photoupload_upload_file'),
  // users with 'upload photos' permission is only allowed
  'access arguments' => array('upload photos'),
  // hide it from menus, available to users
  'type' => MENU_CALLBACK, 
 );
 return $items;
}


Now, we create form for upload. It will contain image file and page node identifier elements.

function photoupload_upload_file($form_state)

{
 // prepare the file upload form
 // set multipart/form-data encoding type
 $form = array('#attributes' => array('enctype' => 'multipart/form-data'));

 // image file selector
 $form['image'] = array(
     '#type' => 'file',
     '#title' => 'Upload photo',
     '#description' => t('Pick a image file to upload')
 );

 // page node identifier. The page to image be attached
 $form['nid'] = array(
  '#type' => 'textfield',
  '#title' => 'Page nid',
 );

 $form['#token'] = FALSE;
 
 // submit button
 $form['submit'] = array('#type' => 'submit', '#value' => 'Upload');
 return $form;
}


Create form submission handler.

function photoupload_upload_file_submit($form, &$form_state)
{
 $dir = file_directory_path();
 
 // unlike form submissions, multipart form submissions are not in
 // $form_state, but rather in $FILES, which requires more checking
 //

 if (!isset($_FILES) || empty($_FILES) || $_FILES['files']['size']['image'] == 0) {
      drupal_set_message("Your file doesn't appear to be here.");
  return ;
 }

 $name = $_FILES['files']['name']['image'];
 $size = $_FILES['files']['size']['image'];
 $type = $_FILES['files']['type']['image'];

 // get page nid
 $nid = $form_state['values']['nid'];

 // this is the actual place where we store the file
 $file = file_save_upload('image', array() , $dir);
 if ($file) {
  $filename = $dir."/".$file->filename;

  $import = _photoupload_attach_photo($filename, $nid);
  drupal_set_message($import); 
 }
 else {
     drupal_set_message("Something went wrong saving your file.");
 }
}


Add function to attach photo to page.

function _photoupload_attach_photo($file, $nid)
{
 global $user;

 // load node by nid
 $node = node_load($nid);

 // create file object
 $name = basename($file);

 $file_obj = new stdClass();
 $file_obj->filename = $name;
 $file_obj->filepath = $file;
 $file_obj->filemime = file_get_mimetype($name);
 $file_obj->filesize = $stats['size'];
 $file_obj->filesource = $file;
 $file_obj->status = FILE_STATUS_TEMPORARY;
 $file_obj->timestamp = time();
 $file_obj->list = 1;
 $file_obj->new = true;

 // write it
 drupal_write_record('files', $file_obj);

 file_set_status($file_obj, 1);
 // attach file to node

 $node->files[$file_obj->fid] = $file_obj;

 // and finally, save node
 node_save($node);
 return "OK";
}


And php closing tag.

?>

We finished with Drupal's module code.

Now, you need enable it in modules administration page.











It's all done with Drupal part. In the next part of article i'll describe how to implement Android application.
Drupal with Android integration: make posts and upload photos. Part II - Android.

вторник, 22 января 2013 г.

Detecting incoming and outgoing phone calls on Android.


In this article i'll show you how to detect incoming and outgoing phone calls on Android platform.
It can be useful, if you need to perform some action when call is made. For example, to block it, to log it, or send call info to a server.

Article gives the step-by-step instructions, how to create the simple demo app, that will detect incoming and outgoing phone calls, show "toast" message with phone number. You can extends and use this code for your own needs.

Incoming calls.

For incoming calls we need to use TelephonyManager class, and it's method listen, to register a listener, that will receive call state, data connection, network, sim and other events, related to telephony. We are interested only in the call state notifications.

This requires android.permission.READ_PHONE_STATE permission.

So, create our listener class, derived from PhoneStateListener, and override onCallStateChanged method, as follows:
 
 /**
  * Listener to detect incoming calls. 
  */
 private class CallStateListener extends PhoneStateListener {
  @Override
  public void onCallStateChanged(int state, String incomingNumber) {
      switch (state) {
          case TelephonyManager.CALL_STATE_RINGING:
          // called when someone is ringing to this phone
    
          Toast.makeText(ctx, 
                  "Incoming: "+incomingNumber, 
                  Toast.LENGTH_LONG).show();
          break;
      }
  }
 }



Now i'll explain onCallStateChanged method.

First argument - state is call state, it can be CALL_STATE_RINGING, CALL_STATE_OFFHOOK or CALL_STATE_IDLE. Ringing is state when someone is calling us, offhook is when there is active or on hold call,  and idle - is when nobody is calling us and there is no active call. We are interested in ringing state.

Second argument - incomingNumber, it's number who is calling us.

As shown in code above, listener show the "toast" message with phone number, when incoming call is ringing.

Next, get instance of TelephonyManager and register listener:

  tm = (TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE);
  tm.listen(callStateListener, PhoneStateListener.LISTEN_CALL_STATE);

When app is no longer need to receive notifications, it must unregister a listener by call:
  tm.listen(callStateListener, PhoneStateListener.LISTEN_NONE);

Outgoing calls.

For outgoing calls, system sends broadcast action android.intent.action.NEW_OUTGOING_CALL. We need to make broadcast receiver, that will receive intent with this action.

To receive this broadcast the android.permission.PROCESS_OUTGOING_CALLS permission is required.

Create broadcast receiver class:
 /**
  * Broadcast receiver to detect the outgoing calls.
  */
 public class OutgoingReceiver extends BroadcastReceiver {
     public OutgoingReceiver() {
     }

     @Override
     public void onReceive(Context context, Intent intent) {
         String number = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
         
         Toast.makeText(ctx, 
           "Outgoing: "+number, 
           Toast.LENGTH_LONG).show();
     }
  
 }

As with incoming calls, this code will show "toast" message, with phone number, when there is outgoing call.

Register the broadcast receiver:
  IntentFilter intentFilter = new IntentFilter(Intent.ACTION_NEW_OUTGOING_CALL);
  ctx.registerReceiver(outgoingReceiver, intentFilter);

We finished with the calls detection code. And how need to create an activity, that will enable/disable calls detection. It will be activity with simple UI, with textview showing detection status, button to enable/disable detection, and exit button.

But, here is the another issue - when our activity losses focus, calls detection is disabled. To prevent this, we have to make service, that will run  that will enable detection on start, and disable on stop.

Create the service:

/**
 * Call detect service. 
 * This service is needed, because MainActivity can lost it's focus,
 * and calls will not be detected.
 * 
 * @author Moskvichev Andrey V.
 *
 */
public class CallDetectService extends Service {
    private CallHelper callHelper;
 
    public CallDetectService() {
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        callHelper = new CallHelper(this);
  
        int res = super.onStartCommand(intent, flags, startId);
        callHelper.start();
        return res;
    }
 
    @Override
    public void onDestroy() {
        super.onDestroy();
  
        callHelper.stop();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // not supporting binding
        return null;
   }
}


Create the activity.

Get UI elements and set onclick button handlers:
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        textViewDetectState = (TextView) findViewById(R.id.textViewDetectState);
        
        buttonToggleDetect = (Button) findViewById(R.id.buttonDetectToggle);
        buttonToggleDetect.setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
                 setDetectEnabled(!detectEnabled);
             }
        });
        
        buttonExit = (Button) findViewById(R.id.buttonExit);
        buttonExit.setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
                 setDetectEnabled(false);
                 MainActivity.this.finish();
             }
        });
    }


Create setDetectEnabled method, that will toggle calls detection:
    private void setDetectEnabled(boolean enable) {
        detectEnabled = enable;
     
        Intent intent = new Intent(this, CallDetectService.class);
        if (enable) {
              // start detect service 
              startService(intent);
            
              buttonToggleDetect.setText("Turn off");
              textViewDetectState.setText("Detecting");
        }
        else {
              // stop detect service
              stopService(intent);
      
              buttonToggleDetect.setText("Turn on");
              textViewDetectState.setText("Not detecting");
        }
    }


AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.bitgriff.androidcalls"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />
    
   <!--
         Permissions required for calls detection.
        -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
 <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
    
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/title_activity_main" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name=".CallDetectService"
            android:enabled="true"
            android:exported="false" >
        </service>
    </application>

</manifest>

Summary.
I shown you how to detect incoming and outgoing phone calls on Android. It a quite simple. Please, send me your comments and suggestions.

In the next articles, i'll show you other aspects of Android platform programming (networking, threads, multimedia processing, integration with websites and webservices, etc).

You can download source code for this article at: AndroidCalls.zip