2 * Copyright (C) 2007-2008 Esmertec AG.
3 * Copyright (C) 2007-2008 The Android Open Source Project
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 package com.android.im.app;
19 import com.android.im.IImConnection;
20 import com.android.im.R;
21 import com.android.im.plugin.BrandingResourceIDs;
22 import com.android.im.service.ImServiceConstants;
24 import android.app.Activity;
25 import android.content.ContentResolver;
26 import android.content.ContentUris;
27 import android.content.ContentValues;
28 import android.content.Intent;
29 import android.database.Cursor;
30 import android.net.Uri;
31 import android.os.Bundle;
32 import android.os.Message;
33 import android.os.RemoteException;
34 import android.provider.Im;
35 import android.util.Log;
36 import android.view.ContextMenu;
37 import android.view.KeyEvent;
38 import android.view.LayoutInflater;
39 import android.view.Menu;
40 import android.view.MenuInflater;
41 import android.view.MenuItem;
42 import android.view.View;
43 import android.view.Window;
44 import android.view.ContextMenu.ContextMenuInfo;
45 import android.widget.AdapterView;
46 import android.widget.AdapterView.AdapterContextMenuInfo;
47 import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
49 import java.util.Observable;
50 import java.util.Observer;
52 public class ContactListActivity extends Activity implements View.OnCreateContextMenuListener{
54 private static final int MENU_START_CONVERSATION = Menu.FIRST;
55 private static final int MENU_VIEW_PROFILE = Menu.FIRST + 1;
56 private static final int MENU_BLOCK_CONTACT = Menu.FIRST + 2;
57 private static final int MENU_DELETE_CONTACT = Menu.FIRST + 3;
58 private static final int MENU_END_CONVERSATION = Menu.FIRST + 4;
60 private static final String FILTER_STATE_KEY = "Filtering";
67 ContactListView mContactListView;
68 ContactListFilterView mFilterView;
69 SimpleAlertHandler mHandler;
71 ContextMenuHandler mContextMenuHandler;
75 Im.ProviderSettings.QueryMap mSettingMap;
79 protected void onCreate(Bundle icicle) {
80 super.onCreate(icicle);
82 getWindow().requestFeature(Window.FEATURE_LEFT_ICON);
84 LayoutInflater inflate = getLayoutInflater();
85 mContactListView = (ContactListView) inflate.inflate(
86 R.layout.contact_list_view, null);
88 setContentView(mContactListView);
90 Intent intent = getIntent();
91 mAccountId = intent.getLongExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID, -1);
92 if (mAccountId == -1) {
96 mApp = ImApp.getApplication(this);
98 ContentResolver cr = getContentResolver();
99 Cursor c = cr.query(ContentUris.withAppendedId(Im.Account.CONTENT_URI, mAccountId),
100 null, null, null, null);
105 if (!c.moveToFirst()) {
111 mProviderId = c.getLong(c.getColumnIndexOrThrow(Im.Account.PROVIDER));
112 mHandler = new MyHandler(this);
113 String username = c.getString(c.getColumnIndexOrThrow(Im.Account.USERNAME));
115 BrandingResources brandingRes = mApp.getBrandingResource(mProviderId);
116 setTitle(brandingRes.getString(
117 BrandingResourceIDs.STRING_BUDDY_LIST_TITLE, username));
118 getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON,
119 brandingRes.getDrawable(BrandingResourceIDs.DRAWABLE_LOGO));
121 mSettingMap = new Im.ProviderSettings.QueryMap(
122 getContentResolver(), mProviderId, true, null);
124 mApp.callWhenServiceConnected(mHandler, new Runnable(){
127 mApp.dismissNotifications(mProviderId);
128 mConn = mApp.getConnection(mProviderId);
130 Log.e(ImApp.LOG_TAG, "The connection has disappeared!");
131 clearConnectionStatus();
134 mContactListView.setConnection(mConn);
135 mContactListView.setHideOfflineContacts(
136 mSettingMap.getHideOfflineContacts());
142 mContextMenuHandler = new ContextMenuHandler();
143 mContactListView.getListView().setOnCreateContextMenuListener(this);
145 mSettingMap.addObserver(new Observer() {
146 public void update(Observable observed, Object updateData) {
148 mContactListView.setHideOfflineContacts(
149 mSettingMap.getHideOfflineContacts());
156 public boolean onCreateOptionsMenu(Menu menu) {
157 MenuInflater inflater = getMenuInflater();
158 inflater.inflate(R.menu.contact_list_menu, menu);
160 BrandingResources brandingRes = mApp.getBrandingResource(mProviderId);
161 menu.findItem(R.id.menu_invite_user).setTitle(
162 brandingRes.getString(BrandingResourceIDs.STRING_MENU_ADD_CONTACT));
168 public boolean onOptionsItemSelected(MenuItem item) {
169 switch (item.getItemId()) {
170 case R.id.menu_invite_user:
171 Intent i = new Intent(ContactListActivity.this, AddContactActivity.class);
172 i.putExtra(ImServiceConstants.EXTRA_INTENT_PROVIDER_ID, mProviderId);
173 i.putExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID, mAccountId);
174 i.putExtra(ImServiceConstants.EXTRA_INTENT_LIST_NAME,
175 mContactListView.getSelectedContactList());
179 case R.id.menu_blocked_contacts:
180 Uri.Builder builder = Im.BlockedList.CONTENT_URI.buildUpon();
181 ContentUris.appendId(builder, mProviderId);
182 ContentUris.appendId(builder, mAccountId);
183 startActivity(new Intent(Intent.ACTION_VIEW, builder.build()));
186 case R.id.menu_view_accounts:
187 Intent intent = new Intent(Intent.ACTION_VIEW);
188 intent.setType(Im.Provider.CONTENT_TYPE);
189 startActivity(intent);
193 case R.id.menu_settings:
194 intent = new Intent(this, SettingActivity.class);
195 intent.putExtra(ImServiceConstants.EXTRA_INTENT_PROVIDER_ID, mProviderId);
196 startActivity(intent);
199 case R.id.menu_sign_out:
204 } catch (RemoteException e) {
208 return super.onOptionsItemSelected(item);
212 protected void onSaveInstanceState(Bundle outState) {
213 super.onSaveInstanceState(outState);
214 outState.putBoolean(FILTER_STATE_KEY, mIsFiltering);
219 protected void onRestoreInstanceState(Bundle savedInstanceState) {
220 boolean isFiltering = savedInstanceState.getBoolean(FILTER_STATE_KEY);
224 super.onRestoreInstanceState(savedInstanceState);
228 public boolean dispatchKeyEvent(KeyEvent event) {
229 int keyCode = event.getKeyCode();
231 boolean handled = false;
233 handled = mFilterView.dispatchKeyEvent(event);
234 if (!handled && (KeyEvent.KEYCODE_BACK == keyCode)
235 && (KeyEvent.ACTION_DOWN == event.getAction())) {
236 showContactListView();
240 handled = mContactListView.dispatchKeyEvent(event);
241 if (!handled && isReadable(keyCode, event)
242 && (KeyEvent.ACTION_DOWN == event.getAction())) {
244 handled = mFilterView.dispatchKeyEvent(event);
249 handled = super.dispatchKeyEvent(event);
255 private static boolean isReadable(int keyCode, KeyEvent event) {
256 if (KeyEvent.isModifierKey(keyCode) || event.isSystem()) {
261 case KeyEvent.KEYCODE_DPAD_CENTER:
262 case KeyEvent.KEYCODE_DPAD_DOWN:
263 case KeyEvent.KEYCODE_DPAD_LEFT:
264 case KeyEvent.KEYCODE_DPAD_RIGHT:
265 case KeyEvent.KEYCODE_DPAD_UP:
266 case KeyEvent.KEYCODE_ENTER:
273 private void showFilterView() {
274 if (mFilterView == null ) {
275 mFilterView = (ContactListFilterView)getLayoutInflater().inflate(
276 R.layout.contact_list_filter_view, null);
277 mFilterView.getListView().setOnCreateContextMenuListener(this);
279 Uri uri = mSettingMap.getHideOfflineContacts() ? Im.Contacts.CONTENT_URI_ONLINE_CONTACTS_BY
280 : Im.Contacts.CONTENT_URI_CONTACTS_BY;
281 uri = ContentUris.withAppendedId(uri, mProviderId);
282 uri = ContentUris.withAppendedId(uri, mAccountId);
283 mFilterView.doFilter(uri, null);
285 setContentView(mFilterView);
286 mFilterView.requestFocus();
290 void showContactListView() {
292 setContentView(mContactListView);
293 mContactListView.requestFocus();
294 mContactListView.invalidate();
295 mIsFiltering = false;
300 protected void onPause() {
302 mApp.unregisterForConnEvents(mHandler);
306 protected void onResume() {
308 mApp.registerForConnEvents(mHandler);
312 protected void onDestroy() {
314 // set connection to null to unregister listeners.
315 mContactListView.setConnection(null);
316 if (mSettingMap != null) {
322 static void log(String msg) {
323 Log.d(ImApp.LOG_TAG, "<ContactListActivity> " +msg);
327 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
328 boolean chatSelected = false;
329 boolean contactSelected = false;
330 Cursor contactCursor;
332 AdapterView.AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
333 mContextMenuHandler.mPosition = info.position;
334 contactSelected = true;
335 contactCursor = mFilterView.getContactAtPosition(info.position);
337 ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuInfo;
338 mContextMenuHandler.mPosition = info.packedPosition;
339 contactSelected = mContactListView.isContactAtPosition(info.packedPosition);
340 chatSelected = mContactListView.isConversationAtPosition(info.packedPosition);
341 contactCursor = mContactListView.getContactAtPosition(info.packedPosition);
344 boolean allowBlock = true;
345 if (contactCursor != null) {
346 //XXX HACK: Yahoo! doesn't allow to block a friend. We can only block a temporary contact.
347 ProviderDef provider = mApp.getProvider(mProviderId);
348 if (Im.ProviderNames.YAHOO.equals(provider.mName)) {
349 int type = contactCursor.getInt(contactCursor.getColumnIndexOrThrow(Im.Contacts.TYPE));
350 allowBlock = (type == Im.Contacts.TYPE_TEMPORARY);
353 int nickNameIndex = contactCursor.getColumnIndexOrThrow(Im.Contacts.NICKNAME);
355 menu.setHeaderTitle(contactCursor.getString(nickNameIndex));
358 BrandingResources brandingRes = mApp.getBrandingResource(mProviderId);
359 String menu_end_conversation = brandingRes.getString(
360 BrandingResourceIDs.STRING_MENU_END_CHAT);
361 String menu_view_profile = brandingRes.getString(
362 BrandingResourceIDs.STRING_MENU_VIEW_PROFILE);
363 String menu_block_contact = brandingRes.getString(
364 BrandingResourceIDs.STRING_MENU_BLOCK_CONTACT);
365 String menu_start_conversation = brandingRes.getString(
366 BrandingResourceIDs.STRING_MENU_START_CHAT);
367 String menu_delete_contact = brandingRes.getString(
368 BrandingResourceIDs.STRING_MENU_DELETE_CONTACT);
371 menu.add(0, MENU_END_CONVERSATION, 0, menu_end_conversation)
372 .setIcon(com.android.internal.R.drawable.ic_menu_end_conversation)
373 .setOnMenuItemClickListener(mContextMenuHandler);
374 menu.add(0, MENU_VIEW_PROFILE, 0, menu_view_profile)
375 .setIcon(R.drawable.ic_menu_my_profile)
376 .setOnMenuItemClickListener(mContextMenuHandler);
378 menu.add(0, MENU_BLOCK_CONTACT, 0, menu_block_contact)
379 .setIcon(com.android.internal.R.drawable.ic_menu_block)
380 .setOnMenuItemClickListener(mContextMenuHandler);
382 } else if (contactSelected) {
383 menu.add(0, MENU_START_CONVERSATION, 0, menu_start_conversation)
384 .setIcon(com.android.internal.R.drawable.ic_menu_start_conversation)
385 .setOnMenuItemClickListener(mContextMenuHandler);
386 menu.add(0, MENU_VIEW_PROFILE, 0, menu_view_profile)
387 .setIcon(R.drawable.ic_menu_view_profile)
388 .setOnMenuItemClickListener(mContextMenuHandler);
390 menu.add(0, MENU_BLOCK_CONTACT, 0, menu_block_contact)
391 .setIcon(com.android.internal.R.drawable.ic_menu_block)
392 .setOnMenuItemClickListener(mContextMenuHandler);
394 menu.add(0, MENU_DELETE_CONTACT, 0, menu_delete_contact)
395 .setIcon(android.R.drawable.ic_menu_delete)
396 .setOnMenuItemClickListener(mContextMenuHandler);
400 void clearConnectionStatus() {
401 ContentResolver cr = getContentResolver();
402 ContentValues values = new ContentValues(3);
404 values.put(Im.AccountStatus.ACCOUNT, mAccountId);
405 values.put(Im.AccountStatus.PRESENCE_STATUS, Im.Presence.OFFLINE);
406 values.put(Im.AccountStatus.CONNECTION_STATUS, Im.ConnectionStatus.OFFLINE);
407 // insert on the "account_status" uri actually replaces the existing value
408 cr.insert(Im.AccountStatus.CONTENT_URI, values);
411 final class ContextMenuHandler implements MenuItem.OnMenuItemClickListener {
414 public boolean onMenuItemClick(MenuItem item) {
417 c = mFilterView.getContactAtPosition((int)mPosition);
419 c = mContactListView.getContactAtPosition(mPosition);
422 switch (item.getItemId()) {
423 case MENU_START_CONVERSATION:
424 mContactListView.startChat(c);
426 case MENU_VIEW_PROFILE:
427 mContactListView.viewContactPresence(c);
429 case MENU_BLOCK_CONTACT:
430 mContactListView.blockContact(c);
432 case MENU_DELETE_CONTACT:
433 mContactListView.removeContact(c);
435 case MENU_END_CONVERSATION:
436 mContactListView.endChat(c);
443 showContactListView();
449 final class MyHandler extends SimpleAlertHandler {
450 public MyHandler(Activity activity) {
455 public void handleMessage(Message msg) {
456 if (msg.what == ImApp.EVENT_CONNECTION_DISCONNECTED) {
457 if (Log.isLoggable(ImApp.LOG_TAG, Log.DEBUG)) {
458 log("Handle event connection disconnected.");
460 promptDisconnectedEvent(msg);
461 long providerId = ((long)msg.arg1 << 32) | msg.arg2;
462 if (providerId == mProviderId) {
463 if (Log.isLoggable(ImApp.LOG_TAG, Log.DEBUG)) {
464 log("Current connection disconnected, finish");
470 super.handleMessage(msg);