DialtactsActivity.java revision 9add0e5ef2a267acbe104e04f4cf6ab24cb8ba3e
1/*
2 * Copyright (C) 2008 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.R;
20import com.android.contacts.activities.ContactsFrontDoor;
21import com.android.contacts.activities.ContactBrowserActivity;
22import com.android.contacts.activities.DialpadActivity;
23import com.android.internal.telephony.ITelephony;
24
25import android.app.Activity;
26import android.app.TabActivity;
27import android.content.Intent;
28import android.content.SharedPreferences;
29import android.net.Uri;
30import android.os.Bundle;
31import android.os.RemoteException;
32import android.os.ServiceManager;
33import android.provider.CallLog.Calls;
34import android.provider.ContactsContract.Intents.UI;
35import android.util.Log;
36import android.view.Window;
37import android.widget.TabHost;
38
39/**
40 * The dialer activity that has one tab with the virtual 12key
41 * dialer, a tab with recent calls in it, a tab with the contacts and
42 * a tab with the favorite. This is the container and the tabs are
43 * embedded using intents.
44 * The dialer tab's title is 'phone', a more common name (see strings.xml).
45 */
46public class DialtactsActivity extends TabActivity implements TabHost.OnTabChangeListener {
47    private static final String TAG = "DialtactsActivity";
48
49    private static final int TAB_INDEX_DIALER = 0;
50    private static final int TAB_INDEX_CALL_LOG = 1;
51    private static final int TAB_INDEX_CONTACTS = 2;
52    private static final int TAB_INDEX_FAVORITES = 3;
53
54    static final String EXTRA_IGNORE_STATE = "ignore-state";
55
56    /** Name of the dialtacts shared preferences */
57    static final String PREFS_DIALTACTS = "dialtacts";
58    /** If true, when handling the contacts intent the favorites tab will be shown instead */
59    static final String PREF_FAVORITES_AS_CONTACTS = "favorites_as_contacts";
60    static final boolean PREF_FAVORITES_AS_CONTACTS_DEFAULT = false;
61
62    /** Last manually selected tab index */
63    private static final String PREF_LAST_MANUALLY_SELECTED_TAB = "last_manually_selected_tab";
64    private static final int PREF_LAST_MANUALLY_SELECTED_TAB_DEFAULT = TAB_INDEX_DIALER;
65
66    private TabHost mTabHost;
67    private String mFilterText;
68    private Uri mDialUri;
69
70    /**
71     * The index of the tab that has last been manually selected (the user clicked on a tab).
72     * This value does not keep track of programmatically set Tabs (e.g. Call Log after a Call)
73     */
74    private int mLastManuallySelectedTab;
75
76    @Override
77    protected void onCreate(Bundle icicle) {
78        super.onCreate(icicle);
79
80        final Intent intent = getIntent();
81        fixIntent(intent);
82
83        requestWindowFeature(Window.FEATURE_NO_TITLE);
84        setContentView(R.layout.dialtacts_activity);
85
86        mTabHost = getTabHost();
87        mTabHost.setOnTabChangedListener(this);
88
89        // Setup the tabs
90        setupDialerTab();
91        setupCallLogTab();
92        setupContactsTab();
93        setupFavoritesTab();
94        setupGroupsTab();
95
96        // Load the last manually loaded tab
97        final SharedPreferences prefs = getSharedPreferences(PREFS_DIALTACTS, MODE_PRIVATE);
98        mLastManuallySelectedTab = prefs.getInt(PREF_LAST_MANUALLY_SELECTED_TAB,
99                PREF_LAST_MANUALLY_SELECTED_TAB_DEFAULT);
100
101        setCurrentTab(intent);
102
103        if (UI.FILTER_CONTACTS_ACTION.equals(intent.getAction())
104                && icicle == null) {
105            setupFilterText(intent);
106        }
107    }
108
109    @Override
110    protected void onPause() {
111        super.onPause();
112
113        final int currentTabIndex = mTabHost.getCurrentTab();
114        final SharedPreferences.Editor editor =
115                getSharedPreferences(PREFS_DIALTACTS, MODE_PRIVATE).edit();
116        if (currentTabIndex == TAB_INDEX_CONTACTS || currentTabIndex == TAB_INDEX_FAVORITES) {
117            editor.putBoolean(PREF_FAVORITES_AS_CONTACTS, currentTabIndex == TAB_INDEX_FAVORITES);
118        }
119        editor.putInt(PREF_LAST_MANUALLY_SELECTED_TAB, mLastManuallySelectedTab);
120
121        editor.apply();
122    }
123
124    private void fixIntent(Intent intent) {
125        // This should be cleaned up: the call key used to send an Intent
126        // that just said to go to the recent calls list.  It now sends this
127        // abstract action, but this class hasn't been rewritten to deal with it.
128        if (Intent.ACTION_CALL_BUTTON.equals(intent.getAction())) {
129            intent.setDataAndType(Calls.CONTENT_URI, Calls.CONTENT_TYPE);
130            intent.putExtra("call_key", true);
131            setIntent(intent);
132        }
133    }
134
135    private void setupCallLogTab() {
136        // Force the class since overriding tab entries doesn't work
137        Intent intent = new Intent("com.android.phone.action.RECENT_CALLS");
138        intent.setClass(this, CallLogActivity.class);
139
140        mTabHost.addTab(mTabHost.newTabSpec("call_log")
141                .setIndicator(getString(R.string.recentCallsIconLabel),
142                        getResources().getDrawable(R.drawable.ic_tab_recent))
143                .setContent(intent));
144    }
145
146    private void setupDialerTab() {
147        Intent intent = new Intent("com.android.phone.action.TOUCH_DIALER");
148        intent.setClass(this, DialpadActivity.class);
149
150        mTabHost.addTab(mTabHost.newTabSpec("dialer")
151                .setIndicator(getString(R.string.dialerIconLabel),
152                        getResources().getDrawable(R.drawable.ic_tab_dialer))
153                .setContent(intent));
154    }
155
156    private void setupContactsTab() {
157        Intent intent = new Intent(UI.LIST_ALL_CONTACTS_ACTION);
158        intent.setClass(this, ContactBrowserActivity.class);
159
160        mTabHost.addTab(mTabHost.newTabSpec("contacts")
161                .setIndicator(getText(R.string.contactsIconLabel),
162                        getResources().getDrawable(R.drawable.ic_tab_contacts))
163                .setContent(intent));
164    }
165
166    private void setupFavoritesTab() {
167        Intent intent = new Intent(UI.LIST_STREQUENT_ACTION);
168        intent.setClass(this, ContactBrowserActivity.class);
169
170        mTabHost.addTab(mTabHost.newTabSpec("favorites")
171                .setIndicator(getString(R.string.contactsFavoritesLabel),
172                        getResources().getDrawable(R.drawable.ic_tab_starred))
173                .setContent(intent));
174    }
175
176    private void setupGroupsTab() {
177        // This is a temporary intent action until the refactoring for the phone/contacts
178        // split is complete.
179        Intent intent = new Intent("com.android.phone.action.GROUPS_LIST");
180                        intent.setClass(this, GroupBrowserActivity.class);
181
182        mTabHost.addTab(mTabHost.newTabSpec("groups")
183                .setIndicator(getString(R.string.contactsGroupsLabel),
184                        getResources().getDrawable(R.drawable.ic_menu_display_all_holo_light))
185                .setContent(intent));
186    }
187
188    /**
189     * Returns true if the intent is due to hitting the green send key while in a call.
190     *
191     * @param intent the intent that launched this activity
192     * @param recentCallsRequest true if the intent is requesting to view recent calls
193     * @return true if the intent is due to hitting the green send key while in a call
194     */
195    private boolean isSendKeyWhileInCall(final Intent intent, final boolean recentCallsRequest) {
196        // If there is a call in progress go to the call screen
197        if (recentCallsRequest) {
198            final boolean callKey = intent.getBooleanExtra("call_key", false);
199
200            try {
201                ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
202                if (callKey && phone != null && phone.showCallScreen()) {
203                    return true;
204                }
205            } catch (RemoteException e) {
206                Log.e(TAG, "Failed to handle send while in call", e);
207            }
208        }
209
210        return false;
211    }
212
213    /**
214     * Sets the current tab based on the intent's request type
215     *
216     * @param intent Intent that contains information about which tab should be selected
217     */
218    private void setCurrentTab(Intent intent) {
219        // If we got here by hitting send and we're in call forward along to the in-call activity
220        final boolean recentCallsRequest = Calls.CONTENT_TYPE.equals(intent.getType());
221        if (isSendKeyWhileInCall(intent, recentCallsRequest)) {
222            finish();
223            return;
224        }
225
226        // Dismiss menu provided by any children activities
227        Activity activity = getLocalActivityManager().
228                getActivity(mTabHost.getCurrentTabTag());
229        if (activity != null) {
230            activity.closeOptionsMenu();
231        }
232
233        // Tell the children activities that they should ignore any possible saved
234        // state and instead reload their state from the parent's intent
235        intent.putExtra(EXTRA_IGNORE_STATE, true);
236
237        // Remember the old manually selected tab index so that it can be restored if it is
238        // overwritten by one of the programmatic tab selections
239        final int savedTabIndex = mLastManuallySelectedTab;
240
241        // Choose the tab based on the inbound intent
242        if (intent.getBooleanExtra(ContactsFrontDoor.EXTRA_FRONT_DOOR, false)) {
243            // Launched through the contacts front door, set the proper contacts tab (sticky
244            // between favorites and contacts)
245            SharedPreferences prefs = getSharedPreferences(PREFS_DIALTACTS, MODE_PRIVATE);
246            boolean favoritesAsContacts = prefs.getBoolean(PREF_FAVORITES_AS_CONTACTS,
247                    PREF_FAVORITES_AS_CONTACTS_DEFAULT);
248            if (favoritesAsContacts) {
249                mTabHost.setCurrentTab(TAB_INDEX_FAVORITES);
250            } else {
251                mTabHost.setCurrentTab(TAB_INDEX_CONTACTS);
252            }
253        } else {
254            // Not launched through the front door, look at the component to determine the tab
255            String componentName = intent.getComponent().getClassName();
256            if (getClass().getName().equals(componentName)) {
257                if (recentCallsRequest) {
258                    mTabHost.setCurrentTab(TAB_INDEX_CALL_LOG);
259                } else {
260                    mTabHost.setCurrentTab(TAB_INDEX_DIALER);
261                }
262            } else {
263                mTabHost.setCurrentTab(mLastManuallySelectedTab);
264            }
265        }
266
267        // Restore to the previous manual selection
268        mLastManuallySelectedTab = savedTabIndex;
269
270        // Tell the children activities that they should honor their saved states
271        // instead of the state from the parent's intent
272        intent.putExtra(EXTRA_IGNORE_STATE, false);
273    }
274
275    @Override
276    public void onNewIntent(Intent newIntent) {
277        setIntent(newIntent);
278        fixIntent(newIntent);
279        setCurrentTab(newIntent);
280        final String action = newIntent.getAction();
281        if (UI.FILTER_CONTACTS_ACTION.equals(action)) {
282            setupFilterText(newIntent);
283        } else if (isDialIntent(newIntent)) {
284            setupDialUri(newIntent);
285        }
286    }
287
288    /** Returns true if the given intent contains a phone number to populate the dialer with */
289    private boolean isDialIntent(Intent intent) {
290        final String action = intent.getAction();
291        if (Intent.ACTION_DIAL.equals(action)) {
292            return true;
293        }
294        if (Intent.ACTION_VIEW.equals(action)) {
295            final Uri data = intent.getData();
296            if (data != null && "tel".equals(data.getScheme())) {
297                return true;
298            }
299        }
300        return false;
301    }
302
303    /**
304     * Retrieves the filter text stored in {@link #setupFilterText(Intent)}.
305     * This text originally came from a FILTER_CONTACTS_ACTION intent received
306     * by this activity. The stored text will then be cleared after after this
307     * method returns.
308     *
309     * @return The stored filter text
310     */
311    public String getAndClearFilterText() {
312        String filterText = mFilterText;
313        mFilterText = null;
314        return filterText;
315    }
316
317    /**
318     * Stores the filter text associated with a FILTER_CONTACTS_ACTION intent.
319     * This is so child activities can check if they are supposed to display a filter.
320     *
321     * @param intent The intent received in {@link #onNewIntent(Intent)}
322     */
323    private void setupFilterText(Intent intent) {
324        // If the intent was relaunched from history, don't apply the filter text.
325        if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
326            return;
327        }
328        String filter = intent.getStringExtra(UI.FILTER_TEXT_EXTRA_KEY);
329        if (filter != null && filter.length() > 0) {
330            mFilterText = filter;
331        }
332    }
333
334    /**
335     * Retrieves the uri stored in {@link #setupDialUri(Intent)}. This uri
336     * originally came from a dial intent received by this activity. The stored
337     * uri will then be cleared after after this method returns.
338     *
339     * @return The stored uri
340     */
341    public Uri getAndClearDialUri() {
342        Uri dialUri = mDialUri;
343        mDialUri = null;
344        return dialUri;
345    }
346
347    /**
348     * Stores the uri associated with a dial intent. This is so child activities can
349     * check if they are supposed to display new dial info.
350     *
351     * @param intent The intent received in {@link #onNewIntent(Intent)}
352     */
353    private void setupDialUri(Intent intent) {
354        // If the intent was relaunched from history, don't reapply the intent.
355        if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
356            return;
357        }
358        mDialUri = intent.getData();
359    }
360
361    @Override
362    public void onBackPressed() {
363        if (isTaskRoot()) {
364            // Instead of stopping, simply push this to the back of the stack.
365            // This is only done when running at the top of the stack;
366            // otherwise, we have been launched by someone else so need to
367            // allow the user to go back to the caller.
368            moveTaskToBack(false);
369        } else {
370            super.onBackPressed();
371        }
372    }
373
374    /** {@inheritDoc} */
375    public void onTabChanged(String tabId) {
376        // Because we're using Activities as our tab children, we trigger
377        // onWindowFocusChanged() to let them know when they're active.  This may
378        // seem to duplicate the purpose of onResume(), but it's needed because
379        // onResume() can't reliably check if a keyguard is active.
380        Activity activity = getLocalActivityManager().getActivity(tabId);
381        if (activity != null) {
382            activity.onWindowFocusChanged(true);
383        }
384
385        // Remember this tab index. This function is also called, if the tab is set automatically
386        // in which case the setter (setCurrentTab) has to set this to its old value afterwards
387        mLastManuallySelectedTab = mTabHost.getCurrentTab();
388    }
389}
390