net.ddns.mlsoftlaberge.contactslist.ui.ContactsBudgetFragment.java Source code

Java tutorial

Introduction

Here is the source code for net.ddns.mlsoftlaberge.contactslist.ui.ContactsBudgetFragment.java

Source

/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.ddns.mlsoftlaberge.contactslist.ui;

import android.annotation.SuppressLint;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.provider.ContactsContract.CommonDataKinds.Note;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;

import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import net.ddns.mlsoftlaberge.contactslist.R;

import java.util.Vector;

/**
 * This fragment displays admins of a specific contact from the contacts provider. It shows the
 * contact's display photo, name and all its mailing addresses. You can also modify this fragment
 * to show other information, such as phone numbers, email addresses and so forth.
 */
public class ContactsBudgetFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {

    public static final String EXTRA_CONTACT_URI = "net.ddns.mlsoftlaberge.contactslist.EXTRA_CONTACT_URI";

    public Uri mContactUri; // Stores the contact Uri for this fragment instance
    public Uri mDataUri; // stores the data uri
    public int nbloaders; // stores the nb loaders who are finished

    // structure of a row of the transaction table
    private class Transac {
        String trdate;
        String amount;
        String descrip;
    }

    private class Clients {
        public String id;
        public String name;
        public String rawid;
        public String phone;
        public String phonetypelabel;
        public String address;
        public String addresstypelabel;
        public String email;
        public String emailtypelabel;
        // tables containing the expanded note data
        public int nbtransac;
        public Vector<Transac> transaclist = new Vector<Transac>(10, 10);

    }

    // table containing the list of clients from the database
    private int nbclients = 0;
    private Clients clientslist[] = new Clients[500];

    public int getClient(String id) {
        for (int i = 0; i < nbclients; ++i) {
            if (clientslist[i].id.equals(id))
                return (i);
        }
        if (nbclients >= 500)
            return (0);
        clientslist[nbclients] = new Clients();
        clientslist[nbclients].id = id;
        clientslist[nbclients].nbtransac = 0;
        nbclients++;
        return (nbclients - 1);
    }

    // holders for the widgets
    private LinearLayout budget_layout;

    /**
     * Fragments require an empty constructor.
     */
    public ContactsBudgetFragment() {
    }

    /**
     * When the Fragment is first created, this callback is invoked. It initializes some key
     * class fields.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Let this fragment contribute menu items
        setHasOptionsMenu(true);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        // Inflates the main layout to be used by this fragment
        final View budgetView = inflater.inflate(R.layout.contacts_budget_fragment, container, false);

        budget_layout = (LinearLayout) budgetView.findViewById(R.id.budget_layout);

        return budgetView;
    }

    // set the contact URI with the new activity or saved activity state
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        Uri uri;
        // If not being created from a previous state
        if (savedInstanceState == null) {
            // Sets the argument extra as the currently displayed contact
            uri = (getArguments() != null ? (Uri) getArguments().getParcelable(EXTRA_CONTACT_URI) : null);
        } else {
            // If being recreated from a saved state, sets the contact from the incoming
            // savedInstanceState Bundle
            uri = ((Uri) savedInstanceState.getParcelable(EXTRA_CONTACT_URI));
        }
        setContact(uri);
    }

    @Override
    public void onResume() {
        super.onResume();
    }

    /**
     * When the Fragment is being saved in order to change activity state, save the
     * currently-selected contact.
     */
    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        // Saves the contact Uri
        outState.putParcelable(EXTRA_CONTACT_URI, mContactUri);
    }

    // create the menu option to modify this contact
    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);
        // Inflates the options menu for this fragment
        inflater.inflate(R.menu.contacts_budget_menu, menu);
    }

    // open the edit-contact activity when asked by the top menu option
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        // When "edit" menu option selected
        case R.id.menu_budget_edit:
            // Standard system edit contact intent
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     * Sets the contact that this Fragment displays, or clears the display if the contact argument
     * is null. This will re-initialize all the views and start the queries to the system contacts
     * provider to populate the contact information.
     *
     * @param contactLookupUri The contact lookup Uri to load and display in this fragment. Passing
     *                         null is valid and the fragment will display a message that no
     *                         contact is currently selected instead.
     */
    public void setContact(Uri contactLookupUri) {

        // A content URI for the Contacts table
        mContactUri = Contacts.CONTENT_URI;
        mDataUri = Uri.parse("content://com.android.contacts/data");

        // If the Uri contains data, load the contact's image and load contact admins.
        if (mContactUri != null) {
            // Starts two queries to to retrieve contact information from the Contacts Provider.
            // restartLoader() is used instead of initLoader() as this method may be called
            // multiple times.
            nbclients = 0;
            nbloaders = 0;
            getLoaderManager().restartLoader(ContactDetailQuery.QUERY_ID, null, this);
            getLoaderManager().restartLoader(ContactAddressQuery.QUERY_ID, null, this);
            getLoaderManager().restartLoader(ContactPhoneQuery.QUERY_ID, null, this);
            getLoaderManager().restartLoader(ContactEmailQuery.QUERY_ID, null, this);
            getLoaderManager().restartLoader(ContactNotesQuery.QUERY_ID, null, this);
        }
    }

    // create a loader to find the results of the contact URI for every querys
    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        switch (id) {
        // Two main queries to load the required information
        case ContactDetailQuery.QUERY_ID:
            // This query loads main contact admins, see
            // ContactDetailQuery for more information.
            return new CursorLoader(getActivity(), mContactUri, ContactDetailQuery.PROJECTION,
                    ContactDetailQuery.SELECTION, null, ContactDetailQuery.SORT_ORDER);
        case ContactAddressQuery.QUERY_ID:
            // This query loads contact address admins, see
            // ContactAddressQuery for more information.
            final Uri uri = Uri.withAppendedPath(mContactUri, Contacts.Data.CONTENT_DIRECTORY);
            return new CursorLoader(getActivity(), mDataUri, ContactAddressQuery.PROJECTION,
                    ContactAddressQuery.SELECTION, null, null);
        case ContactNotesQuery.QUERY_ID:
            // This query loads contact address admins, see
            // ContactAddressQuery for more information.
            final Uri nuri = Uri.withAppendedPath(mContactUri, Contacts.Data.CONTENT_DIRECTORY);
            return new CursorLoader(getActivity(), mDataUri, ContactNotesQuery.PROJECTION,
                    ContactNotesQuery.SELECTION, null, null);
        case ContactPhoneQuery.QUERY_ID:
            // This query loads contact address admins, see
            // ContactAddressQuery for more information.
            final Uri puri = Uri.withAppendedPath(mContactUri, Contacts.Data.CONTENT_DIRECTORY);
            return new CursorLoader(getActivity(), mDataUri, ContactPhoneQuery.PROJECTION,
                    ContactPhoneQuery.SELECTION, null, null);
        case ContactEmailQuery.QUERY_ID:
            // This query loads contact address admins, see
            // ContactAddressQuery for more information.
            final Uri euri = Uri.withAppendedPath(mContactUri, Contacts.Data.CONTENT_DIRECTORY);
            return new CursorLoader(getActivity(), mDataUri, ContactEmailQuery.PROJECTION,
                    ContactEmailQuery.SELECTION, null, null);
        }
        return null;
    }

    // reset the loader
    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        // Nothing to do here. The Cursor does not need to be released as it was never directly
        // bound to anything (like an adapter).
    }

    // process the loader finished request
    // fill all fields on screen, and all layouts with data from the querys
    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        // If this fragment was cleared while the query was running
        // eg. from from a call like setContact(uri) then don't do
        // anything.
        if (mContactUri == null) {
            return;
        }
        if (data == null) {
            return;
        }
        // collect datas depending on the loader id
        switch (loader.getId()) {
        case ContactDetailQuery.QUERY_ID:
            // Moves to the first row in the Cursor
            if (data.moveToFirst()) {
                do {
                    String contactid = data.getString(ContactDetailQuery.ID);
                    String rawcontactid = data.getString(ContactDetailQuery.RAWID);
                    String contactName = data.getString(ContactDetailQuery.DISPLAY_NAME);
                    // place the datas in the clients table
                    int clientno = getClient(rawcontactid);
                    clientslist[clientno].name = contactName;
                    clientslist[clientno].rawid = rawcontactid;
                } while (data.moveToNext());
            }
            nbloaders++;
            break;
        case ContactAddressQuery.QUERY_ID:
            // This query loads the contact address .
            if (data.moveToFirst()) {
                do {
                    // Builds the address layout
                    String contactid = data.getString(ContactAddressQuery.ID);
                    String rawcontactid = data.getString(ContactAddressQuery.RAWID);
                    int addrtype = data.getInt(ContactAddressQuery.TYPE);
                    String addrlabel = data.getString(ContactAddressQuery.LABEL);
                    String addrdata = data.getString(ContactAddressQuery.ADDRESS);
                    // Gets postal address label type
                    CharSequence alabel = StructuredPostal.getTypeLabel(getResources(), addrtype, addrlabel);
                    // place the datas in the clients table
                    int clientno = getClient(rawcontactid);
                    clientslist[clientno].address = addrdata;
                    clientslist[clientno].addresstypelabel = alabel.toString();
                } while (data.moveToNext());
            }
            nbloaders++;
            break;
        case ContactPhoneQuery.QUERY_ID:
            // This query loads the contact phone
            if (data.moveToFirst()) {
                do {
                    String contactid = data.getString(ContactPhoneQuery.ID);
                    String rawcontactid = data.getString(ContactPhoneQuery.RAWID);
                    int phonetype = data.getInt(ContactPhoneQuery.TYPE);
                    String phonelabel = data.getString(ContactPhoneQuery.LABEL);
                    String phonedata = data.getString(ContactPhoneQuery.PHONE);
                    // Gets phone label type
                    CharSequence plabel = android.provider.ContactsContract.CommonDataKinds.Phone
                            .getTypeLabel(getResources(), phonetype, phonelabel);
                    // place the datas in the clients table
                    int clientno = getClient(rawcontactid);
                    clientslist[clientno].phone = phonedata;
                    clientslist[clientno].phonetypelabel = plabel.toString();
                } while (data.moveToNext());
            }
            nbloaders++;
            break;
        case ContactEmailQuery.QUERY_ID:
            // This query loads the contact email
            if (data.moveToFirst()) {
                do {
                    String contactid = data.getString(ContactEmailQuery.ID);
                    String rawcontactid = data.getString(ContactEmailQuery.RAWID);
                    int emailtype = data.getInt(ContactEmailQuery.TYPE);
                    String emaillabel = data.getString(ContactEmailQuery.LABEL);
                    String emaildata = data.getString(ContactEmailQuery.EMAIL);
                    // Gets email label type
                    CharSequence elabel = android.provider.ContactsContract.CommonDataKinds.Email
                            .getTypeLabel(getResources(), emailtype, emaillabel);
                    // place the datas in the clients table
                    int clientno = getClient(rawcontactid);
                    clientslist[clientno].email = emaildata;
                    clientslist[clientno].emailtypelabel = elabel.toString();
                } while (data.moveToNext());
            }
            nbloaders++;
            break;
        case ContactNotesQuery.QUERY_ID:
            // This query loads the contact notes
            // Get the first row of the cursor (table contains only one row)
            if (data.moveToFirst()) {
                do {
                    String contactid = data.getString(ContactNotesQuery.ID);
                    String rawcontactid = data.getString(ContactNotesQuery.RAWID);
                    String notesdata = data.getString(ContactNotesQuery.NOTE);
                    // place the datas in the clients table
                    int clientno = getClient(rawcontactid);
                    expandnote(clientno, notesdata);
                } while (data.moveToNext());
            }
            nbloaders++;
            break;
        }
        // when all loaders are finished, then display the contents
        if (nbloaders >= 5) {
            filltransactionlayout();
        }

    }

    // --------------------------------------------------------------------
    // this is the decoding/encoding part of the transaction table
    // --------------------------------------------------------------------

    // expand the note in a table of fields
    // scan mNotesData and cut it in fields and tables
    private void expandnote(int clientno, String notesdata) {
        // start with an empty list of transactions in the client
        clientslist[clientno].nbtransac = 0;
        clientslist[clientno].transaclist.removeAllElements();
        // expand note in lines
        int i = 0;
        int p;
        while (i < notesdata.length()) {
            // find the position of the end of line (may be end of block too)
            p = notesdata.indexOf('\n', i);
            if (p < 0) {
                p = notesdata.length() - 1;
            }
            String noteline = notesdata.substring(i, p + 1);
            // if an admin line, then decode it
            // else add it to the memo field
            if (noteline.indexOf("ADMIN|") == 0) {
                decodeline(clientno, noteline);
            }
            // advance after the last char eated by line
            i = p + 1;
        }
    }

    // decode the noteline beginning by ADMIN| and add it to the table
    private void decodeline(int clientno, String noteline) {
        // create an empty default transaction
        Transac transac = new Transac();
        // decode the line
        int i = 6;
        int p;
        // search for the date
        p = noteline.indexOf('|', i);
        if (p < 0)
            p = noteline.length() - 1;
        if (p >= i) {
            transac.trdate = noteline.substring(i, p);
            i = p + 1;
            // search for the amount
            p = noteline.indexOf('|', i);
            if (p < 0)
                p = noteline.length() - 1;
            if (p >= i) {
                transac.amount = noteline.substring(i, p);
                i = p + 1;
                // search for the description
                p = noteline.indexOf('|', i);
                if (p < 0)
                    p = noteline.indexOf('\n', i);
                if (p < 0)
                    p = noteline.length();
                if (p >= i) {
                    transac.descrip = noteline.substring(i, p);
                    i = p + 1;
                }
            }
        }
        // add element to the table
        clientslist[clientno].transaclist.addElement(transac);
        clientslist[clientno].nbtransac++;
    }

    /**
     * This interface defines constants used by contact retrieval queries.
     */
    public interface ContactDetailQuery {
        // A unique query ID to distinguish queries being run by the
        // LoaderManager.
        final static int QUERY_ID = 1;

        // The query projection (columns to fetch from the provider)
        @SuppressLint("InlinedApi")
        final static String[] PROJECTION = { Contacts._ID, Contacts.DISPLAY_NAME_PRIMARY, Contacts.STARRED,
                Contacts.LOOKUP_KEY, Contacts.PHOTO_URI, Contacts.NAME_RAW_CONTACT_ID, };

        @SuppressLint("InlinedApi")
        final static String SELECTION = Contacts.DISPLAY_NAME_PRIMARY + "<>''" + " AND " + Contacts.IN_VISIBLE_GROUP
                + "=1";

        @SuppressLint("InlinedApi")
        final static String SORT_ORDER = Contacts.SORT_KEY_PRIMARY;

        // The query column numbers which map to each value in the projection
        final static int ID = 0;
        final static int DISPLAY_NAME = 1;
        final static int STARRED = 2;
        final static int LOOKUP_KEY = 3;
        final static int PHOTO_URI = 4;
        final static int RAWID = 5;
    }

    /**
     * This interface defines constants used by address retrieval queries.
     */
    public interface ContactAddressQuery {
        // A unique query ID to distinguish queries being run by the
        // LoaderManager.
        final static int QUERY_ID = 2;

        // The query projection (columns to fetch from the provider)
        final static String[] PROJECTION = { StructuredPostal._ID, StructuredPostal.FORMATTED_ADDRESS,
                StructuredPostal.TYPE, StructuredPostal.LABEL, StructuredPostal.RAW_CONTACT_ID, };

        // The query selection criteria. In this case matching against the
        // StructuredPostal content mime type.
        final static String SELECTION = Data.MIMETYPE + "='" + StructuredPostal.CONTENT_ITEM_TYPE + "'";

        // The query column numbers which map to each value in the projection
        final static int ID = 0;
        final static int ADDRESS = 1;
        final static int TYPE = 2;
        final static int LABEL = 3;
        final static int RAWID = 4;
    }

    /**
     * This interface defines constants used by address retrieval queries.
     */
    public interface ContactNotesQuery {
        // A unique query ID to distinguish queries being run by the
        // LoaderManager.
        final static int QUERY_ID = 3;

        // The query projection (columns to fetch from the provider)
        final static String[] PROJECTION = { ContactsContract.CommonDataKinds.Note._ID,
                ContactsContract.CommonDataKinds.Note.NOTE, ContactsContract.CommonDataKinds.Note.RAW_CONTACT_ID, };

        // The query selection criteria. In this case matching against the
        // Note content mime type.
        final static String SELECTION = Data.MIMETYPE + "='"
                + ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE + "'";

        // The query column numbers which map to each value in the projection
        final static int ID = 0;
        final static int NOTE = 1;
        final static int RAWID = 2;
    }

    /**
     * This interface defines constants used by address retrieval queries.
     */
    public interface ContactPhoneQuery {
        // A unique query ID to distinguish queries being run by the
        // LoaderManager.
        final static int QUERY_ID = 4;

        // The query projection (columns to fetch from the provider)
        final static String[] PROJECTION = { ContactsContract.CommonDataKinds.Phone._ID,
                ContactsContract.CommonDataKinds.Phone.NUMBER, ContactsContract.CommonDataKinds.Phone.TYPE,
                ContactsContract.CommonDataKinds.Phone.LABEL,
                ContactsContract.CommonDataKinds.Phone.RAW_CONTACT_ID, };

        // The query selection criteria. In this case matching against the
        // Note content mime type.
        final static String SELECTION = Data.MIMETYPE + "='"
                + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE + "'";

        // The query column numbers which map to each value in the projection
        final static int ID = 0;
        final static int PHONE = 1;
        final static int TYPE = 2;
        final static int LABEL = 3;
        final static int RAWID = 4;
    }

    /**
     * This interface defines constants used by address retrieval queries.
     */
    public interface ContactEmailQuery {
        // A unique query ID to distinguish queries being run by the
        // LoaderManager.
        final static int QUERY_ID = 5;

        // The query projection (columns to fetch from the provider)
        final static String[] PROJECTION = { ContactsContract.CommonDataKinds.Email._ID,
                ContactsContract.CommonDataKinds.Email.ADDRESS, ContactsContract.CommonDataKinds.Email.TYPE,
                ContactsContract.CommonDataKinds.Email.LABEL,
                ContactsContract.CommonDataKinds.Email.RAW_CONTACT_ID, };

        // The query selection criteria. In this case matching against the
        // Note content mime type.
        final static String SELECTION = Data.MIMETYPE + "='"
                + ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE + "'";

        // The query column numbers which map to each value in the projection
        final static int ID = 0;
        final static int EMAIL = 1;
        final static int TYPE = 2;
        final static int LABEL = 3;
        final static int RAWID = 4;
    }

    private LinearLayout.LayoutParams tlayoutParams;
    private float gtot;

    // fill the transactions layout with the transaction views
    private void filltransactionlayout() {
        // Each LinearLayout has the same LayoutParams so this can
        // be created once and used for each cumulative layouts of data
        tlayoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        // Clears out the details layout first in case the details
        // layout has data from a previous data load still
        // added as children.
        budget_layout.removeAllViews();
        gtot = 0.0f;
        // loop thru all the clients
        for (int i = 0; i < nbclients; ++i) {
            if (clientslist[i].nbtransac != 0) {
                fillclientlayout(i);
            }
        }
        // add a row with the gtot amount
        LinearLayout tlayout = (LinearLayout) LayoutInflater.from(getActivity())
                .inflate(R.layout.contacts_budget_trans, null, false);
        TextView descrip = (TextView) tlayout.findViewById(R.id.budget_trans_descrip);
        TextView amount = (TextView) tlayout.findViewById(R.id.budget_trans_amount);
        descrip.setText("TOTAL");
        descrip.setTextSize(18.0f);
        amount.setText(String.format("%.2f", gtot));
        amount.setTextSize(18.0f);
        budget_layout.addView(tlayout, tlayoutParams);
    }

    public void fillclientlayout(int clientno) {
        LinearLayout clayout = (LinearLayout) LayoutInflater.from(getActivity())
                .inflate(R.layout.contacts_budget_item, null, false);
        TextView id = (TextView) clayout.findViewById(R.id.budget_id);
        TextView name = (TextView) clayout.findViewById(R.id.budget_name);
        TextView phone = (TextView) clayout.findViewById(R.id.budget_phone);
        TextView email = (TextView) clayout.findViewById(R.id.budget_email);
        TextView address = (TextView) clayout.findViewById(R.id.budget_address);
        LinearLayout blayout = (LinearLayout) clayout.findViewById(R.id.budget_trans);
        id.setText(clientslist[clientno].id);
        name.setText(clientslist[clientno].name);
        phone.setText(clientslist[clientno].phone);
        email.setText(clientslist[clientno].email);
        address.setText(clientslist[clientno].address);
        blayout.removeAllViews();
        // loop thru all transactions of the client
        for (int i = 0; i < clientslist[clientno].nbtransac; ++i) {
            // Builds the transaction layout
            // Inflates the transaction layout
            LinearLayout tlayout = (LinearLayout) LayoutInflater.from(getActivity())
                    .inflate(R.layout.contacts_budget_trans, null, false);
            TextView descrip = (TextView) tlayout.findViewById(R.id.budget_trans_descrip);
            TextView amount = (TextView) tlayout.findViewById(R.id.budget_trans_amount);
            // get the current transaction
            Transac transac = clientslist[clientno].transaclist.elementAt(i);
            // fill the fields with the table data
            descrip.setText(transac.descrip);
            amount.setText(transac.amount);
            // cumulate the total amount
            gtot += Double.valueOf(transac.amount);
            // Adds the new note layout to the notes layout
            blayout.addView(tlayout, tlayoutParams);
        }
        // add the client layout in the budget layout
        budget_layout.addView(clayout, tlayoutParams);
    }

}