package me.devsaki.hentoid.util;
import android.Manifest;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Build;
import android.os.StrictMode;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.IntentCompat;
import android.text.Html;
import android.text.Spanned;
import android.view.View;
import android.webkit.WebResourceResponse;
import android.widget.AbsListView;
import android.widget.EdgeEffect;
import android.widget.Toast;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.Locale;
import me.devsaki.hentoid.HentoidApp;
import me.devsaki.hentoid.R;
import me.devsaki.hentoid.activities.AppLockActivity;
import me.devsaki.hentoid.activities.DownloadsActivity;
import me.devsaki.hentoid.activities.IntroActivity;
import me.devsaki.hentoid.database.domains.Content;
import me.devsaki.hentoid.enums.Site;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
/**
* Created by avluis on 06/05/2016.
* Utility class
* <p/>
* TODO: Add additional image viewers.
*/
public final class Helper {
private static final String TAG = LogHelper.makeLogTag(Helper.class);
private static Toast toast;
/**
* @param apiLevel minimum API level version that has to support the device
* @return true when the caller API version is at least apiLevel
*/
public static boolean isAtLeastAPI(int apiLevel) {
return Build.VERSION.SDK_INT >= apiLevel;
}
public static void viewContent(final Context context, Content content) {
Intent intent = new Intent(context, content.getWebActivityClass());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Consts.INTENT_URL, content.getGalleryUrl());
context.startActivity(intent);
}
public static String getSessionCookie() {
return HentoidApp.getSharedPrefs().getString(Consts.WEB_SESSION_COOKIE, "");
}
public static void setSessionCookie(String sessionCookie) {
HentoidApp.getSharedPrefs()
.edit()
.putString(Consts.WEB_SESSION_COOKIE, sessionCookie)
.apply();
}
private static void clearSharedPreferences() {
SharedPreferences.Editor editor = HentoidApp.getSharedPrefs().edit();
editor.clear();
editor.apply();
}
private static void saveSharedPrefsKey(Context cxt) {
SharedPreferences sp = cxt.getSharedPreferences(
ConstsPrefs.PREFS_VERSION_KEY, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putInt(ConstsPrefs.PREFS_VERSION_KEY, ConstsPrefs.PREFS_VERSION);
editor.apply();
}
public static void queryPrefsKey(Context cxt) {
final int prefsVersion = cxt.getSharedPreferences(
ConstsPrefs.PREFS_VERSION_KEY, Context.MODE_PRIVATE).getInt(
ConstsPrefs.PREFS_VERSION_KEY, 0);
LogHelper.d(TAG, "Current Prefs Key value: " + prefsVersion);
// Use this whenever any incompatible changes are made to Prefs.
if (prefsVersion != ConstsPrefs.PREFS_VERSION) {
LogHelper.d(TAG, "Shared Prefs Key Mismatch! Clearing Prefs!");
// Clear All
clearSharedPreferences();
// Save current Pref version key
saveSharedPrefsKey(cxt.getApplicationContext());
} else {
LogHelper.d(TAG, "Prefs Key Match. Carry on.");
}
}
public static boolean getWebViewOverviewPrefs() {
return HentoidApp.getSharedPrefs().getBoolean(
ConstsPrefs.PREF_WEBVIEW_OVERRIDE_OVERVIEW_LISTS,
ConstsPrefs.PREF_WEBVIEW_OVERRIDE_OVERVIEW_DEFAULT);
}
public static int getWebViewInitialZoomPrefs() {
return Integer.parseInt(
HentoidApp.getSharedPrefs().getString(
ConstsPrefs.PREF_WEBVIEW_INITIAL_ZOOM_LISTS,
ConstsPrefs.PREF_WEBVIEW_INITIAL_ZOOM_DEFAULT + ""));
}
public static boolean getMobileUpdatePrefs() {
return HentoidApp.getSharedPrefs().getBoolean(
ConstsPrefs.PREF_CHECK_UPDATES_LISTS,
ConstsPrefs.PREF_CHECK_UPDATES_DEFAULT);
}
public static void cancelToast() {
if (toast != null) {
toast.cancel();
toast = null;
}
}
// For use whenever Toast messages could stack (e.g., repeated calls to Toast.makeText())
public static void toast(String text) {
Context cxt = HentoidApp.getAppContext();
if (cxt != null) {
toast(cxt, text);
}
}
public static void toast(int resource) {
Context cxt = HentoidApp.getAppContext();
if (cxt != null) {
toast(cxt, cxt.getResources().getString(resource));
}
}
public static void toast(Context cxt, String text) {
toast(cxt, text, DURATION.SHORT);
}
public static void toast(Context cxt, int resource) {
toast(cxt, resource, DURATION.SHORT);
}
public static void toast(Context cxt, String text, DURATION duration) {
toast(cxt, text, -1, duration);
}
private static void toast(Context cxt, int resource, DURATION duration) {
toast(cxt, null, resource, duration);
}
private static void toast(@NonNull Context cxt, @Nullable String text, int res,
DURATION duration) {
String message = null;
if (text != null) {
message = text;
} else if (res != -1) {
message = cxt.getString(res);
} else {
Throwable noResource = new Throwable("You must provide a String or Resource ID!");
try {
throw noResource;
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
int time;
switch (duration) {
case LONG:
time = Toast.LENGTH_LONG;
break;
case SHORT:
default:
time = Toast.LENGTH_SHORT;
break;
}
try {
toast.getView().isShown();
toast.setText(message);
} catch (Exception e) {
LogHelper.d(TAG, "toast is null, creating one instead;");
toast = Toast.makeText(cxt, message, time);
}
toast.show();
}
@SafeVarargs
public static <T> void executeAsyncTask(AsyncTask<T, ?, ?> task, T... params) {
task.execute(params);
}
public static void ignoreSslErrors() {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
public static void launchMainActivity(Context cxt) {
final String appLock = HentoidApp.getSharedPrefs().getString(ConstsPrefs.PREF_APP_LOCK, "");
if (appLock.isEmpty()) {
Intent intent = new Intent(cxt, DownloadsActivity.class);
cxt.startActivity(intent);
} else {
Intent intent = new Intent(cxt, AppLockActivity.class);
cxt.startActivity(intent);
}
}
public static boolean isFirstRun() {
return HentoidApp.getSharedPrefs().getBoolean(
ConstsPrefs.PREF_FIRST_RUN, ConstsPrefs.PREF_FIRST_RUN_DEFAULT);
}
public static void commitFirstRun(boolean commit) {
SharedPreferences.Editor editor = HentoidApp.getSharedPrefs().edit();
editor.putBoolean(ConstsPrefs.PREF_FIRST_RUN, commit);
editor.apply();
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public static boolean permissionsCheck(Activity activity, int permissionRequestCode,
boolean request) {
if (ActivityCompat.checkSelfPermission(activity,
Manifest.permission.READ_EXTERNAL_STORAGE) == PERMISSION_GRANTED) {
return true;
} else {
if (request) {
ActivityCompat.requestPermissions(activity, new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE}, permissionRequestCode);
}
return false;
}
}
// We have asked for permissions, but still denied.
public static void reset(Context cxt, Activity activity) {
Helper.toast(R.string.reset);
Helper.commitFirstRun(true);
Intent intent = new Intent(activity, IntroActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
cxt.startActivity(intent);
activity.finish();
}
public static void doRestart(@NonNull Context cxt) {
try {
PackageManager pm = cxt.getPackageManager();
if (pm != null) {
Intent intent = pm.getLaunchIntentForPackage(cxt.getPackageName());
if (intent != null) {
ComponentName componentName = intent.getComponent();
Intent mainIntent = IntentCompat.makeRestartActivityTask(componentName);
mainIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
cxt.startActivity(mainIntent);
Runtime.getRuntime().exit(0);
} else {
LogHelper.d(TAG, "Was not able to restart application, intent null");
}
} else {
LogHelper.d(TAG, "Was not able to restart application, PM null");
}
} catch (Exception e) {
LogHelper.e(TAG, e, "Was not able to restart application");
}
}
// Sets navigation bar background color
@SuppressLint("NewApi")
public static void setNavBarColor(Activity activity, int color) {
if (Helper.isAtLeastAPI(Build.VERSION_CODES.LOLLIPOP)) {
Context context = activity.getApplicationContext();
int navColor = ContextCompat.getColor(context, color);
activity.getWindow().setNavigationBarColor(navColor);
}
}
// Mainly for use with Android < 5.0 - sets OverScroll Glow and Edge Line
@SuppressLint("NewApi")
public static void changeEdgeEffect(Context cxt, View list, int glowColor, int lineColor) {
if (Helper.isAtLeastAPI(Build.VERSION_CODES.LOLLIPOP)) {
EdgeEffect edgeEffectTop = new EdgeEffect(cxt);
edgeEffectTop.setColor(glowColor);
EdgeEffect edgeEffectBottom = new EdgeEffect(cxt);
edgeEffectBottom.setColor(glowColor);
try {
Field f1 = AbsListView.class.getDeclaredField("mEdgeGlowTop");
f1.setAccessible(true);
f1.set(list, edgeEffectTop);
Field f2 = AbsListView.class.getDeclaredField("mEdgeGlowBottom");
f2.setAccessible(true);
f2.set(list, edgeEffectBottom);
} catch (Exception e) {
e.printStackTrace();
}
} else {
// Android < 5.0 - OverScroll Glow
int glowDrawableId = cxt.getResources().getIdentifier("overscroll_glow", "drawable",
"android");
Drawable androidGlow = ContextCompat.getDrawable(cxt, glowDrawableId);
androidGlow.setColorFilter(ContextCompat.getColor(cxt, glowColor),
PorterDuff.Mode.SRC_ATOP);
// Android < 5.0 - OverScroll Edge Line
final int edgeDrawableId = cxt.getResources().getIdentifier("overscroll_edge",
"drawable", "android");
final Drawable overScrollEdge = ContextCompat.getDrawable(cxt, edgeDrawableId);
overScrollEdge.setColorFilter(ContextCompat.getColor(cxt, lineColor),
PorterDuff.Mode.SRC_ATOP);
}
}
/**
* Get a color value from a theme attribute.
*
* @param context used for getting the color.
* @param attribute theme attribute.
* @param defaultColor default to use.
* @return color value
*/
public static int getThemeColor(Context context, int attribute, int defaultColor) {
int themeColor = 0;
String packageName = context.getPackageName();
try {
Context packageContext = context.createPackageContext(packageName, 0);
ApplicationInfo applicationInfo =
context.getPackageManager().getApplicationInfo(packageName, 0);
packageContext.setTheme(applicationInfo.theme);
Resources.Theme theme = packageContext.getTheme();
TypedArray ta = theme.obtainStyledAttributes(new int[]{attribute});
themeColor = ta.getColor(0, defaultColor);
ta.recycle();
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return themeColor;
}
public static String getActivityName(Context context, int attribute) {
String activityName = context.getString(attribute);
if (!activityName.isEmpty()) {
return activityName;
} else {
activityName = context.getString(R.string.app_name);
}
return activityName;
}
public static int getId(String resourceName, Class<?> c) {
try {
Field idField = c.getDeclaredField(resourceName);
return idField.getInt(idField);
} catch (Exception e) {
try {
throw new ResourceException("No resource ID found for: " + resourceName +
" / " + c, e);
} catch (ResourceException rEx) {
LogHelper.w(TAG, rEx);
}
}
return R.drawable.ic_menu_unknown;
}
public static int getAppVersionCode(@NonNull Context cxt) throws NameNotFoundException {
return cxt.getPackageManager().getPackageInfo(cxt.getPackageName(), 0).versionCode;
}
public static String getAppVersionInfo(@NonNull Context cxt) throws NameNotFoundException {
return cxt.getPackageManager().getPackageInfo(cxt.getPackageName(), 0).versionName;
}
public static String getAppUserAgent(@NonNull Context cxt) throws NameNotFoundException {
return Consts.USER_AGENT + " Hentoid/v" +
cxt.getPackageManager().getPackageInfo(cxt.getPackageName(), 0).versionName;
}
public static WebResourceResponse getWebResourceResponseFromAsset(Site site, String filename,
TYPE type) {
Context cxt = HentoidApp.getAppContext();
String pathPrefix = site.getDescription().toLowerCase(Locale.US) + "/";
String file = pathPrefix + filename;
try {
File asset = new File(cxt.getExternalCacheDir() + "/" + file);
FileInputStream stream = new FileInputStream(asset);
return Helper.getUtf8EncodedWebResourceResponse(stream, type);
} catch (IOException e) {
return null;
}
}
private static WebResourceResponse getUtf8EncodedWebResourceResponse(InputStream open,
TYPE type) {
switch (type) {
case JS:
return new WebResourceResponse("text/js", "UTF-8", open);
case CSS:
return new WebResourceResponse("text/css", "UTF-8", open);
case HTML:
return new WebResourceResponse("text/html", "UTF-8", open);
case PLAIN:
default:
return new WebResourceResponse("text/plain", "UTF-8", open);
}
}
@SuppressLint("NewApi")
public static Spanned fromHtml(String source) {
if (isAtLeastAPI(Build.VERSION_CODES.N)) {
return Html.fromHtml(source, Html.FROM_HTML_MODE_LEGACY, null, null);
} else {
//noinspection deprecation
return Html.fromHtml(source);
}
}
public enum DURATION {SHORT, LONG}
public enum TYPE {JS, CSS, HTML, PLAIN}
/**
* Created by avluis on 06/12/2016.
* Resource ID Exception
*/
private static class ResourceException extends Exception {
private String result;
private Exception code;
ResourceException(String result, Exception code) {
this.result = result;
this.code = code;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public Exception getCode() {
return code;
}
public void setCode(Exception code) {
this.code = code;
}
}
}