/** * galaxy inc. * meetup client for android */ package com.galaxy.meetup.client.android.realtimechat; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Handler; import android.os.IBinder; import android.os.PowerManager; import android.os.SystemClock; import android.util.Log; import com.galaxy.meetup.client.android.c2dm.C2DMReceiver; import com.galaxy.meetup.client.android.content.AudienceData; import com.galaxy.meetup.client.android.content.EsAccount; import com.galaxy.meetup.client.android.service.ServiceThread; import com.galaxy.meetup.client.util.EsLog; /** * * @author sihai * */ public class RealTimeChatService extends Service { private static boolean sConversationsLoaded = false; private static Long sCurrentConversationRowId = null; private static Integer sLastRequestId = Integer.valueOf(0); private static final List sListeners = new ArrayList(); private static final PendingRequestList sPendingRequests = new PendingRequestList(); private static final Map sResults = new ResultsLinkedHashMap(); private static android.os.PowerManager.WakeLock sWakeLock; private BunchClient mBunchClient; private final ConnectRunnable mConnectRunnable = new ConnectRunnable(); private int mConnectionRequestCount; private Handler mHandler; private long mLastConnectAttemptTime; private long mLastConnectRequestTimestamp; private long mLastMessageTime; private long mLastResponseTime; private PendingIntent mLongTermConnect; private boolean mNeedsSync; private final Runnable mPingRunnable = new Runnable() { public final void run() { Intent intent = new Intent(RealTimeChatService.this, RealTimeChatService.class); intent.putExtra("op", 221); startService(intent); } }; private int mReconnectCount; private long mReconnectDelay; private ServiceThread mServiceThread; private final Runnable mStopRunnable = new Runnable() { public final void run() { RealTimeChatService.initWakeLock(RealTimeChatService.this); if(RealTimeChatService.sWakeLock.isHeld()) { if(EsLog.isLoggable("RealTimeChatService", 3)) Log.d("RealTimeChatService", "release wake lock"); RealTimeChatService.sWakeLock.release(); } if(RealTimeChatService.sPendingRequests.isEmpty()) { if(EsLog.isLoggable("RealTimeChatService", 3)) Log.d("RealTimeChatService", "Stop runnable: Stopping service"); stopSelf(); } else { if(EsLog.isLoggable("RealTimeChatService", 3)) Log.d("RealTimeChatService", "Stop runnable: Not stopping, things to do"); RealTimeChatService.sPendingRequests.dump(); } } }; private final Runnable mTimeoutRunnable = new Runnable() { public final void run() { long l = SystemClock.elapsedRealtime() - 15000L; Integer integer; RealTimeChatServiceResult realtimechatserviceresult; for(Iterator iterator = RealTimeChatService.sPendingRequests.getOutdatedRequestIds(l).iterator(); iterator.hasNext(); RealTimeChatService.sResults.put(integer, realtimechatserviceresult)) { integer = (Integer)iterator.next(); if(EsLog.isLoggable("RealTimeChatService", 3)) Log.d("RealTimeChatService", (new StringBuilder(" request ")).append(integer).append(" timed out").toString()); for(Iterator iterator1 = RealTimeChatService.sListeners.iterator(); iterator1.hasNext(); ((RealTimeChatServiceListener)iterator1.next()).onResponseTimeout(integer.intValue())); realtimechatserviceresult = new RealTimeChatServiceResult(integer.intValue(), 3, null); } RealTimeChatService.sPendingRequests.trim(l); RealTimeChatService.sPendingRequests.dump(); } }; private static void initWakeLock(Context context) { if(sWakeLock == null) sWakeLock = ((PowerManager)context.getSystemService("power")).newWakeLock(1, "realtimechat"); } public static void initiateConnection(Context context, EsAccount esaccount) { if(EsLog.isLoggable("RealTimeChatService", 4)) Log.i("RealTimeChatService", "initiateConnection"); initWakeLock(context); if(!sWakeLock.isHeld()) { if(EsLog.isLoggable("RealTimeChatService", 3)) Log.d("RealTimeChatService", "acquiring wake lock"); sWakeLock.acquire(); } Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 111); intent.putExtra("account", esaccount); context.startService(intent); } public static void logout(Context context, EsAccount esaccount) { Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 116); intent.putExtra("account", esaccount); context.startService(intent); } public static boolean getConversationsLoaded() { return sConversationsLoaded; } static String getOrRequestC2dmId(Context context) { SharedPreferences sharedpreferences = context.getSharedPreferences("realtimechat", 0); String s = sharedpreferences.getString("c2dm_registration_id", null); long l = sharedpreferences.getLong("c2dm_registration_time", 0L); String s1 = sharedpreferences.getString("c2dm_registration_build_version", null); String s2; android.content.SharedPreferences.Editor editor; try { s2 = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName; } catch(android.content.pm.PackageManager.NameNotFoundException namenotfoundexception) { if(EsLog.isLoggable("RealTimeChatService", 6)) Log.e("RealTimeChatService", "Can't find package information for current package, continuing anyway", namenotfoundexception); s2 = s1; } if(s1 != null && s1.equals(s2)) { if(System.currentTimeMillis() - l > 0x2932e00L) { if(EsLog.isLoggable("RealTimeChatService", 3)) Log.d("RealTimeChatService", "refreshing registration for expiration"); android.content.SharedPreferences.Editor editor1 = sharedpreferences.edit(); if(s != null) { Log.d("RealTimeChatService", "saving registration"); editor1.putString("sticky_c2dm_registration_id", s); } editor1.commit(); s = null; } } else { if(EsLog.isLoggable("RealTimeChatService", 3)) Log.d("RealTimeChatService", "refreshing registration for update"); editor = sharedpreferences.edit(); if(s != null) { Log.d("RealTimeChatService", "saving registration"); editor.putString("sticky_c2dm_registration_id", s); } editor.commit(); s = null; } if(s == null) C2DMReceiver.requestC2DMRegistrationId(context); return s; } public static String getStickyC2dmId(Context context) { SharedPreferences sharedpreferences = context.getSharedPreferences("realtimechat", 0); String s = sharedpreferences.getString("c2dm_registration_id", null); if(s != null) { android.content.SharedPreferences.Editor editor = sharedpreferences.edit(); editor.putString("sticky_c2dm_registration_id", s); editor.commit(); } else { s = sharedpreferences.getString("sticky_c2dm_registration_id", null); } return s; } public static void registerListener(RealTimeChatServiceListener realtimechatservicelistener) { sListeners.add(realtimechatservicelistener); } public static void unregisterListener(RealTimeChatServiceListener realtimechatservicelistener) { sListeners.remove(realtimechatservicelistener); } public static int checkMessageSent(Context context, EsAccount esaccount, long l, int i) { Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 226); intent.putExtra("account", esaccount); intent.putExtra("message_row_id", l); intent.putExtra("flags", i); return startCommand(context, intent); } public static int inviteParticipants(Context context, EsAccount esaccount, long l, AudienceData audiencedata) { Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 332); intent.putExtra("account", esaccount); intent.putExtra("conversation_row_id", l); intent.putExtra("audience", audiencedata); return startCommand(context, intent); } public static void resetNotifications(Context context, EsAccount esaccount) { Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 340); intent.putExtra("account", esaccount); context.startService(intent); } public static int removeMessage(Context context, EsAccount esaccount, long l) { Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 336); intent.putExtra("account", esaccount); intent.putExtra("message_row_id", l); return startCommand(context, intent); } public static int requestMoreEvents(Context context, EsAccount esaccount, long l) { Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 342); intent.putExtra("account", esaccount); intent.putExtra("conversation_row_id", l); return startCommand(context, intent); } public static int leaveConversation(Context context, EsAccount esaccount, long l) { Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 333); intent.putExtra("account", esaccount); intent.putExtra("conversation_row_id", l); return startCommand(context, intent); } public static int sendTileEvent(Context context, EsAccount esaccount, String s, String s1, int i, String s2, HashMap hashmap) { Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 351); intent.putExtra("account", esaccount); intent.putExtra("conversation_id", s); intent.putExtra("tile_type", s1); intent.putExtra("tile_event_version", 0); intent.putExtra("tile_event_type", s2); intent.putExtra("tile_event_data", hashmap); return startCommand(context, intent); } public static int sendMessage(Context context, EsAccount esaccount, long l, String s, String s1) { Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 331); intent.putExtra("account", esaccount); intent.putExtra("conversation_row_id", l); intent.putExtra("message_text", s); intent.putExtra("uri", s1); return startCommand(context, intent); } public static int sendTypingRequest(Context context, EsAccount esaccount, long l, Client.Typing.Type type) { Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 349); intent.putExtra("account", esaccount); intent.putExtra("conversation_row_id", l); intent.putExtra("typing_status", type.getNumber()); return startCommand(context, intent); } public static int sendLocalPhoto(Context context, EsAccount esaccount, long l, String s) { Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 345); intent.putExtra("account", esaccount); intent.putExtra("conversation_row_id", l); intent.putExtra("uri", s); return startCommand(context, intent); } public static int createConversation(Context context, EsAccount esaccount, AudienceData audiencedata, String s) { Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 330); intent.putExtra("account", esaccount); intent.putExtra("audience", audiencedata); intent.putExtra("message_text", s); return startCommand(context, intent); } public static int setConversationMuted(Context context, EsAccount esaccount, long l, boolean flag) { Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 338); intent.putExtra("account", esaccount); intent.putExtra("conversation_row_id", l); intent.putExtra("conversation_muted", flag); return startCommand(context, intent); } public static int setConversationName(Context context, EsAccount esaccount, long l, String s) { Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 337); intent.putExtra("account", esaccount); intent.putExtra("conversation_row_id", l); intent.putExtra("conversation_name", s); return startCommand(context, intent); } public static void allowDisconnect(Context context, EsAccount esaccount) { if(EsLog.isLoggable("RealTimeChatService", 4)) Log.i("RealTimeChatService", "allowDisconnect"); Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 112); intent.putExtra("account", esaccount); context.startService(intent); } public static void connectAndStayConnected(Context context, EsAccount esaccount) { if(EsLog.isLoggable("RealTimeChatService", 4)) Log.i("RealTimeChatService", "connectAndStayConnected"); initWakeLock(context); if(!sWakeLock.isHeld()) { if(EsLog.isLoggable("RealTimeChatService", 3)) Log.d("RealTimeChatService", "acquiring wake lock"); sWakeLock.acquire(); } Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 110); intent.putExtra("account", esaccount); context.startService(intent); } public static synchronized void setCurrentConversationRowId(Long long1) { if(EsLog.isLoggable("RealTimeChatService", 3)) Log.d("RealTimeChatService", (new StringBuilder("setCurrentConversationRowId ")).append(long1).toString()); sCurrentConversationRowId = long1; } public static int markConversationRead(Context context, EsAccount esaccount, long l) { Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 335); intent.putExtra("account", esaccount); intent.putExtra("conversation_row_id", l); return startCommand(context, intent); } public static int markConversationNotificationsSeen(Context context, EsAccount esaccount, long l) { Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 350); intent.putExtra("account", esaccount); intent.putExtra("conversation_row_id", l); return startCommand(context, intent); } public static boolean isRequestPending(int i) { return sPendingRequests.requestPending(Integer.valueOf(i)); } public static int retrySendMessage(Context context, EsAccount esaccount, long l) { Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 344); intent.putExtra("account", esaccount); intent.putExtra("message_row_id", l); return startCommand(context, intent); } public static RealTimeChatServiceResult removeResult(int i) { return (RealTimeChatServiceResult)sResults.remove(Integer.valueOf(i)); } public static int sendPresenceRequest(Context context, EsAccount esaccount, long l, boolean flag, boolean flag1) { Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 348); intent.putExtra("account", esaccount); intent.putExtra("conversation_row_id", l); intent.putExtra("is_present", flag); intent.putExtra("reciprocate", flag1); return startCommand(context, intent); } public static int requestSuggestedParticipants(Context context, EsAccount esaccount, AudienceData audiencedata, Client.SuggestionsRequest.SuggestionsType suggestionstype) { Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 352); intent.putExtra("account", esaccount); intent.putExtra("audience", audiencedata); intent.putExtra("type", suggestionstype.getNumber()); return startCommand(context, intent); } @Override public IBinder onBind(Intent arg0) { return null; } private static int startCommand(Context context, Intent intent) { Integer integer = sLastRequestId; sLastRequestId = Integer.valueOf(1 + sLastRequestId.intValue()); int i = integer.intValue(); intent.putExtra("rid", i); context.startService(intent); if(EsLog.isLoggable("RealTimeChatService", 3)) Log.d("RealTimeChatService", (new StringBuilder("start command request ")).append(i).append(" opCode ").append(intent.getIntExtra("op", 0)).toString()); sPendingRequests.addRequest(Integer.valueOf(i)); return i; } public static void connectIfLoggedIn(Context context, String s, String s1, String s2) { if(EsLog.isLoggable("RealTimeChatService", 4)) Log.i("RealTimeChatService", "connectIfLoggedIn"); initWakeLock(context); if(!sWakeLock.isHeld()) { if(EsLog.isLoggable("RealTimeChatService", 3)) Log.d("RealTimeChatService", "acquiring wake lock"); sWakeLock.acquire(); } Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 115); intent.putExtra("account_id", s); intent.putExtra("conversation_id", s1); intent.putExtra("message_timestamp", s2); context.startService(intent); } public static void handleC2DMRegistrationError(Context context, String s) { Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 117); intent.putExtra("error", s); context.startService(intent); } public static void handleC2DMRegistration(Context context, String s) { Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 113); intent.putExtra("registration", s); context.startService(intent); } public static void handleC2DMUnregistration(Context context) { Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 114); context.startService(intent); } public static boolean debuggable() { return EsLog.ENABLE_DOGFOOD_FEATURES; } public static int setAcl(Context context, EsAccount esaccount, int i) { Intent intent = new Intent(context, RealTimeChatService.class); intent.putExtra("op", 343); intent.putExtra("account", esaccount); intent.putExtra("acl", i); return startCommand(context, intent); } //====================================================================================== // Inner class //====================================================================================== private final class ConnectRunnable implements Runnable { public EsAccount mAccount; ConnectRunnable() { super(); } public final void run() { if(EsLog.isLoggable("RealTimeChatService", 3)) Log.d("RealTimeChatService", "running connect runnable"); Intent intent = new Intent(RealTimeChatService.this, RealTimeChatService.class); intent.putExtra("op", 220); intent.putExtra("account", mAccount); startService(intent); } } private static final class PendingRequestList { private final LinkedList mRequestList = new LinkedList(); private final HashMap mRequestTimestamps = new HashMap(); PendingRequestList() { } public final void addRequest(Object obj) { long l = SystemClock.elapsedRealtime(); if(EsLog.isLoggable("RealTimeChatService", 3)) Log.d("RealTimeChatService", (new StringBuilder("adding request ")).append(obj).append(" time ").append(l).toString()); mRequestList.addLast(obj); mRequestTimestamps.put(obj, Long.valueOf(l)); } public final void dump() { if(EsLog.isLoggable("RealTimeChatService", 2)) { StringBuilder stringbuilder = new StringBuilder(); Object obj; Long long1; for(Iterator iterator = mRequestList.iterator(); iterator.hasNext(); stringbuilder.append("[").append(obj).append(",").append(long1).append("] ")) { obj = iterator.next(); long1 = (Long)mRequestTimestamps.get(obj); } Log.v("RealTimeChatService", (new StringBuilder("pendingRequests ")).append(stringbuilder.toString()).toString()); } } public final List getOutdatedRequestIds(long l) { LinkedList linkedlist = new LinkedList(); Iterator iterator = mRequestList.iterator(); do { if(!iterator.hasNext()) break; Object obj = iterator.next(); Long long1 = (Long)mRequestTimestamps.get(obj); if(long1 == null || long1.longValue() >= l) break; linkedlist.add(obj); } while(true); return linkedlist; } public final boolean isEmpty() { return mRequestList.isEmpty(); } public final void removeRequest(Object obj) { mRequestTimestamps.remove(obj); } public final boolean requestPending(Object obj) { boolean flag; if((Long)mRequestTimestamps.get(obj) != null) flag = true; else flag = false; return flag; } public final void trim(long l) { for(boolean flag = false; !flag && !mRequestList.isEmpty();) { Object obj = mRequestList.getFirst(); Long long1 = (Long)mRequestTimestamps.get(obj); if(long1 == null || long1.longValue() < l) { mRequestTimestamps.remove(obj); mRequestList.removeFirst(); } else { flag = true; } } } } private static final class ResultsLinkedHashMap extends LinkedHashMap { /** * */ private static final long serialVersionUID = -6565939257603795696L; ResultsLinkedHashMap() { } protected final boolean removeEldestEntry(java.util.Map.Entry entry) { boolean flag; if(size() > 32) flag = true; else flag = false; return flag; } } }