728x90

Android Studio 에서 연락처 저장하는 기능으로 구현에 필요한 내용은 모두 적어둔다.

서버에서 읽어온 사진 이미지까지 저장할 수 있도록 했다.

서버에서 사진 이미지를 읽어오고 나면 Cache에 저장했으므로

MemoryCache memoryCache = new MemoryCache();
Bitmap cachedImage = memoryCache.get(strPhoto);

를 했는데도 불구하고 null 값이 나온다.


연락처(Contacts)에 전화번호를 저장하는 기능은 검사하여 있으면 삭제후 다시 신규 저장하는 방식으로 구현되어 있는데, 이런 로직보다는 데이터가 있으면 비교하여 달라진 것이 있으면 Update 하고 동일하면 패스하는 로직으로 구현하는 것이 바람직하다. 이런 구현방식은 개발자의 몫이기 때문에 여기에는 적어놓지 않는다.


import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.Context;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.os.RemoteException;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.util.Log;
import android.widget.Toast;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;

public class Contacts {
    public Contacts(){
        
    }
    
     // 전화번호부에 존재하는 데이터인지 여부 검사
    // 표시 이름과 휴대폰번호를 기준으로 ContactId 구하기
    public long
ContactsIDExistCheck(ContentResolver cr, String display_name, String number) {
        long rawContactId = -1;

        Uri contactUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
        String[] projection = { PhoneLookup._ID, PhoneLookup.TYPE, PhoneLookup.DISPLAY_NAME };
        Cursor cursor = null;
        try {
            cursor = cr.query(contactUri, projection, null, null, null);
            if (cursor.moveToFirst()) {
                do {
                    String PhoneName = cursor.getString(cursor.getColumnIndex(PhoneLookup.DISPLAY_NAME));
                    if (display_name.equals(PhoneName)) {
                        rawContactId = cursor.getLong(cursor.getColumnIndex(PhoneLookup._ID));
                        System.out.println(" ContactId = " + rawContactId + " 성명 : " + PhoneName);
                    }
                } while (cursor.moveToNext());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        return rawContactId;
    }
   

    public void ContactsIDdelete(ContentResolver cr, Context context, Integer contactId){
        // 전화번호부 삭제
        ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
           
        //remove contact from raw_contact table
        ops.add(ContentProviderOperation.newDelete(ContactsContract.RawContacts.CONTENT_URI).
        withSelection(ContactsContract.RawContacts.CONTACT_ID + "=?", new String[]{String.valueOf(contactId)}).
        build());              
               
        try {
            cr.applyBatch(ContactsContract.AUTHORITY, ops);
        } catch (RemoteException e) {
            e.printStackTrace();
        } catch (OperationApplicationException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public void ContactsIDinsert(ContentResolver cr, Context context, String strName, String strMobileNO) {

        ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
        ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
                .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, null)
                .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, null)
                .build());

        ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
                .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
                .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
                .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, strName)
                .build());

        ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
                .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
                .withValue(ContactsContract.Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)
                .withValue(Phone.NUMBER, strMobileNO)
                .withValue(Phone.TYPE, Phone.TYPE_MOBILE)
                .build());

        try {
            cr.applyBatch(ContactsContract.AUTHORITY, ops);
            Toast.makeText(context, "연락처가 저장되었습니다.", Toast.LENGTH_LONG).show();
        } catch (RemoteException e) {
            e.printStackTrace();
        } catch (OperationApplicationException e) {
            e.printStackTrace();
        } catch (Exception e) {
            Log.e("ContactsAdder", "Exceptoin encoutered while inserting contact: " + e);
        }
           
    }
    
    public void ContactsIDinsert(ContentResolver cr, Context context, String strName, String strMobileNO, String strOfficeNO, String strEmail, String strPhoto) {

        ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
        ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
                .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, null)
                .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, null)
                .build());

        ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
                .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
                .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
                .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, strName)
                .build());

        ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
                .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
                .withValue(ContactsContract.Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)
                .withValue(Phone.NUMBER, strMobileNO)
                .withValue(Phone.TYPE, Phone.TYPE_MOBILE)
                .build());

        ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
                .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
                .withValue(ContactsContract.Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)
                .withValue(Phone.NUMBER, strOfficeNO)
                .withValue(Phone.TYPE, Phone.TYPE_WORK)
                .build());

        ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
                .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
                .withValue(ContactsContract.Data.MIMETYPE, Email.CONTENT_ITEM_TYPE)
                .withValue(Email.DATA, strEmail)
                .withValue(Email.TYPE, Email.TYPE_WORK)
                .build());

        MemoryCache memoryCache = new MemoryCache();
        Bitmap cachedImage = memoryCache.get(strPhoto);
        System.out.println("cachedImage ===" + cachedImage);
        if (cachedImage != null) {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            cachedImage.compress(Bitmap.CompressFormat.JPEG, 60, bos);

            byte[] bytes = bos.toByteArray();

            ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
                    .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
                    .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
                    .withValue(ContactsContract.CommonDataKinds.Photo.DATA15, bytes)
                    .build());
            try {
                bos.flush();
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        try {
            cr.applyBatch(ContactsContract.AUTHORITY, ops);
            Toast.makeText(context, "연락처가 저장되었습니다.", Toast.LENGTH_LONG).show();
        } catch (RemoteException e) {
            e.printStackTrace();
        } catch (OperationApplicationException e) {
            e.printStackTrace();
        } catch (Exception e) {
            Log.e("ContactsAdder", "Exceptoin encoutered while inserting contact: " + e);
        }          
    }   
}


메모리 캐쉬 파일을 수정했다.

import android.graphics.Bitmap;
import android.util.Log;

import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;

public class MemoryCache {
    private static final String TAG = "MemoryCache";
    public static  Map<String, Bitmap> cache= Collections.synchronizedMap(
            new LinkedHashMap<String, Bitmap>(10,1.5f,true));//Last argument true for LRU ordering
    private long size=0;//current allocated size
    private long limit=1000000;//max memory in bytes

    public MemoryCache(){
        //use 25% of available heap size
        setLimit(Runtime.getRuntime().maxMemory()/4);
    }

    public void setLimit(long new_limit){
        limit=new_limit;
        Log.i(TAG, "MemoryCache will use up to "+limit/1024./1024.+"MB");
    }

    public static Bitmap get(String id){
        try{
            if(!cache.containsKey(id))
                return null;
            return cache.get(id);
        }catch(NullPointerException ex){
            ex.printStackTrace();
            return null;
        }
    }

    public void put(String id, Bitmap bitmap){
        try{
            if(cache.containsKey(id))
                size-=getSizeInBytes(cache.get(id));
            cache.put(id, bitmap);
            size+=getSizeInBytes(bitmap);
            checkSize();
        }catch(Throwable th){
            th.printStackTrace();
        }
    }

    private void checkSize() {
        Log.i(TAG, "cache size="+size+" length="+cache.size());
        if(size>limit){
            Iterator<Entry<String, Bitmap>> iter=cache.entrySet().iterator();//least recently accessed item will be the first one iterated
            while(iter.hasNext()){
                Entry<String, Bitmap> entry=iter.next();
                size-=getSizeInBytes(entry.getValue());
                iter.remove();
                if(size<=limit)
                    break;
            }
            Log.i(TAG, "Clean cache. New size "+cache.size());
        }
    }

    public void clear() {
        try{
            //NullPointerException sometimes happen here http://code.google.com/p/osmdroid/issues/detail?id=78
            cache.clear();
            size=0;
        }catch(NullPointerException ex){
            ex.printStackTrace();
        }
    }

    long getSizeInBytes(Bitmap bitmap) {
        if(bitmap==null)
            return 0;
        return bitmap.getRowBytes() * bitmap.getHeight();
    }
}


MainActivity.java 파일 내에 ListViewAdapter 부분

 private class ListViewAdapter extends BaseAdapter {
    ImageLoader imageLoader;
    Context context;

    public ListViewAdapter(Context context) {
        this.context = context;
        imageLoader = new ImageLoader(context);
    }

    @Override
    public int getCount() {
        return arrayList.size(); // 데이터 개수 리턴
    }

    @Override
    public Object getItem(int position) {
        return arrayList.get(position);
    }

    // 지정한 위치(position)에 있는 데이터 리턴
    @Override
    public long getItemId(int position) {
        return position;
    }

    // position에 위치한 데이터를 화면에 출력하는데 사용될 View를 리턴
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final ViewHolder viewHolder;
        final Context context = parent.getContext();

        // 화면에 표시될 View
        if(convertView == null){
            viewHolder = new ViewHolder();

            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.address_item,parent,false);

            convertView.setBackgroundColor(0x00FFFFFF);
            convertView.invalidate();

            // 화면에 표시될 View 로부터 위젯에 대한 참조 획득
            viewHolder.profile_Image = (ImageView) convertView.findViewById(R.id.profile_Image);
            viewHolder.tv_name = (TextView) convertView.findViewById(R.id.child_name);
            viewHolder.tv_mobileNO = (TextView) convertView.findViewById(R.id.child_mobileNO);
            viewHolder.tv_officeNO = (TextView) convertView.findViewById(R.id.child_officeNO);
            viewHolder.child_btn = (ImageView) convertView.findViewById(R.id.child_Btn);

            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }

        // PersonData 에서 position 에 위치한 데이터 참조 획득
        final Address_Item addressItem = arrayList.get(position);

        // 아이템 내 각 위젯에 데이터 반영
        // 선택된 row의 데이터를 표시한다. 표시될 view는 address_itemm.xml 의 각 항목을 이용하여 표시한다.
        //System.out.println("imageurl==" + addressItem.getProfile_image());
        if(addressItem.getProfile_image().equals("")){
            final Bitmap Base_Profile = PHPComm.autoresize_decodeResource(getResources(), R.mipmap.photo_base, 160);
            viewHolder.profile_Image.setImageBitmap(Base_Profile);
        } else {
            String photoURL = Value.IPADDRESS + "/photos/" + addressItem.getProfile_image();
            imageLoader.DisplayImage(photoURL, viewHolder.profile_Image);
        }

        viewHolder.tv_name.setText(addressItem.getName());
        viewHolder.tv_mobileNO.setText(PhoneNumberUtils.formatNumber(addressItem.getMobileNO()));
        viewHolder.tv_officeNO.setText(PhoneNumberUtils.formatNumber(addressItem.getOfficeNO()));

        final String[] items ={"휴대폰 전화걸기","사무실전화 걸기", "연락처 저장"};
        final AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setTitle("해당작업을 선택하세요");
        builder.setItems(items, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                //Toast.makeText(context, items[which] + "선택했습니다.", Toast.LENGTH_SHORT).show();
                switch (which){
                    case 0:
                        if(addressItem.getMobileNO().length() ==0){
                            Toast.makeText(context, "전화걸 휴대폰 번호가 없습니다.",Toast.LENGTH_SHORT).show();
                            break;
                        }
                        AlertDialog dialog1 = new AlertDialog.Builder(context)
                                .setTitle(addressItem.getName())
                                .setMessage(PhoneNumberUtils.formatNumber(addressItem.getMobileNO()) + " 통화하시겠습니까?")
                                .setPositiveButton("예",
                                        new DialogInterface.OnClickListener() {
                                            public void onClick(DialogInterface dialog, int which) {

                                                Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + PhoneNumberUtils.formatNumber(addressItem.getMobileNO())));
                                                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                                                if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
                                                   // TODO: Consider calling
                                                   if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
                                                       requestPermissions(new String[]{Manifest.permission.CALL_PHONE},1000);
                                                   }

                                                   return;
                                                }
                                                startActivity(intent);
                                            }
                                        })
                                .setNegativeButton(
                                        "아니오",
                                        new DialogInterface.OnClickListener() {
                                            public void onClick(DialogInterface dialog,int which) {
                                                dialog.dismiss();
                                            }
                                        }).create();
                        dialog1.show();
                        break;
                    case 1:
                        if(addressItem.getOfficeNO().length() ==0){
                            Toast.makeText(context, "전화걸 사무실 번호가 없습니다.",Toast.LENGTH_SHORT).show();
                            break;
                        }
                        AlertDialog dialog2 = new AlertDialog.Builder(context)
                                .setTitle(addressItem.getName())
                                .setMessage(PhoneNumberUtils.formatNumber(addressItem.getOfficeNO()) + " 통화하시겠습니까?")
                                .setPositiveButton("예",
                                        new DialogInterface.OnClickListener() {
                                            public void onClick(DialogInterface dialog, int which) {

                                                Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + PhoneNumberUtils.formatNumber(addressItem.getOfficeNO())));
                                                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                                                if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
                                                    // TODO: Consider calling
                                                    return;
                                                }
                                                startActivity(intent);
                                            }
                                        })
                                .setNegativeButton(
                                        "아니오",
                                        new DialogInterface.OnClickListener() {
                                            public void onClick(DialogInterface dialog,int which) {
                                                dialog.dismiss();
                                            }
                                        }).create();
                        dialog2.show();
                        break;
                    case 2:
                        // 연락처 저장 함수 호출
                        AlertDialog.Builder SaveContact = new AlertDialog.Builder(context);
                        SaveContact.setMessage("전화번호를 저장하시겠습니까?");
                        DialogInterface.OnClickListener save = new DialogInterface.OnClickListener() {

                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                int rawContactId = 0;
                                Contacts phonebook = new Contacts(); // 전화번호부 객체 생성
                                ContentResolver cr = getContentResolver();
                                String strContactName = addressItem.getName();
                                String strMobileNO = addressItem.getMobileNO();
                                String strofficeNO =addressItem.getOfficeNO();
                                String strEmail ="";
                                String strPhoto ="";
                                if(addressItem.getProfile_image().length()>0){
                                    strPhoto =Value.IPADDRESS + "/photos/" + addressItem.getProfile_image();
                                }
                                //System.out.println("strPhoto ==="+ strPhoto);

                                rawContactId = phonebook.ContactsIDExistCheck(cr, strContactName,strMobileNO);
                                if(rawContactId > 0){
                                    // 기존 전화번호가 존재하면 삭제하고 새로 입력
                                    phonebook.ContactsIDdelete(cr, context, rawContactId);
                                }
                                phonebook.ContactsIDinsert(cr, context, strContactName, strMobileNO, strofficeNO, strEmail, strPhoto);
                            }
                        };

                        DialogInterface.OnClickListener cancel = new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int which) {

                            }
                        };
                        SaveContact.setPositiveButton("저장", save);
                        SaveContact.setNegativeButton("취소", cancel);
                        SaveContact.show();
                        break;
                }
            }
        });
        builder.create();

        viewHolder.child_btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                builder.show();
            }
        });

        convertView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(getApplicationContext(), "상세보기를 눌렀습니다 ==="+ addressItem.getUid(), Toast.LENGTH_SHORT).show();
            }
        });

        return convertView;
    }

    // 아이템 데이터 추가를 위한 메소드
    public void addItem(String profile_image, String uid, String name, String mobileNO, String officeNO){
        Address_Item item = new Address_Item();
        item.setProfile_image(profile_image);
        item.setUid(uid);
        item.setName(name);
        item.setMobileNO(mobileNO);
        item.setOfficeNO(officeNO);

        arrayList.add(item);
    }


    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == 1000) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(MainActivity.this, "권한 요청을 승인했습니다.", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(MainActivity.this, "권한 요청을 거부했습니다.", Toast.LENGTH_SHORT).show();
            }
        }
    }

}


블로그 이미지

Link2Me

,