Friday, July 26, 2013

BB10 QTranslator and QLocale

In our apps, language change is very common functionality. Before jumping into that topic, let us see the basics QLocale and QTransaltor.

      QLocale contains the language and country details. QLocale() will construct a locale object with the system language and locale. If you want to construct a locale of you interest, then initialize like this

QLocale englishLocale = QLocale(QLocale::English, QLocale::UnitedStates);
std::cout << "Locale is : " << englishLocale.name().toStdString();

The output for the above code is en_US.

     To add a new locale support to your application go to bardescreptor.xml --> Localization tab. You can find the default language already defined. To add your support, click on Add and select the language from the list. This will  be like

Select language

  I'm selecting Hindi from the list. Then we can specify the title, app icon, splash screen in the right pane when the locale is changed to Hindi. The specified values will be automatically reflected once the system language is changed to Hindi. Reference picture below.


     Once you build the application, under the translations folder of the project, projectname_hi.ts will be automatically generated. See the red circle in the above picture. We can define, the hindi translation for a specific string in the generated files.

The question is how will we load these .ts files into our application. This leads us to QTranslator.

     QTranslator is capable of loading the language string file(.ts format) for the specified locale with simple steps.
  • Create a locale object
  • Load the translator with that locale
  • Install the translator into the application. 
QLocale hindiLocale = QLocale(QLocale::Hindi, QLocale::India);
QTranslator translator;
QString locale_string = hindiLocale.name();
QString filename = QString("FileHandling_%1").arg(locale_string);
if (translator.load(filename, "app/native/qm")) {
    Application::instance()->installTranslator(&translator);
}

     In the above code, we are instantiating hindiLocale, initializing translator object, getting the name of hindi locale, load it into  translator, install the translator to the application.

If the specified locale is not found, application will install the default locale.

Tuesday, July 23, 2013

Listview with CustomListItem in Blackberry 10

Listview in BB 10 follows MVC architecture. Here we will learn how to add custom rows to listview. To start, we have to look into 4 topics.

  1. Listview component (View)
  2. DataModel (Model)
  3. ListItemProvider (Controller)
  4. ListItem
The below picture illustrates the overview of listview.



The data of the listview is present in DataModel. ListitemProvider is a factory class which creates the listitem with the data present in the DataModel. At run time, listview will pass the datamodel to the ListItemProvider which then uses DataModel and creates a ListItem. The ListItem is a single row of the ListView.

Let us jump into the example for more practical view.


QuotesListItemProvider *myItemProvider = new QuotesListItemProvider(0);
ArrayDataModel *listDataModel = new ArrayDataModel();
// Here Add data to the listmodel .
// In my case I added strings to the dataModel.
ListView *listView = pane->findChild("quotesList");
listView->setDataModel(listDataModel);
listView->setListItemProvider(myItemProvider);

In the above code, I extracted the listview from the QML, initialised ArrayDataModel, appended data to the model, initialized costom ListItemProvider (QuotesListItemProvider), attached datamodel and listitemprovider to the listview.

To define a CustomListItemProvider class it is mandatory to inherit ListItemProvider class. Our custom class should override createItem() and updateItem() functions of ListItemProvider

QuotesListItemProvider.h is 


#include <bb/cascades/ListItemProvider>
#include <bb/cascades/ListView>
#include <bb/cascades/Container>
#include <bb/cascades/VisualNode>
#include <QVariant>

using namespace bb::cascades;


class QuotesListItemProvider: public bb::cascades::ListItemProvider {
public:
    QuotesListItemProvider(Container *container);
    virtual ~QuotesListItemProvider();

    VisualNode * createItem(ListView* list, const QString &type);
    void updateItem(ListView* list, VisualNode *listItem, 
        const QString &type, const QVariantList &indexPath,
        const QVariant &data);

private:
    Container *container;
};

QuotesListItemProvider.cpp is:

#include "QuotesListItemProvider.h"
#include "QuotesListItem.h"
#include <bb/cascades/StandardListItem>
#include <bb/cascades/LayoutProperties>

QuotesListItemProvider::QuotesListItemProvider(Container *container) {
    this->container = container;
}

QuotesListItemProvider::~QuotesListItemProvider()
{

}

VisualNode * QuotesListItemProvider::createItem(ListView* list,
        const QString &type) {
    
    QuotesListItem *myItem = new QuotesListItem(container);
    return myItem;
}

void QuotesListItemProvider::updateItem(ListView* list,
        bb::cascades::VisualNode *listItem, const QString &type,
        const QVariantList &indexPath, const QVariant &data) {
   
    QuotesListItem *myItem = static_cast(listItem);
    myItem->setTitle(data.toString());
}

In createItem() function, ListItem's are created. The created listitems are then passed to updatedItem() function. Observe the first line of UpdateItem method, VisualNode is casted to  CustomListItem(QuotesListItem). And also the parameter 'data' of QVariant type in updateItem() function is the data of our ArraydataModel in ListView. As our DataModel contains strings, I'm converting this into the string format and updated label in QuotesListItem class.

For creating a list row ( Custom ListItem), we have to inherit CustomControl. 

QuotesListItem.h is 

#include <QObject>
#include <QUrl>
#include <iostream>
#include <bb/cascades/CustomControl>
#include <bb/cascades/QmlDocument>
#include <bb/cascades/Container>
#include <bb/cascades/Image>
#include <bb/cascades/ImageView>
#include <bb/cascades/Label>
#include <bb/cascades/controls/activityindicator.h>

using namespace bb::cascades;

class QuotesListItem: public CustomControl {
    Q_OBJECT
public:
    virtual ~QuotesListItem();
    void setTitle(QString text);
    QuotesListItem(Container *container);

private:

    QString pictureURL;
    Label *label;
    void init();
};

QuotesListItem.cpp is


#include "QuotesListItem.h"
#include "bb/cascades/StackLayout"

QuotesListItem::QuotesListItem(Container* container) :
        CustomControl(container) {

    QmlDocument *document = 
             QmlDocument::create("asset:///QuotesListItem.qml");
    Container *rootContainer = document->createRootObject();

    label = rootContainer->findChild

In this class, I'm inflating a QML and used setRoot function of CustomControl class to attach the QML's root object. setTitle() function is used to set the text. This function is called from updateItem()  of QuoteListItemProvider class.


Friday, June 14, 2013

Json parsing in BB 10

Generally, in many development technologies, we will be adding JSON api to project or provided by OS itself and parse the appropriate response using JSON API's. Here it is a different approach.
  1. Convert JSON response to QVariant using JsonDataAccess.
  2. Query the QVariant object for JSON children Nodes.
[
  {
    "id": "13",
    "en_description": "If leadership does not keep",
    "ar_description": "\u0625\u0646 \u0627",
    "date": "2012-09-28"
  },
  {
    "id": "14",
    "en_description": "The role of the leadership",
    "ar_description": "\u062f\u0648\u0631 \u0627",
    "date": "2012-09-28"
  }
]

In the above Json, I am likely to parse the data of "en_descreption" tag. The method to parse is shown below.

void Quotes::onUrlLoad(QString jsonResponse) {

    ArrayDataModel *arrayDataModel = new ArrayDataModel();

    //  Convert Json file into QTList objects.
    bb::data::JsonDataAccess ja;
    const QVariant jsonva = ja.loadFromBuffer(jsonResponse);
    const QList externalip = jsonva.toList();

    //  Iterate Json List and retrieve data, store it in ArraydataModel
    for (int index = 0; index < externalip.size(); index++) {

        QVariantMap map = externalip.at(index).toMap();
        QVariantMap::Iterator iterator = map.begin();
        while (iterator != map.end()) {
            //  Append "en_descreption" tag's value to the ArrayDataModel.
            if (!iterator.key().toStdString().compare("en_description")) {
                arrayDataModel->append(iterator.value().toString());
            }
            ++iterator;
        }   //  end while.
    }   //  end for

}

Above code does the following things 
  1. Loads Json string using JsonDataAcces.
  2. Converts the Json String to root QVariant object with the help of JsonDataAccess.
  3. Get the List of QVariant variables from the root QVariant variable . Note that as our JSON is an array hence we are using jsonva.toList(). Otherwise we have to use jsonva.toMap() as per documentation.
  4. Pull each QVariantMap from the list.
  5. Iterate the map for the values.
  6. Extract values with "en_description" and store it in ArrayDataModel.
That's it .. we are done with parsing and storing JSON data.

Wednesday, June 5, 2013

Navigation Mode's in Android Action Bars

Actions bar's can be represented in 3 different styles or formats

All these modes can be achieved using Sherlock as well as Native Action bar's. Native action bar's are supported from Android OS Version 3.0 and above.

Sherlock ActionBar with NavigationList

NavigationList is one more type of navigation in ActionBar's which serves the same functionality as tabs.

Let me show you how this look's like
Gmail App with Navigation list mode 
In the above pic, Upon click of Inbox, the navigation list appears.

If you are not familiar with the configuration of Sherlock Action Bar project please refer my previous post.

Comming back...To achieve List style of navigation mode, set the navigationmode of Action bar to ActionBar.NAVIGATION_MODE_LIST and pass the respective adapter for this list. The code is shown below.

/**
* Initialises the views and variables of the activity.
 */
private void init() {

    //  initialise ActionBar
    mActionBar = getSupportActionBar();
    mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
        
    //  Initialise Array Adapter.
    final ArrayAdapter arrayAdapter = new ArrayAdapter(this,
         android.R.layout.simple_spinner_dropdown_item, android.R.id.text1);
    arrayAdapter.add("Login");
    arrayAdapter.add("Elections");
    arrayAdapter.add("Maintainance");
    arrayAdapter.add("Results");
        
    //  Set array adapter to navigation list, implement a listener for the list.
            mActionBar.setListNavigationCallbacks(arrayAdapter, new OnNavigationListener() {
            @Override
            public boolean onNavigationItemSelected(int arg0, long arg1) {
                Toast.makeText(SherlockListActionBarActivity.this, arrayAdapter.getItem(arg0), Toast.LENGTH_SHORT).show();
                return false;
            }
        });
}

The above method should be called from onCreate() method of your SherlockActivity.

This method defines ActionBar, Prepares the Adapter for NavigationList, sets call back for Navigation List item click.

Related topics:

Tuesday, June 4, 2013

Android Sherlock Action Bars with Tab's

Action bar's provide a functionality to integrate tabs.

As mentioned in my previous post I'm just recapping how to configure Sherlock API with our project. 

Steps to implement Sherlock action bar include.
  • Download Sherlock source.
  • UnZip the source and find "actionbarsherlock" folder, which is the library project for implementing Sherlock ActionBar.
  • Import the library project into the workspace.
  • Create a new Android Project.
  • Click on properties of the project and add "actionbarsherlock" as library project.

  • Extend your class with SherlockActivity        
public class SherlockAtionBar extends SherlockActivity
  • Override onCreate and setTheme as one of the sherlock theme before calling super.onCreate()
Now, we are almost ready to implement our tabbed action bar.

Here I would like to use a ViewPager, Fragments along with Tab's.  Add tabs to ActionBar, intialiase FragmentPagerAdapter, set FragmentPagerAdapter to ViewPager, integrate tab clicks with viewpager.

The complete activity looks like

import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.ActionBar.Tab;
import com.actionbarsherlock.app.ActionBar.TabListener;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.vl.actionbar.LoginFragment;
import com.vl.actionbar.R;

import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewParent;

import java.util.ArrayList;

/**
 * Sherlock Action Bar to demonstrate Tabs behavior
 * 
 * @author shpolavarapu
 * 
 */
public class SherlockTabsActionBarActivity extends SherlockFragmentActivity {

    private ViewPager mViewPager;
    private ActionBar mSupportActionBar;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setTheme(R.style.Theme_Sherlock_Light_DarkActionBar);
        super.onCreate(savedInstanceState);
        
        init();
    }

    /**
     * Initialises the Views and variables for this activity.
     */
    private void init() {
        
        //  Initialise and set viewpager to the activity.
        mViewPager = new ViewPager(this);
        mViewPager.setId(1232);
        setContentView(mViewPager);
        
        //  Init and set ActionBar Properties.
        mSupportActionBar = getSupportActionBar();
        mSupportActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
        
        //  Initialise Adapter for the view pager.
        SherlockTabAdapter adapter = new SherlockTabAdapter();
        ArrayList<Bundle> bundleAL = new ArrayList<Bundle>();
        String texts[] = {"Login", "Elections", "Polling", "Results"};
        for(int index = 0; index < texts.length; index++)
        {
            //  Prepare Bundle object for each tab.
            Bundle bundle = new Bundle();
            bundle.putString("text", texts[index]);
            bundleAL.add(bundle);
            
            //  Add tabs for the action bar.
            ActionBar.Tab tab = mSupportActionBar.newTab().setText(texts[index])
                    .setTabListener(adapter);
            mSupportActionBar.addTab(tab);
        }
        
        adapter.setBundle(bundleAL);
        mViewPager.setAdapter(adapter);
        mViewPager.setOnPageChangeListener(adapter);
    }




    /**
     * Adapter for setting the content for the tab click's.
     * 
     * @author shpolavarapu
     * 
     */
    private class SherlockTabAdapter extends FragmentPagerAdapter implements ActionBar.TabListener,
            ViewPager.OnPageChangeListener {

        private ArrayList<Bundle> mBundleAL;
        
        public SherlockTabAdapter() {
            super(getSupportFragmentManager());
        }
        
        /**
         * ArrayList of bundle's to pass as Arguments to Fragment.
         * @param bundleAL  ArrayList of bundle's
         */
        void setBundle(ArrayList<Bundle> bundleAL) {
            mBundleAL = bundleAL;
        }
        @Override
        public void onPageScrollStateChanged(int position) {
            
        }

        @Override
        public void onPageScrolled(int arg0, float arg1, int arg2) {

        }

        @Override
        public void onPageSelected(int position) {
            //  Change the tab selection upon page selection.
            mSupportActionBar.setSelectedNavigationItem(position);
        }

        @Override
        public void onTabReselected(Tab arg0, FragmentTransaction arg1) {

        }

        @Override
        public void onTabSelected(Tab tab, FragmentTransaction arg1) {
            //  On Tab selection change the View Pager's Current item position. 
            mViewPager.setCurrentItem(tab.getPosition());
        }

        @Override
        public void onTabUnselected(Tab arg0, FragmentTransaction arg1) {

        }

        @Override
        public Fragment getItem(int pos) {
            return Fragment.instantiate(SherlockTabsActionBarActivity.this, LoginFragment.class.getName(), mBundleAL.get(pos));
        }

        @Override
        public int getCount() {
            return mBundleAL.size();
        }

    }

}

The LoginFragment is posted below.

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class LoginFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        
        View view = inflater.inflate(R.layout.login, null);
        if(getArguments() != null)
        {
            String text = getArguments().getString("text");
            ((TextView)view.findViewById(R.id.tv_login)).setText(text);
        }
        return view;
    }
}

login Layout is posted below :

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white"
    android:orientation="vertical" >

     <TextView
        android:id="@+id/tv_login"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Login" />


</LinearLayout>

Output :


Overview:

 Create Tabs and ArrayList of Bundle data which contain name of the tab. Create a ViewPager and set the FragmentPagerAdaper to it. The Adapter also listens TabClick and ViewPager PageChange. On TabClick, Login Fragment with the bundle data as arguments is instantiated  and supplied to viewpager.

Don't forget to add NavigationMode as ActionBar.NAVIGATION_MODE_TABS. Native Action Bar's implementation with tabs can also use the same code with minor changes,

Related topics : 

Android Action Bar MenuItem's using Sherlock API

Android introduced Action bar's in API level 11 i.e in HoneyComb 3.0. Native implementation is posted here.  There is no backward compatibility as of now. This leads to our point of discussion.

In some cases where our action bar needs to be compatible from Android SDK version 2.1. In such cases we can go with Sherlock API.

Steps to implement Sherlock action bar include.
  • Download Sherlock source.
  • UnZip the source and find "actionbarsherlock" folder, which is the library project for implementing Sherlock ActionBar.
  • Import the library project into the workspace.
  • Create a new Android Project.
  • Click on properties of the project and add "actionbarsherlock" as library project.


Now, we are ready to implement our action bar.

1) Extend your class with SherlockActivity
            
public class SherlockAtionBar extends SherlockActivity

2) Override onCreate and setTheme as one of the sherlock theme before calling super.onCreate()

@Override
protected void onCreate(Bundle savedInstanceState) {
    setTheme(R.style.Theme_Sherlock);   
    super.onCreate(savedInstanceState);
    setContentView(R.layout.login);
}

3) Override onCreateOptionsMenu()


@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Collapsible Action Item
    menu.add("Search").setIcon(R.drawable.ic_search)
                      .setActionView(R.layout.collapsible_edittext)  
                          .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | 
                                  MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);

    // OverFlow menu in actionbar.
    SubMenu submenu = menu.addSubMenu("");
    submenu.setIcon( R.drawable.abs__ic_menu_moreoverflow_normal_holo_dark);

    submenu.add(1, 0, 1, "Cut");
    submenu.add(1, 1, 2, "Copy");
    submenu.add(1, 2, 3, "Paste");
    submenu.getItem(). setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS|
                         MenuItem.SHOW_AS_ACTION_WITH_TEXT);
    // end overflow menu

   return true;
}


My collapsible_edittext layout looks like


<EditText xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:hint="Search"/>


That's it ..... our action bar with menu items and action view is ready. Snapshot of output is shown below.
 
Sherlock action bar with Action view, menu item's and sub menu item's

 I will now explain code in OnCreateMenuOptions(). First with the submenu thing.

If we add menu items to Menu directly using Menu.add(), Actionbar will have different UI for different OS versions of Android. For 3.0 and above it will be shown as overflow menu like in the above picture. For Android OS version's less than 3.0, action bar's overflow type of menu is overrided by the default menu's(at the bottom). I think you got my point... For consistent behaviour of overflow menu's, we have to use SubMenu instead of Menu. Don't forget to add setShowAction() property for the sub menu item. This property will change the style of the menu's to overflow.

We also have some default things for an activity such as Search. Considering these functionalities,  concept of ActionViews is introduced. In the above discussed OnCreateMenuOptions(), we have added search menu item to Menu and provided collapsible_edittext layout for the ActionView. Upon click of search Menu Item the layout appears on the top of action bar like ..

Sherlock ActionBar with Action View.
As per our layout edit text appears with hint as Search.

Split ActionBar :

 We can split the action bar based on the space availability. Just add a tag android:uiOptions = "splitActionBarWhenNarrow".

Observe the change in the below output by using this tag.

Split action bar in Android Phone
The split behavior is different in large screens. See the below output in Nexus 10 device.


From this observation, we can judge that split behavior comes into act when there is no real estate to display on action bar .

Related topics :