blob: 656c5ae782edab92eed090b4e698449680a62c03 [file] [log] [blame]
/*
* Copyright (C) 2015 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 com.android.contacts.editor;
import com.android.contacts.ContactSaveService;
import com.android.contacts.R;
import com.android.contacts.activities.CompactContactEditorActivity;
import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.RawContactDelta;
import com.android.contacts.common.model.RawContactDeltaList;
import com.android.contacts.common.model.ValuesDelta;
import com.android.contacts.common.model.account.AccountType;
import com.android.contacts.common.util.ImplicitIntentsUtil;
import com.android.contacts.detail.PhotoSelectionHandler;
import com.android.contacts.util.ContactPhotoUtils;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import java.io.FileNotFoundException;
/**
* Contact editor with only the most important fields displayed initially.
*/
public class CompactContactEditorFragment extends ContactEditorBaseFragment implements
CompactRawContactsEditorView.Listener, PhotoSourceDialogFragment.Listener {
private static final String KEY_PHOTO_URI = "photo_uri";
private static final String KEY_PHOTO_RAW_CONTACT_ID = "photo_raw_contact_id";
/**
* Displays a PopupWindow with photo edit options.
*/
final class PhotoHandler extends PhotoSelectionHandler implements View.OnClickListener {
/**
* Receiver of photo edit option callbacks.
*/
private final class PhotoListener extends PhotoActionListener {
@Override
public void onRemovePictureChosen() {
getContent().setPhoto(/* bitmap =*/ null);
mUpdatedPhotos.remove(String.valueOf(mPhotoRawContactId));
// Update the mode so the options change if user clicks the photo again
mPhotoMode = getPhotoMode();
}
@Override
public void onPhotoSelected(Uri uri) throws FileNotFoundException {
final Bitmap bitmap = ContactPhotoUtils.getBitmapFromUri(getActivity(), uri);
if (bitmap == null || bitmap.getHeight() <= 0 || bitmap.getWidth() <= 0) {
Log.w(TAG, "Invalid photo selected");
}
getContent().setPhoto(bitmap);
// Clear any previously saved full resolution photos under negative raw contact IDs
// so that we will use the newly selected photo, instead of an old one on rotations.
removeNewRawContactPhotos();
// If a new photo was chosen but not yet saved,
// we need to update the UI immediately
mUpdatedPhotos.putParcelable(String.valueOf(mPhotoRawContactId), uri);
getContent().setFullSizePhoto(uri);
// Update the mode so the options change if user clicks the photo again
mPhotoMode = getPhotoMode();
// Re-create the photo handler so that any additional photo selections create a
// new temp file (and don't hit the one that was just added to the cache).
mPhotoHandler = createPhotoHandler();
}
@Override
public Uri getCurrentPhotoUri() {
return mPhotoUri;
}
@Override
public void onPhotoSelectionDismissed() {
}
}
private PhotoListener mPhotoListener;
private int mPhotoMode;
public PhotoHandler(Context context, int photoMode, RawContactDeltaList state) {
// We pass a null changeAnchorView since we are overriding onClick so that we
// can show the photo options in a dialog instead of a ListPopupWindow (which would
// be anchored at changeAnchorView).
super(context, /* changeAnchorView =*/ null, photoMode, /* isDirectoryContact =*/ false,
state);
mPhotoListener = new PhotoListener();
mPhotoMode = photoMode;
}
@Override
public void onClick(View view) {
PhotoSourceDialogFragment.show(CompactContactEditorFragment.this, mPhotoMode);
}
@Override
public PhotoActionListener getListener() {
return mPhotoListener;
}
@Override
protected void startPhotoActivity(Intent intent, int requestCode, Uri photoUri) {
mPhotoUri = photoUri;
mStatus = Status.SUB_ACTIVITY;
CompactContactEditorFragment.this.startActivityForResult(intent, requestCode);
}
}
private PhotoHandler mPhotoHandler;
private Uri mPhotoUri;
private long mPhotoRawContactId;
@Override
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
if (savedState != null) {
mPhotoUri = savedState.getParcelable(KEY_PHOTO_URI);
mPhotoRawContactId = savedState.getLong(KEY_PHOTO_RAW_CONTACT_ID);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
setHasOptionsMenu(true);
final View view = inflater.inflate(
R.layout.compact_contact_editor_fragment, container, false);
mContent = (LinearLayout) view.findViewById(R.id.editors);
return view;
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putParcelable(KEY_PHOTO_URI, mPhotoUri);
outState.putLong(KEY_PHOTO_RAW_CONTACT_ID, mPhotoRawContactId);
super.onSaveInstanceState(outState);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mStatus == Status.SUB_ACTIVITY) {
mStatus = Status.EDITING;
}
if (mPhotoHandler != null
&& mPhotoHandler.handlePhotoActivityResult(requestCode, resultCode, data)) {
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
@Override
public void onStop() {
super.onStop();
// If anything was left unsaved, save it now
if (!getActivity().isChangingConfigurations() && mStatus == Status.EDITING) {
save(SaveMode.RELOAD, /* backPressed =*/ false);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
return revert();
}
return super.onOptionsItemSelected(item);
}
@Override
protected void bindEditors() {
if (!isReadyToBindEditors()) {
return;
}
// Add input fields for the loaded Contact
final CompactRawContactsEditorView editorView = getContent();
editorView.setListener(this);
editorView.setState(mState, getMaterialPalette(), mViewIdGenerator, mPhotoId, mNameId,
mReadOnlyDisplayName, mHasNewContact, mIsUserProfile);
if (mReadOnlyDisplayName != null) {
mReadOnlyNameEditorView = editorView.getDefaultNameEditorView();
}
// Set up the photo widget
mPhotoHandler = createPhotoHandler();
mPhotoRawContactId = editorView.getPhotoRawContactId();
if (mPhotoRawContactId < 0) {
// Since the raw contact IDs for new contacts are random negative numbers
// we consider any negative key a match
for (String key : mUpdatedPhotos.keySet()) {
try {
if (Integer.parseInt(key) < 0) {
editorView.setFullSizePhoto((Uri) mUpdatedPhotos.getParcelable(key));
break;
}
} catch (NumberFormatException ignored) {
}
}
} else if (mUpdatedPhotos.containsKey(String.valueOf(mPhotoRawContactId))) {
editorView.setFullSizePhoto((Uri) mUpdatedPhotos.getParcelable(
String.valueOf(mPhotoRawContactId)));
}
editorView.setPhotoHandler(mPhotoHandler);
// The editor is ready now so make it visible
editorView.setEnabled(isEnabled());
editorView.setVisibility(View.VISIBLE);
// Refresh the ActionBar as the visibility of the join command
// Activity can be null if we have been detached from the Activity.
invalidateOptionsMenu();
}
private boolean isReadyToBindEditors() {
if (mState.isEmpty()) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "No data to bind editors");
}
return false;
}
if (mIsEdit && !mExistingContactDataReady) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Existing contact data is not ready to bind editors.");
}
return false;
}
if (mHasNewContact && !mNewContactDataReady) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "New contact data is not ready to bind editors.");
}
return false;
}
return true;
}
private PhotoHandler createPhotoHandler() {
return new PhotoHandler(getActivity(), getPhotoMode(), mState);
}
private int getPhotoMode() {
// To determine the options that are available to the user to update their photo
// (i.e. the photo mode), check if any of the writable raw contacts has a photo set
Integer photoMode = null;
boolean hasWritableAccountType = false;
final AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
for (RawContactDelta rawContactDelta : mState) {
if (!rawContactDelta.isVisible()) {
continue;
}
final AccountType accountType = rawContactDelta.getAccountType(accountTypes);
if (accountType.areContactsWritable()) {
hasWritableAccountType = true;
if (getContent().isWritablePhotoSet()) {
photoMode = PhotoActionPopup.Modes.MULTIPLE_WRITE_ABLE_PHOTOS;
break;
}
}
}
// If the mode was not set, base it on whether we saw a writable contact or not
if (photoMode == null) {
photoMode = hasWritableAccountType
? PhotoActionPopup.Modes.NO_PHOTO : PhotoActionPopup.Modes.READ_ONLY_PHOTO;
}
return photoMode;
}
@Override
protected View getAggregationAnchorView(long rawContactId) {
return getContent().getAggregationAnchorView();
}
@Override
protected void setGroupMetaData() {
// The compact editor does not support groups.
}
@Override
protected boolean doSaveAction(int saveMode, boolean backPressed) {
// Save contact. No need to pass the palette since we are finished editing after the save.
final Intent intent = ContactSaveService.createSaveContactIntent(mContext, mState,
SAVE_MODE_EXTRA_KEY, saveMode, isEditingUserProfile(),
((Activity) mContext).getClass(),
CompactContactEditorActivity.ACTION_SAVE_COMPLETED, mUpdatedPhotos, backPressed);
mContext.startService(intent);
return true;
}
@Override
protected void joinAggregate(final long contactId) {
final Intent intent = ContactSaveService.createJoinContactsIntent(
mContext, mContactIdForJoin, contactId, CompactContactEditorActivity.class,
CompactContactEditorActivity.ACTION_JOIN_COMPLETED);
mContext.startService(intent);
}
@Override
public void onRemovePictureChosen() {
if (mPhotoHandler != null) {
mPhotoHandler.getListener().onRemovePictureChosen();
}
}
@Override
public void onTakePhotoChosen() {
if (mPhotoHandler != null) {
mPhotoHandler.getListener().onTakePhotoChosen();
}
}
@Override
public void onPickFromGalleryChosen() {
if (mPhotoHandler != null) {
mPhotoHandler.getListener().onPickFromGalleryChosen();
}
}
@Override
public void onExpandEditor() {
// Determine if this is an insert (new contact) or edit
final boolean isInsert = isInsert(getActivity().getIntent());
if (isInsert) {
// For inserts, prevent any changes from being saved when the base fragment is destroyed
mStatus = Status.CLOSING;
} else if (hasPendingRawContactChanges()) {
// Save whatever is in the form
save(SaveMode.CLOSE, /* backPressed =*/ false);
}
// Prepare an Intent to start the expanded editor
final Intent intent = isInsert
? EditorIntents.createInsertContactIntent(
mState, getDisplayName(), getPhoneticName(), mUpdatedPhotos)
: EditorIntents.createEditContactIntent(mLookupUri, getMaterialPalette(),
mPhotoId, mNameId);
ImplicitIntentsUtil.startActivityInApp(getActivity(), intent);
getActivity().finish();
}
@Override
public void onNameFieldChanged(long rawContactId, ValuesDelta valuesDelta) {
final Activity activity = getActivity();
if (activity == null || activity.isFinishing()) {
return;
}
if (!mIsUserProfile) {
acquireAggregationSuggestions(activity, rawContactId, valuesDelta);
}
}
@Override
public String getDisplayName() {
final StructuredNameEditorView structuredNameEditorView =
getContent().getStructuredNameEditorView();
return structuredNameEditorView == null
? null : structuredNameEditorView.getDisplayName();
}
@Override
public String getPhoneticName() {
final PhoneticNameEditorView phoneticNameEditorView =
getContent().getFirstPhoneticNameEditorView();
return phoneticNameEditorView == null
? null : phoneticNameEditorView.getPhoneticName();
}
private CompactRawContactsEditorView getContent() {
return (CompactRawContactsEditorView) mContent;
}
}