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