org.droid2droid.ui.contacts.AbstractSMSFragment.java Source code

Java tutorial

Introduction

Here is the source code for org.droid2droid.ui.contacts.AbstractSMSFragment.java

Source

/******************************************************************************
 *
 * droid2droid - Distributed Android Framework
 * ==========================================
 *
 * Copyright (C) 2012 by Atos (http://www.http://atos.net)
 * http://www.droid2droid.org
 *
 ******************************************************************************
 *
 * 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 org.droid2droid.ui.contacts;

import static org.droid2droid.Constants.TAG_SMS;
import static org.droid2droid.internal.Constants.D;
import static org.droid2droid.internal.Constants.PREFIX_LOG;
import static org.droid2droid.internal.Constants.W;

import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.droid2droid.R;
import org.droid2droid.RAApplication;
import org.droid2droid.ui.connect.AbstractConnectFragment;

import android.annotation.TargetApi;
import android.content.ComponentCallbacks2;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.SimpleCursorAdapter;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.QuickContactBadge;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;

//TODO: compatibilit 1.6
public abstract class AbstractSMSFragment extends AbstractConnectFragment implements OnScrollListener, TextWatcher,
        PhoneDisambigDialog.CallBack, LoaderManager.LoaderCallbacks<Cursor> {
    private static final int LOADER_ID = 0;
    private static final String[] CONTACTS_PROJECTION = { Contacts._ID, Contacts.DISPLAY_NAME, };
    private static final int POS_ID = 0;

    private static final int FETCH_IMAGE_MSG = 1;
    private static ExecutorService sImageFetchThreadPool = Executors.newFixedThreadPool(3);

    private ContentResolver mContentResolver;
    private View mMain;
    public TextView mUsage;
    protected EditText mEditText;
    protected ListView mList;
    private byte[] mSendedData;
    private String mCurFilter;

    // This is the Adapter being used to display the list's data.
    private SimpleCursorAdapter mAdapter;

    // --------- Manage image download ----------------
    private static HashMap<Long, Reference<Bitmap>> sBitmapCache = new HashMap<Long, Reference<Bitmap>>();;
    private final ImageFetchHandler mHandler = new ImageFetchHandler();

    private class ImageFetchHandler extends Handler {
        @Override
        public void handleMessage(final Message message) {
            if (getActivity().isFinishing()) {
                return;
            }
            switch (message.what) {
            case FETCH_IMAGE_MSG: {
                final QuickContactBadge imageView = (QuickContactBadge) message.obj;
                final long contactId = (long) message.arg1 << 16 | message.arg2;
                if (imageView == null)
                    break;
                final Reference<Bitmap> photoRef = sBitmapCache.get(contactId);
                if (photoRef == null) {
                    setDefaultImage(imageView);
                    ;
                    break;
                }
                final Bitmap photo = photoRef.get();

                if (photo == null) {
                    setDefaultImage(imageView);
                    ;
                    sBitmapCache.remove(contactId);
                    break;
                }
                imageView.setImageBitmap(photo);
                imageView.invalidate();
                break;
            }
            }
        }

        public void clearImageFecthing() {
            removeMessages(FETCH_IMAGE_MSG);
        }
    }

    private class ImageDbFetcher implements Runnable {
        private final long mContactId;

        private final ImageView mImageView;

        public ImageDbFetcher(final long contactId, final ImageView imageView) {
            mContactId = contactId;
            mImageView = imageView;
        }

        @Override
        public void run() {
            if (getActivity().isFinishing()) {
                return;
            }

            if (Thread.interrupted()) {
                return;
            }

            Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, mContactId);
            try {
                InputStream input = ContactsContract.Contacts.openContactPhotoInputStream(mContentResolver, uri);
                if (input != null) {
                    Bitmap contactPhoto = BitmapFactory.decodeStream(input);
                    // TODO: resize bitmap
                    sBitmapCache.put(mContactId, new SoftReference<Bitmap>(contactPhoto));
                    input.close();
                }
            } catch (IOException e) {
                if (W)
                    Log.w(TAG_SMS, PREFIX_LOG + "Error when close image (" + e.getMessage() + ")");
            }

            if (Thread.interrupted()) {
                return;
            }

            // Update must happen on UI thread
            final Message msg = new Message();
            msg.what = FETCH_IMAGE_MSG;
            msg.obj = mImageView;
            msg.arg1 = (int) (mContactId >>> 32);
            msg.arg2 = (int) (mContactId);
            mHandler.sendMessage(msg);
        }
    }

    private class ContactsAdapter extends SimpleCursorAdapter {
        ContactsAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags) {
            super(context, layout, c, from, to, flags);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            return super.getView(position, convertView, parent);
        }

        /** {@inheritDoc} */
        @Override
        public void bindView(final View view, final Context context, final Cursor cursor) {
            super.bindView(view, context, cursor);
            long contactId = cursor.getLong(POS_ID);
            QuickContactBadge imageView = ((QuickContactBadge) view.findViewById(R.id.icon));
            Bitmap photo = null;
            final Reference<Bitmap> ref = sBitmapCache.get(contactId);
            if (ref != null) {
                photo = ref.get();
                if (photo == null) // Lose weak reference
                {
                    sBitmapCache.remove(contactId);
                    setDefaultImage(imageView);
                } else {
                    imageView.setImageBitmap(photo);
                    return;
                }
            } else
                setDefaultImage(imageView);
            sImageFetchThreadPool.execute(new ImageDbFetcher(contactId, imageView));
        }
    }

    @TargetApi(11)
    private void setDefaultImage(QuickContactBadge imageView) {
        if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
            imageView.setImageToDefault();
        } else {
            imageView.setImageResource(R.drawable.ic_contact_list_picture);
        }
    }

    //------------- Manage views --------------
    private static final String[] COLS_VIEW = new String[] { Contacts.DISPLAY_NAME };
    private static final int[] MAPPING_VIEW = new int[] { R.id.name };

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mContentResolver = getActivity().getContentResolver();

        // Create an empty adapter we will use to display the loaded data.
        mAdapter = new ContactsAdapter(getActivity(), R.layout.connect_sms_list_item_icon_text, null, COLS_VIEW,
                MAPPING_VIEW, 0);
        mList.setAdapter(mAdapter);

        getLoaderManager().initLoader(LOADER_ID, null, this);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        mMain = inflater.inflate(R.layout.expose_sms, container, false);

        mUsage = (TextView) mMain.findViewById(R.id.usage);

        mEditText = (EditText) mMain.findViewById(R.id.numberEditText);
        mEditText.addTextChangedListener(this);
        mEditText.setOnEditorActionListener(new OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                if (actionId == EditorInfo.IME_ACTION_SEND) {
                    final String receiver = mEditText.getText().toString();
                    if (mSendedData != null || receiver != null)
                        if (receiver.matches("[0123456789#*()\\- ]*"))
                            sendData(receiver);
                    return true;
                }
                return false;
            }
        });

        mList = (ListView) mMain.findViewById(android.R.id.list);
        mList.setOnScrollListener(this);
        mList.setFastScrollEnabled(true);
        mList.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
        mList.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapter, View view, int position, long arg3) {
                final long id = ((Cursor) adapter.getItemAtPosition(position)).getLong(POS_ID);
                if (id > 0) {
                    RAApplication.hideSoftKeyboard(getActivity());
                    final PhoneDisambigDialog phoneDialog = new PhoneDisambigDialog(getActivity(),
                            AbstractSMSFragment.this, id);
                    phoneDialog.show();
                }
            }
        });
        return mMain;
    }

    @Override
    public void onPause() {
        super.onPause();
        mHandler.clearImageFecthing();
    }

    // --------- Manage loader ----------------
    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        Uri baseUri;
        if (mCurFilter != null) {
            baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(mCurFilter));
        } else {
            baseUri = Contacts.CONTENT_URI;
        }
        // Now create and return a CursorLoader that will take care of
        // creating a Cursor for the data being displayed.
        String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
                + Contacts.DISPLAY_NAME + " != '' ))";
        if (D)
            Log.d(TAG_SMS, PREFIX_LOG + "baseURI=" + baseUri);
        if (D)
            Log.d(TAG_SMS, PREFIX_LOG + "select=" + select);
        return new CursorLoader(getActivity(), baseUri, CONTACTS_PROJECTION, select, null,
                Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        // Swap the new cursor in. (The framework will take care of closing the
        // old cursor once we return.)
        mAdapter.swapCursor(data);

        // The list should now be shown.
        mList.setVisibility(View.VISIBLE);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        // This is called when the last Cursor provided to onLoadFinished()
        // above is about to be closed. We need to make sure we are no
        // longer using it.
        mAdapter.swapCursor(null);
    }

    // --------- Manage scroll ----------------
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        switch (scrollState) {
        case OnScrollListener.SCROLL_STATE_IDLE:
            int first = view.getFirstVisiblePosition();
            int count = view.getChildCount();

            for (int i = 0; i < count; i++) {
                long contactId = mAdapter.getItemId(first + i);
                if (contactId != 0) {
                    QuickContactBadge icon = (QuickContactBadge) mList.getChildAt(i).findViewById(R.id.icon);
                    if (sBitmapCache.containsKey(contactId)) {
                        icon.setImageBitmap(sBitmapCache.get(contactId).get());
                    } else {
                        setDefaultImage(icon);
                        sImageFetchThreadPool.execute(new ImageDbFetcher(contactId, icon));
                    }
                }
            }

            break;
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    }

    // --------- Manage textchange ----------------
    @Override
    public void afterTextChanged(Editable s) {
        String newText = s.toString();
        mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
        getLoaderManager().restartLoader(LOADER_ID, null, this);
    }

    @Override
    public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {

    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {

    }

    public static void onTrimMemory(int level) {
        if (level == ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
            sBitmapCache.clear();
            if (D)
                Log.d(TAG_SMS, PREFIX_LOG + "Clean contacts pictures");
        }
    }
}