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