zoukankan      html  css  js  c++  java
  • 联系人头像编辑保存过程

    以新建联系人 —> 点击头像 —> 选择拍照 —> 设置头像为例

    com.android.contacts.editor.PhotoActionPopup中处理菜单选择
    
    public static ListPopupWindow createPopupMenu(Context context, View anchorView,
                final Listener listener, int mode) {
        ......     final OnItemClickListener clickListener = new OnItemClickListener() {
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
              final ChoiceListItem choice = choices.get(position);
              switch (choice.getId()) {
                ......
                case ChoiceListItem.ID_TAKE_PHOTO:
                  listener.onTakePhotoChosen();  break;
                ......            
              }
              UiClosables.closeQuietly(listPopupWindow);
            }
        }
        ......
        listPopupWindow.setOnItemClickListener(clickListener);
    ...... }

    listener调用onTakePhotoChosen();

    package com.android.contacts.detail;
    public abstract class PhotoSelectionHandler implements OnClickListener {
      ......
      public abstract class PhotoActionListener implements PhotoActionPopup.Listener {
        ......
        public void onTakePhotoChosen() {
          try {
            // Launch camera to take photo for selected contact
            startTakePhotoActivity(mTempPhotoUri);//mTempPhotoUri是缓存目录
          } catch (ActivityNotFoundException e) {
            Toast.makeText(mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
          }
        }
      }
      private void startTakePhotoActivity(Uri photoUri) {
            final Intent intent = getTakePhotoIntent(photoUri);
            startPhotoActivity(intent, REQUEST_CODE_CAMERA_WITH_DATA, photoUri);
        }
    }

    startPhotoActivity具体实现由ContactEditorFragment中的PhotoHandler处理

    package com.android.contacts.editor;
    public class ContactEditorFragment extends Fragment implements SplitContactConfirmationDialogFragment.Listener,
          AggregationSuggestionEngine.Listener, AggregationSuggestionView.Listener, RawContactReadOnlyEditorView.Listener {     ......    
      private final class PhotoHandler extends PhotoSelectionHandler {
          ......
          @Override
            public void startPhotoActivity(Intent intent, int requestCode, Uri photoUri) {
                mRawContactIdRequestingPhoto = mEditor.getRawContactId();
                mCurrentPhotoHandler = this;
                mStatus = Status.SUB_ACTIVITY;
                mCurrentPhotoUri = photoUri;
                ContactEditorFragment.this.startActivityForResult(intent, requestCode);
            }  
    
      }
      ......   
      public void onActivityResult(int requestCode, int resultCode, Intent data) {
            ......
            // See if the photo selection handler handles this result.
            if (mCurrentPhotoHandler != null && mCurrentPhotoHandler.handlePhotoActivityResult(
                    requestCode, resultCode, data)) {
                return;
            }
            ......
      }    
    }

    PhotoSelectionHandler调用handlePhotoActivityResult

    com.android.contacts.detail.PhotoSelectionHandler 
    
    public boolean handlePhotoActivityResult(int requestCode, int resultCode, Intent data) {
            final PhotoActionListener listener = getListener();
            if (resultCode == Activity.RESULT_OK) {
                switch (requestCode) {
                    ......
                    case REQUEST_CODE_CAMERA_WITH_DATA:
                        final Uri uri;
                        boolean isWritable = false;
                        if (data != null && data.getData() != null) {
                            uri = data.getData();//拿到返回的图片uri
                        } else {
                            uri = listener.getCurrentPhotoUri();
                            isWritable = true;
                        }
                        final Uri toCrop;
                        if (isWritable) {
                            // Since this uri belongs to our file provider, we know that it is writable
                            // by us. This means that we don't have to save it into another temporary
                            // location just to be able to crop it.
                            toCrop = uri;
                        } else {
                            toCrop = mTempPhotoUri;//使用缓存路径
                            try {
                                ContactPhotoUtils.savePhotoFromUriToUri(mContext, uri, toCrop, false);//将图片写到缓存目录
                            } catch (SecurityException e) {
                                Log.d(TAG, "Did not have read-access to uri : " + uri);
                                return false;
                            }
                        }
                        doCropPhoto(toCrop, mCroppedPhotoUri);//进入Gallery实现图片切割
                        return true;
                }
            }
            return false;
        }
    /**
         * Sends a newly acquired photo to Gallery for cropping
         */
        private void doCropPhoto(Uri inputUri, Uri outputUri) {
            try {
                // Launch gallery to crop the photo
                final Intent intent = getCropImageIntent(inputUri, outputUri);//Gallery中会将裁剪图片写入到outputUri——即mCroppedPhotoUri
                startPhotoActivity(intent, REQUEST_CROP_PHOTO, inputUri);
            } catch (Exception e) {
                Log.e(TAG, "Cannot crop image", e);
                Toast.makeText(mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
            }
        }

    再次进入ContactEditorFragment中的PhotoHandler调用startPhotoActivity,然后再次返回到onActivityResult,触发PhotoSelectionHandler调用handlePhotoActivityResult

    public boolean handlePhotoActivityResult(int requestCode, int resultCode, Intent data) {
            final PhotoActionListener listener = getListener();
            if (resultCode == Activity.RESULT_OK) {
                switch (requestCode) {
                    // Cropped photo was returned
                    case REQUEST_CROP_PHOTO: {
                        final Uri uri;
                        if (data != null && data.getData() != null) {
                            uri = data.getData();//获取裁剪后的uri
                        } else {
                            uri = mCroppedPhotoUri;//如果没有返回,直接使用mCroppedPhotoUri,Gallery中已经进行了写入操作
                        }
    
                        try {
                            // delete the original temporary photo if it exists
                            mContext.getContentResolver().delete(mTempPhotoUri, null, null);//删除不用的缓存目录
                            listener.onPhotoSelected(uri);
                            return true;
                        } catch (FileNotFoundException e) {
                            return false;
                        }
                    }

    ContactEditorFragment中的PhotoHandler处理onPhotoSelected

          public void onPhotoSelected(Uri uri) throws FileNotFoundException {
                    final Bitmap bitmap = ContactPhotoUtils.getBitmapFromUri(mContext, uri);//通过uri获取Bitmap
                    setPhoto(mRawContactId, bitmap, uri);//用bitmap显示头像,而后转为byte[]传给RawContacDelta,准备写入数据库
                    mCurrentPhotoHandler = null;
                    bindEditors();
                }

    ContactEditorFragment调用setPhoto

    private void setPhoto(long rawContact, Bitmap photo, Uri photoUri) {
            BaseRawContactEditorView requestingEditor = getRawContactEditorView(rawContact);
    
            if (photo == null || photo.getHeight() < 0 || photo.getWidth() < 0) {
                // This is unexpected.
                Log.w(TAG, "Invalid bitmap passed to setPhoto()");
            }
    
            if (requestingEditor != null) {
                requestingEditor.setPhotoBitmap(photo);//显示头像,并将bitmap压缩为96x96大小,转为byte[],传给ValuesDelta
            } else {
                Log.w(TAG, "The contact that requested the photo is no longer present.");
            }
    
            mUpdatedPhotos.putParcelable(String.valueOf(rawContact), photoUri);//将头像uri保存到Bundle——mUpdatedPhotos中,key为Contact id
            mRawContactIdPhoto = String.valueOf(rawContact);//如果是新增联系人, id = -1
        }
    public abstract class BaseRawContactEditorView extends LinearLayout {
            public void setPhotoBitmap(Bitmap bitmap) {
            mPhoto.setPhotoBitmap(bitmap);
        }
    }
    
    public class PhotoEditorView extends LinearLayout implements Editor {
            public void setPhotoBitmap(Bitmap photo) {
            if (photo == null) {
                // Clear any existing photo and return
                mEntry.put(Photo.PHOTO, (byte[])null);
                resetDefault();
                return;
            }
    
            mPhotoImageView.setImageBitmap(photo);
            mFrameView.setEnabled(isEnabled());
            mHasSetPhoto = true;
            mEntry.setFromTemplate(false);
    
            // When the user chooses a new photo mark it as super primary
            mEntry.setSuperPrimary(true);
    
            // Even though high-res photos cannot be saved by passing them via
            // an EntityDeltaList (since they cause the Bundle size limit to be
            // exceeded), we still pass a low-res thumbnail. This simplifies
            // code all over the place, because we don't have to test whether
            // there is a change in EITHER the delta-list OR a changed photo...
            // this way, there is always a change in the delta-list.
            final int size = ContactsUtils.getThumbnailSize(getContext());//从ContentProvider2中获取系统设定的图片大小,默认为96
            final Bitmap scaled = Bitmap.createScaledBitmap(photo, size, size, false);//将头像bitmap压缩为96x96大小
            final byte[] compressed = ContactPhotoUtils.compressBitmap(scaled);//将压缩的bitmap转为二进制byte[]
            if (compressed != null) mEntry.setPhoto(compressed); //private ValuesDelta mEntry;    
    package com.android.contacts.common.model;
    public class ValuesDelta implements Parcelable {
            public void setPhoto(byte[] value) {
            put(ContactsContract.CommonDataKinds.Photo.PHOTO, value);//将byte[]放到data15字段中,(Photo.PHOTO = “data15”)
        }
    
            public void put(String key, byte[] value) {
            ensureUpdate();
            mAfter.put(key, value);//public ContentValues mAfter;
        }
    
            private void ensureUpdate() {
            if (mAfter == null) {
                mAfter = new ContentValues();
            }
        }
    }
        }
    }

    到这里,头像压缩数据byte[]保存到了data15, 头像原始Uri保存到了Bundle——mUpdatedPhotos中。

    -----------------------------------------最后保存过程--------------------------------

    ContactEditorFragment调用save,启动ContactSaveService

    public boolean save(int saveMode) {
        ......
        // Save contact
            Intent intent = ContactSaveService.createSaveContactIntent(mContext, mState,
                    SAVE_MODE_EXTRA_KEY, saveMode, isEditingUserProfile(),
                    ((Activity)mContext).getClass(), ContactEditorActivity.ACTION_SAVE_COMPLETED,
                    mUpdatedPhotos);
            mContext.startService(intent);
    
            // Don't try to save the same photos twice.
            mUpdatedPhotos = new Bundle();
    
            return true;
    }

    ContactSaveService处理

    private void saveContact(Intent intent) {
            ......
            //先保存RawContactDelta中对应的数据库各字段数据,包括data15字段中的压缩头像数据
    
            //RawContactDelta数据保存完毕后,处理头像uri
            if (updatedPhotos != null) {
                ......
                if (!saveUpdatedPhoto(rawContactId, photoUri)) {
                        succeeded = false;
                }
            ......
            }
    }
    private boolean saveUpdatedPhoto(long rawContactId, Uri photoUri) {
            final Uri outputUri = Uri.withAppendedPath(
                    ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
                    RawContacts.DisplayPhoto.CONTENT_DIRECTORY);//= content://com.android.contacts/raw_contacts/rawContactId/display_photo
                                    //手机中的目录:/data/data/com.android.providers.contacts/files/photos/rawContactId
            return ContactPhotoUtils.savePhotoFromUriToUri(this, photoUri, outputUri, true);//将头像文件写到outputUri目录中, rawContactId为文件名
        }
    
    
    package com.android.contacts.util;
    public class ContactPhotoUtils {
    
    /**
         * Given an input photo stored in a uri, save it to a destination uri
         */
        public static boolean savePhotoFromUriToUri(Context context, Uri inputUri, Uri outputUri,
                boolean deleteAfterSave) {
            FileOutputStream outputStream = null;
            InputStream inputStream = null;
            try {
                outputStream = context.getContentResolver()
                        .openAssetFileDescriptor(outputUri, "rw").createOutputStream();
                inputStream = context.getContentResolver().openInputStream(
                        inputUri);
     
                final byte[] buffer = new byte[16 * 1024];
                int length;
                int totalLength = 0;
                while ((length = inputStream.read(buffer)) > 0) {
                    outputStream.write(buffer, 0, length);
                    totalLength += length;
                }
                Log.v(TAG, "Wrote " + totalLength + " bytes for photo " + inputUri.toString());
            } catch (IOException e) {
                Log.e(TAG, "Failed to write photo: " + inputUri.toString() + " because: " + e);
                return false;
            } finally {
                Closeables.closeQuietly(inputStream);
                Closeables.closeQuietly(outputStream);
                if (deleteAfterSave) {
                    context.getContentResolver().delete(inputUri, null, null);
                }
            }
            return true;
        }
    }
    View Code

     综上,头像保存了两份:一份是在数据库data表data15字段中的压缩数据byte[]——大小为96x96(较模糊)

                        一份是将头像文件直接写到了files/photos/rawContactId中——大小为720x720,数据库中保存了文件的大小和名称(即id)(较清晰)

  • 相关阅读:
    [UE4]RPC,远程调用
    [UE4]先报告后广播模式
    [UE4]复制引起的重复对象
    [UE4]封装蓝图函数Print String
    [UE4]碰撞的随机性
    [UE4]Authority,网络控制权
    [UE4]Replications,复制
    [UE4]最简单的虚幻4网络游戏,使用虚幻4内置服务器
    [UE4]Format Text
    [UE4]虚幻4链接独立服务器
  • 原文地址:https://www.cnblogs.com/antoon/p/4423055.html
Copyright © 2011-2022 走看看