package yuku.alkitab.base;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.multidex.MultiDex;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.preference.PreferenceManager;
import android.util.Log;
import android.view.ViewConfiguration;
import com.google.android.gms.analytics.GoogleAnalytics;
import com.google.android.gms.analytics.HitBuilders;
import com.google.android.gms.analytics.Tracker;
import com.google.gson.Gson;
import com.jakewharton.picasso.OkHttp3Downloader;
import com.squareup.leakcanary.LeakCanary;
import com.squareup.picasso.Picasso;
import okhttp3.Cache;
import okhttp3.Call;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.internal.Version;
import yuku.afw.storage.Preferences;
import yuku.alkitab.base.model.MVersionInternal;
import yuku.alkitab.base.model.SyncShadow;
import yuku.alkitab.base.model.VersionImpl;
import yuku.alkitab.base.storage.Prefkey;
import yuku.alkitab.base.sync.Gcm;
import yuku.alkitab.base.sync.Sync;
import yuku.alkitab.debug.BuildConfig;
import yuku.alkitab.debug.R;
import yuku.alkitab.reminder.util.DevotionReminder;
import yuku.alkitabfeedback.FeedbackSender;
import yuku.alkitabintegration.display.Launcher;
import yuku.kirimfidbek.CrashReporter;
import yuku.stethoshim.StethoShim;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class App extends yuku.afw.App {
public static final String TAG = App.class.getSimpleName();
private static boolean initted = false;
private static Tracker APP_TRACKER;
static final Interceptor userAgent = chain -> {
final Request originalRequest = chain.request();
final Request requestWithUserAgent = originalRequest.newBuilder()
.removeHeader("User-Agent")
.addHeader("User-Agent", Version.userAgent() + " " + App.context.getPackageName() + "/" + App.getVersionName())
.build();
return chain.proceed(requestWithUserAgent);
};
enum OkHttpClientWrapper {
INSTANCE;
final OkHttpClient longTimeoutClient;
{
final OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder
.addNetworkInterceptor(userAgent)
.connectTimeout(300, TimeUnit.SECONDS)
.readTimeout(300, TimeUnit.SECONDS)
.writeTimeout(600, TimeUnit.SECONDS);
StethoShim.addNetworkInterceptor(builder);
longTimeoutClient = builder.build();
}
}
enum GsonWrapper {
INSTANCE;
Gson gson = new Gson();
}
public static String downloadString(String url) throws IOException {
return downloadCall(url).execute().body().string();
}
public static byte[] downloadBytes(String url) throws IOException {
return downloadCall(url).execute().body().bytes();
}
public static Call downloadCall(String url) {
return okhttp().newCall(new Request.Builder().url(url).build());
}
public static OkHttpClient getLongTimeoutOkHttpClient() {
return OkHttpClientWrapper.INSTANCE.longTimeoutClient;
}
@Override
public void onCreate() {
super.onCreate();
{ // LeakCanary, also we need the Application instance.
if (LeakCanary.isInAnalyzerProcess(this)) {
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
LeakCanary.install(this);
}
staticInit();
{ // Google Analytics V4
// This can't be in staticInit because we need the Application instance.
final GoogleAnalytics analytics = GoogleAnalytics.getInstance(context);
final Tracker t = analytics.newTracker(context.getString(R.string.ga_trackingId));
t.enableAutoActivityTracking(true);
t.enableAdvertisingIdCollection(true);
APP_TRACKER = t;
analytics.enableAutoActivityReports(this);
}
{ // Stetho call through proxy
StethoShim.initializeWithDefaults(this);
}
}
public synchronized static void staticInit() {
if (initted) return;
initted = true;
final CrashReporter cr = new CrashReporter();
if (BuildConfig.DEBUG) {
Log.d(TAG, "Not activating crash reporter because we are in debug build.");
} else {
cr.activateDefaultUncaughtExceptionHandler();
}
cr.trySend();
final FeedbackSender fs = FeedbackSender.getInstance(context);
fs.trySend();
for (final int preferenceResId : new int[]{
R.xml.settings_display,
R.xml.settings_usage,
R.xml.settings_copy_share,
R.xml.secret_settings,
R.xml.sync_settings,
}) {
PreferenceManager.setDefaultValues(context, preferenceResId, false);
}
// all activities need at least the activeVersion from S, so initialize it here.
synchronized (S.class) {
if (S.activeVersion == null) {
S.activeVersion = VersionImpl.getInternalVersion();
S.activeVersionId = MVersionInternal.getVersionInternalId();
}
}
// also pre-calculate calculated preferences value here
S.calculateAppliedValuesBasedOnPreferences();
{ // GCM
Gcm.renewGcmRegistrationIdIfNeeded(Sync::notifyNewGcmRegistrationId);
}
DevotionReminder.scheduleAlarm();
forceOverflowMenu();
// make sure launcher do not open other variants of the app
Launcher.setAppPackageName(context.getPackageName());
// sync on app start, if we are logged in
if (Preferences.contains(Prefkey.sync_simpleToken)) {
Sync.notifySyncNeeded(SyncShadow.ALL_SYNC_SET_NAMES);
}
if (BuildConfig.DEBUG) {
Log.d(TAG, "Font scale: " + context.getResources().getConfiguration().fontScale);
}
}
private static void forceOverflowMenu() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
return; // no need to do anything, it is already forced on KitKat
}
final ViewConfiguration config = ViewConfiguration.get(context);
try {
final Field sHasPermanentMenuKey = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey");
sHasPermanentMenuKey.setAccessible(true);
sHasPermanentMenuKey.setBoolean(config, false);
} catch (Exception e) {
Log.w(TAG, "ViewConfiguration has no sHasPermanentMenuKey field", e);
}
try {
final Field sHasPermanentMenuKeySet = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKeySet");
sHasPermanentMenuKeySet.setAccessible(true);
sHasPermanentMenuKeySet.setBoolean(config, true);
} catch (Exception e) {
Log.w(TAG, "ViewConfiguration has no sHasPermanentMenuKeySet field", e);
}
}
public static LocalBroadcastManager getLbm() {
return LocalBroadcastManager.getInstance(context);
}
public static Gson getDefaultGson() {
return GsonWrapper.INSTANCE.gson;
}
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
public synchronized static Tracker getTracker() {
return APP_TRACKER;
}
private static ExecutorService eventSubmitter = Executors.newSingleThreadExecutor();
public static void trackEvent(final String category) {
trackEvent(category, category);
}
public static void trackEvent(final String category, final String action) {
final Tracker tracker = getTracker();
if (tracker != null) { // guard against wrong initialization order
eventSubmitter.submit(() -> tracker.send(new HitBuilders.EventBuilder(category, action).build()));
}
}
private static OkHttpClient okhttp;
@NonNull
public static synchronized OkHttpClient okhttp() {
OkHttpClient res = okhttp;
if (res == null) {
final File cacheDir = new File(context.getCacheDir(), "okhttp-cache");
if (!cacheDir.exists()) {
//noinspection ResultOfMethodCallIgnored
cacheDir.mkdirs();
}
final OkHttpClient.Builder builder = new OkHttpClient.Builder()
.cache(new Cache(cacheDir, 50 * 1024 * 1024))
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.addNetworkInterceptor(userAgent);
if (BuildConfig.DEBUG) {
builder.hostnameVerifier((hostname, session) -> true);
}
StethoShim.addNetworkInterceptor(builder);
okhttp = res = builder.build();
}
return res;
}
static Picasso picasso;
@NonNull
public static synchronized Picasso picasso() {
Picasso res;
if (picasso == null) {
picasso = res = new Picasso.Builder(context)
.defaultBitmapConfig(Bitmap.Config.RGB_565)
.downloader(new OkHttp3Downloader(okhttp()))
.build();
return res;
}
return picasso;
}
}