/** * Copyright 2016 Bryan Kelly * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. * * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package io.github.btkelly.gandalf.network; import android.content.Context; import android.os.Handler; import android.os.Looper; import androidx.annotation.Nullable; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonDeserializer; import com.google.gson.JsonSyntaxException; import java.io.File; import java.io.IOException; import java.util.concurrent.TimeUnit; import io.github.btkelly.gandalf.models.Bootstrap; import io.github.btkelly.gandalf.models.BootstrapException; import io.github.btkelly.gandalf.models.BootstrapResponse; import io.github.btkelly.gandalf.utils.StringUtils; import okhttp3.Cache; import okhttp3.Call; import okhttp3.Callback; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; /** * Network class for fetching the bootstrap file */ public class BootstrapApi { private static final int DEFAULT_CACHE_SIZE = 1024; private static final long DEFAULT_CONNECTION_TIMEOUT_SECONDS = 30; @Nullable private final JsonDeserializer<Bootstrap> customDeserializer; private final String bootStrapUrl; private final OkHttpClient okHttpClient; /** * Creates a bootstrap api class * * @param context - Android context used for setting up http cache directory * @param okHttpClient - OkHttpClient to be used for requests, falls back to default if null * @param bootStrapUrl - url to fetch the bootstrap file from * @param customDeserializer - a custom deserializer for parsing the JSON response */ public BootstrapApi(Context context, @Nullable OkHttpClient okHttpClient, String bootStrapUrl, @Nullable JsonDeserializer<Bootstrap> customDeserializer) { this.bootStrapUrl = bootStrapUrl; this.customDeserializer = customDeserializer; if (okHttpClient == null) { File cacheDir = context.getCacheDir(); Cache cache = new Cache(cacheDir, DEFAULT_CACHE_SIZE); this.okHttpClient = new OkHttpClient.Builder() .cache(cache) .connectTimeout(DEFAULT_CONNECTION_TIMEOUT_SECONDS, TimeUnit.SECONDS) .readTimeout(DEFAULT_CONNECTION_TIMEOUT_SECONDS, TimeUnit.SECONDS) .build(); } else { this.okHttpClient = okHttpClient; } } /** * Will execute a request to fetch the application bootstrap file specified by the bootstrap * url set on the Gandalf class. Request is executed on a background thread. * * @param bootstrapCallback - a BootstrapCallback to receive call backs for the network call * @throws IllegalStateException - throws if the bootstrap url has not been set */ public void fetchBootstrap(final BootstrapCallback bootstrapCallback) { if (StringUtils.isBlank(bootStrapUrl)) { throw new IllegalStateException("You must supply a bootstrap url"); } Request request = new Request.Builder() .url(bootStrapUrl) .build(); okHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, final IOException e) { postError(bootstrapCallback, e); } @Override public void onResponse(Call call, Response response) throws IOException { GsonBuilder gsonBuilder = new GsonBuilder(); if (customDeserializer != null) { gsonBuilder.registerTypeAdapter(Bootstrap.class, customDeserializer); } Gson gson = gsonBuilder.create(); try { BootstrapResponse bootstrapResponse = gson.fromJson(response.body().string(), BootstrapResponse.class); if (bootstrapResponse != null) { final Bootstrap bootstrap = bootstrapResponse.getAndroid(); if (bootstrap != null) { new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { bootstrapCallback.onSuccess(bootstrap); } }); } else { postError(bootstrapCallback, new BootstrapException("No \"android\" key found in the JSON response")); } } else { postError(bootstrapCallback, new BootstrapException("No content found in the JSON response")); } } catch (JsonSyntaxException e) { postError(bootstrapCallback, e); } } }); } private void postError(final BootstrapCallback bootstrapCallback, final Exception e) { new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { bootstrapCallback.onError(e); } }); } }