​Projects-On-The-Go-android

Projects-On-The-Go is a sample app used in the guide. It is a project manager/bug-tracker with various features.

Overview

Projects-On-The-Go is a feature-rich, robust project management app that helps you manage your projects and keep track of the progress. It allows members of the projects to collaborate with each other, update status, and stay updated with the latest progress.

Let’s have a look at the major components that form the core of Projects-On-The-Go app.

Projects: In Project-On-The-Go app, you can create projects, and add users who would be working on the project as members.  

Tasks: The app allows you to break down your project into several tasks. You can assign these tasks to the members of the project and track the status from one central place.

Milestones: Milestones are significant phases of your project that needs to be achieved. These milestones could contain a single task or a number of tasks. You can define a milestone and allocate it to any member easily with Projects-On-The-Go app.

Bugs: Members of a project can submit, track, and fix bugs using this app. It also allows all the members to check and update the status of the bugs.

With the help of Roles offered by Built.io Backend, you can create a hierarchy of users, with each level having different set of permissions. The three roles we would be creating in Projects-On-The-Go would be:

Admin: Admin is a user who can create a project and assign moderators and members to it.

Moderators: Moderators have project level access. Moderators can create milestones, tasks and assign members to them.

Members: Members are users working on the project. Members are usually assigned to various tasks and bugs.  

Let us now look at the architecture of the app.

Architecture

We will create the following classes (and fields within) in our project management app:

  • Project
    The Project class has ‘Name’, ‘Description’, ‘Members’, and ‘Moderators’ fields. The data type for the first two fields is String. The ‘Members’ and ‘Moderators’ fields are reference fields that point to objects of the Application User Role class. Using these two fields, you can add users as members and/or moderators for the given project.
  • Fields Type
    Name String
    Description
    String
    Members Reference to Application User Role Class
    Moderators References to Application User Role Class
  • Tasks
    The Tasks class has ‘Name’, ‘Description’, ‘Steps’, ‘Assignees’, and ‘Project’ fields. The data type for the first two fields is String. The next field ‘Steps’ is a group field. A group field, as the name suggests, is a group of fields. The sub-fields within the Steps field are Name (string), Description (string) and Complete (boolean) fields. The ‘Assignees’ and the ‘Project’ fields are reference fields. Assignees point to the objects of Application User class. Here, you can add the name of the members who you wish to assign the task. Project refers to the Project class. Select the project under which you wish to create the task.
  • Field Type
    Name String
    Description String
    Steps Group
    Assignees References to Application User Class     
    Project Reference to Project Class
  • Bugs
    Bugs class has a few more fields, apart from those in the Tasks class. Since bugs can have attachments, there is a separate field for it which accepts any file type. Then there are ‘Reproducible’, ‘Severity’, and ‘Status’ fields that define the nature of the bug. These fields have a regular expression constraint. Entering any other value will throw an error. You also need to specify a due date for the bug.
  • Field Type
    Name String
    Description String
    Attachments Group     
    Reproducible String
    Due Date ISODate                                                          
    Severity String
    Status String
    Project Reference to Project Class
    Assignees Reference to Application User Class
  • Milestones
    The Milestones class has ‘Name’, ‘Description’, ‘Assignees’, and ‘Start Date’, ‘End Date’, and ‘Project’ fields. Since you can assign milestones to the members of a project, ‘Assignees’ is a reference field, pointing to objects of the Application User class.
  • Field Type
    Name String
    Description String
    Assignees References to Application User Class 
    Start Date ISODate
    End Date ISODate
    Project Reference to Project Class
  • Comments
    The Comments class has ‘Content’, ‘For Bug’, ‘For Milestone’, ‘For Task’, and ‘Project’ fields. While the ‘Content’ field accepts String data type, the other four fields are reference fields, pointing to the corresponding objects of Bugs, Tasks, Milestones, or Project class.
  • Field Type
    Content String
    For Bug String
    For Milestone References to Milestones Class      
    For Task References to Tasks Class                          
    Project Reference to Project Class

Logging in a user

A user can log in to Projects-On-The-Go app using his credentials or via Federated Login. Federated Login is a mechanism, through which you can allow users to login using their Google, Facebook, or Twitter account.

You can sign up the users of this app or log them in using the BuiltUISignUpController and BuiltUILoginController classes. These are predefined classes in application provided by Built.io Backend, and can be found in the SDK. Signing up and logging in users become easy by extending these two classes. The following code presents a login as well as a sign up screen with Login and Sign Up options for the user.

//For Login:
//'blt3b011c0e38ed1d82' is application api key
BuiltApplication builtApplication = Built.application(context, "blt3b011c0e38ed1d82");
// add this method to login to specific application
setApplication(builtApplication);
@Override
public void loginSuccess(BuiltUser user) {
  //After successful login, this method gets called.
  //user can get google access token, twitter access token and twitter access token secret.
  String googleAccessToken = user.getGoogleAccessToken();
  String twitterAccessToken = user.getTwitterAccessToken();
  String twitterAccessTokenSecret = user.getTwitterAccessTokenSecret();
}
@Override
public void loginError(BuiltError error) {
  //If error occurs, this method gets called.
}
//For twitter login:
//User need to set twitter consumer key and consumer secret.
setUpTwitter("Your_Consumer_Key", "Your_Consumer_Secret");
//User need to set signUp Intent to open signUp Activity
Intent signUpIntent = new Intent(context,SignUpActivity.class);
setSignUpIntent(signUpIntent);
//For SignUp:
// 'blt3b011c0e38ed1d82' is application api key
BuiltApplication builtApplication = Built.application(context, "blt3b011c0e38ed1d82");
// add this method to signUp to specific application
setApplication(builtApplication);
@Override
public void signUpSuccess(BuiltUser user) {
//After successful SignUp, this method gets called.
}
@Override
public void signUpError(BuiltError error) {
  //If error occures this method gets called.
}

Loading Projects

After you are done with signing up users, it is time to load all the projects in a list view. This can be done with the help of BuiltUIListViewController class found in the SDK.

BuiltUIListViewController constructor takes context and class UID as parameter, and then a query is executed on the class for fetching the objects. The query can be modified according to your needs to retrieve objects. You can also set limit using builtListViewObject.setLimit() function of BuiltUIListViewController to specify the number of objects to fetch.  

The sample code for fetching projects can be viewed in UIProjectListScreen class:

//Intialize BuiltUIListViewController instance.
//'blt3b011c0e38ed1d82' is application api key
BuiltApplication builtApplication = Built.application(context, "blt3b011c0e38ed1d82");
// add this method to fetch objects specific application and class.
BuiltUIListViewController listView = new BuiltUIListViewController(context, builtApplication, "project");
//To enable pull to refresh.
listView.setPullToRefresh(true);
//Set limit for object to load while fetch call.
listView.setLimit(10);
//Add function to built query instance of BuiltUIListViewController class to get extra information.
listView.builtQueryInstance.includeOwner();
//Make a call for load project list.
listView.loadData(new BuiltListViewResultCallBack() {
  @Override
  public View getView(int position, View convertView,ViewGroup parent, BuiltObject object){
    //After successful call this block gets called.
    //return view which is set for row in list view.
    return convertView;
  }
  @Override
  public void onError(BuiltError error) {
    //Error block
  }
  @Override
  public void onAlways() {
    //Always called after onSuccess and onError.
  }
  @Override
  public int getViewTypeCount() {
      return 1;
  }
  @Override
  public int getItemViewType(int position) {
     return position;
  }
});
//Set login layout from Built.io Backend 
//For using BuiltUIListViewController user need to set layout programmatically.
setContentView(builtListViewObject.getLayout());

To have more control, you can manually load projects by using BuiltQuery class, and passing the raw data to ListView.

//Query to fetch project.
//'blt3b011c0e38ed1d82' is application api key
BuiltApplication builtApplication = Built.application(context, "blt3b011c0e38ed1d82");
BuiltQuery  projectQuery  = builtApplication.classWithUid("project").query();
projectQuery.includeOwner().limit(10);
//Execute query to fetch current bugs object.
projectQuery.execInBackground(new QueryResultsCallBack() {
   @Override
   public void onCompletion(ResponseType responseType, QueryResult builtqueryresult, BuiltError builtError){
        if(builtError == null){
            //Success block.
            List<BuiltObject> builtObjects = queryResult.getResultObjects();
            //Make a custom adapter and pass the builtObjects list and model it as per UI.
            customAdapter = new CustomAdapter(context, R.layout.view_row, builtObjects);
            //Setting adapter to ListView instance.
            listViewObject.setAdapter(wallCustomAdapter);
        }else{
            //Error block.
        }       
    }
});

Admin rights

Apart from all other rights enjoyed by Members and Moderators, the Admin has the right to create new projects. Let us first start with creating an admin role and assigning it to a user in Built.io Backend application.

To do this, in your Projects-On-The-Go app in the management console, go to Classes -> System Classes -> Application User Role, click on 'New Object' and enter 'admin' in the 'Name' field. Add a user to assign him/her to the admin role.

To determine whether the user is an admin, we will fetch roles and check within the admin role if the assigned user's uid is present in the role. The following code will perform this action.

//Query to fetch users who are present in admin role.
// 'blt3b011c0e38ed1d82' is application api key
BuiltApplication builtApplication = Built.application(context, "blt3b011c0e38ed1d82");
BuiltQuery  roleQuery  = builtApplication.getRolesQuery();
roleQuery.where("name", "admin");
roleQuery.execInBackground(new QueryResultsCallBack() {
   @Override
   public void onCompletion(ResponseType responseType, QueryResult builtqueryresult, BuiltError builtError){
      if(builtError == null){
             boolean isAdmin = false;
             //Get logged in user.
             BuiltUser user = BuiltUser.getSession();
             //Check the current user is admin or not.
             List<BuiltRole> roles = builtQueryResult.getRoles();
             for (int i = 0; i < roles.size(); i++) {
                 JSONArray array = roles.get(i).getJSONArray("users");
                     for (int j = 0; j < array.length(); j++) {
                               if(user.getUserUid().equalsIgnoreCase(array.optString(i))){
                         isAdmin = true;
                         }
                     }
             }
        if(isAdmin) {
           //User is admin
        }
      }else{
        //Error block.
      }
   }
});

An admin is able to create projects. When creating a project, we are going to decide the moderators and members of the project.

  • The moderator role will have permissions to create, read, update and delete objects within the project. This implies that a moderator can create, read, update and delete milestones, tasks, bugs, and assign members to them.
  • The member role will only have read permission. Only if the member is assigned to an object will he be able to update that object.

Let us take a look at how to do this. First, we will create a moderator role and a member role. We will name our role as projectname_moderators. So if the project name is SampleApp, the moderator role will be named SampleApp_moderators. This is just a convention we will be following. Similarly, the member role will be named SampleApp_members.

The following code is for creating the moderator role. For the users key we will pass in an array of user ids associated with the desired moderators.

//Set a members role.
//'blt3b011c0e38ed1d82' is application api key
BuiltApplication builtApplication = Built.application(context, "blt3b011c0e38ed1d82");
BuiltRole roleObjectMembers = builtApplication.roleWithName("SampleApp_members");
roleObjectMember.set("users", membersUid);

Similarly for members,

//Set a members role.
//'blt3b011c0e38ed1d82' is application api key
BuiltApplication builtApplication = Built.application(context, "blt3b011c0e38ed1d82");
BuiltRole roleObjectMembers = builtApplication.roleWithName("SampleApp_members");
roleObjectMember.set("users", membersUid);

Creating Projects

Finally, we will create projects in our app. Earlier, under Architecture, in the Project class, we saw that Moderators and Members are reference fields that point to the Application User Role class. As a result, we will pass an object of Application User Role (built_io_application_user_role) class to the fields Moderators and Members. We will also give a sample name and description for the project.

//initialize a new BuiltObject with the class uid
//'blt3b011c0e38ed1d82' is application api key
BuiltApplication builtApplication = Built.application(context, "blt3b011c0e38ed1d82");
BuiltClass projectClass = builtApplication.classWithUid("project");
BuiltObject projectObject = projectClass.object();
//Set values of object.
projectObject.set("name", "SampleApp");
projectObject.set("description", "Sample App Describtion");
//Set a members role.
BuiltRole roleObjectMember = builtApplication.roleWithName("SampleApp_members");
roleObjectMember.set("users", membersUid);
//Set a moderator role.
BuiltRole roleObjectModerators = builtApplication.roleWithName("SampleApp_moderators");
roleObjectModerators.set("users", membersUid);
//add this reference role object to builtObject
projectObject.setReference("members", roleObjectMember);
projectObject.setReference("moderators", roleObjectModerators);

Then save the project to Built.io Backend application.

projectObject.saveInBackground(new BuiltResultCallBack() {
   @Override
   public void onCompletion(ResponseType responseType, BuiltError builtError){
      if(builtError == null){
          //Success block.
      }else{
          //Error block.
      }       
   }
});

This creates a new project object along with two roles, SampleApp_moderators and SampleApp_members. After saving, the moderators and members fields will have the role uids as their values.

Notice that we haven't yet assigned permissions to the roles. Now that we have created roles we will set permissions to them. We use BuiltACL to set these permissions. We create a BuiltACL object and set it to our project object.

The following code can be included in the success block after creating the object.

//Create a BuiltACL for object.
BuiltACL aclObject = builtApplication.acl();
//member will get Read Access.
aclObject.setRoleReadAccess("membersRoleUid", true);
//moderators will get Read and Write Access.
aclObject.setRoleReadAccess("moderatorsRoleUid", true);
aclObject.setRoleWriteAccess("moderatorsRoleUid", true);
aclObject.setRoleDeleteAccess("moderatorsRoleUid", true);
//Set ACL values to builtObject which used for creating project.
projectObject.setACL(aclObject);

We again save the project with the ACL set.

projectObject.saveInBackground(new BuiltResultCallBack() {
   @Override
   public void onCompletion(ResponseType responseType, BuiltError builtError){
        if(builtError == null){
          //Success block.
        }else{
          //Error block.
        }       
   }
});

Fetching Bugs

To fetch the bugs for a project, we query the bugs class on Built.io Backend application. We list the objects in a ListView. We will form a BuiltQuery object and execute the query in the following way:

//Create a query object to fetch details of BuiltObject inside bugs class.
// 'blt3b011c0e38ed1d82' is application api key
BuiltApplication builtApplication = Built.application(context, "blt3b011c0e38ed1d82");
BuiltQuery bugsQuery = builtApplication.classWithUid("bugs").query();
//include the owner object in the response.
bugsQuery.includeOwner();
//fetch bugs for current project.
bugsQuery.where("uid", bugUid);
//include assignees objects in the response.
bugsQuery.includeReference(assignees);
//order the response by latest first
bugsQuery.descending("updated_at");
//Execute query to fetch current bugs object.
bugsQuery.execInBackground(new QueryResultsCallBack() {
   @Override
   public void onCompletion(ResponseType responseType, QueryResult builtqueryresult, BuiltError builtError){
        if(builtError == null){
            //Success block.
        }else{
            //Error block.
        }       
    }
});

Fetching Milestones

To fetch Milestones for a project, we query the milestone class on Built.io Backend application. We list the objects in a ListView. We will form a BuiltQuery object and execute the query in the following way:

//Create a query object to fetch details of BuiltObject inside milestone class.
// 'blt3b011c0e38ed1d82' is application api key
BuiltApplication builtApplication = Built.application(context, "blt3b011c0e38ed1d82");
BuiltQuery milstoneQuery = builtApplication.classWithUid("milstone").query();
//include the owner object in the response.
milstoneQuery.includeOwner();
//fetch milestone for current project.
milstoneQuery.where("uid", milestonesUid);
//include assignees objects in the response.
milstoneQuery.includeReference(assignees);
//order the response by latest first
milstoneQuery.descending("updated_at");
//Execute query to fetch current milestone object.
milstoneQuery.execInBackground(new QueryResultsCallBack() {
   @Override
   public void onCompletion(ResponseType responseType, QueryResult builtqueryresult, BuiltError builtError){
        if(builtError == null){
            //Success block.
        }else{
            //Error block.
        }       
    }
});

Fetching Tasks

To fetch Tasks for a project, we query the task class on Built.io Backend application. We list the objects in a ListView. We will form a BuiltQuery; object and fire the query in the following way:

//Create a query object to fetch details of BuiltObject inside milestone class.
// 'blt3b011c0e38ed1d82' is application api key
BuiltApplication builtApplication = Built.application(context, "blt3b011c0e38ed1d82");
BuiltQuery taskQuery = builtApplication.classWithUid("task").query();
//include the owner object in the response.
taskQuery.includeOwner();
//fetch milestone for current project.
taskQuery.where("uid", taskUid);
//include assignees objects in the response.
taskQuery.includeReference(assignees);
//order the response by latest first
taskQuery.descending("updated_at");
//Execute query to fetch current milestone object.
taskQuery.execInBackground(new QueryResultsCallBack() {
    @Override
    public void onCompletion(ResponseType responseType, QueryResult builtqueryresult, BuiltError builtError){
        if(builtError == null){
            //Success block.
        }else{
            //Error block.
        }       
    }
});

Creating Bugs

Creating new bugs is an effortless task. We first create an object in the Bugs class and set the values. Then we set the reference field assignees with the user we want to assign the bug to.

Next, we set the ACL for the new bug. Project moderators can read, update or delete the bug. It is important to note that the assignees should have write access to the bug, giving them the right to change the bug status.

With the help of the following code, we will create a bug, assign users to it, set ACL and finally save it to Built.io Backend application.

//create a BuiltObject with class uid for bugs
// 'blt3b011c0e38ed1d82' is application api key
BuiltApplication builtApplication = Built.application(context, "blt3b011c0e38ed1d82");
BuiltObject bugObject = builtApplication.classWithUid("bugs").object();
//Set values for fields.
bugObject.set("name","Bug name.");
bugObject.set("description", "Bug description");
bugObject.set("reproducible", "Always");
bugObject.set("due_date", "can be from a date picker");
bugObject.set("severity", "Major");
bugObject.set("status", "Open");
bugObject.set("assignees", assigneesUid);
//Set attached files uid as reference to bug.
bugObject.set("attachments", attachedFileUid);
//To set project reference to this bug.
bugObject.setReference("project",projectUid);
BuiltACL aclObject = builtApplication.acl();
//Member have read access for a bug. Moderators have read, update, and delete access for a bug.
//memeber
aclObject.setRoleReadAccess(member_role_uid, true);
//moderator
aclObject.setRoleReadAccess(moderator_role_uid, true);
aclObject.setRoleWriteAccess(moderator_role_uid, true);
aclObject.setRoleDeleteAccess(moderator_role_uid, true);
//Assignees have update(write) access for the bug
aclObject.setUserReadAccess(assigneesUid[i], true);
aclObject.setUserWriteAccess(assigneesUid[i], true);
//Bug can be created by anyone
aclObject.setPublicReadAccess(true);
aclObject.setPublicWriteAccess(true);
//Set ACL to this bug.
bugObject.setACL(aclObject);
//Save the bug object.
bugObject.saveInBackground(new BuiltResultCallBack() {
   @Override
   public void onCompletion(ResponseType responseType, BuiltError builtError){
      if(builtError == null){
        //Success block.
      }else{
        //Error block.
      }       
   }
});

Creating Milestones

We create milestones in a similar way as Bugs.

Creating Tasks

Tasks are created in a similar way as Bugs.