Documentation : Relations - Android

This example shows how to work with models relations using Mobeelizer.

Application consists of two models. First one Order has two fields - name and status, secone one is an order item which contains title field and field definig relation between models. Main screen of application contains list of model entities and two buttons. User can add new order and it's items. He can also synchronize with mobeelizer cloud. There are two users in the system and there's a possibility to switch user on application life time.

Design Your App

To complete this example we need to create new application called Relations in Mobeelizer App Designer.

Next we will configure model. We have to go to 'Models' section and create:

  • OrderEntity (CRUD operations avaliable for everyone) with the following fields:

    Name
    Required property
    Type
    Default value property
    Additional properties
    Credentials
    nameyestext--default
    statusyesinteger1min: 1, max: 5default

     

  • OrderItemEntity (CRUD operations avaliable for everyone) with the following fields:

    Name
    Required property
    Type
    Default value property
    Additional properties
    Credentials
    titleyestext--default
    orderGuidyesbelongs to OrderEntity--default


For this example we leave conflict resolving setting in overwrite state. 

Next section to configure is 'Groups & Roles', by default there is one group called 'users' and one device category 'mobile' and this two together create role 'users-mobile'. This default configuration is perfect for our example and you don't have to change it. 
When everything is done in Create mode, deploy our application to test environment, and create two users with passwords:

  • a - usera
  • b - userb

Finally, download configured template for Android project. Select installed Andoird SDK in target SDK field.

Use the Mobeelizer SDK

Extract downloaded zip file then open eclipse and import project.

Add resource files

Before we start  implementing our example we will add some resource files into the project:

Prepare main screen

We will start with creating main screen controls, as you read before on main screen there is list of model entities, two buttons to synchronize and add new entity and enother one to switch between users.

main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:layout_marginTop="20dip"
            android:layout_marginLeft="50dip"
            android:textSize="25dip"
            android:id="@+id/currentUser"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:bufferType="spannable"
            android:text="Relations" />
                  
        <ImageButton
            android:layout_height="50dip"
            android:layout_width="50dip"
            android:layout_marginLeft="260dip"
            android:layout_marginTop="10dip"
            android:id="@+id/userSwitch"
            android:layout_alignParentLeft="true"/>
    </RelativeLayout>
        <View
        android:id="@+id/titleBarSeparator"
        android:layout_width="fill_parent"
        android:layout_height="2dip"
        android:layout_alignParentLeft="true"
        android:background="#FFB4B4B4" />
    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" >
    </ListView>
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="10dip">
        <Button
            android:enabled="false"
            android:layout_height="wrap_content"
            android:layout_width="100dip"
            android:layout_marginLeft="10dip"
            android:layout_marginTop="10dip"
            android:id="@+id/footerAdd"
            android:layout_alignParentLeft="true"
            android:text="Add" />
        <Button
            android:enabled="false"
            android:layout_height="wrap_content"
            android:layout_width="100dip"
            android:layout_marginLeft="10dip"
            android:layout_marginTop="10dip"
            android:id="@+id/footerSync"
            android:layout_toRightOf="@+id/footerAdd"
            android:text="Sync" />
    </RelativeLayout>
</LinearLayout>

To keep clean in our code we will create an interface to define operations supported by our main screen. First of all we have to have possibility to refresh entities list, there shoud be also posibility to disable and enable all buttons, and to get or set currenty logged in user.

MainScreen.java
public interface MainScreen {
	
	    void disableButtons();
	     
	    void enableButtons();
	     
	    void refreshList();
	     
	    String getCurrentUser();
	     
	    void setCurrentUser(String user);
}

When interface is ready let's implement it on RelationsActivity. In constructor we will get all created controls in layout definition and store it in class private fields. 

RelationsActivity.java
 public class RelationsActivity extends Activity implements MainScreen{
	
	private ListView mList;
	     
	private Button mAddButton, mSyncButton;
	 
	private ImageButton mSwitchUserButton;
	     
	private String currenUser;
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        mAddButton = (Button) findViewById(R.id.footerAdd);
        mSyncButton = (Button) findViewById(R.id.footerSync);
        mSwitchUserButton = (ImageButton) findViewById(R.id.userSwitch);
        mList = (ListView) findViewById(android.R.id.list);
        
        // TODO: Add listeners to buttons
    }

	public void disableButtons() {
        mAddButton.setEnabled(false);
        mSyncButton.setEnabled(false);
        mSwitchUserButton.setEnabled(false);
	}

	public void enableButtons() {
        mAddButton.setEnabled(true);
        mSyncButton.setEnabled(true);
        mSwitchUserButton.setEnabled(true);
	}

	public void refreshList() {
		// TODO:
	}

	public String getCurrentUser() {
        return currenUser;
	}

	public void setCurrentUser(String user) {
        currenUser = user;
        if(user != null){
            if(currenUser.equals("a")){
                mSwitchUserButton.setImageResource(R.drawable.bt_user_a_big);
            }
            else if(currenUser.equals("b")){
                mSwitchUserButton.setImageResource(R.drawable.bt_user_b_big);
            }
        }
	}
}

Switch beetwen users

Before we implement refreshing entities list we have to create switching user things. We will create separated class for that called "SwichUserOperations", this class will implement OnClickListener interface, so object of this class can be set as onClickListener in 'Switch' button. This class will also implement MobeelizerOperationCallback interface and we will use it as a login method callback. 

SwitchUserOperations.java
public class SwitchUserOperations implements OnClickListener , MobeelizerOperationCallback{
    
   private static final String USER_A_LOGIN = "a";
    
   private static final String USER_B_LOGIN = "b";
    
   private static final String USER_A_PASS = "usera";
    
   private static final String USER_B_PASS = "userb";
   private String userToLoggIn;
   private String password;
    
   private MainScreen page;
    
   public SwitchUserOperations(MainScreen page){
       this.page= page;
   }
    
   public void onClick(View v) {
       page.disableButtons();
       if(page.getCurrentUser() == USER_A_LOGIN){
           userToLoggIn = USER_B_LOGIN;
           password = USER_B_PASS;
       }
       else {
           userToLoggIn = USER_A_LOGIN;
           password = USER_A_PASS; 
       }
       Mobeelizer.login(userToLoggIn, password, this);
   }
   public void onFailure(MobeelizerOperationError arg0) {
       page.setCurrentUser(null);
       page.refreshList();
       page.enableButtons();
   }
   public void onSuccess() {
       page.setCurrentUser(userToLoggIn);
       page.refreshList();
       page.enableButtons();
   }
}

It is easy, right? When user click on switch button, we will check which user was logged in recently and then we will login other one. We have to also disable all buttons before login. When login will finish with success we will set current user value into main screen and then enable all buttons. 

Last thing to do in switching users is setting object of just created class to button as a onclick listener.

RelationsActivity.java
	@Override
    public void onCreate(Bundle savedInstanceState) {
        ...
        SwitchUserOperations switchOperations = new SwitchUserOperations(this);
        mSwitchUserButton.setOnClickListener(switchOperations);
        switchOperations.onClick(null);
		...
    }

Get current user entities

Ok we can switch between users now and it is time to create refreshing entity list code. To show our entities on list we have to create addapter for that, and list item layout template.

Add  list_item.xml file to layout folder, add implement it like this:

list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
	    >
	<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
	    android:layout_width="match_parent"
	    android:layout_height="40dp"
	    android:minHeight="50dp"
	    android:orientation="vertical" >
	    <TextView
	        android:id="@+id/listItemName"
	        android:layout_width="wrap_content"
	        android:layout_height="wrap_content"
	        android:layout_centerVertical="true"/>
	    <ImageView
	        android:id="@+id/listItemUser"
	        android:layout_width="wrap_content"
	        android:layout_height="wrap_content"
	        android:layout_alignParentRight="true"
	        android:layout_centerVertical="true"
	        android:layout_marginRight="10dp"/>
	</RelativeLayout>  
	<ListView
		android:id="@+id/orderItemsList"
	    android:layout_height="0dp"
		android:layout_weight="1"
        android:layout_width="fill_parent"
        android:dividerHeight="2dp"
		android:layout_marginLeft="40dip">
	</ListView>
	<Button
	    android:id="@+id/addOrderItemButton"
	    android:layout_marginLeft="40dip"
	    android:layout_width="wrap_content"
    	android:layout_height="40dip"
	    android:text="Add item"
	    />
</LinearLayout>

As you can see in just created code order entity will contains list of their items inside. Becouse of that we will need template for items in this list and adapter to fill it, let's create new file in layout directory and call it order_list_item.xml.

order_list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
	    android:layout_width="match_parent"
	    android:layout_height="30dp"
	    android:minHeight="30dp"
	    android:orientation="vertical" >
	    <TextView
	        android:id="@+id/listItemTitle"
	        android:layout_width="wrap_content"
	        android:layout_height="wrap_content"
	        android:layout_centerVertical="true"/>
	    <ImageView
	        android:id="@+id/listOrderItemUser"
	        android:layout_width="wrap_content"
	        android:layout_height="wrap_content"
	        android:layout_alignParentRight="true"
	        android:layout_centerVertical="true"
	        android:layout_marginRight="10dp"/>
</RelativeLayout>

When templates are ready it is time to create adapters. Create new class OrderListAdapterwhich extends ArrayAdapter<OrderEntity> and override getView method. 

OrderListAdapter.java
public class OrderListAdapter extends ArrayAdapter<OrderEntity> {
    private int resourceId = 0;
     
    private LayoutInflater inflater;
     
    private MainScreen mView;
    
    public OrderListAdapter(MainScreen view, Context context, int resource,
            List<OrderEntity> objects) {
        super(context, 0, objects);
        this.mView = view;
        this.resourceId = resource;
        this.inflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }
     
    public static class ViewHolder {
        public ImageView author;
        public TextView name;
        public ListView items;
        public Button addButton;
    }
     
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view = convertView;
        ViewHolder holder;
        if (view == null) {
            view = inflater.inflate(resourceId, parent, false);
            holder = new ViewHolder();
            holder.author = (ImageView) view.findViewById(R.id.listItemUser);
            holder.name = (TextView) view.findViewById(R.id.listItemName);
            holder.items = (ListView) view.findViewById(R.id.orderItemsList);
            holder.addButton = (Button) view.findViewById(R.id.addOrderItemButton);
            view.setTag(holder);
        } else {
            holder = (ViewHolder) view.getTag();
        }
        OrderEntity item = (OrderEntity) getItem(position);
        if(item.getOwner().equals("a")){
            holder.author.setImageResource(R.drawable.bt_user_a_small); 
        }
        else if(item.getOwner().equals("b")){
            holder.author.setImageResource(R.drawable.bt_user_b_small);
        }
        List<OrderItemEntity> items = Mobeelizer.getDatabase().find(OrderItemEntity.class).add(MobeelizerRestrictions.belongsTo("orderGuid", OrderEntity.class, item.getGuid())).list();
        
        // TODO: add on click listener to add item button and fill items list. 
 
        // Calculates height of the list. 
        LayoutParams params = holder.items.getLayoutParams();
        params.height = items.size() * 50;
        holder.items.setLayoutParams(params);
        
        holder.items.setAdapter(adapter);
        holder.name.setText("Order "+ item.getName());
        return view;
    }
}

This code is pretty simple. In constructor we are getting resource of item template definition and list of items. In overridden method we have to convert our OrderEntity class object to View object, in this case we have to also fill list of order items, we can't do it before we implement adapter for it. 

Create new class called OrderItemsListAdapter extending ArrayAdapter<OrderItemEntity>.

OrderItemsListAdapter.java
public class OrderItemsListAdapter extends ArrayAdapter<OrderItemEntity> {
    private int resourceId = 0;
     
    private LayoutInflater inflater;
     
    public OrderItemsListAdapter(Context context, int resource,
            List<OrderItemEntity> objects) {
        super(context, 0, objects);
        this.resourceId = resource;
        this.inflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }
     
    public static class ViewHolder {
        public ImageView author;
        public TextView title;
    }
     
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view = convertView;
        ViewHolder holder;
        if (view == null) {
            view = inflater.inflate(resourceId, parent, false);
            holder = new ViewHolder();
            holder.author = (ImageView) view.findViewById(R.id.listOrderItemUser);
            holder.title = (TextView) view.findViewById(R.id.listItemTitle);
            view.setTag(holder);
        } else {
            holder = (ViewHolder) view.getTag();
        }
        OrderItemEntity item = (OrderItemEntity) getItem(position);
        if(item.getOwner().equals("a")){
            holder.author.setImageResource(R.drawable.bt_user_a_small); 
        }
        else if(item.getOwner().equals("b")){
            holder.author.setImageResource(R.drawable.bt_user_b_small);
        }
     
        holder.title.setText("- "+ item.getTitle());
        return view;
    }
}

This adapter is much easier then OrderListAdapter. We are only creating view object for simple model. When it is created we can come back to OrderListAdapter and fill order items list. 

OrderListAdapter.java
public class OrderListAdapter extends ArrayAdapter<OrderEntity> {
    private int resourceId = 0;
     
    private LayoutInflater inflater;
     
    private MainScreen mView;
    
    public OrderListAdapter(MainScreen view, Context context, int resource,
            List<OrderEntity> objects) {
        ...
        List<OrderItemEntity> items = Mobeelizer.getDatabase().find(OrderItemEntity.class).add(MobeelizerRestrictions.belongsTo("orderGuid", OrderEntity.class, item.getGuid())).list();
        OrderItemsListAdapter adapter = new OrderItemsListAdapter(getContext(), R.layout.order_list_item, items);
        ...
    }
}

When OrderListAdapter is ready it is time to come back to refreshList method in RelationsActivity class.

RelationsActivity.java
...
	public void refreshList() {
        if(Mobeelizer.isLoggedIn()){
			List<OrderEntity> entities = Mobeelizer.getDatabase().list(OrderEntity.class);
	        OrderListAdapter adapter = new OrderListAdapter(this, this, R.layout.list_item, entities);
	        mList.setAdapter(adapter);
		}
	}
...

We are getting list of all entities from database, creating new adapter and then setting created object into list control.

Add entities into database

It is time now to implement adding new entities to database. As in switch user code we will also creates seperated class for that which will implement OnClickListener. Create new class AddOperations.

AddOperation.java
public class AddOperation implements OnClickListener {
    
    private MainScreen mView;
     
    public AddOperation(MainScreen view){
        this.mView = view;
    }
     
    public void onClick(View arg0) {
    	if(Mobeelizer.isLoggedIn()){
	        OrderEntity entity = new OrderEntity();
	        entity.setName(mView.getCurrentUser().toUpperCase() + "/000"+ Mobeelizer.getDatabase().list(OrderEntity.class).size() + 1);
	        Mobeelizer.getDatabase().save(entity);
	        this.mView.refreshList();
    	}
    }
}

When user click on button we will create new object set it values and save into database, on the end we will refresh entities list displayed on main screen.

Remember to set object of this class as add button onclick listener in RelationsActivity class. 

RelationsActivity.java
    @Override
    public void onCreate(Bundle savedInstanceState) {
		...
        mAddButton.setOnClickListener(new AddOperation(this));
    }

We can add new order into database let's create adding item into order items list. We have already created button for that on the end of each items list, we just need to create onClickListener for it. Create new AddItemOperation class and implement it list this:

AddItemOperation.java
public class AddItemOperation  implements OnClickListener {
    
    private MainScreen mView;
    
    private OrderEntity mOrder;

    String[] mTitles;
    
    private static Random mRand;
     
    static {
        mRand = new Random(System.nanoTime());
    }

    public AddItemOperation(MainScreen view, Resources res, OrderEntity order){
        this.mView = view;
        this.mOrder = order;
        this.mTitles = res.getStringArray(R.array.movieTitles);
    }
     
    public void onClick(View arg0) {
    	if(Mobeelizer.isLoggedIn()){
	        OrderItemEntity entity = new OrderItemEntity();
	        entity.setTitle(mTitles[mRand.nextInt(mTitles.length)]);
	        entity.setOrderGuid(mOrder.getGuid());
	        Mobeelizer.getDatabase().save(entity);
	        this.mView.refreshList();
    	}
    }
}

This operation have to be created for each order objects because of that constructor get object of OrderEntity class. In constructor we are loading list of movie titles from resources and creating object to generate random numbers. When user click on button we will create new object set it values create relation and save into database, on the end we will refresh entities list displayed on main screen.

Now, we just have to sets object of this class into each add item button. Open OrderListAdapter class and add this code:

OrderListAdapter.java
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ...
        OrderItemsListAdapter adapter = new OrderItemsListAdapter(getContext(), R.layout.order_list_item, items);
        ...
    }

Synchronize with mobeelizer

Greate, our example is almost ready, last thing to do is synchronization, but not be afraid it will be as easy as other operations. We will create new class for that again, and we will implements OnClickListener and MobeelizerOperationCallback interfaces in it.

SyncOperations.java
public class SyncOperations implements OnClickListener , MobeelizerOperationCallback {
     
    private MainScreen view;
    public SyncOperations(MainScreen view){
        this.view = view;
    }
    public void onFailure(MobeelizerOperationError arg0) {
        view.refreshList();
        view.enableButtons();
    }
    public void onSuccess() {
        view.refreshList();
        view.enableButtons();
    }
    public void onClick(View arg0) {
        view.disableButtons();
        if(Mobeelizer.isLoggedIn()){
            Mobeelizer.sync(this);
        }
    }
}

It is actually just calling Mobeelizer sync method. When synchronization finished without any errors given entities list is refreshed, and that is all.

And remember to add created operation classes as buttons onClick listeners.

RelationsActivity.java
    @Override
    public void onCreate(Bundle savedInstanceState) {
        ...
        mSyncButton.setOnClickListener(new SyncOperations(this));
    }

Congratulations, relation example is completed. You can test it now, you can also add your own code to it, for example you can implement removing order items or orders.

Conclusion

To conclude, this is the simple example of using relations in Mobeelizer. We have two models connected with 'belongs to' field. We can create orders, add items to them and synchronize with Mobeelizer cloud.

Attachments: