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 private static final int TAB_INDEX_DIALER = 0; 48 private static final int TAB_INDEX_CALL_LOG = 1; 49 private static final int TAB_INDEX_CONTACTS = 2; 50 private static final int TAB_INDEX_FAVORITES = 3; 51 52 static final String EXTRA_IGNORE_STATE = "ignore-state"; 53 54 /** If true, when handling the contacts intent the favorites tab will be shown instead */ 55 private static final String PREF_FAVORITES_AS_CONTACTS = "favorites_as_contacts"; 56 private static final boolean PREF_FAVORITES_AS_CONTACTS_DEFAULT = false; 57 58 private TabHost mTabHost; 59 private String mFilterText; 60 private Uri mDialUri; 61 62 @Override 63 protected void onCreate(Bundle icicle) { 64 super.onCreate(icicle); 65 66 final Intent intent = getIntent(); 67 68 requestWindowFeature(Window.FEATURE_NO_TITLE); 69 setContentView(R.layout.dialer_activity); 70 71 mTabHost = getTabHost(); 72 mTabHost.setOnTabChangedListener(this); 73 74 // Setup the tabs 75 setupDialerTab(); 76 setupCallLogTab(); 77 setupContactsTab(); 78 setupFavoritesTab(); 79 80 setCurrentTab(intent); 81 82 if (intent.getAction().equals(UI.FILTER_CONTACTS_ACTION) 83 && icicle == null) { 84 setupFilterText(intent); 85 } 86 } 87 88 @Override 89 protected void onPause() { 90 super.onPause(); 91 92 final int currentTabIndex = mTabHost.getCurrentTab(); 93 final SharedPreferences.Editor editor = 94 getSharedPreferences(StickyTabs.PREFERENCES_NAME, MODE_PRIVATE).edit(); 95 if (currentTabIndex == TAB_INDEX_CONTACTS || currentTabIndex == TAB_INDEX_FAVORITES) { 96 editor.putBoolean(PREF_FAVORITES_AS_CONTACTS, currentTabIndex == TAB_INDEX_FAVORITES); 97 } 98 99 editor.apply(); 100 } 101 102 private void setupCallLogTab() { 103 // Force the class since overriding tab entries doesn't work 104 Intent intent = new Intent("com.android.phone.action.RECENT_CALLS"); 105 intent.setClass(this, RecentCallsListActivity.class); 106 StickyTabs.setTab(intent, TAB_INDEX_CALL_LOG); 107 108 mTabHost.addTab(mTabHost.newTabSpec("call_log") 109 .setIndicator(getString(R.string.recentCallsIconLabel), 110 getResources().getDrawable(R.drawable.ic_tab_recent)) 111 .setContent(intent)); 112 } 113 114 private void setupDialerTab() { 115 Intent intent = new Intent("com.android.phone.action.TOUCH_DIALER"); 116 intent.setClass(this, TwelveKeyDialer.class); 117 StickyTabs.setTab(intent, TAB_INDEX_DIALER); 118 119 mTabHost.addTab(mTabHost.newTabSpec("dialer") 120 .setIndicator(getString(R.string.dialerIconLabel), 121 getResources().getDrawable(R.drawable.ic_tab_dialer)) 122 .setContent(intent)); 123 } 124 125 private void setupContactsTab() { 126 Intent intent = new Intent(UI.LIST_DEFAULT); 127 intent.setClass(this, ContactsListActivity.class); 128 StickyTabs.setTab(intent, TAB_INDEX_CONTACTS); 129 130 mTabHost.addTab(mTabHost.newTabSpec("contacts") 131 .setIndicator(getText(R.string.contactsIconLabel), 132 getResources().getDrawable(R.drawable.ic_tab_contacts)) 133 .setContent(intent)); 134 } 135 136 private void setupFavoritesTab() { 137 Intent intent = new Intent(UI.LIST_STREQUENT_ACTION); 138 intent.setClass(this, ContactsListActivity.class); 139 StickyTabs.setTab(intent, TAB_INDEX_FAVORITES); 140 141 mTabHost.addTab(mTabHost.newTabSpec("favorites") 142 .setIndicator(getString(R.string.contactsFavoritesLabel), 143 getResources().getDrawable(R.drawable.ic_tab_starred)) 144 .setContent(intent)); 145 } 146 147 /** 148 * Sets the current tab based on the intent's request type 149 * 150 * @param intent Intent that contains information about which tab should be selected 151 */ 152 private void setCurrentTab(Intent intent) { 153 // Dismiss menu provided by any children activities 154 Activity activity = getLocalActivityManager(). 155 getActivity(mTabHost.getCurrentTabTag()); 156 if (activity != null) { 157 activity.closeOptionsMenu(); 158 } 159 160 // Tell the children activities that they should ignore any possible saved 161 // state and instead reload their state from the parent's intent 162 intent.putExtra(EXTRA_IGNORE_STATE, true); 163 164 // Choose the tab based on the inbound intent 165 String componentName = intent.getComponent().getClassName(); 166 if (getClass().getName().equals(componentName)) { 167 if (phoneIsInUse()) { 168 // We are in a call, show the dialer tab (which allows going back to the call) 169 mTabHost.setCurrentTab(TAB_INDEX_DIALER); 170 } else if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) { 171 // launched from history (long-press home) --> nothing to change 172 } else if (isDialIntent(intent)) { 173 // The dialer was explicitly requested 174 mTabHost.setCurrentTab(TAB_INDEX_DIALER); 175 } else if (Calls.CONTENT_TYPE.equals(intent.getType())) { 176 // After a call, show the call log 177 mTabHost.setCurrentTab(TAB_INDEX_CALL_LOG); 178 } else { 179 // Load the last tab used to make a phone call. default to the dialer in 180 // first launch 181 mTabHost.setCurrentTab(StickyTabs.loadTab(this, TAB_INDEX_DIALER)); 182 } 183 } else if (FAVORITES_ENTRY_COMPONENT.equals(componentName)) { 184 mTabHost.setCurrentTab(TAB_INDEX_FAVORITES); 185 } else { 186 // Launched as "Contacts" --> Go either to favorites or contacts, whichever is more 187 // recent 188 final SharedPreferences prefs = getSharedPreferences(StickyTabs.PREFERENCES_NAME, 189 MODE_PRIVATE); 190 final boolean favoritesAsContacts = prefs.getBoolean(PREF_FAVORITES_AS_CONTACTS, 191 PREF_FAVORITES_AS_CONTACTS_DEFAULT); 192 if (favoritesAsContacts) { 193 mTabHost.setCurrentTab(TAB_INDEX_FAVORITES); 194 } else { 195 mTabHost.setCurrentTab(TAB_INDEX_CONTACTS); 196 } 197 } 198 199 // Tell the children activities that they should honor their saved states 200 // instead of the state from the parent's intent 201 intent.putExtra(EXTRA_IGNORE_STATE, false); 202 } 203 204 @Override 205 public void onNewIntent(Intent newIntent) { 206 setIntent(newIntent); 207 setCurrentTab(newIntent); 208 final String action = newIntent.getAction(); 209 if (action.equals(UI.FILTER_CONTACTS_ACTION)) { 210 setupFilterText(newIntent); 211 } else if (isDialIntent(newIntent)) { 212 setupDialUri(newIntent); 213 } 214 } 215 216 /** Returns true if the given intent contains a phone number to populate the dialer with */ 217 private boolean isDialIntent(Intent intent) { 218 final String action = intent.getAction(); 219 if (Intent.ACTION_DIAL.equals(action)) { 220 return true; 221 } 222 if (Intent.ACTION_VIEW.equals(action)) { 223 final Uri data = intent.getData(); 224 if (data != null && "tel".equals(data.getScheme())) { 225 return true; 226 } 227 } 228 return false; 229 } 230 231 /** 232 * Retrieves the filter text stored in {@link #setupFilterText(Intent)}. 233 * This text originally came from a FILTER_CONTACTS_ACTION intent received 234 * by this activity. The stored text will then be cleared after after this 235 * method returns. 236 * 237 * @return The stored filter text 238 */ 239 public String getAndClearFilterText() { 240 String filterText = mFilterText; 241 mFilterText = null; 242 return filterText; 243 } 244 245 /** 246 * Stores the filter text associated with a FILTER_CONTACTS_ACTION intent. 247 * This is so child activities can check if they are supposed to display a filter. 248 * 249 * @param intent The intent received in {@link #onNewIntent(Intent)} 250 */ 251 private void setupFilterText(Intent intent) { 252 // If the intent was relaunched from history, don't apply the filter text. 253 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) { 254 return; 255 } 256 String filter = intent.getStringExtra(UI.FILTER_TEXT_EXTRA_KEY); 257 if (filter != null && filter.length() > 0) { 258 mFilterText = filter; 259 } 260 } 261 262 /** 263 * Retrieves the uri stored in {@link #setupDialUri(Intent)}. This uri 264 * originally came from a dial intent received by this activity. The stored 265 * uri will then be cleared after after this method returns. 266 * 267 * @return The stored uri 268 */ 269 public Uri getAndClearDialUri() { 270 Uri dialUri = mDialUri; 271 mDialUri = null; 272 return dialUri; 273 } 274 275 /** 276 * Stores the uri associated with a dial intent. This is so child activities can 277 * check if they are supposed to display new dial info. 278 * 279 * @param intent The intent received in {@link #onNewIntent(Intent)} 280 */ 281 private void setupDialUri(Intent intent) { 282 // If the intent was relaunched from history, don't reapply the intent. 283 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) { 284 return; 285 } 286 mDialUri = intent.getData(); 287 } 288 289 @Override 290 public void onBackPressed() { 291 if (isTaskRoot()) { 292 // Instead of stopping, simply push this to the back of the stack. 293 // This is only done when running at the top of the stack; 294 // otherwise, we have been launched by someone else so need to 295 // allow the user to go back to the caller. 296 moveTaskToBack(false); 297 } else { 298 super.onBackPressed(); 299 } 300 } 301 302 /** {@inheritDoc} */ 303 @Override 304 public void onTabChanged(String tabId) { 305 // Because we're using Activities as our tab children, we trigger 306 // onWindowFocusChanged() to let them know when they're active. This may 307 // seem to duplicate the purpose of onResume(), but it's needed because 308 // onResume() can't reliably check if a keyguard is active. 309 Activity activity = getLocalActivityManager().getActivity(tabId); 310 if (activity != null) { 311 activity.onWindowFocusChanged(true); 312 } 313 } 314 315 /** 316 * @return true if the phone is "in use", meaning that at least one line 317 * is active (ie. off hook or ringing or dialing). 318 */ 319 private boolean phoneIsInUse() { 320 boolean phoneInUse = false; 321 try { 322 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone")); 323 if (phone != null) phoneInUse = !phone.isIdle(); 324 } catch (RemoteException e) { 325 Log.w(TAG, "phone.isIdle() failed", e); 326 } 327 return phoneInUse; 328 } 329 } 330