package com.tenor.android.core.network; import android.app.Application; import android.content.Context; import android.support.annotation.IntRange; import android.support.annotation.NonNull; import android.text.TextUtils; import com.google.gson.Gson; import com.tenor.android.core.constant.StringConstant; import com.tenor.android.core.network.constant.Protocol; import com.tenor.android.core.network.constant.Protocols; import com.tenor.android.core.util.AbstractGsonUtils; import com.tenor.android.core.util.AbstractListUtils; import java.io.File; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import okhttp3.Cache; import okhttp3.Interceptor; import okhttp3.OkHttpClient; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; /** * Do NOT call {@link ApiService} directly, use its wrapper, {@link ApiClient} */ public class ApiService<T> implements IApiService<T> { private final T mClient; private final String mApiKey; private final String mEndpoint; protected ApiService(Builder<T> builder) { mClient = create(builder); mApiKey = builder.apiKey; mEndpoint = builder.endpoint; } @Override public synchronized T get() { return mClient; } @Override public synchronized T create(@NonNull Builder<T> builder) { Context ctx = builder.context; if (!(ctx instanceof Application)) { ctx = ctx.getApplicationContext(); } final File cacheDir = new File(ctx.getCacheDir().getAbsolutePath(), ctx.getPackageName()); final Cache cache = new Cache(cacheDir, 10 * 1024 * 1024); OkHttpClient.Builder http = new OkHttpClient.Builder() .cache(cache) .writeTimeout(builder.timeout, TimeUnit.SECONDS); for (Interceptor interceptor : builder.interceptors) { http.addInterceptor(interceptor); } Retrofit retrofit = new Retrofit.Builder() .baseUrl(builder.endpoint) .client(http.build()) .addConverterFactory(GsonConverterFactory.create(builder.gson)) .build(); return retrofit.create(builder.cls); } @NonNull @Override public String getEndpoint() { return mEndpoint; } @NonNull @Override public String getApiKey() { if (TextUtils.isEmpty(mApiKey)) { throw new IllegalStateException("API key cannot be null or empty."); } return mApiKey; } public interface IBuilder<T> extends Serializable { IBuilder<T> apiKey(@NonNull String apiKey); IBuilder<T> gson(@NonNull Gson gson); /** * Set API protocol type * <p> * Call this on the onCreate() of your Application * * @param protocol {@link Protocol} */ IBuilder<T> protocol(@Protocol String protocol); /** * Set API server * <p> * Call this on the onCreate() of your Application * * @param serverName server name, default value is "api" */ IBuilder<T> server(@NonNull String serverName); /** * Set {@link Interceptor} * * @param interceptor the interceptor */ IBuilder<T> interceptor(@NonNull Interceptor interceptor); /** * Set {@link List}<{@link Interceptor}> * * @param interceptors the list of interceptors */ IBuilder<T> interceptors(@NonNull List<Interceptor> interceptors); /** * Network timeout * * @param timeout between 0 to 30 seconds */ IBuilder<T> timeout(@IntRange(from = 0, to = 30) int timeout); /** * Set API endpoint directly; however, using {@link #protocol(String)} and * {@link #server(String)} are recommended over using this. */ IBuilder<T> endpoint(@NonNull String endpoint); IApiService<T> build(); } public static class Builder<T> implements IBuilder<T> { private static final long serialVersionUID = -3581428418516126896L; protected static final String API_ENDPOINT_FORMATTER = "%1$s://%2$s.tenor.com/v1/"; protected static final String SERVER_NAME = "api"; @Protocol private String protocol = Protocols.HTTPS; @NonNull private String serverName = SERVER_NAME; @NonNull private String endpoint = String.format(API_ENDPOINT_FORMATTER, protocol, serverName); @IntRange(from = 0, to = 30) private int timeout = 15; @NonNull private List<Interceptor> interceptors = new ArrayList<>(); @NonNull private String apiKey = StringConstant.EMPTY; @NonNull private Gson gson = AbstractGsonUtils.getInstance(); private final Context context; private final Class<T> cls; /** * Call this on the onCreate() of your subclass of {@link Application} */ public Builder(@NonNull Context context, @NonNull Class<T> cls) { this.context = context; this.cls = cls; } @Override public IBuilder<T> apiKey(@NonNull String apiKey) { if (TextUtils.isEmpty(apiKey)) { throw new IllegalStateException("API key cannot be null or empty."); } this.apiKey = apiKey; return this; } @Override public IBuilder<T> gson(@NonNull Gson gson) { this.gson = gson; return this; } /** * Set API protocol type * <p> * Call this on the onCreate() of your Application * * @param protocol {@link Protocols} */ @Override public IBuilder<T> protocol(@Protocol String protocol) { this.protocol = Protocols.getOrHttps(protocol); this.endpoint = String.format(API_ENDPOINT_FORMATTER, this.protocol, this.serverName); return this; } /** * Set API server * <p> * Call this on the onCreate() of your Application * * @param server server name, default value is "api" */ @Override public IBuilder<T> server(@NonNull String server) { this.serverName = !TextUtils.isEmpty(server) ? server : SERVER_NAME; this.endpoint = String.format(API_ENDPOINT_FORMATTER, this.protocol, this.serverName); return this; } /** * Set {@link Interceptor} * * @param interceptor the interceptor */ @Override public IBuilder<T> interceptor(@NonNull Interceptor interceptor) { this.interceptors.add(interceptor); return this; } /** * Set {@link List}<{@link Interceptor}> * * @param interceptors the list of interceptors */ @Override public IBuilder<T> interceptors(@NonNull List<Interceptor> interceptors) { if (!AbstractListUtils.isEmpty(interceptors)) { this.interceptors.addAll(interceptors); } return this; } @Override public IBuilder<T> timeout(@IntRange(from = 0, to = 30) int timeout) { if (timeout >= 0 && timeout <= 30 && this.timeout != timeout) { this.timeout = timeout; } return this; } /** * Set API endpoint directly; however, using {@link #protocol(String)} and * {@link #server(String)} are recommended over using this. */ @Override public IBuilder<T> endpoint(@NonNull String endpoint) { if (!TextUtils.isEmpty(endpoint)) { this.endpoint = endpoint; } return this; } @Override public IApiService<T> build() { return new ApiService<>(this); } } }