16fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro/* 26fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Copyright (C) 2011 The Android Open Source Project 36fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * 46fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Licensed under the Apache License, Version 2.0 (the "License"); 56fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * you may not use this file except in compliance with the License. 66fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * You may obtain a copy of the License at 76fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * 86fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * http://www.apache.org/licenses/LICENSE-2.0 96fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * 106fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Unless required by applicable law or agreed to in writing, software 116fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * distributed under the License is distributed on an "AS IS" BASIS, 126fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * See the License for the specific language governing permissions and 146fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * limitations under the License. 156fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 166fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoropackage com.android.contacts.activities; 176fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 186fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.animation.Animator; 196fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.animation.AnimatorListenerAdapter; 206fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.animation.ObjectAnimator; 216fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.animation.PropertyValuesHolder; 226fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.app.Activity; 236fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.content.Context; 246fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.content.Intent; 25272122caf9adb8414451bb37f56db659dace1db5Makoto Onukiimport android.content.res.Configuration; 266fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.graphics.Bitmap; 276fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.graphics.Rect; 2814d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoroimport android.net.Uri; 296fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.os.Bundle; 306fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.os.Parcelable; 316fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.view.View; 326fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.view.ViewGroup.MarginLayoutParams; 336fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.widget.FrameLayout.LayoutParams; 346fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoroimport android.widget.ImageView; 356fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 3679a6b5d3a885bc508b4b98c92a946fe12643c444Chiao Chengimport com.android.contacts.common.ContactPhotoManager; 37e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.ContactSaveService; 38e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.R; 39e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.detail.PhotoSelectionHandler; 40e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.editor.PhotoActionPopup; 41851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.RawContactDeltaList; 42e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.util.ContactPhotoUtils; 43e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.util.SchedulingUtils; 44e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Cheng 456fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 466fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro/** 476fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Popup activity for choosing a contact photo within the Contacts app. 486fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 496fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoropublic class PhotoSelectionActivity extends Activity { 506fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 512f77c85543e6c497e63cd0e216b70e58c1d13ec3Daniel Lehmann private static final String TAG = "PhotoSelectionActivity"; 522f77c85543e6c497e63cd0e216b70e58c1d13ec3Daniel Lehmann 536fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** Number of ms for the animation to expand the photo. */ 546fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private static final int PHOTO_EXPAND_DURATION = 100; 556fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 566fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** Number of ms for the animation to contract the photo on activity exit. */ 576fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private static final int PHOTO_CONTRACT_DURATION = 50; 586fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 596fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** Number of ms for the animation to hide the backdrop on finish. */ 606fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private static final int BACKDROP_FADEOUT_DURATION = 100; 616fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 62ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus /** Key used to persist photo-filename (NOT full file-path). */ 63ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus private static final String KEY_CURRENT_PHOTO_FILE = "currentphotofile"; 64ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus 65ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus /** Key used to persist whether a sub-activity is currently in progress. */ 66ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus private static final String KEY_SUB_ACTIVITY_IN_PROGRESS = "subinprogress"; 67ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus 6814d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro /** Intent extra to get the photo URI. */ 6914d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro public static final String PHOTO_URI = "photo_uri"; 706fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 716fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** Intent extra to get the entity delta list. */ 726fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public static final String ENTITY_DELTA_LIST = "entity_delta_list"; 736fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 746fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** Intent extra to indicate whether the contact is the user's profile. */ 756fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public static final String IS_PROFILE = "is_profile"; 766fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 776fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** Intent extra to indicate whether the contact is from a directory (non-editable). */ 786fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public static final String IS_DIRECTORY_CONTACT = "is_directory_contact"; 796fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 806fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** 816fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Intent extra to indicate whether the photo should be animated to show the full contents of 826fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * the photo (on a larger portion of the screen) when clicked. If unspecified or false, the 836fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * photo will not move from its original location. 846fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 856fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public static final String EXPAND_PHOTO = "expand_photo"; 866fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 876fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** Source bounds of the image that was clicked on. */ 886fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private Rect mSourceBounds; 896fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 9014d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro /** 9114d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro * The photo URI. May be null, in which case the default avatar will be used. 9214d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro */ 9314d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro private Uri mPhotoUri; 946fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 956fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** Entity delta list of the contact. */ 96851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu private RawContactDeltaList mState; 976fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 986fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** Whether the contact is the user's profile. */ 996fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private boolean mIsProfile; 1006fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 1016fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** Whether the contact is from a directory. */ 1026fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private boolean mIsDirectoryContact; 1036fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 1046fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** Whether to animate the photo to an expanded view covering more of the screen. */ 1056fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private boolean mExpandPhoto; 1066fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 1072d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu /** 1082d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu * Side length (in pixels) of the expanded photo if to be expanded. Photos are expected to 1092d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu * be square. 1102d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu */ 1112d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu private int mExpandedPhotoSize; 1122d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu 1132d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu /** Height (in pixels) to leave underneath the expanded photo to show the list popup */ 1142d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu private int mHeightOffset; 1152d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu 1166fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** The semi-transparent backdrop. */ 1176fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private View mBackdrop; 1186fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 1196fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** The photo view. */ 1206fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private ImageView mPhotoView; 1216fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 1226fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** The photo handler attached to this activity, if any. */ 1236fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private PhotoHandler mPhotoHandler; 1246fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 1256fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** Animator to expand the photo out to full size. */ 1266fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private ObjectAnimator mPhotoAnimator; 1276fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 1286fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** Listener for the animation. */ 1296fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private AnimatorListenerAdapter mAnimationListener; 1306fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 1316fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** Whether a change in layout of the photo has occurred that has no animation yet. */ 1326fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private boolean mAnimationPending; 1336fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 1346fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** Prior position of the image (for animating). */ 1356fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro Rect mOriginalPos = new Rect(); 1366fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 1376fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** Layout params for the photo view before we started animating. */ 1386fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private LayoutParams mPhotoStartParams; 1396fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 1406fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** Layout params for the photo view after we finished animating. */ 1416fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private LayoutParams mPhotoEndParams; 1426fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 1436fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** Whether a sub-activity is currently in progress. */ 1446fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private boolean mSubActivityInProgress; 1456fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 146272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki private boolean mCloseActivityWhenCameBackFromSubActivity; 1476fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 1486fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** 149ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus * A photo result received by the activity, persisted across activity lifecycle. 150ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus */ 151ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus private PendingPhotoResult mPendingPhotoResult; 152ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus 153ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus /** 1546fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * The photo file being interacted with, if any. Saved/restored between activity instances. 1556fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 156ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus private String mCurrentPhotoFile; 1576fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 1586fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro @Override 1596fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro protected void onCreate(Bundle savedInstanceState) { 1606fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro super.onCreate(savedInstanceState); 1616fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro setContentView(R.layout.photoselection_activity); 162ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus if (savedInstanceState != null) { 163ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus mCurrentPhotoFile = savedInstanceState.getString(KEY_CURRENT_PHOTO_FILE); 164ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus mSubActivityInProgress = savedInstanceState.getBoolean(KEY_SUB_ACTIVITY_IN_PROGRESS); 165ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus } 1666fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 1676fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro // Pull data out of the intent. 1686fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro final Intent intent = getIntent(); 16914d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro mPhotoUri = intent.getParcelableExtra(PHOTO_URI); 170851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu mState = (RawContactDeltaList) intent.getParcelableExtra(ENTITY_DELTA_LIST); 1716fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mIsProfile = intent.getBooleanExtra(IS_PROFILE, false); 1726fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mIsDirectoryContact = intent.getBooleanExtra(IS_DIRECTORY_CONTACT, false); 1736fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mExpandPhoto = intent.getBooleanExtra(EXPAND_PHOTO, false); 1746fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 1752d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu // Pull out photo expansion properties from resources 1762d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu mExpandedPhotoSize = getResources().getDimensionPixelSize( 1772d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu R.dimen.detail_contact_photo_expanded_size); 1782d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu mHeightOffset = getResources().getDimensionPixelOffset( 1792d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu R.dimen.expanded_photo_height_offset); 1802d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu 1816fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mBackdrop = findViewById(R.id.backdrop); 1826fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mPhotoView = (ImageView) findViewById(R.id.photo); 1836fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mSourceBounds = intent.getSourceBounds(); 1846fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 1856fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro // Fade in the background. 1866fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro animateInBackground(); 1876fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 1886fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro // Dismiss the dialog on clicking the backdrop. 1896fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mBackdrop.setOnClickListener(new View.OnClickListener() { 1906fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro @Override 1916fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public void onClick(View v) { 1926fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro finish(); 1936fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 1946fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro }); 1956fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 1966fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro // Wait until the layout pass to show the photo, so that the source bounds will match up. 1976f5557e3dc3cd12182026bdfea17b15d4d9b7dfcJosh Gargus SchedulingUtils.doAfterLayout(mBackdrop, new Runnable() { 1986fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro @Override 19951f9c0f2f2923d14cced5096b40f017dd87b68d3Josh Gargus public void run() { 2006fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro displayPhoto(); 2016fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 20251f9c0f2f2923d14cced5096b40f017dd87b68d3Josh Gargus }); 2036fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 2046fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 2052d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu /** 2062d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu * Compute the adjusted expanded photo size to fit within the enclosing view with the same 2072d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu * aspect ratio. 2082d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu * @param enclosingView This is the view that the photo must fit within. 2092d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu * @param heightOffset This is the amount of height to leave open for the photo action popup. 2102d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu */ 2112d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu private int getAdjustedExpandedPhotoSize(View enclosingView, int heightOffset) { 2122d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu // pull out the bounds of the backdrop 2132d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu final Rect bounds = new Rect(); 2142d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu enclosingView.getDrawingRect(bounds); 2152d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu final int boundsWidth = bounds.width(); 2162d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu final int boundsHeight = bounds.height() - heightOffset; 2172d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu 2182d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu // ensure that the new expanded photo size can fit within the backdrop 2192d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu final float alpha = Math.min((float) boundsHeight / (float) mExpandedPhotoSize, 2202d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu (float) boundsWidth / (float) mExpandedPhotoSize); 2212d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu if (alpha < 1.0f) { 2222d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu // need to shrink width and height while maintaining aspect ratio 2232d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu return (int) (alpha * mExpandedPhotoSize); 2242d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu } else { 2252d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu return mExpandedPhotoSize; 2262d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu } 2272d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu } 2282d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu 2296fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro @Override 230272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki public void onConfigurationChanged(Configuration newConfig) { 231272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki super.onConfigurationChanged(newConfig); 232272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki 233272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki // The current look may not seem right on the new configuration, so let's just close self. 234272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki 235272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki if (!mSubActivityInProgress) { 236272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki finishImmediatelyWithNoAnimation(); 237272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki } else { 238272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki // A sub-activity is in progress, so don't close it yet, but close it when we come back 239272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki // to this activity. 240272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki mCloseActivityWhenCameBackFromSubActivity = true; 241272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki } 242272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki } 243272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki 244272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki @Override 2456fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public void finish() { 2466fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro if (!mSubActivityInProgress) { 2476fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro closePhotoAndFinish(); 2486fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } else { 249272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki finishImmediatelyWithNoAnimation(); 2506fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 2516fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 2526fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 2536fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro /** 2546fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * Builds a well-formed intent for invoking this activity. 2556fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * @param context The context. 25614d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro * @param photoUri The URI of the current photo (may be null, in which case the default 25714d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro * avatar image will be displayed). 25814d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro * @param photoBitmap The bitmap of the current photo (may be null, in which case the default 25914d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro * avatar image will be displayed). 26014d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro * @param photoBytes The bytes for the current photo (may be null, in which case the default 26114d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro * avatar image will be displayed). 2626fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * @param photoBounds The pixel bounds of the current photo. 2636fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * @param delta The entity delta list for the contact. 2646fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * @param isProfile Whether the contact is the user's profile. 2656fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * @param isDirectoryContact Whether the contact comes from a directory (non-editable). 2666fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * @param expandPhotoOnClick Whether the photo should be expanded on click or not (generally, 2676fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * this should be true for phones, and false for tablets). 2686fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * @return An intent that can be used to invoke the photo selection activity. 2696fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro */ 27014d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro public static Intent buildIntent(Context context, Uri photoUri, Bitmap photoBitmap, 271851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu byte[] photoBytes, Rect photoBounds, RawContactDeltaList delta, boolean isProfile, 27214d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro boolean isDirectoryContact, boolean expandPhotoOnClick) { 2736fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro Intent intent = new Intent(context, PhotoSelectionActivity.class); 27414d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro if (photoUri != null && photoBitmap != null && photoBytes != null) { 27514d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro intent.putExtra(PHOTO_URI, photoUri); 27614d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro } 2776fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro intent.setSourceBounds(photoBounds); 2786fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro intent.putExtra(ENTITY_DELTA_LIST, (Parcelable) delta); 2796fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro intent.putExtra(IS_PROFILE, isProfile); 2806fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro intent.putExtra(IS_DIRECTORY_CONTACT, isDirectoryContact); 2816fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro intent.putExtra(EXPAND_PHOTO, expandPhotoOnClick); 2826fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro return intent; 2836fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 2846fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 285272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki private void finishImmediatelyWithNoAnimation() { 2866fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro super.finish(); 2876fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 2886fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 2896fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro @Override 2906fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro protected void onDestroy() { 2916fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro super.onDestroy(); 2926fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro if (mPhotoAnimator != null) { 2936fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mPhotoAnimator.cancel(); 2946fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mPhotoAnimator = null; 2956fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 2966fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro if (mPhotoHandler != null) { 2976fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mPhotoHandler.destroy(); 2986fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mPhotoHandler = null; 2996fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 3006fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 3016fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 3026fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private void displayPhoto() { 30314d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro // Animate the photo view into its end location. 30414d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro final int[] pos = new int[2]; 30514d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro mBackdrop.getLocationOnScreen(pos); 30614d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro LayoutParams layoutParams = new LayoutParams(mSourceBounds.width(), 30714d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro mSourceBounds.height()); 30814d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro mOriginalPos.left = mSourceBounds.left - pos[0]; 30914d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro mOriginalPos.top = mSourceBounds.top - pos[1]; 31014d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro mOriginalPos.right = mOriginalPos.left + mSourceBounds.width(); 31114d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro mOriginalPos.bottom = mOriginalPos.top + mSourceBounds.height(); 31214d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro layoutParams.setMargins(mOriginalPos.left, mOriginalPos.top, mOriginalPos.right, 31314d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro mOriginalPos.bottom); 31414d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro mPhotoStartParams = layoutParams; 31514d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro mPhotoView.setLayoutParams(layoutParams); 31614d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro mPhotoView.requestLayout(); 31714d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro 3182f77c85543e6c497e63cd0e216b70e58c1d13ec3Daniel Lehmann // Load the photo. 3192f77c85543e6c497e63cd0e216b70e58c1d13ec3Daniel Lehmann int photoWidth = getPhotoEndParams().width; 3202f77c85543e6c497e63cd0e216b70e58c1d13ec3Daniel Lehmann if (mPhotoUri != null) { 3212f77c85543e6c497e63cd0e216b70e58c1d13ec3Daniel Lehmann // If we have a URI, the bitmap should be cached directly. 3222f77c85543e6c497e63cd0e216b70e58c1d13ec3Daniel Lehmann ContactPhotoManager.getInstance(this).loadPhoto(mPhotoView, mPhotoUri, photoWidth, 3232f77c85543e6c497e63cd0e216b70e58c1d13ec3Daniel Lehmann false); 3242f77c85543e6c497e63cd0e216b70e58c1d13ec3Daniel Lehmann } else { 3252f77c85543e6c497e63cd0e216b70e58c1d13ec3Daniel Lehmann // Fall back to avatar image. 3262f77c85543e6c497e63cd0e216b70e58c1d13ec3Daniel Lehmann mPhotoView.setImageResource(ContactPhotoManager.getDefaultAvatarResId(this, photoWidth, 3272f77c85543e6c497e63cd0e216b70e58c1d13ec3Daniel Lehmann false)); 3282f77c85543e6c497e63cd0e216b70e58c1d13ec3Daniel Lehmann } 3292f77c85543e6c497e63cd0e216b70e58c1d13ec3Daniel Lehmann 33014d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro mPhotoView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { 33114d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro @Override 33214d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro public void onLayoutChange(View v, int left, int top, int right, int bottom, 33314d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro int oldLeft, int oldTop, int oldRight, int oldBottom) { 33414d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro if (mAnimationPending) { 33514d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro mAnimationPending = false; 33614d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro PropertyValuesHolder pvhLeft = 33714d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro PropertyValuesHolder.ofInt("left", mOriginalPos.left, left); 33814d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro PropertyValuesHolder pvhTop = 33914d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro PropertyValuesHolder.ofInt("top", mOriginalPos.top, top); 34014d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro PropertyValuesHolder pvhRight = 34114d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro PropertyValuesHolder.ofInt("right", mOriginalPos.right, right); 34214d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro PropertyValuesHolder pvhBottom = 34314d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro PropertyValuesHolder.ofInt("bottom", mOriginalPos.bottom, bottom); 34414d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(mPhotoView, 34514d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro pvhLeft, pvhTop, pvhRight, pvhBottom).setDuration( 34614d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro PHOTO_EXPAND_DURATION); 34714d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro if (mAnimationListener != null) { 34814d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro anim.addListener(mAnimationListener); 3496fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 35014d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro anim.start(); 3516fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 35214d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro } 35314d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro }); 35414d2083c4c4c52b0d8b600a93c294af32cd6a826Dave Santoro attachPhotoHandler(); 3556fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 3566fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 3572d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu /** 3582d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu * This sets the photo's layout params at the end of the animation. 3592d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu * <p> 3602d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu * The scheme is to enlarge the photo to the desired size with the enlarged photo shifted 3612d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu * to the top left of the screen as much as possible while keeping the underlying smaller 3622d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu * photo occluded. 3632d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu */ 3646fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private LayoutParams getPhotoEndParams() { 3656fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro if (mPhotoEndParams == null) { 3666fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mPhotoEndParams = new LayoutParams(mPhotoStartParams); 3676fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro if (mExpandPhoto) { 3682d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu final int adjustedPhotoSize = getAdjustedExpandedPhotoSize(mBackdrop, 3692d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu mHeightOffset); 3702d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu int widthDelta = adjustedPhotoSize - mPhotoStartParams.width; 3712d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu int heightDelta = adjustedPhotoSize - mPhotoStartParams.height; 3722d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu if (widthDelta >= 1 || heightDelta >= 1) { 3732d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu // This is an actual expansion. 3742d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu mPhotoEndParams.width = adjustedPhotoSize; 3752d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu mPhotoEndParams.height = adjustedPhotoSize; 3762d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu mPhotoEndParams.topMargin = 3772d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu Math.max(mPhotoStartParams.topMargin - heightDelta, 0); 3782d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu mPhotoEndParams.leftMargin = 3792d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu Math.max(mPhotoStartParams.leftMargin - widthDelta, 0); 3802d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu mPhotoEndParams.bottomMargin = 0; 3812d4632c2eb7d2dc3bb94bf64c14c471aafa67b0fMaurice Chu mPhotoEndParams.rightMargin = 0; 3826fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 3836fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 3846fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 3856fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro return mPhotoEndParams; 3866fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 3876fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 3886fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private void animatePhotoOpen() { 3896fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mAnimationListener = new AnimatorListenerAdapter() { 3906fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private void capturePhotoPos() { 3916fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mPhotoView.requestLayout(); 3926fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mOriginalPos.left = mPhotoView.getLeft(); 3936fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mOriginalPos.top = mPhotoView.getTop(); 3946fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mOriginalPos.right = mPhotoView.getRight(); 3956fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mOriginalPos.bottom = mPhotoView.getBottom(); 3966fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 3976fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 3986fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro @Override 3996fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public void onAnimationEnd(Animator animation) { 4006fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro capturePhotoPos(); 4016fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro if (mPhotoHandler != null) { 4026fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mPhotoHandler.onClick(mPhotoView); 4036fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 4046fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 4056fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 4066fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro @Override 4076fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public void onAnimationCancel(Animator animation) { 4086fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro capturePhotoPos(); 4096fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 4106fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro }; 4116fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro animatePhoto(getPhotoEndParams()); 4126fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 4136fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 4146fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private void closePhotoAndFinish() { 4156fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mAnimationListener = new AnimatorListenerAdapter() { 4166fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro @Override 4176fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public void onAnimationEnd(Animator animation) { 4186fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro // After the photo animates down, fade it away and finish. 4196fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro ObjectAnimator anim = ObjectAnimator.ofFloat( 4206fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mPhotoView, "alpha", 0f).setDuration(PHOTO_CONTRACT_DURATION); 4216fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro anim.addListener(new AnimatorListenerAdapter() { 4226fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro @Override 4236fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public void onAnimationEnd(Animator animation) { 424272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki finishImmediatelyWithNoAnimation(); 4256fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 4266fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro }); 4276fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro anim.start(); 4286fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 4296fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro }; 4306fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 4316fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro animatePhoto(mPhotoStartParams); 4326fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro animateAwayBackground(); 4336fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 4346fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 4356fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private void animatePhoto(MarginLayoutParams to) { 4366fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro // Cancel any existing animation. 4376fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro if (mPhotoAnimator != null) { 4386fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mPhotoAnimator.cancel(); 4396fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 4406fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 4416fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mPhotoView.setLayoutParams(to); 4426fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mAnimationPending = true; 4436fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mPhotoView.requestLayout(); 4446fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 4456fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 4466fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private void animateInBackground() { 4476fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro ObjectAnimator.ofFloat(mBackdrop, "alpha", 0, 0.5f).setDuration( 4486fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro PHOTO_EXPAND_DURATION).start(); 4496fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 4506fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 4516fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private void animateAwayBackground() { 4526fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro ObjectAnimator.ofFloat(mBackdrop, "alpha", 0f).setDuration( 4536fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro BACKDROP_FADEOUT_DURATION).start(); 4546fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 4556fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 4566fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro @Override 457ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus protected void onSaveInstanceState(Bundle outState) { 458ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus super.onSaveInstanceState(outState); 459ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus outState.putString(KEY_CURRENT_PHOTO_FILE, mCurrentPhotoFile); 460ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus outState.putBoolean(KEY_SUB_ACTIVITY_IN_PROGRESS, mSubActivityInProgress); 461ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus } 462ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus 463ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus @Override 4646fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro protected void onActivityResult(int requestCode, int resultCode, Intent data) { 4656fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro if (mPhotoHandler != null) { 4666fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro mSubActivityInProgress = false; 4676fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro if (mPhotoHandler.handlePhotoActivityResult(requestCode, resultCode, data)) { 468ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus // Clear out any pending photo result. 469ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus mPendingPhotoResult = null; 4706fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } else { 471272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki // User cancelled the sub-activity and returning to the photo selection activity. 472272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki if (mCloseActivityWhenCameBackFromSubActivity) { 473272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki finishImmediatelyWithNoAnimation(); 474272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki } else { 475272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki // Re-display options. 476272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki mPhotoHandler.onClick(mPhotoView); 477272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki } 4786fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 4796fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } else { 480ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus // Create a pending photo result to be handled when the photo handler is created. 481ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus mPendingPhotoResult = new PendingPhotoResult(requestCode, resultCode, data); 4826fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 4836fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 4846fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 4856fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private void attachPhotoHandler() { 486ced983d7a816256d93fdea1f81e63e4598c18875Josh Gargus // Always provide the same two choices (take a photo with the camera, select a photo 487ced983d7a816256d93fdea1f81e63e4598c18875Josh Gargus // from the gallery), but with slightly different wording. 488ced983d7a816256d93fdea1f81e63e4598c18875Josh Gargus // Note: don't worry about this being a read-only contact; this code will not be invoked. 489ced983d7a816256d93fdea1f81e63e4598c18875Josh Gargus int mode = (mPhotoUri == null) ? PhotoActionPopup.Modes.NO_PHOTO 490ced983d7a816256d93fdea1f81e63e4598c18875Josh Gargus : PhotoActionPopup.Modes.PHOTO_DISALLOW_PRIMARY; 491ced983d7a816256d93fdea1f81e63e4598c18875Josh Gargus // We don't want to provide a choice to remove the photo for two reasons: 492ced983d7a816256d93fdea1f81e63e4598c18875Josh Gargus // 1) the UX designs don't call for it 493ced983d7a816256d93fdea1f81e63e4598c18875Josh Gargus // 2) even if we wanted to, the implementation would be moderately hairy 494ced983d7a816256d93fdea1f81e63e4598c18875Josh Gargus mode &= ~PhotoActionPopup.Flags.REMOVE_PHOTO; 495ced983d7a816256d93fdea1f81e63e4598c18875Josh Gargus 496ced983d7a816256d93fdea1f81e63e4598c18875Josh Gargus mPhotoHandler = new PhotoHandler(this, mPhotoView, mode, mState); 497272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki 498ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus if (mPendingPhotoResult != null) { 499ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus mPhotoHandler.handlePhotoActivityResult(mPendingPhotoResult.mRequestCode, 500ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus mPendingPhotoResult.mResultCode, mPendingPhotoResult.mData); 501ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus mPendingPhotoResult = null; 502ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus } else { 503ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus // Setting the photo in displayPhoto() resulted in a relayout 504ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus // request... to avoid jank, wait until this layout has happened. 505ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus SchedulingUtils.doAfterLayout(mBackdrop, new Runnable() { 506ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus @Override 507ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus public void run() { 508ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus animatePhotoOpen(); 509ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus } 510ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus }); 511ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus } 5126fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 5136fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 5146fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro private final class PhotoHandler extends PhotoSelectionHandler { 515272122caf9adb8414451bb37f56db659dace1db5Makoto Onuki private final PhotoActionListener mListener; 5166fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 517e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus private PhotoHandler( 518851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu Context context, View photoView, int photoMode, RawContactDeltaList state) { 519e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus super(context, photoView, photoMode, PhotoSelectionActivity.this.mIsDirectoryContact, 520e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus state); 521e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus mListener = new PhotoListener(); 522e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus } 5236f5557e3dc3cd12182026bdfea17b15d4d9b7dfcJosh Gargus 524e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus @Override 525e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus public PhotoActionListener getListener() { 526e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus return mListener; 527e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus } 5286fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 529e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus @Override 530ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus public void startPhotoActivity(Intent intent, int requestCode, String photoFile) { 531e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus mSubActivityInProgress = true; 532e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus mCurrentPhotoFile = photoFile; 533e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus PhotoSelectionActivity.this.startActivityForResult(intent, requestCode); 534e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus } 5356fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 536e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus private final class PhotoListener extends PhotoActionListener { 5376fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro @Override 5386fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public void onPhotoSelected(Bitmap bitmap) { 539851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu RawContactDeltaList delta = getDeltaForAttachingPhotoToContact(); 540e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus long rawContactId = getWritableEntityId(); 541ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus final String croppedPath = ContactPhotoUtils.pathForCroppedPhoto( 542ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus PhotoSelectionActivity.this, mCurrentPhotoFile); 543e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus Intent intent = ContactSaveService.createSaveContactIntent( 544ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus mContext, delta, "", 0, mIsProfile, null, null, rawContactId, croppedPath); 5456fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro startService(intent); 5466fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro finish(); 5476fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 5486fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 5496fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro @Override 550ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus public String getCurrentPhotoFile() { 5516fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro return mCurrentPhotoFile; 5526fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 5536fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro 5546fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro @Override 5556fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public void onPhotoSelectionDismissed() { 5566fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro if (!mSubActivityInProgress) { 5576fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro finish(); 5586fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 5596fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 5606fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 5616fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 562ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus 563ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus private static class PendingPhotoResult { 564ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus final private int mRequestCode; 565ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus final private int mResultCode; 566ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus final private Intent mData; 567ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus private PendingPhotoResult(int requestCode, int resultCode, Intent data) { 568ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus mRequestCode = requestCode; 569ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus mResultCode = resultCode; 570ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus mData = data; 571ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus } 572ebc17929007ecf12f25b8e90023766e8a083d11eJosh Gargus } 5736fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro} 574