1/*
2 * Copyright (C) 2007-2008 Esmertec AG.
3 * Copyright (C) 2007-2008 The Android Open Source Project
4 *
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
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
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.
16 */
17
18package com.android.im.app;
19
20import java.util.ArrayList;
21import java.util.HashMap;
22import java.util.Iterator;
23import java.util.List;
24import java.util.Map;
25
26import android.app.Activity;
27import android.app.Application;
28import android.content.ComponentName;
29import android.content.ContentResolver;
30import android.content.ContentUris;
31import android.content.ContentValues;
32import android.content.Context;
33import android.content.Intent;
34import android.content.ServiceConnection;
35import android.content.pm.PackageManager;
36import android.content.pm.PackageManager.NameNotFoundException;
37import android.content.res.Resources;
38import android.database.Cursor;
39import android.net.ConnectivityManager;
40import android.net.Uri;
41import android.os.Broadcaster;
42import android.os.Handler;
43import android.os.IBinder;
44import android.os.Message;
45import android.os.RemoteException;
46import android.util.Log;
47
48import com.android.im.IConnectionCreationListener;
49import com.android.im.IImConnection;
50import com.android.im.IRemoteImService;
51import com.android.im.R;
52import com.android.im.app.adapter.ConnectionListenerAdapter;
53import com.android.im.engine.ImConnection;
54import com.android.im.engine.ImErrorInfo;
55import com.android.im.plugin.BrandingResourceIDs;
56import com.android.im.plugin.ImPlugin;
57import com.android.im.plugin.ImPluginInfo;
58import com.android.im.provider.Imps;
59import com.android.im.service.ImServiceConstants;
60
61public class ImApp extends Application {
62    public static final String LOG_TAG = "ImApp";
63
64    public static final String EXTRA_INTENT_SEND_TO_USER = "Send2_U";
65    public static final String EXTRA_INTENT_PASSWORD = "password";
66
67    public static final String IMPS_CATEGORY = "com.android.im.IMPS_CATEGORY";
68
69    private static ImApp sImApp;
70
71    IRemoteImService mImService;
72
73    HashMap<Long, IImConnection> mConnections;
74    MyConnListener mConnectionListener;
75    HashMap<Long, ProviderDef> mProviders;
76
77    Broadcaster mBroadcaster;
78
79    /** A queue of messages that are waiting to be sent when service is connected.*/
80    ArrayList<Message> mQueue = new ArrayList<Message>();
81
82    /** A flag indicates that we have called to start the service.*/
83    private boolean mServiceStarted;
84    private Context mApplicationContext;
85    private Resources mPrivateResources;
86
87    private HashMap<String, BrandingResources> mBrandingResources;
88    private BrandingResources mDefaultBrandingResources;
89
90    public static final int EVENT_SERVICE_CONNECTED = 100;
91    public static final int EVENT_CONNECTION_CREATED = 150;
92    public static final int EVENT_CONNECTION_LOGGING_IN = 200;
93    public static final int EVENT_CONNECTION_LOGGED_IN = 201;
94    public static final int EVENT_CONNECTION_LOGGING_OUT = 202;
95    public static final int EVENT_CONNECTION_DISCONNECTED = 203;
96    public static final int EVENT_CONNECTION_SUSPENDED = 204;
97    public static final int EVENT_USER_PRESENCE_UPDATED = 300;
98    public static final int EVENT_UPDATE_USER_PRESENCE_ERROR = 301;
99
100    private static final String[] PROVIDER_PROJECTION = {
101        Imps.Provider._ID,
102        Imps.Provider.NAME,
103        Imps.Provider.FULLNAME,
104        Imps.Provider.SIGNUP_URL,
105    };
106
107    private static final String[] ACCOUNT_PROJECTION = {
108        Imps.Account._ID,
109        Imps.Account.PROVIDER,
110        Imps.Account.NAME,
111        Imps.Account.USERNAME,
112        Imps.Account.PASSWORD,
113    };
114
115    static final void log(String log) {
116        Log.d(LOG_TAG, log);
117    }
118
119    public static ImApp getApplication(Activity activity) {
120        // TODO should this be synchronized?
121        if (sImApp == null) {
122            initialize(activity);
123        }
124
125        return sImApp;
126    }
127
128    /**
129     * Initialize performs the manual ImApp instantiation and initialization. When the
130     * ImApp is started first in the process, the ImApp public constructor should be called,
131     * and sImApp initialized. So calling initialize() later should have no effect. However,
132     * if another application runs in the same process and is started first, the ImApp
133     * application object won't be instantiated, and we need to call initialize() manually to
134     * instantiate and initialize it.
135     */
136    private static void initialize(Activity activity) {
137        // construct the TalkApp manually and call onCreate().
138        sImApp = new ImApp();
139        sImApp.mApplicationContext = activity.getApplication();
140        sImApp.mPrivateResources = activity.getResources();
141        sImApp.onCreate();
142    }
143
144    @Override
145    public Resources getResources() {
146        if (mApplicationContext == this) {
147            return super.getResources();
148        }
149
150        return mPrivateResources;
151    }
152
153    @Override
154    public ContentResolver getContentResolver() {
155        if (mApplicationContext == this) {
156            return super.getContentResolver();
157        }
158
159        return mApplicationContext.getContentResolver();
160    }
161
162    public ImApp() {
163        super();
164        mConnections = new HashMap<Long, IImConnection>();
165        mApplicationContext = this;
166        sImApp = this;
167    }
168
169    @Override
170    public void onCreate() {
171        super.onCreate();
172        mBroadcaster = new Broadcaster();
173        loadDefaultBrandingRes();
174    }
175
176    @Override
177    public void onTerminate() {
178        stopImServiceIfInactive();
179        if (mImService != null) {
180            try {
181                mImService.removeConnectionCreatedListener(mConnCreationListener);
182            } catch (RemoteException e) {
183                Log.w(LOG_TAG, "failed to remove ConnectionCreatedListener");
184            }
185        }
186
187        super.onTerminate();
188    }
189
190    public synchronized void startImServiceIfNeed() {
191        if(!mServiceStarted) {
192            if(Log.isLoggable(LOG_TAG, Log.DEBUG)) log("start ImService");
193
194            Intent serviceIntent = new Intent();
195            serviceIntent.setComponent(ImServiceConstants.IM_SERVICE_COMPONENT);
196            mApplicationContext.startService(serviceIntent);
197            mApplicationContext.bindService(serviceIntent, mImServiceConn, Context.BIND_AUTO_CREATE);
198            mServiceStarted = true;
199
200            mConnectionListener = new MyConnListener(new Handler());
201        }
202    }
203
204    public synchronized void stopImServiceIfInactive() {
205        boolean hasActiveConnection = true;
206        synchronized (mConnections) {
207            hasActiveConnection = !mConnections.isEmpty();
208        }
209
210        if (!hasActiveConnection && mServiceStarted) {
211            if (Log.isLoggable(LOG_TAG, Log.DEBUG))
212                log("stop ImService because there's no active connections");
213
214            if(mImService != null) {
215                mApplicationContext.unbindService(mImServiceConn);
216                mImService = null;
217            }
218            Intent intent = new Intent();
219            intent.setComponent(ImServiceConstants.IM_SERVICE_COMPONENT);
220            mApplicationContext.stopService(intent);
221            mServiceStarted = false;
222        }
223    }
224
225    private ServiceConnection mImServiceConn = new ServiceConnection() {
226        public void onServiceConnected(ComponentName className, IBinder service) {
227            if(Log.isLoggable(LOG_TAG, Log.DEBUG))
228                log("service connected");
229
230            mImService = IRemoteImService.Stub.asInterface(service);
231            fetchActiveConnections();
232
233            synchronized (mQueue) {
234                for (Message msg : mQueue) {
235                    msg.sendToTarget();
236                }
237                mQueue.clear();
238            }
239            Message msg = Message.obtain(null, EVENT_SERVICE_CONNECTED);
240            mBroadcaster.broadcast(msg);
241        }
242
243        public void onServiceDisconnected(ComponentName className) {
244            if(Log.isLoggable(LOG_TAG, Log.DEBUG))
245                log("service disconnected");
246
247            mConnections.clear();
248            mImService = null;
249        }
250    };
251
252    public boolean serviceConnected() {
253        return mImService != null;
254    }
255
256    public boolean isBackgroundDataEnabled() {
257        ConnectivityManager manager =
258                (ConnectivityManager) mApplicationContext.getSystemService(CONNECTIVITY_SERVICE);
259        return manager.getBackgroundDataSetting();
260    }
261
262    public static long insertOrUpdateAccount(ContentResolver cr,
263            long providerId, String userName, String pw) {
264        String selection = Imps.Account.PROVIDER + "=? AND " + Imps.Account.USERNAME + "=?";
265        String[] selectionArgs = {Long.toString(providerId), userName };
266
267        Cursor c = cr.query(Imps.Account.CONTENT_URI, ACCOUNT_PROJECTION,
268                selection, selectionArgs, null);
269        if (c != null && c.moveToFirst()) {
270            // Update the password
271            c.updateString(c.getColumnIndexOrThrow(Imps.Account.PASSWORD), pw);
272            c.commitUpdates();
273
274            long id = c.getLong(c.getColumnIndexOrThrow(Imps.Account._ID));
275            c.close();
276            return id;
277        } else {
278            ContentValues values = new ContentValues(4);
279            values.put(Imps.Account.PROVIDER, providerId);
280            values.put(Imps.Account.NAME, userName);
281            values.put(Imps.Account.USERNAME, userName);
282            values.put(Imps.Account.PASSWORD, pw);
283
284            Uri result = cr.insert(Imps.Account.CONTENT_URI, values);
285            return ContentUris.parseId(result);
286        }
287    }
288
289    private void loadImProviderSettings() {
290        if (mProviders != null) {
291            return;
292        }
293
294        mProviders = new HashMap<Long, ProviderDef>();
295        ContentResolver cr = getContentResolver();
296
297        String selectionArgs[] = new String[1];
298        selectionArgs[0] = ImApp.IMPS_CATEGORY;
299
300        Cursor c = cr.query(Imps.Provider.CONTENT_URI, PROVIDER_PROJECTION,
301                Imps.Provider.CATEGORY+"=?", selectionArgs, null);
302        if (c == null) {
303            return;
304        }
305
306        try {
307            while (c.moveToNext()) {
308                long id = c.getLong(0);
309                String providerName = c.getString(1);
310                String fullName = c.getString(2);
311                String signUpUrl = c.getString(3);
312
313                mProviders.put(id, new ProviderDef(id, providerName, fullName, signUpUrl));
314            }
315        } finally {
316            c.close();
317        }
318    }
319
320    private void loadDefaultBrandingRes() {
321        HashMap<Integer, Integer> resMapping = new HashMap<Integer, Integer>();
322
323        resMapping.put(BrandingResourceIDs.DRAWABLE_LOGO, R.drawable.imlogo_s);
324        resMapping.put(BrandingResourceIDs.DRAWABLE_PRESENCE_ONLINE,
325                android.R.drawable.presence_online);
326        resMapping.put(BrandingResourceIDs.DRAWABLE_PRESENCE_AWAY,
327                android.R.drawable.presence_away);
328        resMapping.put(BrandingResourceIDs.DRAWABLE_PRESENCE_BUSY,
329                android.R.drawable.presence_busy);
330        resMapping.put(BrandingResourceIDs.DRAWABLE_PRESENCE_INVISIBLE,
331                android.R.drawable.presence_invisible);
332        resMapping.put(BrandingResourceIDs.DRAWABLE_PRESENCE_OFFLINE,
333                android.R.drawable.presence_offline);
334        resMapping.put(BrandingResourceIDs.DRAWABLE_READ_CHAT,
335                R.drawable.status_chat);
336        resMapping.put(BrandingResourceIDs.DRAWABLE_UNREAD_CHAT,
337                R.drawable.status_chat_new);
338        resMapping.put(BrandingResourceIDs.DRAWABLE_BLOCK,
339                R.drawable.ic_im_block);
340
341        resMapping.put(BrandingResourceIDs.STRING_ARRAY_SMILEY_NAMES,
342                R.array.default_smiley_names);
343        resMapping.put(BrandingResourceIDs.STRING_ARRAY_SMILEY_TEXTS,
344                R.array.default_smiley_texts);
345
346        resMapping.put(BrandingResourceIDs.STRING_PRESENCE_AVAILABLE,
347                R.string.presence_available);
348        resMapping.put(BrandingResourceIDs.STRING_PRESENCE_BUSY,
349                R.string.presence_busy);
350        resMapping.put(BrandingResourceIDs.STRING_PRESENCE_AWAY,
351                R.string.presence_away);
352        resMapping.put(BrandingResourceIDs.STRING_PRESENCE_IDLE,
353                R.string.presence_idle);
354        resMapping.put(BrandingResourceIDs.STRING_PRESENCE_OFFLINE,
355                R.string.presence_offline);
356        resMapping.put(BrandingResourceIDs.STRING_PRESENCE_INVISIBLE,
357                R.string.presence_invisible);
358        resMapping.put(BrandingResourceIDs.STRING_LABEL_USERNAME,
359                R.string.label_username);
360        resMapping.put(BrandingResourceIDs.STRING_ONGOING_CONVERSATION,
361                R.string.ongoing_conversation);
362        resMapping.put(BrandingResourceIDs.STRING_ADD_CONTACT_TITLE,
363                R.string.add_contact_title);
364        resMapping.put(BrandingResourceIDs.STRING_LABEL_INPUT_CONTACT,
365                R.string.input_contact_label);
366        resMapping.put(BrandingResourceIDs.STRING_BUTTON_ADD_CONTACT,
367                R.string.invite_label);
368        resMapping.put(BrandingResourceIDs.STRING_CONTACT_INFO_TITLE,
369                R.string.contact_profile_title);
370
371        resMapping.put(BrandingResourceIDs.STRING_MENU_ADD_CONTACT,
372                R.string.menu_add_contact);
373        resMapping.put(BrandingResourceIDs.STRING_MENU_BLOCK_CONTACT,
374                R.string.menu_block_contact);
375        resMapping.put(BrandingResourceIDs.STRING_MENU_CONTACT_LIST,
376                R.string.menu_view_contact_list);
377        resMapping.put(BrandingResourceIDs.STRING_MENU_DELETE_CONTACT,
378                R.string.menu_remove_contact);
379        resMapping.put(BrandingResourceIDs.STRING_MENU_END_CHAT,
380                R.string.menu_end_conversation);
381        resMapping.put(BrandingResourceIDs.STRING_MENU_INSERT_SMILEY,
382                R.string.menu_insert_smiley);
383        resMapping.put(BrandingResourceIDs.STRING_MENU_START_CHAT,
384                R.string.menu_start_chat);
385        resMapping.put(BrandingResourceIDs.STRING_MENU_VIEW_PROFILE,
386                R.string.menu_view_profile);
387        resMapping.put(BrandingResourceIDs.STRING_MENU_SWITCH_CHATS,
388                R.string.menu_switch_chats);
389
390        resMapping.put(BrandingResourceIDs.STRING_TOAST_CHECK_AUTO_SIGN_IN,
391                R.string.check_auto_sign_in);
392        resMapping.put(BrandingResourceIDs.STRING_LABEL_SIGN_UP,
393                R.string.sign_up);
394
395        mDefaultBrandingResources = new BrandingResources(this, resMapping,
396                null /* default res */);
397    }
398
399    private void loadThirdPartyResources() {
400        ImPluginHelper helper = ImPluginHelper.getInstance(this);
401        helper.loadAvaiablePlugins();
402        ArrayList<ImPlugin> pluginList = helper.getPluginObjects();
403        ArrayList<ImPluginInfo> infoList = helper.getPluginsInfo();
404        int N = pluginList.size();
405        PackageManager pm = getPackageManager();
406        for (int i = 0; i < N; i++) {
407            ImPlugin plugin = pluginList.get(i);
408            ImPluginInfo pluginInfo = infoList.get(i);
409
410            try {
411                Resources packageRes = pm.getResourcesForApplication(pluginInfo.mPackageName);
412
413                Map<Integer, Integer> resMap = plugin.getResourceMap();
414                int[] smileyIcons = plugin.getSmileyIconIds();
415
416                BrandingResources res = new BrandingResources(packageRes, resMap,
417                        smileyIcons, mDefaultBrandingResources);
418                mBrandingResources.put(pluginInfo.mProviderName, res);
419            } catch (NameNotFoundException e) {
420                Log.e(LOG_TAG, "Failed to load third party resources.", e);
421            }
422        }
423    }
424
425    public long getProviderId(String name) {
426        loadImProviderSettings();
427        for (ProviderDef provider: mProviders.values()) {
428            if(provider.mName.equals(name)) {
429                return provider.mId;
430            }
431        }
432        return -1;
433    }
434
435    public ProviderDef getProvider(long id) {
436        loadImProviderSettings();
437        return mProviders.get(id);
438    }
439
440    public List<ProviderDef> getProviders() {
441        loadImProviderSettings();
442        ArrayList<ProviderDef> result = new ArrayList<ProviderDef>();
443        result.addAll(mProviders.values());
444        return result;
445    }
446
447    public BrandingResources getBrandingResource(long providerId) {
448        ProviderDef provider = getProvider(providerId);
449        if (provider == null) {
450            return mDefaultBrandingResources;
451        }
452        if (mBrandingResources == null) {
453            mBrandingResources = new HashMap<String, BrandingResources>();
454            loadThirdPartyResources();
455        }
456        BrandingResources res = mBrandingResources.get(provider.mName);
457        return res == null ? mDefaultBrandingResources : res;
458    }
459
460    public IImConnection createConnection(long providerId) throws RemoteException {
461        if (mImService == null) {
462            // Service hasn't been connected or has died.
463            return null;
464        }
465        IImConnection conn = getConnection(providerId);
466        if (conn == null) {
467            conn = mImService.createConnection(providerId);
468        }
469        return conn;
470    }
471
472    IImConnection getConnection(long providerId) {
473        synchronized (mConnections) {
474            return mConnections.get(providerId);
475        }
476    }
477
478    public IImConnection getConnectionByAccount(long accountId) {
479        synchronized (mConnections) {
480            for (IImConnection conn : mConnections.values()) {
481                try {
482                    if (conn.getAccountId() == accountId) {
483                        return conn;
484                    }
485                } catch (RemoteException e) {
486                    // No server!
487                }
488            }
489            return null;
490        }
491    }
492
493    public List<IImConnection> getActiveConnections() {
494        synchronized (mConnections) {
495            ArrayList<IImConnection> result = new ArrayList<IImConnection>();
496            result.addAll(mConnections.values());
497            return result;
498        }
499    }
500
501    public void callWhenServiceConnected(Handler target, Runnable callback) {
502        Message msg = Message.obtain(target, callback);
503        if (serviceConnected()) {
504            msg.sendToTarget();
505        } else {
506            startImServiceIfNeed();
507            synchronized (mQueue) {
508                mQueue.add(msg);
509            }
510        }
511    }
512
513    public void removePendingCall(Handler target) {
514        synchronized (mQueue) {
515           Iterator<Message> iter = mQueue.iterator();
516           while (iter.hasNext()) {
517               Message msg = iter.next();
518               if (msg.getTarget() == target) {
519                   iter.remove();
520               }
521           }
522       }
523   }
524
525    public void registerForBroadcastEvent(int what, Handler target) {
526        mBroadcaster.request(what, target, what);
527    }
528
529    public void unregisterForBroadcastEvent(int what, Handler target) {
530        mBroadcaster.cancelRequest(what, target, what);
531    }
532
533    public void registerForConnEvents(Handler handler) {
534        mBroadcaster.request(EVENT_CONNECTION_CREATED, handler,
535                EVENT_CONNECTION_CREATED);
536        mBroadcaster.request(EVENT_CONNECTION_LOGGING_IN, handler,
537                EVENT_CONNECTION_LOGGING_IN);
538        mBroadcaster.request(EVENT_CONNECTION_LOGGED_IN, handler,
539                EVENT_CONNECTION_LOGGED_IN);
540        mBroadcaster.request(EVENT_CONNECTION_LOGGING_OUT, handler,
541                EVENT_CONNECTION_LOGGING_OUT);
542        mBroadcaster.request(EVENT_CONNECTION_SUSPENDED, handler,
543                EVENT_CONNECTION_SUSPENDED);
544        mBroadcaster.request(EVENT_CONNECTION_DISCONNECTED, handler,
545                EVENT_CONNECTION_DISCONNECTED);
546        mBroadcaster.request(EVENT_USER_PRESENCE_UPDATED, handler,
547                EVENT_USER_PRESENCE_UPDATED);
548        mBroadcaster.request(EVENT_UPDATE_USER_PRESENCE_ERROR, handler,
549                EVENT_UPDATE_USER_PRESENCE_ERROR);
550    }
551
552    public void unregisterForConnEvents(Handler handler) {
553        mBroadcaster.cancelRequest(EVENT_CONNECTION_CREATED, handler,
554                EVENT_CONNECTION_CREATED);
555        mBroadcaster.cancelRequest(EVENT_CONNECTION_LOGGING_IN, handler,
556                EVENT_CONNECTION_LOGGING_IN);
557        mBroadcaster.cancelRequest(EVENT_CONNECTION_LOGGED_IN, handler,
558                EVENT_CONNECTION_LOGGED_IN);
559        mBroadcaster.cancelRequest(EVENT_CONNECTION_LOGGING_OUT, handler,
560                EVENT_CONNECTION_LOGGING_OUT);
561        mBroadcaster.cancelRequest(EVENT_CONNECTION_SUSPENDED, handler,
562                EVENT_CONNECTION_SUSPENDED);
563        mBroadcaster.cancelRequest(EVENT_CONNECTION_DISCONNECTED, handler,
564                EVENT_CONNECTION_DISCONNECTED);
565        mBroadcaster.cancelRequest(EVENT_USER_PRESENCE_UPDATED, handler,
566                EVENT_USER_PRESENCE_UPDATED);
567        mBroadcaster.cancelRequest(EVENT_UPDATE_USER_PRESENCE_ERROR, handler,
568                EVENT_UPDATE_USER_PRESENCE_ERROR);
569    }
570
571    void broadcastConnEvent(int what, long providerId, ImErrorInfo error) {
572        if(Log.isLoggable(LOG_TAG, Log.DEBUG)){
573            log("broadcasting connection event " + what + ", provider id " + providerId);
574        }
575        android.os.Message msg = android.os.Message.obtain(
576                null,
577                what,
578                (int)(providerId >> 32), (int)providerId,
579                error);
580        mBroadcaster.broadcast(msg);
581    }
582
583    public void dismissNotifications(long providerId) {
584        if (mImService != null) {
585            try {
586                mImService.dismissNotifications(providerId);
587            } catch (RemoteException e) {
588            }
589        }
590    }
591
592    public void dismissChatNotification(long providerId, String username) {
593        if (mImService != null) {
594            try {
595                mImService.dismissChatNotification(providerId, username);
596            } catch (RemoteException e) {
597            }
598        }
599    }
600
601    private void fetchActiveConnections() {
602          try {
603            // register the listener before fetch so that we won't miss any connection.
604            mImService.addConnectionCreatedListener(mConnCreationListener);
605            synchronized (mConnections) {
606                for(IBinder binder: (List<IBinder>) mImService.getActiveConnections()) {
607                    IImConnection conn = IImConnection.Stub.asInterface(binder);
608                    long providerId = conn.getProviderId();
609                    if (!mConnections.containsKey(providerId)) {
610                        mConnections.put(providerId, conn);
611                        conn.registerConnectionListener(mConnectionListener);
612                    }
613                }
614            }
615        } catch (RemoteException e) {
616            Log.e(LOG_TAG, "fetching active connections", e);
617        }
618    }
619
620    private final IConnectionCreationListener mConnCreationListener
621            = new IConnectionCreationListener.Stub() {
622        public void onConnectionCreated(IImConnection conn)
623                throws RemoteException {
624            long providerId = conn.getProviderId();
625            synchronized (mConnections) {
626                if (!mConnections.containsKey(providerId)) {
627                    mConnections.put(providerId, conn);
628                    conn.registerConnectionListener(mConnectionListener);
629                }
630            }
631            broadcastConnEvent(EVENT_CONNECTION_CREATED, providerId, null);
632        }
633      };
634
635    private final class MyConnListener extends ConnectionListenerAdapter {
636        public MyConnListener(Handler handler) {
637            super(handler);
638        }
639
640        @Override
641        public void onConnectionStateChange(IImConnection conn, int state,
642                ImErrorInfo error) {
643            if(Log.isLoggable(LOG_TAG, Log.DEBUG)){
644                log("onConnectionStateChange(" + state + ", " + error + ")");
645            }
646
647            try {
648                int what = -1;
649                long providerId = conn.getProviderId();
650                switch (state) {
651                case ImConnection.LOGGED_IN:
652                    what = EVENT_CONNECTION_LOGGED_IN;
653                    break;
654
655                case ImConnection.LOGGING_IN:
656                    what = EVENT_CONNECTION_LOGGING_IN;
657                    break;
658
659                case ImConnection.LOGGING_OUT:
660                    what = EVENT_CONNECTION_LOGGING_OUT;
661                    synchronized (mConnections) {
662                        mConnections.remove(providerId);
663                    }
664                    break;
665
666                case ImConnection.DISCONNECTED:
667                    what = EVENT_CONNECTION_DISCONNECTED;
668                    synchronized (mConnections) {
669                        mConnections.remove(providerId);
670                    }
671                    // stop the service if there isn't an active connection anymore.
672                    stopImServiceIfInactive();
673                    break;
674
675                case ImConnection.SUSPENDED:
676                    what = EVENT_CONNECTION_SUSPENDED;
677                    break;
678                }
679                if (what != -1) {
680                    broadcastConnEvent(what, providerId, error);
681                }
682            } catch (RemoteException e) {
683                Log.e(LOG_TAG, "onConnectionStateChange", e);
684            }
685        }
686
687        @Override
688        public void onUpdateSelfPresenceError(IImConnection connection,
689                ImErrorInfo error) {
690            if(Log.isLoggable(LOG_TAG, Log.DEBUG)){
691                log("onUpdateUserPresenceError(" + error + ")");
692            }
693            try {
694                long providerId = connection.getProviderId();
695                broadcastConnEvent(EVENT_UPDATE_USER_PRESENCE_ERROR, providerId,
696                        error);
697            } catch (RemoteException e) {
698                Log.e(LOG_TAG, "onUpdateUserPresenceError", e);
699            }
700        }
701
702        @Override
703        public void onSelfPresenceUpdated(IImConnection connection) {
704            if(Log.isLoggable(LOG_TAG, Log.DEBUG)) log("onUserPresenceUpdated");
705
706            try {
707                long providerId = connection.getProviderId();
708                broadcastConnEvent(EVENT_USER_PRESENCE_UPDATED, providerId,
709                        null);
710            } catch (RemoteException e) {
711                Log.e(LOG_TAG, "onUserPresenceUpdated", e);
712            }
713        }
714    }
715}
716