/** * Receive notification from ContactSyncEngine that a Contact sync has completed. Start Activity * sync if we have not previously synced. */ @Override public void onSyncComplete(ServiceStatus status) { LogUtils.logD( "ActivityEngine onSyncComplete, time:" + mLastStatusUpdated + ", state=" + mState); // fire off background grab of activities if (ServiceStatus.SUCCESS == status && (mLastStatusUpdated == 0) && (mState == State.IDLE)) { addStatusesSyncRequest(); LogUtils.logD("ActivityEngine onSyncComplete FULL_SYNC_FIRST_TIME."); } }
/** Handle an outstanding UI request. */ @Override protected void processUiRequest(ServiceUiRequest requestId, Object data) { LogUtils.logD("ActivityEngine processUiRequest:" + requestId); switch (requestId) { case UPDATE_STATUSES: // this is full sync or push, or "refresh" button requestStatusesFromServer(true); break; case FETCH_STATUSES: // "more" button requestStatusesFromServer(false); break; case FETCH_TIMELINES: // "more" button - we only need time lines enqueueRequest( ServiceUiRequest.FETCH_TIMELINES.ordinal(), ActivitiesState.FETCHING_OLDER_TIMELINE); newState(State.FETCH_OLDER_CALLLOG_FROM_NATIVE_DB); startCallLogSync(false); break; case UPDATE_PHONE_CALLS: // something on the NAB has changed newState(State.UPDATE_CALLOG_FROM_NATIVE); startCallLogSync(true); break; case UPDATE_SMS: newState(State.FETCH_SMS_FROM_NATIVE_DB); startFetchingSMS(); break; default: break; } }
/** * This method stores the current busy state of engine in ApplicationCache to make it available to * UI. This method is normally called before setting new state of the engine. * * @param requestId int - the request Id for network communications, or the ServiceUIRequest * ordinal for fetching/updating timelines * @param requestType one of UPDATING_STATUSES, FETCHING_OLDER_STATUSES, FETCHING_OLDER_TIMELINE */ private void enqueueRequest(int requestId, ActivitiesState requestType) { synchronized (mQueueMutex) { if (!mActiveRequests.containsKey(requestId)) { LogUtils.logE("ActivityEngine.enqueueRequest:" + requestId + ", " + requestType); mActiveRequests.put(requestId, requestType); cacheRequestType(requestType, true); } else { LogUtils.logE( "ActivityEngine.enqueueRequest: already have this type!" + requestId + ", " + requestType); } } }
/** {@inheritDoc} */ @Override public void onContactSyncStateChange( ContactSyncEngine.Mode mode, ContactSyncEngine.State oldState, ContactSyncEngine.State newState) { LogUtils.logD("ActivityEngine onContactSyncStateChange called."); }
/** * This method stores removes a busy state of engine and males the change available to UI through * the ApplicationCache. This method is normally called before setting new state of the engine. * * @param requestId int - the request Id for network communications, or the ServiceUIRequest * ordinal for fetching/updating timelines * @param requestType one of UPDATING_STATUSES, FETCHING_OLDER_STATUSES, FETCHING_OLDER_TIMELINE */ private void dequeueRequest(int requestId) { synchronized (mQueueMutex) { ActivitiesState requestType = mActiveRequests.get(requestId); if (requestType != null) { mActiveRequests.remove(requestId); LogUtils.logE("ActivityEngine.dequeueRequest:" + requestId + ", " + requestType); cacheRequestType(requestType, false); } else { LogUtils.logE( "ActivityEngine.dequeueRequest: the request is not in the queue!" + requestId + ", " + requestType); } } }
/** * Request Activities (Status/Timeline) events from Server. * * @param refresh boolean - is true when fetching latest statuses, false - when older */ private void requestStatusesFromServer(boolean refresh) { if (!checkConnectivity()) { mRequestActivitiesRequired = true; return; } mRequestActivitiesRequired = false; if (!isContactSyncReady() || !EngineManager.getInstance().getContactSyncEngine().isFirstTimeSyncComplete()) { // this method will then call completeUiRequest(status, null); onSyncHelperComplete(ServiceStatus.ERROR_NOT_READY); return; } mLastStatusUpdated = StateTable.fetchLatestStatusUpdateTime(mDb.getReadableDatabase()); mOldestStatusUpdated = StateTable.fetchOldestStatusUpdate(mDb.getReadableDatabase()); LogUtils.logD("ActivityEngine getActivites last update = " + mLastStatusUpdated); int reqId = Activities.getActivities(this, null, applyActivitiesFilter(refresh)); if (reqId > 0) { setReqId(reqId); enqueueRequest( reqId, refresh ? ActivitiesState.UPDATING_STATUSES : ActivitiesState.FETCHING_OLDER_STATUSES); if (mLastStatusUpdated == 0) { newState(State.FETCH_STATUSES_FIRST_TIME); } else { newState(State.UPDATE_STATUSES); } } }
/** * TODO: investigate why duplicates here can appear. this method might be not necessary. Remove * Activities from list of Activities with Activity IDS matching ones we have already retrieved * (i.e .duplicates). */ private void removeDuplicates(ArrayList<ActivityItem> activityList) { if (activityList.size() == 0) { return; } int dupCount = 0; List<Long> actIdList = new ArrayList<Long>(); mDb.fetchActivitiesIds(actIdList, findFirstStatusUpdateTime(activityList)); for (int i = 0; i < activityList.size(); ) { boolean inc = true; Long id = activityList.get(i).activityId; if (id != null) { for (Long l : actIdList) { if (l.compareTo(id) == 0) { activityList.remove(i); inc = false; dupCount++; break; } } } if (inc) { i++; } } LogUtils.logD("ActivityEngine removeDuplicates. Count dups = " + dupCount); }
private void updateOldestStatusUpdateTime() { long tloStatus = StateTable.fetchLatestStatusUpdateTime(mDb.getReadableDatabase()); // modify the timelines dates if (mOldestStatusUpdated < tloStatus) { StateTable.modifyOldestStatusTime(mOldestStatusUpdated, mDb.getWritableDatabase()); LogUtils.logD("ActivityEngine: oldest status update set to = " + mOldestStatusUpdated); } }
/** * This method is necessary for the tests. It catches the InvalidParameterException coming from * EngineManager.getInstance(), when the instance is null * * @return boolean - TRUE if ContactSyncEngine is not null */ private boolean isContactSyncReady() { try { return (EngineManager.getInstance() != null) && (EngineManager.getInstance().getContactSyncEngine() != null); } catch (InvalidParameterException ipe) { LogUtils.logE(ipe.toString()); return false; } }
/** * Create ChatMessage from Hashtable generated by Hessian-decoder * * @param hash Hashtable containing ChatMessage parameters */ public void createFromHashtable(Hashtable<String, Object> hash) { LogUtils.logI("Conversation.createFromHashtable() hash[" + hash.toString() + "]"); Enumeration<String> e = hash.keys(); while (e.hasMoreElements()) { String key = e.nextElement(); Tags tag = Tags.findTag(key); if (tag != null) { setValue(tag, hash.get(key)); } } }
/** * Handle Status or Timeline Activity change Push message * * @param evt Push message type (Status change or Timeline change). */ private void handlePushRequest(PushMessageTypes evt) { LogUtils.logD("ActivityEngine handlePushRequest"); switch (evt) { case STATUS_ACTIVITY_CHANGE: case TIMELINE_ACTIVITY_CHANGE: addUiRequestToQueue(ServiceUiRequest.UPDATE_STATUSES, null); break; default: // do nothing } }
/** * ActivitiesEngine run implementation Processes a response if one is available, Processes any * events in engine's UI queue. Issues get-activities request to server as part of sync. */ @Override public void run() { LogUtils.logD("ActivityEngine run"); processTimeout(); if (mNextCleanup < System.currentTimeMillis()) { cleanDatabase(); mNextCleanup = System.currentTimeMillis() + ACTIVITES_CLEANUP_SEC; LogUtils.logD("ActivityEngine.run() Clean database again at [" + mNextCleanup + "]"); return; } if (isCommsResponseOutstanding() && processCommsInQueue()) { return; } if (isUiRequestOutstanding()) { processUiQueue(); } if (mRequestActivitiesRequired) { requestStatusesFromServer(true); } }
private Map<String, List<String>> applyActivitiesFilter(boolean refresh) { Map<String, List<String>> filter = new Hashtable<String, List<String>>(); // filter out types we're not interested in List<String> statusFilter = new ArrayList<String>(); statusFilter.add(FILTER_TRUE); filter.put(FILTER_STATUS, statusFilter); if (mLastStatusUpdated > 0) { if (refresh) { List<String> updateFilter = new ArrayList<String>(); LogUtils.logD( "ActivityEngine TimeFilter newer= '" + FILTER_GT + (mLastStatusUpdated / MS_IN_SECOND) + "'"); updateFilter.add(FILTER_GT + mLastStatusUpdated / MS_IN_SECOND); filter.put(FILTER_UPDATED, updateFilter); } else { List<String> updateFilter = new ArrayList<String>(); LogUtils.logD( "ActivityEngine TimeFilter older= '" + FILTER_GT + (mOldestStatusUpdated / MS_IN_SECOND) + "'"); updateFilter.add(FILTER_GT + mOldestStatusUpdated / MS_IN_SECOND); filter.put(FILTER_UPDATED, updateFilter); } } else { // 1st time List<String> fNum = new ArrayList<String>(); fNum.add(FILTER_NUM); filter.put(FILTER_LIDS, fNum); List<String> sort = new ArrayList<String>(); sort.add(FILTER_UPDATED_REV); filter.put(FILTER_SORT, sort); mOldestStatusUpdated = (System.currentTimeMillis() - WEEK_OLD_MILLIS) / MS_IN_SECOND; } return filter; }
/** * Handle response received from transport layer (via EngineManager) * * @param resp Received Response item either a Status/Timeline related push message or a response * to a get activities request. */ @Override protected void processCommsResponse(DecodedResponse resp) { LogUtils.logD("ActivitiesEngine processCommsResponse"); // handle push response if (resp.mReqId == 0 && resp.mDataTypes.size() > 0) { PushEvent evt = (PushEvent) resp.mDataTypes.get(0); handlePushRequest(evt.mMessageType); } else { dequeueRequest(resp.mReqId); handleGetActivitiesResponse(resp.mDataTypes); } }
/** * Handle GetActivities response message received from Server * * @param reqId Request ID contained in response. This should match an ID of a request we have * issued to the Server. * @param data List array of ActivityItem items returned from Server. */ private void handleGetActivitiesResponse(List<BaseDataType> data) { /** Array of Activities retrieved from Server. */ ArrayList<ActivityItem> activityList = new ArrayList<ActivityItem>(); ServiceStatus errorStatus = getResponseStatus(BaseDataType.ACTIVITY_ITEM_DATA_TYPE, data); LogUtils.logE( "ActivityEngine.handleGetActivitiesResponse status from generic = " + errorStatus); if (ServiceStatus.SUCCESS == errorStatus) { for (BaseDataType item : data) { if (item.getType() == BaseDataType.ACTIVITY_ITEM_DATA_TYPE) { activityList.add((ActivityItem) item); } else { LogUtils.logE( "ActivityEngine.handleGetActivitiesResponse will not handle strange type = " + item.getType()); } } errorStatus = updateDatabase(activityList); // we set timeout for the next execution } // this method will then call completeUiRequest(status, null); onSyncHelperComplete(errorStatus); }
/** * Changes the state of the engine. * * @param newState The new state */ private void newState(State newState) { State oldState = mState; synchronized (mMutex) { mState = newState; } switch (mState) { case FETCH_OLDER_CALLLOG_FROM_NATIVE_DB: case UPDATE_STATUSES: case FETCH_STATUSES_FIRST_TIME: fireNewState(ServiceUiRequest.UPDATING_UI, null); break; case IDLE: fireNewState(ServiceUiRequest.UPDATING_UI_FINISHED, null); break; default: // nothing to do } LogUtils.logV("ActivitiesEngine.newState(): " + oldState + " -> " + mState); }
private ServiceStatus updateDatabase(ArrayList<ActivityItem> activityList) { ServiceStatus errorStatus = ServiceStatus.SUCCESS; // add retrieved items to Activities table in db removeDuplicates(activityList); // update the newest activity Long temp = findLastStatusUpdateTime(activityList); if (temp != Long.MIN_VALUE) { mLastStatusUpdated = temp; } if (activityList.size() > 0) { LogUtils.logD("ActivityEngine Added ActivityItems = " + activityList.size()); // update database errorStatus = mDb.addActivities(activityList); if (errorStatus == ServiceStatus.SUCCESS) { updateLatestStatusUpdateTime(); updateOldestStatusUpdateTime(); } } return errorStatus; }
/** * Drive state machine for Activities sync. If we are currently fetching native events run the * appropriated ISyncHelper. Otherwise request retrieval of Activities from Server. */ @Override protected void onTimeoutEvent() { // run now LogUtils.logV("ActivitiesEngine onTimeoutEvent:" + mState); switch (mState) { case FETCH_CALLOG_FIRST_TIME: case FETCH_OLDER_CALLLOG_FROM_NATIVE_DB: case FETCH_SMS_FROM_NATIVE_DB: case UPDATE_CALLOG_FROM_NATIVE: mActiveSyncHelper.run(); if (mActiveSyncHelper != null) { // will be null when the last batch is read, so no need to // fire timeout event - onSyncComplete sets it to null setTimeout(READ_TIMELINES_TIMEOUT_MILLS); } break; case IDLE: addStatusesSyncRequest(); break; default: // do nothing } }
@SuppressWarnings("unchecked") private void setValue(Tags key, Object value) { switch (key) { case PAYLOAD: Hashtable payload = (Hashtable) value; if (payload.containsKey(CONVERSATION_ID)) { mConversationId = (String) payload.get(CONVERSATION_ID); } if (payload.containsKey(TOS)) { mTos = (List<String>) payload.get(TOS); } break; case USERID: mUserId = (Long) value; break; case TYPE: mType = (String) value; break; default: LogUtils.logE( "Conversation.setValue() key[" + key + "] value[" + value + "] Unsupported KEY"); } }
/** * Read Identity item from Parcel. * * @param in Parcel containing Identity information. */ private void readFromParcel(Parcel in) { mPluginId = null; mNetwork = null; mNetworkUrl = null; mIconUrl = null; mAuthType = null; mIconMime = null; mOrder = -1; mName = null; mCapabilities = null; boolean[] validDataList = new boolean[MemberData.values().length]; in.readBooleanArray(validDataList); if (validDataList[MemberData.PLUGIN_ID.ordinal()]) { mPluginId = in.readString(); } if (validDataList[MemberData.NETWORK.ordinal()]) { mNetwork = in.readString(); } if (validDataList[MemberData.NETWORK_URL.ordinal()]) { try { mNetworkUrl = new URL(in.readString()); } catch (MalformedURLException e) { LogUtils.logW( "Identity.readFromParcel() " + "MalformedURLException on MemberData.NETWORK_URL"); } } if (validDataList[MemberData.ICON_URL.ordinal()]) { try { mIconUrl = new URL(in.readString()); } catch (MalformedURLException e) { LogUtils.logW( "Identity.readFromParcel() " + "MalformedURLException on MemberData.ICON_URL"); } } if (validDataList[MemberData.AUTH_TYPE.ordinal()]) { mAuthType = in.readString(); } if (validDataList[MemberData.ICON_MIME.ordinal()]) { mIconMime = in.readString(); } if (validDataList[MemberData.ORDER.ordinal()]) { mOrder = in.readInt(); } if (validDataList[MemberData.NAME.ordinal()]) { mName = in.readString(); } int noOfCapabilities = in.readInt(); if (noOfCapabilities > 0) { mCapabilities = new ArrayList<IdentityCapability>(noOfCapabilities); for (int i = 0; i < noOfCapabilities; i++) { IdentityCapability cap = IdentityCapability.CREATOR.createFromParcel(in); mCapabilities.add(cap); } } }
/** * Sets the value of the member data item associated with the specified tag. * * @param tag Current tag. * @param val Value associated with the tag. */ private void setValue(Tags tag, Object val) { switch (tag) { case AUTH_TYPE: mAuthType = (String) val; break; case ICON_MIME: mIconMime = (String) val; break; case ICON2_MIME: // TODO: Remove TAG value? // mIcon2Mime = (String)val; break; case ICON_URL: try { mIconUrl = new URL((String) val); } catch (MalformedURLException e) { LogUtils.logE("Wrong icon url: '" + val + "'"); mIconUrl = null; } break; case ICON2_URL: try { mIcon2Url = new URL((String) val); } catch (MalformedURLException e) { LogUtils.logE("Wrong icon url: '" + val + "'"); mIcon2Url = null; } break; case IDENTITY_CAPABILITY_LIST: /** Create id capability list. */ @SuppressWarnings("unchecked") Vector<Hashtable<String, Object>> v = (Vector<Hashtable<String, Object>>) val; if (mCapabilities == null) { mCapabilities = new ArrayList<IdentityCapability>(); } for (Hashtable<String, Object> obj : v) { IdentityCapability cap = new IdentityCapability(); cap.createFromHashtable(obj); mCapabilities.add(cap); } break; case IDENTITY_MAIN_TAG: // Not currently handled. break; case NAME: mName = (String) val; break; case NETWORK: mNetwork = (String) val; break; case NETWORK_URL: try { mNetworkUrl = new URL((String) val); } catch (MalformedURLException e) { LogUtils.logE("Wrong network url: '" + val + "'"); mNetworkUrl = null; } break; case ORDER: mOrder = (Integer) val; break; case PLUGIN_ID: mPluginId = (String) val; break; case ACTIVE: mActive = (Boolean) val; break; case CREATED: mCreated = (Long) val; break; case DISPLAY_NAME: mDisplayName = (String) val; break; case IDENTITY_ID: mIdentityId = (String) val; break; case IDENTITY_TYPE: mIdentityType = (String) val; break; case UPDATED: mUpdated = (Long) val; break; case USER_ID: mUserId = ((Long) val).intValue(); break; case USER_NAME: mUserName = (String) val; break; case COUNTRY_LIST: if (mCountryList == null) { mCountryList = new ArrayList<String>(); } break; default: // Do nothing. break; } }
/** {@inheritDoc} */ @Override public void onProgressEvent(ContactSyncEngine.State currentState, int percent) { LogUtils.logD("ActivityEngine onProgressEvent"); }
/** This method adds a request to get latest timelines. */ protected void addGetNewPhonesCallsRequest() { LogUtils.logD("ActivitiesEngine addGetNewTimelinesRequest()"); // TODO: I noticed there 2 NAB change events coming when the phone call // is made: try to filter one out addUiRequestToQueue(ServiceUiRequest.UPDATE_PHONE_CALLS, null); }
/** This method adds a request to get latest timelines. */ protected void addGetNewSMSRequest() { LogUtils.logD("ActivitiesEngine addGetNewTimelinesRequest()"); addUiRequestToQueue(ServiceUiRequest.UPDATE_SMS, null); }
/** This method adds a request to get older timelines. */ public void addOlderTimelinesRequest() { LogUtils.logD("ActivitiesEngine addOlderTimelinesRequest()"); addUiRequestToQueue(ServiceUiRequest.FETCH_TIMELINES, null); }
/** This method adds a request to start sync of older statuses from Now+ server. */ public void addGetOlderStatusesRequest() { LogUtils.logD("ActivityEngine addGetOlderStatusesRequest"); addUiRequestToQueue(ServiceUiRequest.FETCH_STATUSES, null); }
/** This method adds a request to start sync of the most recent statuses from Now+ server. */ public void addStatusesSyncRequest() { LogUtils.logD("ActivityEngine addStatusesSyncRequest()"); addUiRequestToQueue(ServiceUiRequest.UPDATE_STATUSES, null); }