Click here to Skip to main content
14,265,815 members
Rate this:
Please Sign up or sign in to vote.
See more:
I created a custom ListView that displays data from a SQLite database using customCursorAdapter which extends CursorAdapter and then I added OnItemClickListener to the ListView, but when I tap an Item in the ListView nothing happens

What I have tried:

and here is what I have tried
first: the layout for the custom list item
<?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="wrap_content"
    android:layoutDirection="rtl">

    <TextView
        android:id="@+id/txtLICustomerName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@id/ibDeleteCustomer"
        android:layout_alignBottom="@id/ibDeleteCustomer"
        android:layout_alignParentStart="true"
        android:layout_toStartOf="@id/ibUpdateCustomer"
        android:text=""
        android:textSize="24dp" />

    <TextView
        android:id="@+id/txtLICustomerBalance"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/txtLICustomerName"
        android:layout_alignParentStart="true"
        android:text=""
        android:textSize="24dp" />

    <ImageButton
        android:id="@+id/ibDeleteCustomer"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentEnd="true"
        android:src="@drawable/ic_close_black_48dpn" />

    <ImageButton
        android:id="@+id/ibUpdateCustomer"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toStartOf="@+id/ibDeleteCustomer"
        android:src="@drawable/ic_create_black_48dpn" />

    <ImageButton
        android:id="@+id/ibCallCustomer"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toStartOf="@+id/ibUpdateCustomer"
        android:src="@drawable/ic_call_black_48dpn" />

    <ImageButton
        android:id="@+id/ibViewCustomerDetails"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignStart="@id/ibDeleteCustomer"
        android:layout_alignTop="@id/txtLICustomerBalance"
        android:layout_alignEnd="@id/ibDeleteCustomer"
        android:src="@drawable/ic_chrome_reader_mode_black_48dpn" />

</RelativeLayout>


second: the layout of the activity that holds the ListView
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout

    xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    xmlns:app="http://schemas.android.com/apk/res-auto"

    android:orientation="vertical"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:layoutDirection="rtl"

    tools:context=".ViewCustomersActivity">

    <TextView

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:layout_marginTop="5dp"

        android:layout_marginStart="5dp"

        android:layout_gravity="start"

        android:background="@android:color/holo_blue_bright"

        android:textColor="@color/colorBlue"

        android:text="@string/stringChooseCustomer"

        android:textSize="24sp" />

    <ListView

        android:id="@+id/lstCustomers"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:layout_marginStart="8dp"

        android:layout_gravity="start"

        android:textDirection="rtl" />

</LinearLayout>


Third: the custom cursor adapter
package com.nsystems.elkest;

import android.content.Context;
import android.database.Cursor;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.ImageButton;
import android.widget.TextView;

public class CustomerCursorAdapter extends CursorAdapter {
    public CustomerCursorAdapter(Context context, Cursor c, boolean autoRequery) {
        super(context, c, autoRequery);
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        return LayoutInflater.from(context).inflate(R.layout.customer_list_row, parent, false);
    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        TextView txtLICustomerName = (TextView)view.findViewById(R.id.txtLICustomerName);
        TextView txtLICustomerBalance = (TextView)view.findViewById(R.id.txtLICustomerBalance);

        ImageButton ibUpdateCustomer = (ImageButton)view.findViewById(R.id.ibUpdateCustomer);
        ImageButton ibDeleteCustomer = (ImageButton)view.findViewById(R.id.ibDeleteCustomer);
        ImageButton ibCallCustomer = (ImageButton)view.findViewById(R.id.ibCallCustomer);
        ImageButton ibViewCustomerDetails = (ImageButton)view.findViewById(R.id.ibViewCustomerDetails);

        String customerName="";
        String customerBalance="";
        String customerId="";
        String customerPhone="01287622467";

        customerId = Long.toString(cursor.getLong(0));
        customerName = cursor.getString(1);
        customerBalance = cursor.getString(2);

        txtLICustomerName.setText(customerName);
        txtLICustomerBalance.setText(customerBalance);

        ibCallCustomer.setTag(customerPhone);
        ibDeleteCustomer.setTag(customerId);
        ibUpdateCustomer.setTag(customerId);
        ibViewCustomerDetails.setTag(customerId);
    }

}


Fourth: the activity that connects them all
package com.nsystems.elkest;

import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.graphics.Color;
import android.os.AsyncTask;
import android.support.annotation.ColorRes;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.CursorAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.nsystems.elkest.Data.KestSQLiteHelper;

public class ViewCustomersActivity extends AppCompatActivity {

    long townId;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_customers);

        Intent intentCaller = getIntent();
        townId = intentCaller.getLongExtra("TownId", 0);
        new getCustomersAsyncTask().execute(townId);

        AdapterView.OnItemClickListener itemClickListener = new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Toast.makeText(ViewCustomersActivity.this,"hiiiiiiiiiii",Toast.LENGTH_LONG).show();
                /*int customerBalance;
                String customerName;
                String balance;
                customerName = ((TextView) view.findViewById(R.id.txtLICustomerName)).getText().toString();
                Toast.makeText(ViewCustomersActivity.this,customerName,Toast.LENGTH_LONG).show();
                balance = ((TextView) view.findViewById(R.id.txtLICustomerBalance)).getText().toString();
                if (balance.isEmpty())
                    balance = "0";
                Toast.makeText(ViewCustomersActivity.this,balance,Toast.LENGTH_LONG).show();
                customerBalance = Integer.valueOf(balance);
                Intent intentStartTransactionActivity = new Intent(ViewCustomersActivity.this, TransactionActivity.class);
                intentStartTransactionActivity.putExtra("TownId", townId);
                intentStartTransactionActivity.putExtra("CustomerId", id);
                intentStartTransactionActivity.putExtra("CustomerName", customerName);
                intentStartTransactionActivity.putExtra("CustomerBalance", customerBalance);
                startActivity(intentStartTransactionActivity);*/
            }
        };
        ListView lstOfCustomers = (ListView) findViewById(R.id.lstCustomers);
        lstOfCustomers.setOnItemClickListener(itemClickListener);
    }

    @Override
    protected void onResume() {
        super.onResume();
        new getCustomersAsyncTask().execute(townId);
    }




    private class getCustomersAsyncTask extends AsyncTask<Long, Void, Result> {
        ListView lstCustomers;
        Cursor cursorCustomers;

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            lstCustomers = (ListView) findViewById(R.id.lstCustomers);
        }

        @Override
        protected Result doInBackground(Long... longs) {
            Result result = new Result();
            SQLiteOpenHelper kestDataBaseHelper = new KestSQLiteHelper(ViewCustomersActivity.this);
            try {
                SQLiteDatabase dbKest = kestDataBaseHelper.getReadableDatabase();
                SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
                queryBuilder.setTables("Customer LEFT OUTER JOIN MoneyTrans ON Customer._id = MoneyTrans.Customer_id AND Customer.Closed=0 AND MoneyTrans.Closed=0");
                cursorCustomers = queryBuilder.query(dbKest,
                        new String[]{"Customer._id", "Customer.Customer_Name", "SUM(Money)"},
                        "Customer.Town_id=?", new String[]{Long.toString(longs[0])},
                        "Customer.Customer_Name", null, "Customer._id");
                result.setSuccess(true);
                return result;
            } catch (Exception exception) {
                result.setSuccess(false);
                result.setException(exception);
                return result;
            }
        }

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

            if (result.isSuccess()) {
                CustomerCursorAdapter adapterLstTowns= new CustomerCursorAdapter(ViewCustomersActivity.this, cursorCustomers,true);
                lstCustomers.setAdapter(adapterLstTowns);

                /*lstCustomers.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                    @Override
                    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                        Toast.makeText(ViewCustomersActivity.this,"Hiiiiiiiiiiiiiiiiiiii2",Toast.LENGTH_LONG).show();
                    }
                });*/
            } else {
                Toast toast = Toast.makeText(ViewCustomersActivity.this, result.getException().getMessage().toString(), Toast.LENGTH_LONG);
                toast.show();
            }
        }
    }
}
Posted
Updated 2 days ago
Comments
David Crow 4 days ago
   
Just a guess, but since adapterLstTowns and cursorCustomers are both local to getCustomersAsyncTask, when that task goes out of scope, maybe those local variables do too. Try moving them to the main activity.
Member 9687317 yesterday
   
No sir, this didn't work

1 solution

Rate this:
Please Sign up or sign in to vote.

Solution 1

I believe that your issue is that the ImageButtons are trapping the onClickEvents, try changing the ImageButtons to ImageViews e.g.


<ImageView
        android:id="@+id/ibDeleteCustomer"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:src="@drawable/ic_launcher_foreground"
        />


Along with :-

ImageView ibUpdateCustomer = (ImageView) view.findViewById(R.id.ibUpdateCustomer);
ImageView ibDeleteCustomer = (ImageView) view.findViewById(R.id.ibDeleteCustomer);
ImageView ibCallCustomer = (ImageView) view.findViewById(R.id.ibCallCustomer);
ImageView ibViewCustomerDetails = (ImageView) view.findViewById(R.id.ibViewCustomerDetails);


However

I suspect that what you want is to handle the buttons/views individually. That is setting the ListView's onItemClick listener, as per your current code, handles the **Item** rather than a specific Button/View within the item. As such the above fix will invoke the Toast but the View returned from the Listener will be the RelativeLayout. You would not be able to know which Button has been clicked and thus what action (Update/Delete/Call or View) to take.

What you need to do, if you want to handle the buttons/views themselves is set onCLickListeners for the buttons/views to handle them being clicked.

A quick, but discouraged way, is to use the onClick attribute within the xml.

For Example :-

Using :-

<ImageView
    android:id="@+id/ibDeleteCustomer"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentEnd="true"
    android:layout_alignParentRight="true"
    android:src="@drawable/ic_launcher_foreground"
    android:onClick="doDeleteOnClick"
    />


see the last line

And then adding the following to the Activity's code :-

public void doDeleteOnClick(View v) {
        Toast.makeText(v.getContext(),"You clicked the delete button for id " + ((String) v.getTag()), Toast.LENGTH_SHORT).show();
    }


Would issue the Toast for the respective row, displaying the id retrieved from the Tag.

Working Example

The following code is a working example that handles ListView Item clicks (by adding a row to the database and thus the refreshed ListView) and also ImageView clicks (by Toasting a message specific to the button).

The code is based upon your code, but some changes have been incorporated for convenience (e.g. the underlying database used for the source of the ListView is a single table, also the data is retrieved from the main thread.)

customer_list_row.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="wrap_content"
    android:layoutDirection="rtl">

    <TextView
        android:id="@+id/txtLICustomerName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@id/ibDeleteCustomer"
        android:layout_alignBottom="@id/ibDeleteCustomer"
        android:layout_alignParentStart="true"
        android:layout_alignParentLeft="true"
        android:layout_toStartOf="@id/ibUpdateCustomer"
        android:layout_toLeftOf="@+id/ibUpdateCustomer"
        android:text=""
        android:textSize="24dp" />

    <TextView
        android:id="@+id/txtLICustomerBalance"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/txtLICustomerName"
        android:layout_alignParentStart="true"
        android:layout_alignParentLeft="true"
        android:text=""
        android:textSize="24dp" />

    <ImageView
        android:id="@+id/ibDeleteCustomer"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:src="@drawable/ic_launcher_foreground"
        android:onClick="doDeleteOnClick"
        />

    <ImageView
        android:id="@+id/ibUpdateCustomer"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toStartOf="@+id/ibDeleteCustomer"
        android:layout_toLeftOf="@+id/ibDeleteCustomer"
        android:src="@drawable/ic_launcher_foreground"
        android:onClick="doUpdateOnClick"
        />

    <ImageView
        android:id="@+id/ibCallCustomer"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toStartOf="@+id/ibUpdateCustomer"
        android:layout_toLeftOf="@+id/ibUpdateCustomer"
        android:src="@drawable/ic_launcher_foreground"
        android:onClick="doCallOnCLick"
        />

    <ImageView
        android:id="@+id/ibViewCustomerDetails"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignStart="@id/ibDeleteCustomer"
        android:layout_alignLeft="@+id/ibDeleteCustomer"
        android:layout_alignTop="@id/txtLICustomerBalance"
        android:layout_alignEnd="@id/ibDeleteCustomer"
        android:layout_alignRight="@+id/ibDeleteCustomer"
        android:src="@drawable/ic_launcher_foreground"
        android:onClick="doViewOnClick"
        />
</RelativeLayout>


CustomerCursorAdapter.java

public class CustomerCursorAdapter extends CursorAdapter {

    public CustomerCursorAdapter(Context context, Cursor c, boolean autoRequery) {
        super(context, c, autoRequery);
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        return LayoutInflater.from(context).inflate(R.layout.customer_list_row, parent, false);
    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        TextView txtLICustomerName = (TextView)view.findViewById(R.id.txtLICustomerName);
        TextView txtLICustomerBalance = (TextView)view.findViewById(R.id.txtLICustomerBalance);

        ImageView ibUpdateCustomer = (ImageView) view.findViewById(R.id.ibUpdateCustomer);
        ImageView ibDeleteCustomer = (ImageView) view.findViewById(R.id.ibDeleteCustomer);
        ImageView ibCallCustomer = (ImageView) view.findViewById(R.id.ibCallCustomer);
        ImageView ibViewCustomerDetails = (ImageView)view.findViewById(R.id.ibViewCustomerDetails);

        String customerName="";
        String customerBalance="";
        String customerId="";
        String customerPhone="01287622467";

        customerId = Long.toString(cursor.getLong(0));
        customerName = cursor.getString(1);
        customerBalance = cursor.getString(2);

        txtLICustomerName.setText(customerName);
        txtLICustomerBalance.setText(customerBalance);

        ibCallCustomer.setTag(customerPhone);
        ibDeleteCustomer.setTag(customerId);
        ibUpdateCustomer.setTag(customerId);
        ibViewCustomerDetails.setTag(customerId);
    }
}


MainActivity.java (the equivalent of ViewCustomersActivity)

public class MainActivity extends AppCompatActivity {

    long townId;
    KestSQLiteHelper kestDB;
    Cursor cursor;
    CustomerCursorAdapter adapterLstTowns;
    ListView lstOfCustomers;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lstOfCustomers = this.findViewById(R.id.lstCustomers);
        kestDB = new KestSQLiteHelper(this);
        addSomeData(); // Add some testing data

        //Intent intentCaller = getIntent();
        townId =  /*intentCaller.getLongExtra("TownId", 0); */  1;
        //new getCustomersAsyncTask().execute(townId);
        //More of your code
    }

    @Override
    protected void onResume() {
        super.onResume();
        manageListView(); // Refreshes the listview when resuming (
        // note that onResume is called when the App is started so this invokes the initialiastion of thw adapter/listview)
        //new getCustomersAsyncTask().execute(townId);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (!cursor.isClosed()) {
            cursor.close();
        }
    }

    /**
     * Handles management of the ListView setting up the adapter,
     * tying it to the ListView and setting the onItemClick listener once when the activity is started
     * subsequently, when called it just refreshes the ListView by swapping the Cursor
     */
    public void manageListView() {
        cursor = getDataForListView();
        if (adapterLstTowns == null) {
            adapterLstTowns = new CustomerCursorAdapter(this,cursor,true);
            lstOfCustomers.setAdapter(adapterLstTowns);
            lstOfCustomers.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                    Toast.makeText(view.getContext(),"Hiiiiiiiiiiiiiiiiiiii2",Toast.LENGTH_SHORT).show();
                    kestDB.addCustomer("ANOTHER" + String.valueOf(System.currentTimeMillis()),300);
                    manageListView();
                }
            });
        } else {
            adapterLstTowns.swapCursor(cursor);
        }
    }

    /**
     * Handle the ibDeleteCustomer view being clicked.
     */
    public void doDeleteOnClick(View v) {
        Toast.makeText(v.getContext(),"You clicked the DELETE button for id " + ((String) v.getTag()), Toast.LENGTH_SHORT).show();
    }
    public void doUpdateOnClick(View v) {
        Toast.makeText(v.getContext(),"You clicked the UPDATE button for id " + ((String) v.getTag()),Toast.LENGTH_SHORT).show();
    }
    public void doCallOnCLick(View v) {
        Toast.makeText(v.getContext(),"You clicked the CALL button for # " + ((String) v.getTag()),Toast.LENGTH_SHORT).show();
    }
    public void doViewOnClick(View v) {
        Toast.makeText(v.getContext(),"You clicked the VIEW button for id " + ((String) v.getTag()),Toast.LENGTH_SHORT).show();
    }

    /**
     * routine to get the data (note done on UI thread for simplicity/convenience)
     */
    public Cursor getDataForListView() {
        //!!!!!!!!!! For simplicity of the solution Database call is made on main UI thread
        SQLiteDatabase db = kestDB.getWritableDatabase();
        return db.query(
                KestSQLiteHelper.TBL_CUSTOMER,
                new String[]{KestSQLiteHelper.COL_CUSTOMER_ID,
                        KestSQLiteHelper.COL_CUSTOMERNAME,"1000"},
                null,null,null,null,null
        );
    }

    /**
     * Add some testing data (note the underlying data has been simplified for convenience)
     */
    private void addSomeData() {
        if(DatabaseUtils.queryNumEntries(kestDB.getWritableDatabase(),KestSQLiteHelper.TBL_CUSTOMER) < 1) {
            kestDB.addCustomer("Fred",100);
            kestDB.addCustomer("Mary",200);
        }
    }
}


KestSQLiteHelper.java (simplified DB for demo)

public class KestSQLiteHelper extends SQLiteOpenHelper {

    public static final String DBNAME = "mydb";
    public static final int DBVERSION = 1;

    public static final String TBL_CUSTOMER = "Customer";
    public static final String COL_CUSTOMER_ID = BaseColumns._ID;
    public static final String COL_CUSTOMERNAME = "Customer_Name";
    public static final String COL_CUSTOMER_TOWNID = "Town_id";


    public KestSQLiteHelper(@Nullable Context context) {
        super(context, DBNAME, null, DBVERSION);
    }


    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE IF NOT EXISTS " + TBL_CUSTOMER + "(" +
                COL_CUSTOMER_ID + " INTEGER PRIMARY KEY, " +
                COL_CUSTOMERNAME + " TEXT," +
                COL_CUSTOMER_TOWNID + " INTEGER" +
                ")");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }

    public long addCustomer(String customerName, long townId) {
        ContentValues cv = new ContentValues();
        cv.put(COL_CUSTOMERNAME,customerName);
        cv.put(COL_CUSTOMER_TOWNID,townId);
        SQLiteDatabase db = this.getWritableDatabase();
        return db.insert(TBL_CUSTOMER,null,cv);
    }
}


activity_main.xml (the equivalent of activity_view_customers.xml)

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

    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layoutDirection="rtl"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:layout_marginStart="5dp"
        android:layout_marginLeft="5dp"
        android:layout_gravity="start"
        android:background="@android:color/holo_blue_bright"
        android:textColor="#FF0000FF"
        android:text="Customer"
        android:textSize="24sp" />
    <ListView
        android:id="@+id/lstCustomers"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="5dp"
        android:layout_gravity="start"
        android:textDirection="rtl" />
</LinearLayout>


The above handles button Clicks and also Item clicks. However, changing to use ImageButtons instead of ImageViews disables the Item click handling.
   
v5
Comments
Member 9687317 yesterday
   
Image buttons are not trapping onClickEvents, actually I handle button clicks with no problems, the problem is when I click the list item (not the buttons in it) nothing happenss
TheOldFogie yesterday
   
The question has been updated with a proof of concept working example. That is if you create an App with the working example then you will see that it demonstrates trapping both Item and Button (ImageView) clicks. If you then simply change the ImageView's to ImageButtons the working example then only catches the Button clicks. There are XML options that allow Item onClick events to be handled. However I find it easier to use Views rather than Buttons. (I think descendantFocusability is one of the attributes that needs to be adjusted and perhaps clickable.)

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)




CodeProject, 503-250 Ferrand Drive Toronto Ontario, M3C 3G8 Canada +1 416-849-8900 x 100