4646import com .optimizely .ab .event .internal .payload .Event ;
4747import com .optimizely .ab .android .user_experiment_record .AndroidUserExperimentRecord ;
4848
49+ import org .json .JSONObject ;
4950import org .slf4j .Logger ;
5051import org .slf4j .LoggerFactory ;
5152
@@ -117,6 +118,91 @@ void setOptimizelyStartListener(@Nullable OptimizelyStartListener optimizelyStar
117118 this .optimizelyStartListener = optimizelyStartListener ;
118119 }
119120
121+ /**
122+ * Initialize Optimizely Synchronously
123+ * <p>
124+ * Instantiates and returns an {@link OptimizelyClient} instance. Will also cache the instance
125+ * for future lookups via getClient
126+ * @param context any {@link Context} instance
127+ * @param datafile the datafile
128+ * @return an {@link OptimizelyClient} instance
129+ */
130+ public OptimizelyClient initialize (@ NonNull Context context , @ NonNull String datafile ) {
131+ if (!isAndroidVersionSupported ()) {
132+ return optimizelyClient ;
133+ }
134+
135+ AndroidUserExperimentRecord userExperimentRecord =
136+ (AndroidUserExperimentRecord ) AndroidUserExperimentRecord .newInstance (getProjectId (), context );
137+ // The User Experiment Record is started on the main thread on an asynchronous start.
138+ // Starting simply creates the file if it doesn't exist so it's not
139+ // terribly expensive. Blocking the UI thread prevents touch input...
140+ userExperimentRecord .start ();
141+ try {
142+ optimizelyClient = buildOptimizely (context , datafile , userExperimentRecord );
143+ } catch (ConfigParseException e ) {
144+ logger .error ("Unable to parse compiled data file" , e );
145+ }
146+
147+
148+ // After instantiating the OptimizelyClient, we will begin the datafile sync so that next time
149+ // the user can instantiate with the latest datafile
150+ final Intent intent = new Intent (context .getApplicationContext (), DataFileService .class );
151+ if (dataFileServiceConnection == null ) {
152+ this .dataFileServiceConnection = new DataFileServiceConnection (this );
153+ context .getApplicationContext ().bindService (intent , dataFileServiceConnection , Context .BIND_AUTO_CREATE );
154+ }
155+
156+ return optimizelyClient ;
157+ }
158+
159+ /**
160+ * Initialize Optimizely Synchronously
161+ * <p>
162+ * Instantiates and returns an {@link OptimizelyClient} instance. Will also cache the instance
163+ * for future lookups via getClient. The datafile should be stored in res/raw.
164+ *
165+ * @param context any {@link Context} instance
166+ * @param dataFileRes the R id that the data file is located under.
167+ * @return an {@link OptimizelyClient} instance
168+ */
169+ @ NonNull
170+ public OptimizelyClient initialize (@ NonNull Context context , @ RawRes int dataFileRes ) {
171+ try {
172+ String datafile = loadRawResource (context , dataFileRes );
173+ return initialize (context , datafile );
174+ } catch (IOException e ) {
175+ logger .error ("Unable to load compiled data file" , e );
176+ }
177+
178+ // return dummy client if not able to initialize a valid one
179+ return optimizelyClient ;
180+ }
181+
182+ /**
183+ * Initialize Optimizely Synchronously
184+ * <p>
185+ * Instantiates and returns an {@link OptimizelyClient} instance using the datafile cached on disk
186+ * if not available then it will return a dummy instance.
187+ * @param context any {@link Context} instance
188+ * @return an {@link OptimizelyClient} instance
189+ */
190+ public OptimizelyClient initialize (@ NonNull Context context ) {
191+ DataFileCache dataFileCache = new DataFileCache (
192+ projectId ,
193+ new Cache (context , LoggerFactory .getLogger (Cache .class )),
194+ LoggerFactory .getLogger (DataFileCache .class )
195+ );
196+
197+ JSONObject datafile = dataFileCache .load ();
198+ if (datafile != null ) {
199+ return initialize (context , datafile .toString ());
200+ }
201+
202+ // return dummy client if not able to initialize a valid one
203+ return optimizelyClient ;
204+ }
205+
120206 /**
121207 * Starts Optimizely asynchronously
122208 * <p>
@@ -130,30 +216,32 @@ void setOptimizelyStartListener(@Nullable OptimizelyStartListener optimizelyStar
130216 * @param optimizelyStartListener callback that {@link OptimizelyClient} instances are sent to.
131217 */
132218 @ TargetApi (Build .VERSION_CODES .ICE_CREAM_SANDWICH )
133- public void start (@ NonNull Activity activity , @ NonNull OptimizelyStartListener optimizelyStartListener ) {
219+ public void initialize (@ NonNull Activity activity , @ NonNull OptimizelyStartListener optimizelyStartListener ) {
134220 if (!isAndroidVersionSupported ()) {
135221 return ;
136222 }
137223 activity .getApplication ().registerActivityLifecycleCallbacks (new OptlyActivityLifecycleCallbacks (this ));
138- start (activity .getApplication (), optimizelyStartListener );
224+ initialize (activity .getApplicationContext (), optimizelyStartListener );
139225 }
140226
141227 /**
142228 * @param context any type of context instance
143229 * @param optimizelyStartListener callback that {@link OptimizelyClient} instances are sent to.
144- * @see #start (Activity, OptimizelyStartListener)
230+ * @see #initialize (Activity, OptimizelyStartListener)
145231 * <p>
146232 * This method does the same thing except it can be used with a generic {@link Context}.
147233 * When using this method be sure to call {@link #stop(Context)} to unbind {@link DataFileService}.
148234 */
149- public void start (@ NonNull Context context , @ NonNull OptimizelyStartListener optimizelyStartListener ) {
235+ public void initialize (@ NonNull Context context , @ NonNull OptimizelyStartListener optimizelyStartListener ) {
150236 if (!isAndroidVersionSupported ()) {
151237 return ;
152238 }
153239 this .optimizelyStartListener = optimizelyStartListener ;
154- this .dataFileServiceConnection = new DataFileServiceConnection (this );
155240 final Intent intent = new Intent (context .getApplicationContext (), DataFileService .class );
156- context .getApplicationContext ().bindService (intent , dataFileServiceConnection , Context .BIND_AUTO_CREATE );
241+ if (dataFileServiceConnection == null ) {
242+ this .dataFileServiceConnection = new DataFileServiceConnection (this );
243+ context .getApplicationContext ().bindService (intent , dataFileServiceConnection , Context .BIND_AUTO_CREATE );
244+ }
157245 }
158246
159247 @ TargetApi (Build .VERSION_CODES .ICE_CREAM_SANDWICH )
@@ -165,7 +253,7 @@ void stop(@NonNull Activity activity, @NonNull OptlyActivityLifecycleCallbacks o
165253 /**
166254 * Unbinds {@link DataFileService}
167255 * <p>
168- * Calling this is not necessary if using {@link #start (Activity, OptimizelyStartListener)} which
256+ * Calling this is not necessary if using {@link #initialize (Activity, OptimizelyStartListener)} which
169257 * handles unbinding implicitly.
170258 *
171259 * @param context any {@link Context} instance
@@ -185,12 +273,11 @@ public void stop(@NonNull Context context) {
185273 /**
186274 * Gets a cached Optimizely instance
187275 * <p>
188- * If {@link #start (Activity, OptimizelyStartListener)} or {@link #start (Context, OptimizelyStartListener)}
276+ * If {@link #initialize (Activity, OptimizelyStartListener)} or {@link #initialize (Context, OptimizelyStartListener)}
189277 * has not been called yet the returned {@link OptimizelyClient} instance will be a dummy instance
190278 * that logs warnings in order to prevent {@link NullPointerException}.
191279 * <p>
192- * If {@link #getOptimizely(Context, int)} was used the built {@link OptimizelyClient} instance
193- * will be updated. Using {@link #start(Activity, OptimizelyStartListener)} or {@link #start(Context, OptimizelyStartListener)}
280+ * Using {@link #initialize(Activity, OptimizelyStartListener)} or {@link #initialize(Context, OptimizelyStartListener)}
194281 * will update the cached instance with a new {@link OptimizelyClient} built from a cached local
195282 * datafile on disk or a remote datafile on the CDN.
196283 *
@@ -202,41 +289,6 @@ public OptimizelyClient getOptimizely() {
202289 return optimizelyClient ;
203290 }
204291
205- /**
206- * Create an instance of {@link OptimizelyClient} from a compiled in datafile
207- * <p>
208- * After using this method successfully {@link #getOptimizely()} will contain a
209- * cached instance built from the compiled in datafile. The datafile should be
210- * stored in res/raw.
211- *
212- * @param context any {@link Context} instance
213- * @param dataFileRes the R id that the data file is located under.
214- * @return an {@link OptimizelyClient} instance
215- */
216- @ NonNull
217- public OptimizelyClient getOptimizely (@ NonNull Context context , @ RawRes int dataFileRes ) {
218- if (!isAndroidVersionSupported ()) {
219- return optimizelyClient ;
220- }
221-
222- AndroidUserExperimentRecord userExperimentRecord =
223- (AndroidUserExperimentRecord ) AndroidUserExperimentRecord .newInstance (getProjectId (), context );
224- // Blocking File I/O is necessary here in order to provide a synchronous API
225- // The User Experiment Record is started off the of the main thread when starting
226- // asynchronously. Starting simply creates the file if it doesn't exist so it's not
227- // terribly expensive. Blocking the UI the thread prevents touch input...
228- userExperimentRecord .start ();
229- try {
230- optimizelyClient = buildOptimizely (context , loadRawResource (context , dataFileRes ), userExperimentRecord );
231- } catch (ConfigParseException e ) {
232- logger .error ("Unable to parse compiled data file" , e );
233- } catch (IOException e ) {
234- logger .error ("Unable to load compiled data file" , e );
235- }
236-
237- return optimizelyClient ;
238- }
239-
240292 private String loadRawResource (Context context , @ RawRes int rawRes ) throws IOException {
241293 Resources res = context .getResources ();
242294 InputStream in = res .openRawResource (rawRes );
@@ -249,6 +301,21 @@ private String loadRawResource(Context context, @RawRes int rawRes) throws IOExc
249301 }
250302 }
251303
304+ /**
305+ * Check if the datafile is cached on the disk
306+ * @param context any {@link Context} instance
307+ * @return True if the datafile is cached on the disk
308+ */
309+ public boolean isDatafileCached (Context context ) {
310+ DataFileCache dataFileCache = new DataFileCache (
311+ projectId ,
312+ new Cache (context , LoggerFactory .getLogger (Cache .class )),
313+ LoggerFactory .getLogger (DataFileCache .class )
314+ );
315+
316+ return dataFileCache .exists ();
317+ }
318+
252319 @ NonNull
253320 String getProjectId () {
254321 return projectId ;
0 commit comments