Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
14792 manas 1
package com.facebook;
2
 
3
import android.os.Bundle;
4
import android.text.format.DateUtils;
5
 
6
import com.facebook.internal.Logger;
7
 
8
import java.io.Serializable;
9
 
10
class FacebookTimeSpentData implements Serializable {
11
    // Constants
12
    private static final long serialVersionUID = 1L;
13
    private static final String TAG = AppEventsLogger.class.getCanonicalName();
14
    private static final long FIRST_TIME_LOAD_RESUME_TIME = -1;
15
    private static final long INTERRUPTION_THRESHOLD_MILLISECONDS = 1000;
16
    private static final long NUM_MILLISECONDS_IDLE_TO_BE_NEW_SESSION =
17
            60 * DateUtils.SECOND_IN_MILLIS;
18
    private static final long APP_ACTIVATE_SUPPRESSION_PERIOD_IN_MILLISECONDS =
19
            5 * DateUtils.MINUTE_IN_MILLIS;
20
 
21
    // Should be kept in sync with the iOS sdk
22
    private static final long[] INACTIVE_SECONDS_QUANTA =
23
        new long[] {
24
            5 * DateUtils.MINUTE_IN_MILLIS,
25
            15 * DateUtils.MINUTE_IN_MILLIS,
26
            30 * DateUtils.MINUTE_IN_MILLIS,
27
            1 * DateUtils.HOUR_IN_MILLIS,
28
            6 * DateUtils.HOUR_IN_MILLIS,
29
            12 * DateUtils.HOUR_IN_MILLIS,
30
            1 * DateUtils.DAY_IN_MILLIS,
31
            2 * DateUtils.DAY_IN_MILLIS,
32
            3 * DateUtils.DAY_IN_MILLIS,
33
            7 * DateUtils.DAY_IN_MILLIS,
34
            14 * DateUtils.DAY_IN_MILLIS,
35
            21 * DateUtils.DAY_IN_MILLIS,
36
            28 * DateUtils.DAY_IN_MILLIS,
37
            60 * DateUtils.DAY_IN_MILLIS,
38
            90 * DateUtils.DAY_IN_MILLIS,
39
            120 * DateUtils.DAY_IN_MILLIS,
40
            150 * DateUtils.DAY_IN_MILLIS,
41
            180 * DateUtils.DAY_IN_MILLIS,
42
            365 * DateUtils.DAY_IN_MILLIS,
43
        };
44
 
45
    private boolean isWarmLaunch;
46
    private boolean isAppActive;
47
    private long lastActivateEventLoggedTime;
48
 
49
    // Member data that's persisted to disk
50
    private long lastResumeTime;
51
    private long lastSuspendTime;
52
    private long millisecondsSpentInSession;
53
    private int interruptionCount;
54
    private String firstOpenSourceApplication;
55
 
56
    /**
57
     * Serialization proxy for the FacebookTimeSpentData class. This is version 1 of
58
     * serialization. Future serializations may differ in format. This
59
     * class should not be modified. If serializations formats change,
60
     * create a new class SerializationProxyVx.
61
     */
62
    private static class SerializationProxyV1 implements Serializable {
63
        private static final long serialVersionUID = 6L;
64
 
65
        private final long lastResumeTime;
66
        private final long lastSuspendTime;
67
        private final long millisecondsSpentInSession;
68
        private final int interruptionCount;
69
 
70
        SerializationProxyV1(
71
            long lastResumeTime,
72
            long lastSuspendTime,
73
            long millisecondsSpentInSession,
74
            int interruptionCount
75
        ) {
76
            this.lastResumeTime = lastResumeTime;
77
            this.lastSuspendTime = lastSuspendTime;
78
            this.millisecondsSpentInSession = millisecondsSpentInSession;
79
            this.interruptionCount = interruptionCount;
80
        }
81
 
82
        private Object readResolve() {
83
            return new FacebookTimeSpentData(
84
                lastResumeTime,
85
                lastSuspendTime,
86
                millisecondsSpentInSession,
87
                interruptionCount);
88
        }
89
    }
90
 
91
 
92
    /**
93
     * Constructor to be used for V1 serialization only, DO NOT CHANGE.
94
     */
95
    private FacebookTimeSpentData(
96
            long lastResumeTime,
97
            long lastSuspendTime,
98
            long millisecondsSpentInSession,
99
            int interruptionCount
100
 
101
    ) {
102
        resetSession();
103
        this.lastResumeTime = lastResumeTime;
104
        this.lastSuspendTime = lastSuspendTime;
105
        this.millisecondsSpentInSession = millisecondsSpentInSession;
106
        this.interruptionCount = interruptionCount;
107
    }
108
 
109
    /**
110
     * Serialization proxy for the FacebookTimeSpentData class. This is version 2 of
111
     * serialization. Future serializations may differ in format. This
112
     * class should not be modified. If serializations formats change,
113
     * create a new class SerializationProxyVx.
114
     */
115
    private static class SerializationProxyV2 implements Serializable {
116
        private static final long serialVersionUID = 6L;
117
 
118
        private final long lastResumeTime;
119
        private final long lastSuspendTime;
120
        private final long millisecondsSpentInSession;
121
        private final int interruptionCount;
122
        private final String firstOpenSourceApplication;
123
 
124
        SerializationProxyV2(
125
                long lastResumeTime,
126
                long lastSuspendTime,
127
                long millisecondsSpentInSession,
128
                int interruptionCount,
129
                String firstOpenSourceApplication
130
 
131
        ) {
132
            this.lastResumeTime = lastResumeTime;
133
            this.lastSuspendTime = lastSuspendTime;
134
            this.millisecondsSpentInSession = millisecondsSpentInSession;
135
            this.interruptionCount = interruptionCount;
136
            this.firstOpenSourceApplication = firstOpenSourceApplication;
137
        }
138
 
139
        private Object readResolve() {
140
            return new FacebookTimeSpentData(
141
                    lastResumeTime,
142
                    lastSuspendTime,
143
                    millisecondsSpentInSession,
144
                    interruptionCount,
145
                    firstOpenSourceApplication);
146
        }
147
    }
148
 
149
    FacebookTimeSpentData() {
150
        resetSession();
151
    }
152
 
153
    /**
154
     * Constructor to be used for V2 serialization only, DO NOT CHANGE.
155
     */
156
    private FacebookTimeSpentData(
157
        long lastResumeTime,
158
        long lastSuspendTime,
159
        long millisecondsSpentInSession,
160
        int interruptionCount,
161
        String firstOpenSourceApplication
162
    ) {
163
        resetSession();
164
        this.lastResumeTime = lastResumeTime;
165
        this.lastSuspendTime = lastSuspendTime;
166
        this.millisecondsSpentInSession = millisecondsSpentInSession;
167
        this.interruptionCount = interruptionCount;
168
        this.firstOpenSourceApplication = firstOpenSourceApplication;
169
    }
170
 
171
    private Object writeReplace() {
172
        return new SerializationProxyV2(
173
                lastResumeTime,
174
                lastSuspendTime,
175
                millisecondsSpentInSession,
176
                interruptionCount,
177
                firstOpenSourceApplication
178
        );
179
    }
180
 
181
    void onSuspend(AppEventsLogger logger, long eventTime) {
182
        if (!isAppActive) {
183
            Logger.log(LoggingBehavior.APP_EVENTS, TAG, "Suspend for inactive app");
184
            return;
185
        }
186
 
187
        long now = eventTime;
188
        long delta = (now - lastResumeTime);
189
        if (delta < 0) {
190
            Logger.log(LoggingBehavior.APP_EVENTS, TAG, "Clock skew detected");
191
            delta = 0;
192
        }
193
        millisecondsSpentInSession += delta;
194
        lastSuspendTime = now;
195
        isAppActive = false;
196
    }
197
 
198
    void onResume(AppEventsLogger logger, long eventTime, String sourceApplicationInfo) {
199
        long now = eventTime;
200
 
201
        // Retain old behavior for activated app event - log the event if the event hasn't
202
        // been logged in the previous suppression interval or this is a cold launch.
203
        // If this is a cold launch, always log the event. Otherwise, use the last
204
        // event log time to determine if the app activate should be suppressed or not.
205
        if (isColdLaunch() ||
206
            ((now - lastActivateEventLoggedTime) > APP_ACTIVATE_SUPPRESSION_PERIOD_IN_MILLISECONDS)) {
207
            Bundle eventParams = new Bundle();
208
            eventParams.putString(
209
                    AppEventsConstants.EVENT_PARAM_SOURCE_APPLICATION,
210
                    sourceApplicationInfo);
211
            logger.logEvent(AppEventsConstants.EVENT_NAME_ACTIVATED_APP, eventParams);
212
            lastActivateEventLoggedTime = now;
213
        }
214
 
215
        // If this is an application that's not calling onSuspend yet, log and return. We can't
216
        // track time spent for this application as there are no calls to onSuspend.
217
        if (isAppActive) {
218
          Logger.log(LoggingBehavior.APP_EVENTS, TAG, "Resume for active app");
219
          return;
220
        }
221
 
222
        long interruptionDurationMillis = wasSuspendedEver() ? now - lastSuspendTime : 0;
223
        if (interruptionDurationMillis < 0) {
224
          Logger.log(LoggingBehavior.APP_EVENTS, TAG, "Clock skew detected");
225
          interruptionDurationMillis = 0;
226
        }
227
 
228
        // If interruption duration is > new session threshold, then log old session
229
        // event and start a new session.
230
        if (interruptionDurationMillis > NUM_MILLISECONDS_IDLE_TO_BE_NEW_SESSION) {
231
            logAppDeactivatedEvent(logger, interruptionDurationMillis);
232
        } else {
233
            // We're not logging this resume event - check to see if this should count
234
            // as an interruption
235
            if (interruptionDurationMillis > INTERRUPTION_THRESHOLD_MILLISECONDS) {
236
                interruptionCount++;
237
            }
238
        }
239
 
240
        // Set source application only for the first resume of the timespent session.
241
        if (interruptionCount == 0) {
242
            firstOpenSourceApplication = sourceApplicationInfo;
243
        }
244
 
245
        lastResumeTime = now;
246
        isAppActive = true;
247
    }
248
 
249
    private void logAppDeactivatedEvent(AppEventsLogger logger,
250
                                        long interruptionDurationMillis) {
251
        // Log the old session information and clear the data
252
        Bundle eventParams = new Bundle();
253
        eventParams.putInt(
254
                AppEventsConstants.EVENT_NAME_SESSION_INTERRUPTIONS,
255
                interruptionCount);
256
        eventParams.putString(
257
                AppEventsConstants.EVENT_NAME_TIME_BETWEEN_SESSIONS,
258
                String.format("session_quanta_%d", getQuantaIndex(interruptionDurationMillis)));
259
        eventParams.putString(
260
                AppEventsConstants.EVENT_PARAM_SOURCE_APPLICATION,
261
                firstOpenSourceApplication);
262
        logger.logEvent(
263
                AppEventsConstants.EVENT_NAME_DEACTIVATED_APP,
264
                (millisecondsSpentInSession/DateUtils.SECOND_IN_MILLIS),
265
                eventParams);
266
        resetSession();
267
    }
268
 
269
    private static int getQuantaIndex(long timeBetweenSessions) {
270
        int quantaIndex = 0;
271
 
272
        while (
273
            quantaIndex < INACTIVE_SECONDS_QUANTA.length &&
274
            INACTIVE_SECONDS_QUANTA[quantaIndex] < timeBetweenSessions
275
        ) {
276
            ++quantaIndex;
277
        }
278
 
279
        return quantaIndex;
280
    }
281
 
282
    private void resetSession() {
283
        isAppActive = false;
284
        lastResumeTime = FIRST_TIME_LOAD_RESUME_TIME;
285
        lastSuspendTime = FIRST_TIME_LOAD_RESUME_TIME;
286
        interruptionCount = 0;
287
        millisecondsSpentInSession = 0;
288
    }
289
 
290
    private boolean wasSuspendedEver() {
291
        return lastSuspendTime != FIRST_TIME_LOAD_RESUME_TIME;
292
    }
293
 
294
    private boolean isColdLaunch() {
295
        // On the very first call in the process lifecycle, this will always
296
        // return true. After that, it will always return false.
297
        boolean result = !isWarmLaunch;
298
        isWarmLaunch = true;
299
        return result;
300
    }
301
}