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