package com.kickstarter;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.kickstarter.libs.AndroidPayCapability;
import com.kickstarter.libs.ApiEndpoint;
import com.kickstarter.libs.AutoParcelAdapterFactory;
import com.kickstarter.libs.Build;
import com.kickstarter.libs.BuildCheck;
import com.kickstarter.libs.CurrentConfig;
import com.kickstarter.libs.CurrentConfigType;
import com.kickstarter.libs.CurrentUser;
import com.kickstarter.libs.CurrentUserType;
import com.kickstarter.libs.DateTimeTypeConverter;
import com.kickstarter.libs.DeviceRegistrar;
import com.kickstarter.libs.DeviceRegistrarType;
import com.kickstarter.libs.Environment;
import com.kickstarter.libs.Font;
import com.kickstarter.libs.InternalToolsType;
import com.kickstarter.libs.KSCurrency;
import com.kickstarter.libs.KSString;
import com.kickstarter.libs.Koala;
import com.kickstarter.libs.KoalaTrackingClient;
import com.kickstarter.libs.Logout;
import com.kickstarter.libs.PushNotifications;
import com.kickstarter.libs.preferences.BooleanPreference;
import com.kickstarter.libs.preferences.BooleanPreferenceType;
import com.kickstarter.libs.preferences.IntPreference;
import com.kickstarter.libs.preferences.IntPreferenceType;
import com.kickstarter.libs.preferences.StringPreference;
import com.kickstarter.libs.preferences.StringPreferenceType;
import com.kickstarter.libs.qualifiers.AccessTokenPreference;
import com.kickstarter.libs.qualifiers.ActivitySamplePreference;
import com.kickstarter.libs.qualifiers.ApiRetrofit;
import com.kickstarter.libs.qualifiers.AppRatingPreference;
import com.kickstarter.libs.qualifiers.ApplicationContext;
import com.kickstarter.libs.qualifiers.ConfigPreference;
import com.kickstarter.libs.qualifiers.GamesNewsletterPreference;
import com.kickstarter.libs.qualifiers.PackageNameString;
import com.kickstarter.libs.qualifiers.UserPreference;
import com.kickstarter.libs.qualifiers.WebEndpoint;
import com.kickstarter.libs.qualifiers.WebRetrofit;
import com.kickstarter.libs.utils.PlayServicesCapability;
import com.kickstarter.libs.utils.Secrets;
import com.kickstarter.services.ApiClient;
import com.kickstarter.services.ApiClientType;
import com.kickstarter.services.ApiService;
import com.kickstarter.services.KSWebViewClient;
import com.kickstarter.services.WebClient;
import com.kickstarter.services.WebClientType;
import com.kickstarter.services.WebService;
import com.kickstarter.services.interceptors.ApiRequestInterceptor;
import com.kickstarter.services.interceptors.KSRequestInterceptor;
import com.kickstarter.services.interceptors.WebRequestInterceptor;
import org.joda.time.DateTime;
import java.net.CookieManager;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import okhttp3.CookieJar;
import okhttp3.JavaNetCookieJar;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import rx.Scheduler;
import rx.schedulers.Schedulers;
@Module
public final class ApplicationModule {
private final Application application;
public ApplicationModule(final @NonNull Application application) {
this.application = application;
}
@Provides
@Singleton
Environment provideEnvironment(final @NonNull @ActivitySamplePreference IntPreferenceType activitySamplePreference,
final @NonNull AndroidPayCapability androidPayCapability,
final @NonNull ApiClientType apiClient,
final @NonNull BuildCheck buildCheck,
final @NonNull CookieManager cookieManager,
final @NonNull CurrentConfigType currentConfig,
final @NonNull CurrentUserType currentUser,
final @NonNull Gson gson,
final @NonNull @AppRatingPreference BooleanPreferenceType hasSeenAppRatingPreference,
final @NonNull @GamesNewsletterPreference BooleanPreferenceType hasSeenGamesNewsletterPreference,
final @NonNull Koala koala,
final @NonNull KSCurrency ksCurrency,
final @NonNull KSString ksString,
final @NonNull PlayServicesCapability playServicesCapability,
final @NonNull Scheduler scheduler,
final @NonNull SharedPreferences sharedPreferences,
final @NonNull WebClientType webClient) {
return Environment.builder()
.activitySamplePreference(activitySamplePreference)
.androidPayCapability(androidPayCapability)
.apiClient(apiClient)
.buildCheck(buildCheck)
.cookieManager(cookieManager)
.currentConfig(currentConfig)
.currentUser(currentUser)
.gson(gson)
.hasSeenAppRatingPreference(hasSeenAppRatingPreference)
.hasSeenGamesNewsletterPreference(hasSeenGamesNewsletterPreference)
.koala(koala)
.ksCurrency(ksCurrency)
.ksString(ksString)
.playServicesCapability(playServicesCapability)
.scheduler(scheduler)
.sharedPreferences(sharedPreferences)
.webClient(webClient)
.build();
}
@Provides
@Singleton
@NonNull ApiClientType provideApiClientType(final @NonNull ApiService apiService, final @NonNull Gson gson) {
return new ApiClient(apiService, gson);
}
@Provides
@Singleton
@NonNull
OkHttpClient provideOkHttpClient(final @NonNull ApiRequestInterceptor apiRequestInterceptor, final @NonNull CookieJar cookieJar,
final @NonNull HttpLoggingInterceptor httpLoggingInterceptor, final @NonNull KSRequestInterceptor ksRequestInterceptor,
final @NonNull Build build, final @NonNull WebRequestInterceptor webRequestInterceptor) {
final OkHttpClient.Builder builder = new OkHttpClient.Builder();
// Only log in debug mode to avoid leaking sensitive information.
if (build.isDebug()) {
builder.addInterceptor(httpLoggingInterceptor);
}
return builder
.addInterceptor(apiRequestInterceptor)
.addInterceptor(webRequestInterceptor)
.addInterceptor(ksRequestInterceptor)
.cookieJar(cookieJar)
.build();
}
@Provides
@Singleton
@ApiRetrofit
@NonNull Retrofit provideApiRetrofit(final @NonNull ApiEndpoint apiEndpoint,
final @NonNull Gson gson,
final @NonNull OkHttpClient okHttpClient) {
return createRetrofit(apiEndpoint.url(), gson, okHttpClient);
}
@Provides
@Singleton
@NonNull ApiRequestInterceptor provideApiRequestInterceptor(final @NonNull String clientId,
final @NonNull CurrentUserType currentUser, final @NonNull ApiEndpoint endpoint) {
return new ApiRequestInterceptor(clientId, currentUser, endpoint.url());
}
@Provides
@Singleton
@NonNull
ApiService provideApiService(final @ApiRetrofit @NonNull Retrofit retrofit) {
return retrofit.create(ApiService.class);
}
@Provides
@Singleton
String provideClientId(final @NonNull ApiEndpoint apiEndpoint) {
return apiEndpoint == ApiEndpoint.PRODUCTION
? Secrets.Api.Client.PRODUCTION
: Secrets.Api.Client.STAGING;
}
@Provides
@Singleton
@NonNull KSRequestInterceptor provideKSRequestInterceptor(final @NonNull Build build) {
return new KSRequestInterceptor(build);
}
@Provides
@Singleton
@NonNull HttpLoggingInterceptor provideHttpLoggingInterceptor() {
final HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
return interceptor;
}
@Provides
@Singleton
@NonNull WebClientType provideWebClientType(final @NonNull WebService webService) {
return new WebClient(webService);
}
@Provides
@Singleton
@WebRetrofit
@NonNull Retrofit provideWebRetrofit(@NonNull @WebEndpoint final String webEndpoint,
final @NonNull Gson gson,
final @NonNull OkHttpClient okHttpClient) {
return createRetrofit(webEndpoint, gson, okHttpClient);
}
@Provides
@Singleton
@NonNull WebRequestInterceptor provideWebRequestInterceptor(final @NonNull CurrentUserType currentUser,
@NonNull @WebEndpoint final String endpoint, final @NonNull InternalToolsType internalTools, final @NonNull Build build, final @NonNull AndroidPayCapability androidPayCapability) {
return new WebRequestInterceptor(currentUser, endpoint, internalTools, build, androidPayCapability);
}
@Provides
@Singleton
@NonNull
WebService provideWebService(final @WebRetrofit @NonNull Retrofit retrofit) {
return retrofit.create(WebService.class);
}
private @NonNull Retrofit createRetrofit(final @NonNull String baseUrl, final @NonNull Gson gson, final @NonNull OkHttpClient okHttpClient) {
return new Retrofit.Builder()
.client(okHttpClient)
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
}
@Provides
@Singleton
@AccessTokenPreference
@NonNull StringPreferenceType provideAccessTokenPreference(final @NonNull SharedPreferences sharedPreferences) {
return new StringPreference(sharedPreferences, "access_token");
}
@Provides
@Singleton
@NonNull AndroidPayCapability provideAndroidPayCapability(final @NonNull PlayServicesCapability playServicesCapability,
final @ApplicationContext @NonNull Context context) {
return new AndroidPayCapability(playServicesCapability, context);
}
@Provides
@Singleton
@NonNull PlayServicesCapability providePlayServicesCapability(final @ApplicationContext @NonNull Context context) {
return new PlayServicesCapability(context);
}
@Provides
@Singleton
@ConfigPreference
@NonNull StringPreferenceType providesConfigPreference(final @NonNull SharedPreferences sharedPreferences) {
return new StringPreference(sharedPreferences, "config");
}
@Provides
@Singleton
@ActivitySamplePreference
@NonNull IntPreferenceType provideActivitySamplePreference(final @NonNull SharedPreferences sharedPreferences) {
return new IntPreference(sharedPreferences, "last_seen_activity_id");
}
@Provides
@Singleton
@AppRatingPreference
@NonNull BooleanPreferenceType provideAppRatingPreference(final @NonNull SharedPreferences sharedPreferences) {
return new BooleanPreference(sharedPreferences, "has_seen_app_rating");
}
@Provides
@Singleton
Application provideApplication() {
return application;
}
@Provides
@Singleton
Koala provideKoala(final @ApplicationContext @NonNull Context context, final @NonNull CurrentUserType currentUser,
final @NonNull AndroidPayCapability androidPayCapability) {
return new Koala(new KoalaTrackingClient(context, currentUser, androidPayCapability));
}
@Provides
@Singleton
Scheduler provideScheduler() {
return Schedulers.computation();
}
@Provides
@Singleton
@ApplicationContext
Context provideApplicationContext() {
return application;
}
@Provides
@Singleton
AssetManager provideAssetManager() {
return application.getAssets();
}
@Provides
@Singleton
@NonNull Build provideBuild(final @NonNull PackageInfo packageInfo) {
return new Build(packageInfo);
}
@Provides
@Singleton
CurrentConfigType provideCurrentConfig(final @NonNull AssetManager assetManager,
final @NonNull Gson gson,
final @ConfigPreference @NonNull StringPreferenceType configPreference) {
return new CurrentConfig(assetManager, gson, configPreference);
}
@Provides
@Singleton
CookieJar provideCookieJar(final @NonNull CookieManager cookieManager) {
return new JavaNetCookieJar(cookieManager);
}
@Provides
@Singleton
CookieManager provideCookieManager() {
return new CookieManager();
}
@Provides
@Singleton
CurrentUserType provideCurrentUser(@AccessTokenPreference final @NonNull StringPreferenceType accessTokenPreference,
final @NonNull DeviceRegistrarType deviceRegistrar,
final @NonNull Gson gson,
@NonNull @UserPreference final StringPreferenceType userPreference) {
return new CurrentUser(accessTokenPreference, deviceRegistrar, gson, userPreference);
}
@Provides
@Singleton
@NonNull DeviceRegistrarType provideDeviceRegistrar(final @NonNull PlayServicesCapability playServicesCapability,
final @ApplicationContext @NonNull Context context) {
return new DeviceRegistrar(playServicesCapability, context);
}
@Provides
@Singleton
@GamesNewsletterPreference
@NonNull BooleanPreferenceType provideGamesNewsletterPreference(final @NonNull SharedPreferences sharedPreferences) {
return new BooleanPreference(sharedPreferences, "has_seen_games_newsletter");
}
@Provides
@Singleton
@WebEndpoint
@NonNull String provideWebEndpoint(final @NonNull ApiEndpoint apiEndpoint) {
return (apiEndpoint == ApiEndpoint.PRODUCTION) ?
"https://www.kickstarter.com" :
apiEndpoint.url().replaceAll("(?<=\\Ahttps?:\\/\\/)api.", "");
}
@Provides
@Singleton
Font provideFont(final @NonNull AssetManager assetManager) {
return new Font(assetManager);
}
@Provides
@Singleton
Gson provideGson() {
return new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.registerTypeAdapter(DateTime.class, new DateTimeTypeConverter())
.registerTypeAdapterFactory(new AutoParcelAdapterFactory())
.create();
}
@Provides
@Singleton
KSCurrency provideKSCurrency(final @NonNull CurrentConfigType currentConfig) {
return new KSCurrency(currentConfig);
}
@Provides
@Singleton
@NonNull KSString provideKSString(final @PackageNameString @NonNull String packageName, final @NonNull Resources resources) {
return new KSString(packageName, resources);
}
@Provides
KSWebViewClient provideKSWebViewClient(final @NonNull OkHttpClient okHttpClient,
@WebEndpoint final String webEndpoint) {
return new KSWebViewClient(okHttpClient, webEndpoint);
}
@Provides
@Singleton
Logout provideLogout(final @NonNull CookieManager cookieManager, final @NonNull CurrentUserType currentUser) {
return new Logout(cookieManager, currentUser);
}
@Provides
@Singleton
@NonNull PushNotifications providePushNotifications(final @ApplicationContext @NonNull Context context, final @NonNull ApiClientType client,
final @NonNull DeviceRegistrarType deviceRegistrar) {
return new PushNotifications(context, client, deviceRegistrar);
}
@Provides
@Singleton
PackageInfo providePackageInfo(final @NonNull Application application) {
try {
return application.getPackageManager().getPackageInfo(application.getPackageName(), 0);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
throw new RuntimeException();
}
}
@Provides
@Singleton
@PackageNameString
String providePackageName(final @NonNull Application application) {
return application.getPackageName();
}
@Provides
@Singleton
Resources provideResources(final @ApplicationContext @NonNull Context context) {
return context.getResources();
}
@Provides
@Singleton
SharedPreferences provideSharedPreferences() {
return PreferenceManager.getDefaultSharedPreferences(application);
}
@Provides
@Singleton
@UserPreference
@NonNull StringPreferenceType provideUserPreference(final @NonNull SharedPreferences sharedPreferences) {
return new StringPreference(sharedPreferences, "user");
}
}