ContactDetailActivity.java revision 942cec2d8a248b1a22e95f4d573d235e281ee91a
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.ContactsSearchManager;
23import com.android.contacts.R;
24import com.android.contacts.detail.ContactDetailAboutFragment;
25import com.android.contacts.detail.ContactDetailDisplayUtils;
26import com.android.contacts.detail.ContactDetailFragment;
27import com.android.contacts.detail.ContactDetailFragmentCarousel;
28import com.android.contacts.detail.ContactDetailTabCarousel;
29import com.android.contacts.detail.ContactDetailUpdatesFragment;
30import com.android.contacts.interactions.ContactDeletionInteraction;
31import com.android.contacts.util.PhoneCapabilityTester;
32
33import android.accounts.Account;
34import android.app.Fragment;
35import android.app.FragmentManager;
36import android.content.ActivityNotFoundException;
37import android.content.ContentValues;
38import android.content.Intent;
39import android.net.Uri;
40import android.os.Bundle;
41import android.support.v13.app.FragmentPagerAdapter;
42import android.support.v4.view.ViewPager;
43import android.support.v4.view.ViewPager.OnPageChangeListener;
44import android.util.Log;
45import android.view.KeyEvent;
46import android.view.Menu;
47import android.view.MenuInflater;
48import android.view.MenuItem;
49import android.view.View;
50import android.view.View.OnClickListener;
51import android.view.ViewGroup;
52import android.widget.AbsListView;
53import android.widget.AbsListView.OnScrollListener;
54import android.widget.CheckBox;
55import android.widget.Toast;
56
57import java.util.ArrayList;
58
59public class ContactDetailActivity extends ContactsActivity {
60    private static final String TAG = "ContactDetailActivity";
61
62    public static final int FRAGMENT_COUNT = 2;
63
64    private ContactLoader.Result mContactData;
65    private Uri mLookupUri;
66
67    private ContactDetailAboutFragment mAboutFragment;
68    private ContactDetailUpdatesFragment mUpdatesFragment;
69
70    private ContactDetailTabCarousel mTabCarousel;
71    private ViewPager mViewPager;
72
73    private ContactDetailFragmentCarousel mFragmentCarousel;
74
75    @Override
76    public void onCreate(Bundle savedState) {
77        super.onCreate(savedState);
78        if (PhoneCapabilityTester.isUsingTwoPanes(this)) {
79            // This activity must not be shown. We have to select the contact in the
80            // PeopleActivity instead ==> Create a forward intent and finish
81            final Intent originalIntent = getIntent();
82            Intent intent = new Intent();
83            intent.setAction(originalIntent.getAction());
84            intent.setDataAndType(originalIntent.getData(), originalIntent.getType());
85            intent.setFlags(
86                    Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | Intent.FLAG_ACTIVITY_FORWARD_RESULT
87                            | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
88
89            intent.setClass(this, PeopleActivity.class);
90            startActivity(intent);
91            finish();
92            return;
93        }
94
95        setContentView(R.layout.contact_detail_activity);
96
97        // Narrow width screens have a {@link ViewPager} and {@link ContactDetailTabCarousel}
98        mViewPager = (ViewPager) findViewById(R.id.pager);
99        if (mViewPager != null) {
100            mViewPager.setAdapter(new ViewPagerAdapter(getFragmentManager()));
101            mViewPager.setOnPageChangeListener(mOnPageChangeListener);
102        }
103
104        mTabCarousel = (ContactDetailTabCarousel) findViewById(R.id.tab_carousel);
105        if (mTabCarousel != null) {
106            mTabCarousel.setListener(mTabCarouselListener);
107        }
108
109        // Otherwise, wide width screens have a {@link ContactDetailFragmentCarousel}
110        mFragmentCarousel = (ContactDetailFragmentCarousel) findViewById(R.id.fragment_carousel);
111        if (mFragmentCarousel != null) {
112            if (mAboutFragment != null) mFragmentCarousel.setAboutFragment(mAboutFragment);
113            if (mUpdatesFragment != null) mFragmentCarousel.setUpdatesFragment(mUpdatesFragment);
114        }
115
116        Log.i(TAG, getIntent().getData().toString());
117    }
118
119    @Override
120    public void onAttachFragment(Fragment fragment) {
121        if (fragment instanceof ContactDetailAboutFragment) {
122            mAboutFragment = (ContactDetailAboutFragment) fragment;
123            mAboutFragment.setListener(mFragmentListener);
124            mAboutFragment.setVerticalScrollListener(mVerticalScrollListener);
125            mAboutFragment.loadUri(getIntent().getData());
126        } else if (fragment instanceof ContactDetailUpdatesFragment) {
127            mUpdatesFragment = (ContactDetailUpdatesFragment) fragment;
128        }
129    }
130
131    @Override
132    public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
133            boolean globalSearch) {
134        if (globalSearch) {
135            super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
136        } else {
137            ContactsSearchManager.startSearch(this, initialQuery);
138        }
139    }
140
141    @Override
142    public boolean onCreateOptionsMenu(Menu menu) {
143        super.onCreateOptionsMenu(menu);
144        MenuInflater inflater = getMenuInflater();
145        inflater.inflate(R.menu.star, menu);
146        return true;
147    }
148
149    @Override
150    public boolean onPrepareOptionsMenu(Menu menu) {
151        MenuItem starredMenuItem = menu.findItem(R.id.menu_star);
152        ViewGroup starredContainer = (ViewGroup) getLayoutInflater().inflate(
153                R.layout.favorites_star, null, false);
154        final CheckBox starredView = (CheckBox) starredContainer.findViewById(R.id.star);
155        starredView.setOnClickListener(new OnClickListener() {
156            @Override
157            public void onClick(View v) {
158                // Toggle "starred" state
159                // Make sure there is a contact
160                if (mLookupUri != null) {
161                    Intent intent = ContactSaveService.createSetStarredIntent(
162                            ContactDetailActivity.this, mLookupUri, starredView.isChecked());
163                    ContactDetailActivity.this.startService(intent);
164                }
165            }
166        });
167        // If there is contact data, update the starred state
168        if (mContactData != null) {
169            ContactDetailDisplayUtils.setStarred(mContactData, starredView);
170        }
171        starredMenuItem.setActionView(starredContainer);
172        return true;
173    }
174
175    @Override
176    public boolean onKeyDown(int keyCode, KeyEvent event) {
177        FragmentKeyListener mCurrentFragment;
178        switch (getCurrentPage()) {
179            case 0:
180                mCurrentFragment = (FragmentKeyListener) mAboutFragment;
181                break;
182            case 1:
183                mCurrentFragment = (FragmentKeyListener) mUpdatesFragment;
184                break;
185            default:
186                throw new IllegalStateException("Invalid current item for ViewPager");
187        }
188        if (mCurrentFragment.handleKeyDown(keyCode)) return true;
189
190        return super.onKeyDown(keyCode, event);
191    }
192
193    private int getCurrentPage() {
194        if (mViewPager != null) {
195            return mViewPager.getCurrentItem();
196        } else if (mFragmentCarousel != null) {
197            return mFragmentCarousel.getCurrentPage();
198        }
199        throw new IllegalStateException("Can't figure out the currently selected page. The activity"
200                + "must either have the ViewPager or fragment carousel");
201    }
202
203    private final ContactDetailFragment.Listener mFragmentListener =
204            new ContactDetailFragment.Listener() {
205        @Override
206        public void onContactNotFound() {
207            finish();
208        }
209
210        @Override
211        public void onDetailsLoaded(ContactLoader.Result result) {
212            if (result == null) {
213                return;
214            }
215            mContactData = result;
216            mLookupUri = result.getLookupUri();
217            invalidateOptionsMenu();
218            if (mTabCarousel != null) {
219                mTabCarousel.loadData(result);
220            }
221        }
222
223        @Override
224        public void onEditRequested(Uri contactLookupUri) {
225            startActivity(new Intent(Intent.ACTION_EDIT, contactLookupUri));
226        }
227
228        @Override
229        public void onItemClicked(Intent intent) {
230            try {
231                startActivity(intent);
232            } catch (ActivityNotFoundException e) {
233                Log.e(TAG, "No activity found for intent: " + intent);
234            }
235        }
236
237        @Override
238        public void onDeleteRequested(Uri contactUri) {
239            ContactDeletionInteraction.start(ContactDetailActivity.this, contactUri, true);
240        }
241
242        @Override
243        public void onCreateRawContactRequested(
244                ArrayList<ContentValues> values, Account account) {
245            Toast.makeText(ContactDetailActivity.this, R.string.toast_making_personal_copy,
246                    Toast.LENGTH_LONG).show();
247            Intent serviceIntent = ContactSaveService.createNewRawContactIntent(
248                    ContactDetailActivity.this, values, account,
249                    ContactDetailActivity.class, Intent.ACTION_VIEW);
250            startService(serviceIntent);
251
252        }
253    };
254
255    public class ViewPagerAdapter extends FragmentPagerAdapter{
256
257        public ViewPagerAdapter(FragmentManager fm) {
258            super(fm);
259        }
260
261        @Override
262        public Fragment getItem(int position) {
263            switch (position) {
264                case 0:
265                    return new ContactDetailAboutFragment();
266                case 1:
267                    return new ContactDetailUpdatesFragment();
268            }
269            throw new IllegalStateException("No fragment at position " + position);
270        }
271
272        @Override
273        public int getCount() {
274            return FRAGMENT_COUNT;
275        }
276    }
277
278    private OnPageChangeListener mOnPageChangeListener = new OnPageChangeListener() {
279
280        @Override
281        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
282            // The user is horizontally dragging the {@link ViewPager}, so send
283            // these scroll changes to the tab carousel. Ignore these events though if the carousel
284            // is actually controlling the {@link ViewPager} scrolls because it will already be
285            // in the correct position.
286            if (mViewPager.isFakeDragging()) {
287                return;
288            }
289            int x = (int) ((position + positionOffset) *
290                    mTabCarousel.getAllowedHorizontalScrollLength());
291            mTabCarousel.scrollTo(x, 0);
292        }
293
294        @Override
295        public void onPageSelected(int position) {
296            // Since a new page has been selected by the {@link ViewPager},
297            // update the tab selection in the carousel.
298            mTabCarousel.setCurrentTab(position);
299        }
300
301        @Override
302        public void onPageScrollStateChanged(int state) {}
303
304    };
305
306    private ContactDetailTabCarousel.Listener mTabCarouselListener =
307            new ContactDetailTabCarousel.Listener() {
308
309        @Override
310        public void onTouchDown() {
311            // The user just started scrolling the carousel, so begin "fake dragging" the
312            // {@link ViewPager} if it's not already doing so.
313            if (mViewPager.isFakeDragging()) {
314                return;
315            }
316            mViewPager.beginFakeDrag();
317        }
318
319        @Override
320        public void onTouchUp() {
321            // The user just stopped scrolling the carousel, so stop "fake dragging" the
322            // {@link ViewPager} if was doing so before.
323            if (mViewPager.isFakeDragging()) {
324                mViewPager.endFakeDrag();
325            }
326        }
327
328        @Override
329        public void onScrollChanged(int l, int t, int oldl, int oldt) {
330            // The user is scrolling the carousel, so send the scroll deltas to the
331            // {@link ViewPager} so it can move in sync.
332            if (mViewPager.isFakeDragging()) {
333                mViewPager.fakeDragBy(oldl-l);
334            }
335        }
336
337        @Override
338        public void onTabSelected(int position) {
339            // The user selected a tab, so update the {@link ViewPager}
340            mViewPager.setCurrentItem(position);
341        }
342    };
343
344    private OnScrollListener mVerticalScrollListener = new OnScrollListener() {
345
346        @Override
347        public void onScroll(
348                AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
349            if (mTabCarousel == null) {
350                return;
351            }
352            // Only re-position the tab carousel vertically if the FIRST item is still visible on
353            // the screen, otherwise the carousel should be in the correct place (pinned at the
354            // top).
355            if (firstVisibleItem != 0) {
356                return;
357            }
358            View topView = view.getChildAt(firstVisibleItem);
359            if (topView == null) {
360                return;
361            }
362            int amtToScroll = Math.max((int) view.getChildAt(firstVisibleItem).getY(),
363                    -mTabCarousel.getAllowedVerticalScrollLength());
364            mTabCarousel.setY(amtToScroll);
365        }
366
367        @Override
368        public void onScrollStateChanged(AbsListView view, int scrollState) {}
369
370    };
371
372    /**
373     * This interface should be implemented by {@link Fragment}s within this
374     * activity so that the activity can determine whether the currently
375     * displayed view is handling the key event or not.
376     */
377    public interface FragmentKeyListener {
378        /**
379         * Returns true if the key down event will be handled by the implementing class, or false
380         * otherwise.
381         */
382        public boolean handleKeyDown(int keyCode);
383    }
384}
385