ContactDetailActivity.java revision 2a45e35ab3273c8901d9df3671e51614dc0250c6
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.contacts.activities;
18
19import com.android.contacts.ContactLoader;
20import com.android.contacts.ContactSaveService;
21import com.android.contacts.ContactsActivity;
22import com.android.contacts.R;
23import com.android.contacts.detail.ContactDetailDisplayUtils;
24import com.android.contacts.detail.ContactDetailFragment;
25import com.android.contacts.detail.ContactDetailLayoutController;
26import com.android.contacts.detail.ContactLoaderFragment;
27import com.android.contacts.detail.ContactLoaderFragment.ContactLoaderFragmentListener;
28import com.android.contacts.interactions.ContactDeletionInteraction;
29import com.android.contacts.model.AccountWithDataSet;
30import com.android.contacts.util.PhoneCapabilityTester;
31
32import android.app.ActionBar;
33import android.app.Fragment;
34import android.content.ActivityNotFoundException;
35import android.content.ContentValues;
36import android.content.Intent;
37import android.net.Uri;
38import android.os.Bundle;
39import android.os.Handler;
40import android.text.TextUtils;
41import android.util.Log;
42import android.view.KeyEvent;
43import android.view.Menu;
44import android.view.MenuInflater;
45import android.view.MenuItem;
46import android.view.MenuItem.OnMenuItemClickListener;
47import android.view.View;
48import android.view.View.OnClickListener;
49import android.view.ViewGroup;
50import android.view.accessibility.AccessibilityEvent;
51import android.view.accessibility.AccessibilityManager;
52import android.widget.CheckBox;
53import android.widget.Toast;
54
55import java.util.ArrayList;
56
57public class ContactDetailActivity extends ContactsActivity {
58    private static final String TAG = "ContactDetailActivity";
59
60    /** Shows a toogle button for hiding/showing updates. Don't submit with true */
61    private static final boolean DEBUG_TRANSITIONS = false;
62
63    /**
64     * Boolean intent key that specifies whether pressing the "up" affordance in this activity
65     * should cause it to finish itself or launch an intent to bring the user back to a specific
66     * parent activity - the {@link PeopleActivity}.
67     */
68    public static final String INTENT_KEY_FINISH_ACTIVITY_ON_UP_SELECTED =
69            "finishActivityOnUpSelected";
70
71    private ContactLoader.Result mContactData;
72    private Uri mLookupUri;
73    private boolean mFinishActivityOnUpSelected;
74
75    private ContactDetailLayoutController mContactDetailLayoutController;
76    private ContactLoaderFragment mLoaderFragment;
77
78    private Handler mHandler = new Handler();
79
80    @Override
81    protected void onCreate(Bundle savedState) {
82        super.onCreate(savedState);
83        if (PhoneCapabilityTester.isUsingTwoPanes(this)) {
84            // This activity must not be shown. We have to select the contact in the
85            // PeopleActivity instead ==> Create a forward intent and finish
86            final Intent originalIntent = getIntent();
87            Intent intent = new Intent();
88            intent.setAction(originalIntent.getAction());
89            intent.setDataAndType(originalIntent.getData(), originalIntent.getType());
90            intent.setFlags(
91                    Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | Intent.FLAG_ACTIVITY_FORWARD_RESULT
92                            | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
93
94            intent.setClass(this, PeopleActivity.class);
95            startActivity(intent);
96            finish();
97            return;
98        }
99
100        mFinishActivityOnUpSelected = getIntent().getBooleanExtra(
101                INTENT_KEY_FINISH_ACTIVITY_ON_UP_SELECTED, false);
102
103        setContentView(R.layout.contact_detail_activity);
104
105        mContactDetailLayoutController = new ContactDetailLayoutController(this, savedState,
106                getFragmentManager(), findViewById(R.id.contact_detail_container),
107                mContactDetailFragmentListener);
108
109        // We want the UP affordance but no app icon.
110        // Setting HOME_AS_UP, SHOW_TITLE and clearing SHOW_HOME does the trick.
111        ActionBar actionBar = getActionBar();
112        if (actionBar != null) {
113            actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_TITLE,
114                    ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_TITLE
115                    | ActionBar.DISPLAY_SHOW_HOME);
116            actionBar.setTitle("");
117        }
118
119        Log.i(TAG, getIntent().getData().toString());
120    }
121
122    @Override
123    public void onAttachFragment(Fragment fragment) {
124         if (fragment instanceof ContactLoaderFragment) {
125            mLoaderFragment = (ContactLoaderFragment) fragment;
126            mLoaderFragment.setListener(mLoaderFragmentListener);
127            mLoaderFragment.loadUri(getIntent().getData());
128        }
129    }
130
131    @Override
132    public boolean onCreateOptionsMenu(Menu menu) {
133        super.onCreateOptionsMenu(menu);
134        MenuInflater inflater = getMenuInflater();
135        inflater.inflate(R.menu.star, menu);
136        if (DEBUG_TRANSITIONS) {
137            final MenuItem toggleSocial =
138                    menu.add(mLoaderFragment.getLoadStreamItems() ? "less" : "more");
139            toggleSocial.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
140            toggleSocial.setOnMenuItemClickListener(new OnMenuItemClickListener() {
141                @Override
142                public boolean onMenuItemClick(MenuItem item) {
143                    mLoaderFragment.toggleLoadStreamItems();
144                    invalidateOptionsMenu();
145                    return false;
146                }
147            });
148        }
149        return true;
150    }
151
152    @Override
153    public boolean onPrepareOptionsMenu(Menu menu) {
154        MenuItem starredMenuItem = menu.findItem(R.id.menu_star);
155        ViewGroup starredContainer = (ViewGroup) getLayoutInflater().inflate(
156                R.layout.favorites_star, null, false);
157        final CheckBox starredView = (CheckBox) starredContainer.findViewById(R.id.star);
158        starredView.setOnClickListener(new OnClickListener() {
159            @Override
160            public void onClick(View v) {
161                // Toggle "starred" state
162                // Make sure there is a contact
163                if (mLookupUri != null) {
164                    Intent intent = ContactSaveService.createSetStarredIntent(
165                            ContactDetailActivity.this, mLookupUri, starredView.isChecked());
166                    ContactDetailActivity.this.startService(intent);
167                }
168            }
169        });
170        // If there is contact data, update the starred state
171        if (mContactData != null) {
172            ContactDetailDisplayUtils.setStarred(mContactData, starredView);
173        }
174        starredMenuItem.setActionView(starredContainer);
175        return true;
176    }
177
178    @Override
179    public boolean onKeyDown(int keyCode, KeyEvent event) {
180        // First check if the {@link ContactLoaderFragment} can handle the key
181        if (mLoaderFragment != null && mLoaderFragment.handleKeyDown(keyCode)) return true;
182
183        // Otherwise find the correct fragment to handle the event
184        FragmentKeyListener mCurrentFragment = mContactDetailLayoutController.getCurrentPage();
185        if (mCurrentFragment != null && mCurrentFragment.handleKeyDown(keyCode)) return true;
186
187        // In the last case, give the key event to the superclass.
188        return super.onKeyDown(keyCode, event);
189    }
190
191    @Override
192    protected void onSaveInstanceState(Bundle outState) {
193        super.onSaveInstanceState(outState);
194        if (mContactDetailLayoutController != null) {
195            mContactDetailLayoutController.onSaveInstanceState(outState);
196        }
197    }
198
199    private final ContactLoaderFragmentListener mLoaderFragmentListener =
200            new ContactLoaderFragmentListener() {
201        @Override
202        public void onContactNotFound() {
203            finish();
204        }
205
206        @Override
207        public void onDetailsLoaded(final ContactLoader.Result result) {
208            if (result == null) {
209                return;
210            }
211            // Since {@link FragmentTransaction}s cannot be done in the onLoadFinished() of the
212            // {@link LoaderCallbacks}, then post this {@link Runnable} to the {@link Handler}
213            // on the main thread to execute later.
214            mHandler.post(new Runnable() {
215                @Override
216                public void run() {
217                    // If the activity is destroyed (or will be destroyed soon), don't update the UI
218                    if (isFinishing()) {
219                        return;
220                    }
221                    mContactData = result;
222                    mLookupUri = result.getLookupUri();
223                    invalidateOptionsMenu();
224                    setupTitle();
225                    mContactDetailLayoutController.setContactData(mContactData);
226                }
227            });
228        }
229
230        @Override
231        public void onEditRequested(Uri contactLookupUri) {
232            Intent intent = new Intent(Intent.ACTION_EDIT, contactLookupUri);
233            intent.putExtra(
234                    ContactEditorActivity.INTENT_KEY_FINISH_ACTIVITY_ON_SAVE_COMPLETED, true);
235            // Don't finish the detail activity after launching the editor because when the
236            // editor is done, we will still want to show the updated contact details using
237            // this activity.
238            startActivity(intent);
239        }
240
241        @Override
242        public void onDeleteRequested(Uri contactUri) {
243            ContactDeletionInteraction.start(ContactDetailActivity.this, contactUri, true);
244        }
245    };
246
247    /**
248     * Setup the activity title and subtitle with contact name and company.
249     */
250    private void setupTitle() {
251        CharSequence displayName = ContactDetailDisplayUtils.getDisplayName(this, mContactData);
252        String company =  ContactDetailDisplayUtils.getCompany(this, mContactData);
253
254        ActionBar actionBar = getActionBar();
255        actionBar.setTitle(displayName);
256        actionBar.setSubtitle(company);
257
258        if (!TextUtils.isEmpty(displayName) &&
259                AccessibilityManager.getInstance(this).isEnabled()) {
260            View decorView = getWindow().getDecorView();
261            decorView.setContentDescription(displayName);
262            decorView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
263        }
264    }
265
266    private final ContactDetailFragment.Listener mContactDetailFragmentListener =
267            new ContactDetailFragment.Listener() {
268        @Override
269        public void onItemClicked(Intent intent) {
270            if (intent == null) {
271                return;
272            }
273            try {
274                startActivity(intent);
275            } catch (ActivityNotFoundException e) {
276                Log.e(TAG, "No activity found for intent: " + intent);
277            }
278        }
279
280        @Override
281        public void onCreateRawContactRequested(
282                ArrayList<ContentValues> values, AccountWithDataSet account) {
283            Toast.makeText(ContactDetailActivity.this, R.string.toast_making_personal_copy,
284                    Toast.LENGTH_LONG).show();
285            Intent serviceIntent = ContactSaveService.createNewRawContactIntent(
286                    ContactDetailActivity.this, values, account,
287                    ContactDetailActivity.class, Intent.ACTION_VIEW);
288            startService(serviceIntent);
289
290        }
291    };
292
293    /**
294     * This interface should be implemented by {@link Fragment}s within this
295     * activity so that the activity can determine whether the currently
296     * displayed view is handling the key event or not.
297     */
298    public interface FragmentKeyListener {
299        /**
300         * Returns true if the key down event will be handled by the implementing class, or false
301         * otherwise.
302         */
303        public boolean handleKeyDown(int keyCode);
304    }
305
306    @Override
307    public boolean onOptionsItemSelected(MenuItem item) {
308
309        switch (item.getItemId()) {
310            case android.R.id.home:
311                if (mFinishActivityOnUpSelected) {
312                    finish();
313                    return true;
314                }
315                Intent intent = new Intent(this, PeopleActivity.class);
316                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
317                startActivity(intent);
318                finish();
319                return true;
320            default:
321                break;
322        }
323        return super.onOptionsItemSelected(item);
324    }
325}
326