Wednesday, January 7, 2015

Android.mk for C++ native library compilation

Here is the sample template Android.mk which helps in compiling C++ Native source code into Shared library

Note: mentioned values differs from which features you need/or you dont need.


Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libabc
LOCAL_MODULE_TAGS := eng
LOCAL_CPP_EXTENSION := .cc
LOCAL_RTTI_FLAG := -frtti
LOCAL_SRC_FILES := main.cc
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
include external/stlport/libstlport.mk
LOCAL_LDFLAGS += -lc -lz
LOCAL_SHARED_LIBRARIES := libgabi++ libstlport
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)

Application.mk

APP_PLATFORM := android-9
APP_ABI := armeabi
APP_STL := stlport_static(or gnustl_static)
APP_CPPFLAGS := -fexceptions

Android Application: Thread Safe DB Access & Custom View display

After a pretty long timegap I am back again with Android development work. Today I add an opportunity to look into android application. Today I focus on key functionality such as

1. Custom View Display
2. Singleton Sqlite DB Connection across multiple threads

I have written a sample application, I am pasting code snippets which will explain both.

Note: There could be typo please correct it or put your comments so that I can correct it back

DBHelper.java

public class DBHelper extends SQLiteOpenHelper{
private static final String DB_NAME="Billing";

public DBHelper(Context context) {
super(context, DB_NAME, null, 1);
}

@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
db.execSQL("create table particular " + "(id integer primary key, particulars text, qty text, price text, date text)");
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
db.execSQL("DROP TABLE IF EXISTS particular");
   onCreate(db);
}
}

DatabaseManager.java

public class DatabaseManager {

private AtomicInteger mOpenCounter = new AtomicInteger();
private static DatabaseManager instance;
private static SQLiteOpenHelper mDatabaseHelper;
private SQLiteDatabase mDatabase;

public static synchronized void initializeInstance(SQLiteOpenHelper helper) {
if (instance == null) {
instance = new DatabaseManager();
mDatabaseHelper = helper;
}
}

public static synchronized DatabaseManager getInstance() {
if (instance == null) {
throw new IllegalStateException(
DatabaseManager.class.getSimpleName()
+ " is not initialized, call initializeInstance(..) method first.");
}
return instance;
}

public synchronized SQLiteDatabase openDatabase() {
if (mOpenCounter.incrementAndGet() == 1) {
// Opening new database
mDatabase = mDatabaseHelper.getWritableDatabase();
}
return mDatabase;
}
public synchronized SQLiteDatabase openReadDatabase() {
if (mOpenCounter.incrementAndGet() == 1) {
// Opening new database
mDatabase = mDatabaseHelper.getReadableDatabase();
}
return mDatabase;
}

public synchronized void closeDatabase() {
if (mOpenCounter.decrementAndGet() == 0) {
// Closing database
mDatabase.close();
}
}
}

Particular.java(Is the MainActivity)

package com.example.billtracking;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.app.ActionBar;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;

public class Particular extends ActionBarActivity {
EditText edtDate;
Button bAccept;
Button bClear;
public EditText edtParticular;
public EditText edtQty;
public EditText edtPrice;
public ListView listView;
ListViewAdapterHelper adapter;
static Map<Integer, List<String>> map = new HashMap<Integer, List<String>>();
static int i = 1;
private MenuItem menuItem;
public EditText edtSearch;
private ProgressDialog prgDialog;
public static final int progress_bar_type = 0;

/*
* FragmentManager fm; private boolean isTaskRunning = false;
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final ActionBar actionBar = getActionBar();

edtDate = (EditText) findViewById(R.id.date);
Date myDate = new Date();
edtDate.setText(new SimpleDateFormat("dd/MM/yyyy").format(myDate));

edtParticular = (EditText) findViewById(R.id.particular);
edtQty = (EditText) findViewById(R.id.qty);
edtPrice = (EditText) findViewById(R.id.price);

listView = (ListView) findViewById(R.id.list);

bAccept = (Button) findViewById(R.id.accept);
bClear = (Button) findViewById(R.id.clear);
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME
| ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_CUSTOM);

bAccept.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
listView.setAdapter(null);
// Create Inner Thread Class
Thread background = new Thread(new Runnable() {
public void run() {
DatabaseManager.initializeInstance(new DBHelper(
getApplicationContext()));
SQLiteDatabase dbSqlite = DatabaseManager.getInstance()
.openDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put("particulars", edtParticular
.getText().toString().trim());
contentValues.put("qty", edtQty.getText().toString()
.trim());
contentValues.put("price", edtPrice.getText()
.toString().trim());
contentValues.put("date", edtDate.getText().toString()
.trim());
dbSqlite.insert("particular", null, contentValues);
DatabaseManager.getInstance().closeDatabase();
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(getApplicationContext(),
"Successfully inserted",
Toast.LENGTH_SHORT).show();
}
});
}
});
background.start();

List<String> arraylist = new ArrayList<String>();
arraylist.add(edtParticular.getText().toString().trim());
arraylist.add(edtQty.getText().toString().trim());
arraylist.add(edtPrice.getText().toString().trim());
arraylist.add(edtDate.getText().toString().trim());
map.put(i++, arraylist);
listView.setAdapter(new ListViewAdapterHelper(getBaseContext(),
map));
}
});

bClear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (map.size() > 0) {
map.clear();
i = 1;
listView.setAdapter(null);
}
}
});
}

@Override
public void onResume() {
super.onResume();
if (map.size() > 0) {
listView.setAdapter(new ListViewAdapterHelper(getBaseContext(), map));
}
}

@Override
public void onPause() {
super.onPause();
listView.setAdapter(null);
}

@Override
public void onDestroy() {
super.onDestroy();
listView.setAdapter(null);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return super.onCreateOptionsMenu(menu);
}

@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case progress_bar_type:
prgDialog = new ProgressDialog(this);
prgDialog.setMessage("Displaying Usage Statistics...");
prgDialog.setIndeterminate(false);
prgDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
prgDialog.setCancelable(true);
prgDialog.show();
return prgDialog;
default:
return null;
}
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.search_action:
menuItem = item;
menuItem.setActionView(R.layout.search_layout);
menuItem.collapseActionView();
edtSearch = (EditText) item.getActionView().findViewById(
R.id.txt_search);

edtSearch.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if ((event.getAction() == KeyEvent.ACTION_DOWN)
&& (keyCode == KeyEvent.KEYCODE_ENTER)) {
if (!edtSearch.getText().toString().trim().equals("")) {
/*ThreadProcess tp = new ThreadProcess();
tp.execute(edtSearch.getText().toString().trim());*/
}
}
return false;
}
});
break;
default:
break;
}
return true;
}

/* class ThreadProcess extends AsyncTask<String, String, String> {

@Override
protected void onPreExecute() {
* int currentOrientation =
* getResources().getConfiguration().orientation; if
* (currentOrientation == Configuration.ORIENTATION_PORTRAIT) {
* setRequestedOrientation
* (ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } else {
* setRequestedOrientation
* (ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); }
 
showDialog(progress_bar_type);
}

@Override
protected void onPostExecute(String result) {
// setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);

dismissDialog(progress_bar_type);
Intent intent = new Intent(Particular.this, SearchResultsActivity.class);
startActivity(intent);
}

@Override
protected String doInBackground(String... params) {
// TODO Auto-generated method stub
try {
Thread.sleep(7000);
Map<String, Double> map = new HashMap<String, Double>();
DatabaseManager.initializeInstance(new DBHelper(
getApplicationContext()));
SQLiteDatabase dbSqlite = DatabaseManager.getInstance().openReadDatabase();
//where particulars=? ORDER BY date"
Cursor cursor = dbSqlite.rawQuery("select * from particular where particulars=? ORDER BY date", params);
Log.d("com.example.billtracking", "cursor getCount = "+cursor.getCount());
if (cursor.getCount() > 0) {
cursor.moveToFirst();
do {
String key = cursor.getString(cursor
.getColumnIndex("date"));
Double value = Double.parseDouble(cursor
.getString(cursor.getColumnIndex("qty")));

String[] sDate = key.split("/");
Double qtyTemp;

// Calculate total qty of an item within same month
if (map.containsKey(sDate[1])) {
qtyTemp = map.get(sDate[1]);
map.put(sDate[1], qtyTemp + value);
} else
map.put(sDate[1], value);

} while (cursor.moveToNext());
}
DatabaseManager.getInstance().closeDatabase();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}*/
}

listview_row.xml

<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="top"
    android:paddingTop="0dip" 
    android:stretchColumns="*"
    android:shrinkColumns="*">

    <TableRow>
        <TableLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="top"
            android:paddingTop="0dip" >

            <TableRow>

                <TextView
                    android:id="@+id/textParticular"
                    android:layout_width="wrap_content"
                    android:layout_weight="1"
                    android:layout_height="wrap_content"
                    android:textColor="#000000"
                    android:layout_gravity="left|top"
                    android:layout_marginLeft="10dip"
                    android:layout_marginTop="2dip"
                    android:gravity="left" />                   

                <TextView
                    android:id="@+id/textQty"
                    android:layout_width="wrap_content"
                    android:layout_weight="1"
                    android:textColor="#000000"
                    android:layout_height="wrap_content"
                    android:layout_gravity="top|right"
                    android:layout_marginRight="10dip"
                    android:gravity="right"
                    android:layout_marginTop="2dip" />                    
                    
            </TableRow>

            <TableRow>

                <TextView
                    android:id="@+id/textPrice"
                    android:layout_width="wrap_content"
                    android:textColor="#000000"
                    android:layout_weight="1"
                    android:layout_height="wrap_content"
                    android:layout_gravity="bottom|left"
                    android:layout_marginLeft="10dip"
                    android:layout_marginTop="4dip"
                    android:gravity="left"
                    android:text="" />

                <TextView
                    android:id="@+id/textDate"
                    android:layout_width="wrap_content"
                    android:layout_weight="1"
                    android:textColor="#000000"
                    android:layout_height="wrap_content"
                    android:layout_gravity="bottom|right"
                    android:layout_marginRight="10dip"
                    android:layout_marginTop="4dip"
                    android:gravity="right"
                    android:text="" />
                    
            </TableRow>
        </TableLayout>       
    </TableRow>
</TableLayout>


search_layout.xml

<?xml version="1.0" encoding="utf-8"?>

<EditText xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/txt_search"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:inputType="text"
    android:textColor="#ffffff"
    android:drawableLeft="@drawable/ic_action_search"
    android:hint="Usage Statistics"
    android:focusableInTouchMode="true"
    >

</EditText>


main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingLeft="16dp"
    android:paddingRight="16dp" >

    <EditText
        android:id="@+id/particular"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/particular" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <EditText
            android:id="@+id/qty"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="@string/qty" />

        <EditText
            android:id="@+id/price"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/price"     
            android:maxLines="1" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal" 
        >

        <EditText
            android:id="@+id/date"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="@string/date"
            android:inputType="date"
            android:maxLines="1" />

        <Button
            android:id="@+id/accept"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="@string/accept"
            android:paddingBottom="0dp"/>
        <Button
            android:id="@+id/clear"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="@string/clear"
            android:paddingBottom="0dp"/>
       
    </LinearLayout>
    <ListView
        android:id="@+id/list"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" 
        android:layout_weight="1"/>

</LinearLayout>

menu/main.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:billtracking="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="com.example.billtracking.MainActivity" >

    <item
        android:id="@+id/search_action"
        android:actionViewClass="android.widget.SearchView"
        android:icon="@drawable/ic_action_search"
        android:title="@string/action_search"
        billtracking:showAsAction="collapseActionView|ifRoom"/>
    <item
        android:id="@+id/empty"
        android:icon="@drawable/ic_action_overflow"        
        android:title="@string/overflow_menu"
        billtracking:showAsAction="always">
       <!--  <menu>
            <item
                android:id="@+id/rate"
                android:icon="@drawable/ic_menu_friendslist"
                android:showAsAction="always|withText"
                android:title="List"/>
        </menu> -->
    </item>
    <item
        android:id="@+id/action_settings"
        android:orderInCategory="100"
        android:title="@string/action_settings"
        billtracking:showAsAction="never"/>

</menu>