blob: 037ac8e315fcb56839d8b9d51d3ea4edbe304954 [file] [log] [blame]
Walter Jang3f990ba2015-01-27 17:38:30 +00001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.contacts.editor;
18
Walter Jang49ed2032015-02-11 20:09:05 -080019import com.android.contacts.ContactSaveService;
Walter Jang3f990ba2015-01-27 17:38:30 +000020import com.android.contacts.R;
Walter Jang49ed2032015-02-11 20:09:05 -080021import com.android.contacts.activities.CompactContactEditorActivity;
Walter Jang3efae4a2015-02-18 11:12:00 -080022import com.android.contacts.common.model.AccountTypeManager;
23import com.android.contacts.common.model.RawContactDelta;
24import com.android.contacts.common.model.RawContactDeltaList;
Walter Jang151f3e62015-02-26 15:29:40 -080025import com.android.contacts.common.model.ValuesDelta;
Walter Jang3efae4a2015-02-18 11:12:00 -080026import com.android.contacts.common.model.account.AccountType;
Walter Jang36d6a162015-02-20 10:56:59 -080027import com.android.contacts.common.util.ImplicitIntentsUtil;
Walter Jang3efae4a2015-02-18 11:12:00 -080028import com.android.contacts.detail.PhotoSelectionHandler;
29import com.android.contacts.util.ContactPhotoUtils;
Walter Jang3f990ba2015-01-27 17:38:30 +000030
Walter Jang49ed2032015-02-11 20:09:05 -080031import android.app.Activity;
Walter Jang3efae4a2015-02-18 11:12:00 -080032import android.content.Context;
Walter Jang3f990ba2015-01-27 17:38:30 +000033import android.content.Intent;
Walter Jang3efae4a2015-02-18 11:12:00 -080034import android.graphics.Bitmap;
35import android.net.Uri;
Walter Jang3f990ba2015-01-27 17:38:30 +000036import android.os.Bundle;
Walter Jangcab3dce2015-02-09 17:48:03 -080037import android.util.Log;
Walter Jang3f990ba2015-01-27 17:38:30 +000038import android.view.LayoutInflater;
Walter Jang0e72ce92015-02-23 12:27:21 -080039import android.view.Menu;
40import android.view.MenuItem;
Walter Jang3f990ba2015-01-27 17:38:30 +000041import android.view.View;
42import android.view.ViewGroup;
43import android.widget.LinearLayout;
44
Walter Jang3efae4a2015-02-18 11:12:00 -080045import java.io.FileNotFoundException;
46
Walter Jang3f990ba2015-01-27 17:38:30 +000047/**
48 * Contact editor with only the most important fields displayed initially.
49 */
Walter Jangb6ca2722015-02-20 11:10:25 -080050public class CompactContactEditorFragment extends ContactEditorBaseFragment implements
Walter Jang0e72ce92015-02-23 12:27:21 -080051 CompactRawContactsEditorView.Listener, PhotoSourceDialogFragment.Listener {
Walter Jang3f990ba2015-01-27 17:38:30 +000052
Walter Jang3efae4a2015-02-18 11:12:00 -080053 private static final String KEY_PHOTO_URI = "photo_uri";
54 private static final String KEY_PHOTO_RAW_CONTACT_ID = "photo_raw_contact_id";
Walter Jang3efae4a2015-02-18 11:12:00 -080055
56 /**
57 * Displays a PopupWindow with photo edit options.
58 */
Walter Jang0e72ce92015-02-23 12:27:21 -080059 final class PhotoHandler extends PhotoSelectionHandler implements View.OnClickListener {
Walter Jang3efae4a2015-02-18 11:12:00 -080060
61 /**
62 * Receiver of photo edit option callbacks.
63 */
64 private final class PhotoListener extends PhotoActionListener {
Walter Jang0e72ce92015-02-23 12:27:21 -080065
Walter Jang3efae4a2015-02-18 11:12:00 -080066 @Override
67 public void onRemovePictureChosen() {
68 getContent().setPhoto(/* bitmap =*/ null);
69 mUpdatedPhotos.remove(String.valueOf(mPhotoRawContactId));
Walter Jangebfe75b2015-03-12 15:31:21 -070070
71 // Update the mode so the options change if user clicks the photo again
72 mPhotoMode = getPhotoMode();
Walter Jang3efae4a2015-02-18 11:12:00 -080073 }
74
75 @Override
76 public void onPhotoSelected(Uri uri) throws FileNotFoundException {
77 final Bitmap bitmap = ContactPhotoUtils.getBitmapFromUri(getActivity(), uri);
78 if (bitmap == null || bitmap.getHeight() <= 0 || bitmap.getWidth() <= 0) {
79 Log.w(TAG, "Invalid photo selected");
80 }
81 getContent().setPhoto(bitmap);
Walter Jang41b3ea12015-03-09 17:30:06 -070082
Walter Jang1e8801b2015-03-10 15:57:05 -070083 // Clear any previously saved full resolution photos under negative raw contact IDs
84 // so that we will use the newly selected photo, instead of an old one on rotations.
85 removeNewRawContactPhotos();
86
Walter Jang41b3ea12015-03-09 17:30:06 -070087 // If a new photo was chosen but not yet saved,
88 // we need to update the UI immediately
Walter Jang3efae4a2015-02-18 11:12:00 -080089 mUpdatedPhotos.putParcelable(String.valueOf(mPhotoRawContactId), uri);
Walter Jang41b3ea12015-03-09 17:30:06 -070090 getContent().setFullSizePhoto(uri);
Walter Jangebfe75b2015-03-12 15:31:21 -070091
92 // Update the mode so the options change if user clicks the photo again
93 mPhotoMode = getPhotoMode();
Walter Jangac48ce42015-04-28 08:59:41 -070094
95 // Re-create the photo handler so that any additional photo selections create a
96 // new temp file (and don't hit the one that was just added to the cache).
97 mPhotoHandler = createPhotoHandler();
Walter Jang3efae4a2015-02-18 11:12:00 -080098 }
99
100 @Override
101 public Uri getCurrentPhotoUri() {
102 return mPhotoUri;
103 }
104
105 @Override
106 public void onPhotoSelectionDismissed() {
107 }
108 }
109
110 private PhotoListener mPhotoListener;
Walter Jang0e72ce92015-02-23 12:27:21 -0800111 private int mPhotoMode;
Walter Jang3efae4a2015-02-18 11:12:00 -0800112
Walter Jang0e72ce92015-02-23 12:27:21 -0800113 public PhotoHandler(Context context, int photoMode, RawContactDeltaList state) {
114 // We pass a null changeAnchorView since we are overriding onClick so that we
115 // can show the photo options in a dialog instead of a ListPopupWindow (which would
116 // be anchored at changeAnchorView).
117 super(context, /* changeAnchorView =*/ null, photoMode, /* isDirectoryContact =*/ false,
118 state);
Walter Jang3efae4a2015-02-18 11:12:00 -0800119 mPhotoListener = new PhotoListener();
Walter Jang0e72ce92015-02-23 12:27:21 -0800120 mPhotoMode = photoMode;
121 }
122
123 @Override
124 public void onClick(View view) {
125 PhotoSourceDialogFragment.show(CompactContactEditorFragment.this, mPhotoMode);
Walter Jang3efae4a2015-02-18 11:12:00 -0800126 }
127
128 @Override
129 public PhotoActionListener getListener() {
130 return mPhotoListener;
131 }
132
133 @Override
134 protected void startPhotoActivity(Intent intent, int requestCode, Uri photoUri) {
135 mPhotoUri = photoUri;
136 mStatus = Status.SUB_ACTIVITY;
137
138 CompactContactEditorFragment.this.startActivityForResult(intent, requestCode);
139 }
140 }
141
142 private PhotoHandler mPhotoHandler;
143 private Uri mPhotoUri;
144 private long mPhotoRawContactId;
Walter Jang3efae4a2015-02-18 11:12:00 -0800145
146 @Override
147 public void onCreate(Bundle savedState) {
148 super.onCreate(savedState);
149
150 if (savedState != null) {
151 mPhotoUri = savedState.getParcelable(KEY_PHOTO_URI);
152 mPhotoRawContactId = savedState.getLong(KEY_PHOTO_RAW_CONTACT_ID);
Walter Jang3efae4a2015-02-18 11:12:00 -0800153 }
154 }
155
Walter Jang3f990ba2015-01-27 17:38:30 +0000156 @Override
Walter Jang3f990ba2015-01-27 17:38:30 +0000157 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
Walter Janged8f6c92015-01-30 16:07:47 -0800158 setHasOptionsMenu(true);
159
Walter Jang3f990ba2015-01-27 17:38:30 +0000160 final View view = inflater.inflate(
161 R.layout.compact_contact_editor_fragment, container, false);
162 mContent = (LinearLayout) view.findViewById(R.id.editors);
163 return view;
164 }
165
Walter Janged8f6c92015-01-30 16:07:47 -0800166 @Override
Walter Jang3efae4a2015-02-18 11:12:00 -0800167 public void onSaveInstanceState(Bundle outState) {
168 outState.putParcelable(KEY_PHOTO_URI, mPhotoUri);
169 outState.putLong(KEY_PHOTO_RAW_CONTACT_ID, mPhotoRawContactId);
Walter Jang3efae4a2015-02-18 11:12:00 -0800170 super.onSaveInstanceState(outState);
171 }
172
173 @Override
Walter Janged8f6c92015-01-30 16:07:47 -0800174 public void onActivityResult(int requestCode, int resultCode, Intent data) {
175 if (mStatus == Status.SUB_ACTIVITY) {
176 mStatus = Status.EDITING;
177 }
Walter Jang3efae4a2015-02-18 11:12:00 -0800178 if (mPhotoHandler != null
179 && mPhotoHandler.handlePhotoActivityResult(requestCode, resultCode, data)) {
180 return;
181 }
Walter Janged8f6c92015-01-30 16:07:47 -0800182 super.onActivityResult(requestCode, resultCode, data);
183 }
184
Walter Jangba59deb2015-01-26 11:23:48 -0800185 @Override
Walter Jang5a7a23b2015-03-06 10:54:26 -0800186 public void onStop() {
187 super.onStop();
188
189 // If anything was left unsaved, save it now
190 if (!getActivity().isChangingConfigurations() && mStatus == Status.EDITING) {
191 save(SaveMode.RELOAD);
192 }
193 }
194
195 @Override
Walter Jangba59deb2015-01-26 11:23:48 -0800196 protected void bindEditors() {
Walter Jangcab3dce2015-02-09 17:48:03 -0800197 if (!isReadyToBindEditors()) {
198 return;
199 }
200
Walter Jangd35e5ef2015-02-24 09:18:16 -0800201 // Add input fields for the loaded Contact
Walter Jang3efae4a2015-02-18 11:12:00 -0800202 final CompactRawContactsEditorView editorView = getContent();
Walter Jangb6ca2722015-02-20 11:10:25 -0800203 editorView.setListener(this);
Walter Jang921287e2015-03-10 09:53:36 -0700204 editorView.setState(mState, getMaterialPalette(), mViewIdGenerator);
Walter Jangcab3dce2015-02-09 17:48:03 -0800205
Walter Jangd35e5ef2015-02-24 09:18:16 -0800206 // Set up the photo widget
Walter Jang3efae4a2015-02-18 11:12:00 -0800207 mPhotoHandler = createPhotoHandler();
208 mPhotoRawContactId = editorView.getPhotoRawContactId();
Walter Jang1e8801b2015-03-10 15:57:05 -0700209 if (mPhotoRawContactId < 0) {
210 // Since the raw contact IDs for new contacts are random negative numbers
211 // we consider any negative key a match
212 for (String key : mUpdatedPhotos.keySet()) {
213 try {
214 if (Integer.parseInt(key) < 0) {
215 editorView.setFullSizePhoto((Uri) mUpdatedPhotos.getParcelable(key));
216 break;
217 }
218 } catch (NumberFormatException ignored) {
219 }
220 }
221 } else if (mUpdatedPhotos.containsKey(String.valueOf(mPhotoRawContactId))) {
Walter Jang41b3ea12015-03-09 17:30:06 -0700222 editorView.setFullSizePhoto((Uri) mUpdatedPhotos.getParcelable(
223 String.valueOf(mPhotoRawContactId)));
224 }
Walter Jang3efae4a2015-02-18 11:12:00 -0800225 editorView.setPhotoHandler(mPhotoHandler);
226
Walter Jangd35e5ef2015-02-24 09:18:16 -0800227 // The editor is ready now so make it visible
228 editorView.setEnabled(isEnabled());
229 editorView.setVisibility(View.VISIBLE);
230
231 // Refresh the ActionBar as the visibility of the join command
232 // Activity can be null if we have been detached from the Activity.
Walter Jangcab3dce2015-02-09 17:48:03 -0800233 invalidateOptionsMenu();
234 }
235
236 private boolean isReadyToBindEditors() {
237 if (mState.isEmpty()) {
238 if (Log.isLoggable(TAG, Log.VERBOSE)) {
239 Log.v(TAG, "No data to bind editors");
240 }
241 return false;
242 }
243 if (mIsEdit && !mExistingContactDataReady) {
244 if (Log.isLoggable(TAG, Log.VERBOSE)) {
245 Log.v(TAG, "Existing contact data is not ready to bind editors.");
246 }
247 return false;
248 }
249 if (mHasNewContact && !mNewContactDataReady) {
250 if (Log.isLoggable(TAG, Log.VERBOSE)) {
251 Log.v(TAG, "New contact data is not ready to bind editors.");
252 }
253 return false;
254 }
255 return true;
Walter Jangba59deb2015-01-26 11:23:48 -0800256 }
257
Walter Jang3efae4a2015-02-18 11:12:00 -0800258 private PhotoHandler createPhotoHandler() {
Walter Jangebfe75b2015-03-12 15:31:21 -0700259 return new PhotoHandler(getActivity(), getPhotoMode(), mState);
260 }
261
262 private int getPhotoMode() {
Walter Jang3efae4a2015-02-18 11:12:00 -0800263 // To determine the options that are available to the user to update their photo
264 // (i.e. the photo mode), check if any of the writable raw contacts has a photo set
265 Integer photoMode = null;
266 boolean hasWritableAccountType = false;
267 final AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
268 for (RawContactDelta rawContactDelta : mState) {
269 if (!rawContactDelta.isVisible()) {
270 continue;
271 }
272 final AccountType accountType = rawContactDelta.getAccountType(accountTypes);
273 if (accountType.areContactsWritable()) {
274 hasWritableAccountType = true;
275 if (getContent().isWritablePhotoSet()) {
Brian Attwellb0afa202015-05-15 12:25:23 -0700276 photoMode = PhotoActionPopup.Modes.MULTIPLE_WRITE_ABLE_PHOTOS;
Walter Jang3efae4a2015-02-18 11:12:00 -0800277 break;
278 }
279 }
280 }
281 // If the mode was not set, base it on whether we saw a writable contact or not
282 if (photoMode == null) {
283 photoMode = hasWritableAccountType
284 ? PhotoActionPopup.Modes.NO_PHOTO : PhotoActionPopup.Modes.READ_ONLY_PHOTO;
285 }
Walter Jangebfe75b2015-03-12 15:31:21 -0700286 return photoMode;
Walter Jang3efae4a2015-02-18 11:12:00 -0800287 }
288
Walter Jang3f990ba2015-01-27 17:38:30 +0000289 @Override
Walter Jangd35e5ef2015-02-24 09:18:16 -0800290 protected View getAggregationAnchorView(long rawContactId) {
291 return getContent().getAggregationAnchorView();
292 }
293
294 @Override
Walter Jang92f8ccc2015-02-06 10:23:37 -0800295 protected void setGroupMetaData() {
Walter Jang49ed2032015-02-11 20:09:05 -0800296 // The compact editor does not support groups.
Walter Jang3f990ba2015-01-27 17:38:30 +0000297 }
298
299 @Override
Walter Jangb5510732015-02-03 13:56:15 -0800300 protected boolean doSaveAction(int saveMode) {
Walter Jang921287e2015-03-10 09:53:36 -0700301 // Save contact. No need to pass the palette since we are finished editing after the save.
Walter Jang49ed2032015-02-11 20:09:05 -0800302 final Intent intent = ContactSaveService.createSaveContactIntent(mContext, mState,
303 SAVE_MODE_EXTRA_KEY, saveMode, isEditingUserProfile(),
304 ((Activity) mContext).getClass(),
Walter Jang921287e2015-03-10 09:53:36 -0700305 CompactContactEditorActivity.ACTION_SAVE_COMPLETED, mUpdatedPhotos);
Walter Jang49ed2032015-02-11 20:09:05 -0800306 mContext.startService(intent);
307
308 return true;
309 }
310
311 @Override
312 protected void joinAggregate(final long contactId) {
313 final Intent intent = ContactSaveService.createJoinContactsIntent(
Brian Attwelld3946ca2015-03-03 11:13:49 -0800314 mContext, mContactIdForJoin, contactId, CompactContactEditorActivity.class,
Walter Jang49ed2032015-02-11 20:09:05 -0800315 CompactContactEditorActivity.ACTION_JOIN_COMPLETED);
316 mContext.startService(intent);
Walter Jang3f990ba2015-01-27 17:38:30 +0000317 }
Walter Jangb6ca2722015-02-20 11:10:25 -0800318
319 @Override
Walter Jang0e72ce92015-02-23 12:27:21 -0800320 public void onRemovePictureChosen() {
321 if (mPhotoHandler != null) {
322 mPhotoHandler.getListener().onRemovePictureChosen();
323 }
324 }
325
326 @Override
327 public void onTakePhotoChosen() {
328 if (mPhotoHandler != null) {
329 mPhotoHandler.getListener().onTakePhotoChosen();
330 }
331 }
332
333 @Override
334 public void onPickFromGalleryChosen() {
335 if (mPhotoHandler != null) {
336 mPhotoHandler.getListener().onPickFromGalleryChosen();
337 }
338 }
339
340 @Override
Walter Jangb6ca2722015-02-20 11:10:25 -0800341 public void onExpandEditor() {
Walter Jang36d6a162015-02-20 10:56:59 -0800342 // Determine if this is an insert (new contact) or edit
Walter Jang5a7a23b2015-03-06 10:54:26 -0800343 final boolean isInsert = isInsert(getActivity().getIntent());
Walter Jang36d6a162015-02-20 10:56:59 -0800344
345 if (isInsert) {
346 // For inserts, prevent any changes from being saved when the base fragment is destroyed
347 mStatus = Status.CLOSING;
Walter Jang36d6a162015-02-20 10:56:59 -0800348 } else {
Walter Jangf638b6f2015-03-02 16:15:17 -0800349 // Prevent a Toast from being displayed as we transition to the full editor
350 mShowToastAfterSave = false;
351
352 // Save whatever is in the form
Walter Jang36d6a162015-02-20 10:56:59 -0800353 save(SaveMode.RELOAD);
354 }
355
Walter Jang5a7a23b2015-03-06 10:54:26 -0800356 // Prepare an Intent to start the expanded editor
357 final Intent intent = isInsert
Walter Jang1e8801b2015-03-10 15:57:05 -0700358 ? EditorIntents.createInsertContactIntent(mState, getDisplayName(), mUpdatedPhotos)
Walter Jang921287e2015-03-10 09:53:36 -0700359 : EditorIntents.createEditContactIntent(mLookupUri, getMaterialPalette());
Walter Jang36d6a162015-02-20 10:56:59 -0800360 ImplicitIntentsUtil.startActivityInApp(getActivity(), intent);
Walter Jang5a7a23b2015-03-06 10:54:26 -0800361
Walter Jang921287e2015-03-10 09:53:36 -0700362 getActivity().finish();
Walter Jangb6ca2722015-02-20 11:10:25 -0800363 }
Walter Jang3efae4a2015-02-18 11:12:00 -0800364
Walter Jang151f3e62015-02-26 15:29:40 -0800365 @Override
366 public void onNameFieldChanged(long rawContactId, ValuesDelta valuesDelta) {
367 final Activity activity = getActivity();
368 if (activity == null || activity.isFinishing()) {
369 return;
370 }
371 if (!mIsUserProfile) {
372 acquireAggregationSuggestions(activity, rawContactId, valuesDelta);
373 }
374 }
375
Walter Jang5a7a23b2015-03-06 10:54:26 -0800376 @Override
Walter Jang5a7a23b2015-03-06 10:54:26 -0800377 public String getDisplayName() {
378 final StructuredNameEditorView structuredNameEditorView =
379 getContent().getStructuredNameEditorView();
380 return structuredNameEditorView == null
381 ? null : structuredNameEditorView.getDisplayName();
382 }
383
Walter Jang3efae4a2015-02-18 11:12:00 -0800384 private CompactRawContactsEditorView getContent() {
385 return (CompactRawContactsEditorView) mContent;
386 }
Walter Jang3f990ba2015-01-27 17:38:30 +0000387}