Ammar Githam
4 years ago
19 changed files with 644 additions and 975 deletions
-
3app/src/main/java/awais/instagrabber/InstaGrabberApplication.java
-
2app/src/main/java/awais/instagrabber/customviews/FormattedNumberTextView.java
-
5app/src/main/java/awais/instagrabber/customviews/Tooltip.java
-
214app/src/main/java/awais/instagrabber/utils/CombinedDrawable.kt
-
175app/src/main/java/awais/instagrabber/utils/CookieUtils.kt
-
33app/src/main/java/awais/instagrabber/utils/CubicInterpolation.java
-
43app/src/main/java/awais/instagrabber/utils/DataBox.java
-
72app/src/main/java/awais/instagrabber/utils/DeepLinkParser.kt
-
150app/src/main/java/awais/instagrabber/utils/LocaleUtils.kt
-
3app/src/main/java/awais/instagrabber/utils/MediaUploadHelper.kt
-
61app/src/main/java/awais/instagrabber/utils/NullSafePair.kt
-
182app/src/main/java/awais/instagrabber/utils/NumberUtils.kt
-
92app/src/main/java/awais/instagrabber/utils/RankedRecipientsCache.kt
-
13app/src/main/java/awais/instagrabber/utils/SerializablePair.kt
-
66app/src/main/java/awais/instagrabber/utils/UpdateCheckCommon.kt
-
39app/src/main/java/awais/instagrabber/utils/UserAgentUtils.kt
-
162app/src/main/java/awais/instagrabber/utils/ViewUtils.kt
-
12app/src/main/java/awais/instagrabber/utils/extensions/AnyExtensions.kt
-
4app/src/main/java/awais/instagrabber/viewmodels/UserSearchViewModel.java
@ -1,185 +1,157 @@ |
|||
package awais.instagrabber.utils;/* |
|||
/* |
|||
* This is the source code of Telegram for Android v. 5.x.x. |
|||
* It is licensed under GNU GPL v. 2 or later. |
|||
* You should have received a copy of the license in this archive (see LICENSE). |
|||
* |
|||
* <p> |
|||
* Copyright Nikolai Kudashov, 2013-2018. |
|||
*/ |
|||
|
|||
import android.graphics.Canvas; |
|||
import android.graphics.ColorFilter; |
|||
import android.graphics.drawable.Drawable; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
|
|||
public class CombinedDrawable extends Drawable implements Drawable.Callback { |
|||
|
|||
private final Drawable background; |
|||
private final Drawable icon; |
|||
private int left; |
|||
private int top; |
|||
private int iconWidth; |
|||
private int iconHeight; |
|||
private int backWidth; |
|||
private int backHeight; |
|||
private int offsetX; |
|||
private int offsetY; |
|||
private boolean fullSize; |
|||
|
|||
public CombinedDrawable(Drawable backgroundDrawable, Drawable iconDrawable, int leftOffset, int topOffset) { |
|||
background = backgroundDrawable; |
|||
icon = iconDrawable; |
|||
left = leftOffset; |
|||
top = topOffset; |
|||
package awais.instagrabber.utils |
|||
|
|||
import android.graphics.Canvas |
|||
import android.graphics.ColorFilter |
|||
import android.graphics.PixelFormat |
|||
import android.graphics.drawable.Drawable |
|||
|
|||
class CombinedDrawable : Drawable, Drawable.Callback { |
|||
val background: Drawable |
|||
val icon: Drawable? |
|||
private var left = 0 |
|||
private var top = 0 |
|||
private var iconWidth = 0 |
|||
private var iconHeight = 0 |
|||
private var backWidth = 0 |
|||
private var backHeight = 0 |
|||
private var offsetX = 0 |
|||
private var offsetY = 0 |
|||
private var fullSize = false |
|||
|
|||
constructor(backgroundDrawable: Drawable, iconDrawable: Drawable?, leftOffset: Int, topOffset: Int) { |
|||
background = backgroundDrawable |
|||
icon = iconDrawable |
|||
left = leftOffset |
|||
top = topOffset |
|||
if (iconDrawable != null) { |
|||
iconDrawable.setCallback(this); |
|||
iconDrawable.callback = this |
|||
} |
|||
} |
|||
|
|||
public CombinedDrawable(Drawable backgroundDrawable, Drawable iconDrawable) { |
|||
background = backgroundDrawable; |
|||
icon = iconDrawable; |
|||
constructor(backgroundDrawable: Drawable, iconDrawable: Drawable?) { |
|||
background = backgroundDrawable |
|||
icon = iconDrawable |
|||
if (iconDrawable != null) { |
|||
iconDrawable.setCallback(this); |
|||
} |
|||
} |
|||
|
|||
public void setIconSize(int width, int height) { |
|||
iconWidth = width; |
|||
iconHeight = height; |
|||
iconDrawable.callback = this |
|||
} |
|||
|
|||
public void setCustomSize(int width, int height) { |
|||
backWidth = width; |
|||
backHeight = height; |
|||
} |
|||
|
|||
public void setIconOffset(int x, int y) { |
|||
offsetX = x; |
|||
offsetY = y; |
|||
fun setIconSize(width: Int, height: Int) { |
|||
iconWidth = width |
|||
iconHeight = height |
|||
} |
|||
|
|||
public Drawable getIcon() { |
|||
return icon; |
|||
fun setCustomSize(width: Int, height: Int) { |
|||
backWidth = width |
|||
backHeight = height |
|||
} |
|||
|
|||
public Drawable getBackground() { |
|||
return background; |
|||
fun setIconOffset(x: Int, y: Int) { |
|||
offsetX = x |
|||
offsetY = y |
|||
} |
|||
|
|||
public void setFullsize(boolean value) { |
|||
fullSize = value; |
|||
fun setFullsize(value: Boolean) { |
|||
fullSize = value |
|||
} |
|||
|
|||
@Override |
|||
public void setColorFilter(ColorFilter colorFilter) { |
|||
icon.setColorFilter(colorFilter); |
|||
override fun setColorFilter(colorFilter: ColorFilter?) { |
|||
icon?.colorFilter = colorFilter |
|||
} |
|||
|
|||
@Override |
|||
public boolean isStateful() { |
|||
return icon.isStateful(); |
|||
override fun isStateful(): Boolean { |
|||
return icon?.isStateful ?: false |
|||
} |
|||
|
|||
@Override |
|||
public boolean setState(@NonNull int[] stateSet) { |
|||
icon.setState(stateSet); |
|||
return true; |
|||
override fun setState(stateSet: IntArray): Boolean { |
|||
icon?.state = stateSet |
|||
return true |
|||
} |
|||
|
|||
@NonNull |
|||
@Override |
|||
public int[] getState() { |
|||
return icon.getState(); |
|||
override fun getState(): IntArray { |
|||
return icon?.state ?: super.getState() |
|||
} |
|||
|
|||
@Override |
|||
protected boolean onStateChange(int[] state) { |
|||
return true; |
|||
override fun onStateChange(state: IntArray): Boolean { |
|||
return true |
|||
} |
|||
|
|||
@Override |
|||
public void jumpToCurrentState() { |
|||
icon.jumpToCurrentState(); |
|||
override fun jumpToCurrentState() { |
|||
icon?.jumpToCurrentState() |
|||
} |
|||
|
|||
@Override |
|||
public ConstantState getConstantState() { |
|||
return icon.getConstantState(); |
|||
override fun getConstantState(): ConstantState? { |
|||
return icon?.constantState |
|||
} |
|||
|
|||
@Override |
|||
public void draw(@NonNull Canvas canvas) { |
|||
background.setBounds(getBounds()); |
|||
background.draw(canvas); |
|||
if (icon != null) { |
|||
override fun draw(canvas: Canvas) { |
|||
background.bounds = bounds |
|||
background.draw(canvas) |
|||
if (icon == null) return |
|||
if (fullSize) { |
|||
android.graphics.Rect bounds = getBounds(); |
|||
val bounds = bounds |
|||
if (left != 0) { |
|||
icon.setBounds(bounds.left + left, bounds.top + top, bounds.right - left, bounds.bottom - top); |
|||
icon.setBounds(bounds.left + left, bounds.top + top, bounds.right - left, bounds.bottom - top) |
|||
} else { |
|||
icon.setBounds(bounds); |
|||
icon.bounds = bounds |
|||
} |
|||
} else { |
|||
int x; |
|||
int y; |
|||
val x: Int |
|||
val y: Int |
|||
if (iconWidth != 0) { |
|||
x = getBounds().centerX() - iconWidth / 2 + left + offsetX; |
|||
y = getBounds().centerY() - iconHeight / 2 + top + offsetY; |
|||
icon.setBounds(x, y, x + iconWidth, y + iconHeight); |
|||
x = bounds.centerX() - iconWidth / 2 + left + offsetX |
|||
y = bounds.centerY() - iconHeight / 2 + top + offsetY |
|||
icon.setBounds(x, y, x + iconWidth, y + iconHeight) |
|||
} else { |
|||
x = getBounds().centerX() - icon.getIntrinsicWidth() / 2 + left; |
|||
y = getBounds().centerY() - icon.getIntrinsicHeight() / 2 + top; |
|||
icon.setBounds(x, y, x + icon.getIntrinsicWidth(), y + icon.getIntrinsicHeight()); |
|||
} |
|||
x = bounds.centerX() - icon.intrinsicWidth / 2 + left |
|||
y = bounds.centerY() - icon.intrinsicHeight / 2 + top |
|||
icon.setBounds(x, y, x + icon.intrinsicWidth, y + icon.intrinsicHeight) |
|||
} |
|||
icon.draw(canvas); |
|||
} |
|||
icon.draw(canvas) |
|||
} |
|||
|
|||
@Override |
|||
public void setAlpha(int alpha) { |
|||
icon.setAlpha(alpha); |
|||
background.setAlpha(alpha); |
|||
override fun setAlpha(alpha: Int) { |
|||
icon?.alpha = alpha |
|||
background.alpha = alpha |
|||
} |
|||
|
|||
@Override |
|||
public int getIntrinsicWidth() { |
|||
return backWidth != 0 ? backWidth : background.getIntrinsicWidth(); |
|||
override fun getIntrinsicWidth(): Int { |
|||
return if (backWidth != 0) backWidth else background.intrinsicWidth |
|||
} |
|||
|
|||
@Override |
|||
public int getIntrinsicHeight() { |
|||
return backHeight != 0 ? backHeight : background.getIntrinsicHeight(); |
|||
override fun getIntrinsicHeight(): Int { |
|||
return if (backHeight != 0) backHeight else background.intrinsicHeight |
|||
} |
|||
|
|||
@Override |
|||
public int getMinimumWidth() { |
|||
return backWidth != 0 ? backWidth : background.getMinimumWidth(); |
|||
override fun getMinimumWidth(): Int { |
|||
return if (backWidth != 0) backWidth else background.minimumWidth |
|||
} |
|||
|
|||
@Override |
|||
public int getMinimumHeight() { |
|||
return backHeight != 0 ? backHeight : background.getMinimumHeight(); |
|||
override fun getMinimumHeight(): Int { |
|||
return if (backHeight != 0) backHeight else background.minimumHeight |
|||
} |
|||
|
|||
@Override |
|||
public int getOpacity() { |
|||
return icon.getOpacity(); |
|||
override fun getOpacity(): Int { |
|||
return icon?.opacity ?: PixelFormat.UNKNOWN |
|||
} |
|||
|
|||
@Override |
|||
public void invalidateDrawable(@NonNull Drawable who) { |
|||
invalidateSelf(); |
|||
override fun invalidateDrawable(who: Drawable) { |
|||
invalidateSelf() |
|||
} |
|||
|
|||
@Override |
|||
public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) { |
|||
scheduleSelf(what, when); |
|||
override fun scheduleDrawable(who: Drawable, what: Runnable, `when`: Long) { |
|||
scheduleSelf(what, `when`) |
|||
} |
|||
|
|||
@Override |
|||
public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) { |
|||
unscheduleSelf(what); |
|||
override fun unscheduleDrawable(who: Drawable, what: Runnable) { |
|||
unscheduleSelf(what) |
|||
} |
|||
} |
@ -1,43 +0,0 @@ |
|||
package awais.instagrabber.utils; |
|||
|
|||
import android.content.Context; |
|||
import android.database.sqlite.SQLiteDatabase; |
|||
import android.database.sqlite.SQLiteOpenHelper; |
|||
import android.util.Log; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.annotation.Nullable; |
|||
|
|||
public final class DataBox extends SQLiteOpenHelper { |
|||
private static final String TAG = "DataBox"; |
|||
|
|||
private static DataBox sInstance; |
|||
|
|||
private final static int VERSION = 3; |
|||
|
|||
public static synchronized DataBox getInstance(final Context context) { |
|||
if (sInstance == null) sInstance = new DataBox(context.getApplicationContext()); |
|||
return sInstance; |
|||
} |
|||
|
|||
private DataBox(@Nullable final Context context) { |
|||
super(context, "cookiebox.db", null, VERSION); |
|||
} |
|||
|
|||
@Override |
|||
public void onCreate(@NonNull final SQLiteDatabase db) { |
|||
Log.i(TAG, "Creating tables..."); |
|||
Log.i(TAG, "Tables created!"); |
|||
} |
|||
|
|||
@Override |
|||
public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) { |
|||
Log.i(TAG, String.format("Updating DB from v%d to v%d", oldVersion, newVersion)); |
|||
// switch without break, so that all migrations from a previous version to new are run |
|||
switch (oldVersion) { |
|||
case 1: |
|||
case 2: |
|||
} |
|||
Log.i(TAG, String.format("DB update from v%d to v%d completed!", oldVersion, newVersion)); |
|||
} |
|||
} |
@ -1,67 +1,29 @@ |
|||
package awais.instagrabber.utils; |
|||
package awais.instagrabber.utils |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.annotation.Nullable; |
|||
import java.util.regex.Pattern |
|||
|
|||
import com.google.common.collect.ImmutableMap; |
|||
object DeepLinkParser { |
|||
private val TYPE_PATTERN_MAP: Map<DeepLink.Type, DeepLinkPattern> = mapOf( |
|||
DeepLink.Type.USER to DeepLinkPattern("instagram://user?username="), |
|||
) |
|||
|
|||
import java.util.Map; |
|||
import java.util.regex.Pattern; |
|||
|
|||
public final class DeepLinkParser { |
|||
private static final Map<DeepLink.Type, DeepLinkPattern> TYPE_PATTERN_MAP = ImmutableMap |
|||
.<DeepLink.Type, DeepLinkPattern>builder() |
|||
.put(DeepLink.Type.USER, new DeepLinkPattern("instagram://user?username=")) |
|||
.build(); |
|||
|
|||
@Nullable |
|||
public static DeepLink parse(@NonNull final String text) { |
|||
for (final Map.Entry<DeepLink.Type, DeepLinkPattern> entry : TYPE_PATTERN_MAP.entrySet()) { |
|||
if (text.startsWith(entry.getValue().getPatternText())) { |
|||
final String value = entry.getValue().getPattern().matcher(text).replaceAll(""); |
|||
return new DeepLink(entry.getKey(), value); |
|||
} |
|||
} |
|||
return null; |
|||
@JvmStatic |
|||
fun parse(text: String): DeepLink? { |
|||
for ((key, value) in TYPE_PATTERN_MAP) { |
|||
if (text.startsWith(value.patternText)) { |
|||
return DeepLink(key, value.pattern.matcher(text).replaceAll("")) |
|||
} |
|||
|
|||
public static class DeepLinkPattern { |
|||
private final String patternText; |
|||
private final Pattern pattern; |
|||
|
|||
public DeepLinkPattern(final String patternText) { |
|||
this.patternText = patternText; |
|||
pattern = Pattern.compile(patternText, Pattern.LITERAL); |
|||
} |
|||
|
|||
public String getPatternText() { |
|||
return patternText; |
|||
} |
|||
|
|||
public Pattern getPattern() { |
|||
return pattern; |
|||
} |
|||
} |
|||
|
|||
public static class DeepLink { |
|||
private final Type type; |
|||
private final String value; |
|||
|
|||
public DeepLink(final Type type, final String value) { |
|||
this.type = type; |
|||
this.value = value; |
|||
} |
|||
|
|||
public Type getType() { |
|||
return type; |
|||
return null |
|||
} |
|||
|
|||
public String getValue() { |
|||
return value; |
|||
data class DeepLinkPattern(val patternText: String) { |
|||
val pattern: Pattern = Pattern.compile(patternText, Pattern.LITERAL) |
|||
} |
|||
|
|||
public enum Type { |
|||
USER, |
|||
data class DeepLink(val type: Type, val value: String) { |
|||
enum class Type { |
|||
USER |
|||
} |
|||
} |
|||
} |
@ -1,91 +1,83 @@ |
|||
package awais.instagrabber.utils; |
|||
package awais.instagrabber.utils |
|||
|
|||
import android.content.Context; |
|||
import android.content.res.Configuration; |
|||
import android.content.res.Resources; |
|||
import android.view.ContextThemeWrapper; |
|||
|
|||
import androidx.annotation.Nullable; |
|||
|
|||
import java.util.Locale; |
|||
|
|||
import awais.instagrabber.fragments.settings.PreferenceKeys; |
|||
import android.content.Context |
|||
import android.content.res.Configuration |
|||
import android.view.ContextThemeWrapper |
|||
import awais.instagrabber.fragments.settings.PreferenceKeys |
|||
import java.util.* |
|||
|
|||
// taken from my app TESV Console Codes |
|||
public final class LocaleUtils { |
|||
private static Locale defaultLocale, currentLocale; |
|||
|
|||
public static void setLocale(Context baseContext) { |
|||
if (defaultLocale == null) defaultLocale = Locale.getDefault(); |
|||
|
|||
if (baseContext instanceof ContextThemeWrapper) |
|||
baseContext = ((ContextThemeWrapper) baseContext).getBaseContext(); |
|||
|
|||
if (Utils.settingsHelper == null) |
|||
Utils.settingsHelper = new SettingsHelper(baseContext); |
|||
|
|||
final String appLanguageSettings = Utils.settingsHelper.getString(PreferenceKeys.APP_LANGUAGE); |
|||
final String lang = LocaleUtils.getCorrespondingLanguageCode(appLanguageSettings); |
|||
|
|||
currentLocale = TextUtils.isEmpty(lang) ? defaultLocale : |
|||
(lang.contains("_") ? new Locale(lang.split("_")[0], lang.split("_")[1]) : new Locale(lang)); |
|||
Locale.setDefault(currentLocale); |
|||
|
|||
final Resources res = baseContext.getResources(); |
|||
final Configuration config = res.getConfiguration(); |
|||
|
|||
config.locale = currentLocale; |
|||
config.setLocale(currentLocale); |
|||
config.setLayoutDirection(currentLocale); |
|||
|
|||
res.updateConfiguration(config, res.getDisplayMetrics()); |
|||
object LocaleUtils { |
|||
private var defaultLocale: Locale? = null |
|||
|
|||
@JvmStatic |
|||
var currentLocale: Locale? = null |
|||
private set |
|||
|
|||
@JvmStatic |
|||
fun setLocale(baseContext: Context) { |
|||
var baseContext1 = baseContext |
|||
if (defaultLocale == null) defaultLocale = Locale.getDefault() |
|||
if (baseContext1 is ContextThemeWrapper) baseContext1 = baseContext1.baseContext |
|||
if (Utils.settingsHelper == null) Utils.settingsHelper = SettingsHelper(baseContext1) |
|||
val appLanguageSettings = Utils.settingsHelper.getString(PreferenceKeys.APP_LANGUAGE) |
|||
val lang = getCorrespondingLanguageCode(appLanguageSettings) |
|||
currentLocale = when { |
|||
TextUtils.isEmpty(lang) -> defaultLocale |
|||
lang!!.contains("_") -> { |
|||
val split = lang.split("_") |
|||
Locale(split[0], split[1]) |
|||
} |
|||
|
|||
public static Locale getCurrentLocale() { |
|||
return currentLocale; |
|||
else -> Locale(lang) |
|||
} |
|||
|
|||
public static void updateConfig(final ContextThemeWrapper wrapper) { |
|||
if (currentLocale != null) { |
|||
final Configuration configuration = new Configuration(); |
|||
configuration.locale = currentLocale; |
|||
configuration.setLocale(currentLocale); |
|||
wrapper.applyOverrideConfiguration(configuration); |
|||
currentLocale?.let { |
|||
Locale.setDefault(it) |
|||
val res = baseContext1.resources |
|||
val config = res.configuration |
|||
// config.locale = currentLocale |
|||
config.setLocale(it) |
|||
config.setLayoutDirection(it) |
|||
res.updateConfiguration(config, res.displayMetrics) |
|||
} |
|||
} |
|||
|
|||
@Nullable |
|||
public static String getCorrespondingLanguageCode(final String appLanguageSettings) { |
|||
if (TextUtils.isEmpty(appLanguageSettings)) return null; |
|||
|
|||
final int appLanguageIndex = Integer.parseInt(appLanguageSettings); |
|||
switch (appLanguageIndex) { |
|||
case 1: return "en"; |
|||
case 2: return "fr"; |
|||
case 3: return "es"; |
|||
case 4: return "zh_CN"; |
|||
case 5: return "in"; |
|||
case 6: return "it"; |
|||
case 7: return "de"; |
|||
case 8: return "pl"; |
|||
case 9: return "tr"; |
|||
case 10: return "pt"; |
|||
case 11: return "fa"; |
|||
case 12: return "mk"; |
|||
case 13: return "vi"; |
|||
case 14: return "zh_TW"; |
|||
case 15: return "ca"; |
|||
case 16: return "ru"; |
|||
case 17: return "hi"; |
|||
case 18: return "nl"; |
|||
case 19: return "sk"; |
|||
case 20: return "ja"; |
|||
case 21: return "el"; |
|||
case 22: return "eu"; |
|||
case 23: return "sv"; |
|||
case 24: return "ko"; |
|||
@JvmStatic |
|||
fun updateConfig(wrapper: ContextThemeWrapper) { |
|||
if (currentLocale == null) return |
|||
val configuration = Configuration() |
|||
// configuration.locale = currentLocale |
|||
configuration.setLocale(currentLocale) |
|||
wrapper.applyOverrideConfiguration(configuration) |
|||
} |
|||
|
|||
return null; |
|||
fun getCorrespondingLanguageCode(appLanguageSettings: String): String? { |
|||
if (TextUtils.isEmpty(appLanguageSettings)) return null |
|||
when (appLanguageSettings.toInt()) { |
|||
1 -> return "en" |
|||
2 -> return "fr" |
|||
3 -> return "es" |
|||
4 -> return "zh_CN" |
|||
5 -> return "in" |
|||
6 -> return "it" |
|||
7 -> return "de" |
|||
8 -> return "pl" |
|||
9 -> return "tr" |
|||
10 -> return "pt" |
|||
11 -> return "fa" |
|||
12 -> return "mk" |
|||
13 -> return "vi" |
|||
14 -> return "zh_TW" |
|||
15 -> return "ca" |
|||
16 -> return "ru" |
|||
17 -> return "hi" |
|||
18 -> return "nl" |
|||
19 -> return "sk" |
|||
20 -> return "ja" |
|||
21 -> return "el" |
|||
22 -> return "eu" |
|||
23 -> return "sv" |
|||
24 -> return "ko" |
|||
} |
|||
return null |
|||
} |
|||
} |
@ -1,149 +1,87 @@ |
|||
package awais.instagrabber.utils; |
|||
@file:JvmName("NumberUtils") |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.annotation.Nullable; |
|||
package awais.instagrabber.utils |
|||
|
|||
import java.util.Locale; |
|||
import java.util.Random; |
|||
import java.util.* |
|||
import kotlin.math.ln |
|||
import kotlin.math.pow |
|||
|
|||
public final class NumberUtils { |
|||
// @NonNull |
|||
// public static String millisToString(final long timeMs) { |
|||
// final long totalSeconds = timeMs / 1000; |
|||
// |
|||
// final long seconds = totalSeconds % 60; |
|||
// final long minutes = totalSeconds / 60 % 60; |
|||
// final long hours = totalSeconds / 3600; |
|||
// |
|||
// final String strSec = Long.toString(seconds); |
|||
// final String strMin = Long.toString(minutes); |
|||
// |
|||
// final String strRetSec = strSec.length() > 1 ? strSec : "0" + seconds; |
|||
// final String strRetMin = strMin.length() > 1 ? strMin : "0" + minutes; |
|||
// |
|||
// final String retMinSec = strRetMin + ':' + strRetSec; |
|||
// |
|||
// if (hours > 0) |
|||
// return Long.toString(hours) + ':' + retMinSec; |
|||
// return retMinSec; |
|||
// } |
|||
|
|||
public static int getResultingHeight(final int requiredWidth, final int height, final int width) { |
|||
return requiredWidth * height / width; |
|||
fun getResultingHeight(requiredWidth: Int, height: Int, width: Int): Int { |
|||
return requiredWidth * height / width |
|||
} |
|||
|
|||
public static int getResultingWidth(final int requiredHeight, final int height, final int width) { |
|||
return requiredHeight * width / height; |
|||
fun getResultingWidth(requiredHeight: Int, height: Int, width: Int): Int { |
|||
return requiredHeight * width / height |
|||
} |
|||
|
|||
public static long random(long origin, long bound) { |
|||
final Random random = new Random(); |
|||
long r = random.nextLong(); |
|||
long n = bound - origin, m = n - 1; |
|||
if ((n & m) == 0L) // power of two |
|||
r = (r & m) + origin; |
|||
else if (n > 0L) { // reject over-represented candidates |
|||
//noinspection StatementWithEmptyBody |
|||
for (long u = r >>> 1; // ensure non-negative |
|||
u + m - (r = u % n) < 0L; // rejection check |
|||
u = random.nextLong() >>> 1) // retry |
|||
; |
|||
r += origin; |
|||
} else { // range not representable as long |
|||
while (r < origin || r >= bound) |
|||
r = random.nextLong(); |
|||
// TODO Replace all usages with kotlin Random.nextLong() once converted to kotlin |
|||
fun random(origin: Long, bound: Long): Long { |
|||
val random = Random() |
|||
var r = random.nextLong() |
|||
val n = bound - origin |
|||
val m = n - 1 |
|||
when { |
|||
n and m == 0L -> r = (r and m) + origin // power of two |
|||
n > 0L -> { |
|||
// reject over-represented candidates |
|||
var u = r ushr 1 // ensure non-negative |
|||
while (u + m - u % n.also { r = it } < 0L) { // rejection check |
|||
// retry |
|||
u = random.nextLong() ushr 1 |
|||
} |
|||
r += origin |
|||
} |
|||
else -> { |
|||
// range not representable as long |
|||
while (r < origin || r >= bound) r = random.nextLong() |
|||
} |
|||
} |
|||
return r; |
|||
return r |
|||
} |
|||
|
|||
@NonNull |
|||
public static NullSafePair<Integer, Integer> calculateWidthHeight(final int height, final int width, final int maxHeight, final int maxWidth) { |
|||
fun calculateWidthHeight(height: Int, width: Int, maxHeight: Int, maxWidth: Int): NullSafePair<Int, Int> { |
|||
if (width > maxWidth) { |
|||
int tempHeight = getResultingHeight(maxWidth, height, width); |
|||
int tempWidth = maxWidth; |
|||
var tempHeight = getResultingHeight(maxWidth, height, width) |
|||
var tempWidth = maxWidth |
|||
if (tempHeight > maxHeight) { |
|||
tempWidth = getResultingWidth(maxHeight, tempHeight, tempWidth); |
|||
tempHeight = maxHeight; |
|||
tempWidth = getResultingWidth(maxHeight, tempHeight, tempWidth) |
|||
tempHeight = maxHeight |
|||
} |
|||
return new NullSafePair<>(tempWidth, tempHeight); |
|||
return NullSafePair(tempWidth, tempHeight) |
|||
} |
|||
if ((height < maxHeight && width < maxWidth) || (height > maxHeight)) { |
|||
int tempWidth = getResultingWidth(maxHeight, height, width); |
|||
int tempHeight = maxHeight; |
|||
if (height < maxHeight && width < maxWidth || height > maxHeight) { |
|||
var tempWidth = getResultingWidth(maxHeight, height, width) |
|||
var tempHeight = maxHeight |
|||
if (tempWidth > maxWidth) { |
|||
tempHeight = getResultingHeight(maxWidth, tempHeight, tempWidth); |
|||
tempWidth = maxWidth; |
|||
tempHeight = getResultingHeight(maxWidth, tempHeight, tempWidth) |
|||
tempWidth = maxWidth |
|||
} |
|||
return new NullSafePair<>(tempWidth, tempHeight); |
|||
return NullSafePair(tempWidth, tempHeight) |
|||
} |
|||
return new NullSafePair<>(width, height); |
|||
return NullSafePair(width, height) |
|||
} |
|||
|
|||
public static float roundFloat2Decimals(final float value) { |
|||
return ((int) ((value + (value >= 0 ? 1 : -1) * 0.005f) * 100)) / 100f; |
|||
fun roundFloat2Decimals(value: Float): Float { |
|||
return ((value + (if (value >= 0) 1 else -1) * 0.005f) * 100).toInt() / 100f |
|||
} |
|||
|
|||
@NonNull |
|||
public static String abbreviate(final long number) { |
|||
return abbreviate(number, null); |
|||
} |
|||
|
|||
|
|||
@NonNull |
|||
public static String abbreviate(final long number, @Nullable final AbbreviateOptions options) { |
|||
fun abbreviate(number: Long, options: AbbreviateOptions? = null): String { |
|||
// adapted from https://stackoverflow.com/a/9769590/1436766 |
|||
int threshold = 1000; |
|||
boolean addSpace = false; |
|||
var threshold = 1000 |
|||
var addSpace = false |
|||
if (options != null) { |
|||
threshold = options.getThreshold(); |
|||
addSpace = options.addSpaceBeforePrefix(); |
|||
threshold = options.threshold |
|||
addSpace = options.addSpaceBeforePrefix |
|||
} |
|||
if (number < threshold) return "" + number; |
|||
int exp = (int) (Math.log(number) / Math.log(threshold)); |
|||
return String.format(Locale.US, |
|||
if (number < threshold) return "" + number |
|||
val exp = (ln(number.toDouble()) / ln(threshold.toDouble())).toInt() |
|||
return String.format( |
|||
Locale.US, |
|||
"%.1f%s%c", |
|||
number / Math.pow(threshold, exp), |
|||
addSpace ? " " : "", |
|||
"kMGTPE".charAt(exp - 1)); |
|||
number / threshold.toDouble().pow(exp.toDouble()), |
|||
if (addSpace) " " else "", |
|||
"kMGTPE"[exp - 1] |
|||
) |
|||
} |
|||
|
|||
public static final class AbbreviateOptions { |
|||
private final int threshold; |
|||
private final boolean addSpaceBeforePrefix; |
|||
|
|||
public static final class Builder { |
|||
|
|||
private int threshold = 1000; |
|||
private boolean addSpaceBeforePrefix = false; |
|||
|
|||
public Builder setThreshold(final int threshold) { |
|||
this.threshold = threshold; |
|||
return this; |
|||
} |
|||
|
|||
public Builder setAddSpaceBeforePrefix(final boolean addSpaceBeforePrefix) { |
|||
this.addSpaceBeforePrefix = addSpaceBeforePrefix; |
|||
return this; |
|||
} |
|||
|
|||
@NonNull |
|||
public AbbreviateOptions build() { |
|||
return new AbbreviateOptions(threshold, addSpaceBeforePrefix); |
|||
} |
|||
|
|||
} |
|||
|
|||
private AbbreviateOptions(final int threshold, final boolean addSpaceBeforePrefix) { |
|||
this.threshold = threshold; |
|||
this.addSpaceBeforePrefix = addSpaceBeforePrefix; |
|||
} |
|||
|
|||
public int getThreshold() { |
|||
return threshold; |
|||
} |
|||
|
|||
public boolean addSpaceBeforePrefix() { |
|||
return addSpaceBeforePrefix; |
|||
} |
|||
} |
|||
} |
|||
data class AbbreviateOptions(val threshold: Int = 1000, val addSpaceBeforePrefix: Boolean = false) |
@ -1,69 +1,27 @@ |
|||
package awais.instagrabber.utils; |
|||
|
|||
import java.time.LocalDateTime; |
|||
import java.time.temporal.ChronoUnit; |
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
|
|||
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient; |
|||
import awais.instagrabber.repositories.responses.directmessages.RankedRecipientsResponse; |
|||
|
|||
public class RankedRecipientsCache { |
|||
private static final Object LOCK = new Object(); |
|||
|
|||
private static RankedRecipientsCache instance; |
|||
|
|||
public static RankedRecipientsCache getInstance() { |
|||
if (instance == null) { |
|||
synchronized (LOCK) { |
|||
if (instance == null) { |
|||
instance = new RankedRecipientsCache(); |
|||
} |
|||
} |
|||
} |
|||
return instance; |
|||
} |
|||
|
|||
private LocalDateTime lastUpdatedOn; |
|||
private RankedRecipientsResponse response; |
|||
private boolean updateInitiated = false; |
|||
private boolean failed = false; |
|||
|
|||
private RankedRecipientsCache() {} |
|||
|
|||
public List<RankedRecipient> getRankedRecipients() { |
|||
if (response != null) { |
|||
return response.getRankedRecipients(); |
|||
} |
|||
return Collections.emptyList(); |
|||
} |
|||
|
|||
public void setRankedRecipientsResponse(final RankedRecipientsResponse response) { |
|||
this.response = response; |
|||
lastUpdatedOn = LocalDateTime.now(); |
|||
} |
|||
|
|||
public boolean isExpired() { |
|||
if (lastUpdatedOn == null || response == null) { |
|||
return true; |
|||
} |
|||
final long expiresInSecs = response.getExpires(); |
|||
return LocalDateTime.now().isAfter(lastUpdatedOn.plus(expiresInSecs, ChronoUnit.SECONDS)); |
|||
} |
|||
|
|||
public boolean isUpdateInitiated() { |
|||
return updateInitiated; |
|||
} |
|||
|
|||
public void setUpdateInitiated(final boolean updateInitiated) { |
|||
this.updateInitiated = updateInitiated; |
|||
} |
|||
|
|||
public boolean isFailed() { |
|||
return failed; |
|||
} |
|||
|
|||
public void setFailed(final boolean failed) { |
|||
this.failed = failed; |
|||
package awais.instagrabber.utils |
|||
|
|||
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient |
|||
import awais.instagrabber.repositories.responses.directmessages.RankedRecipientsResponse |
|||
import java.time.LocalDateTime |
|||
import java.time.temporal.ChronoUnit |
|||
|
|||
object RankedRecipientsCache { |
|||
private var lastUpdatedOn: LocalDateTime? = null |
|||
var isUpdateInitiated = false |
|||
var isFailed = false |
|||
val rankedRecipients: List<RankedRecipient> |
|||
get() = response?.rankedRecipients ?: emptyList() |
|||
|
|||
var response: RankedRecipientsResponse? = null |
|||
set(value) { |
|||
field = value |
|||
lastUpdatedOn = LocalDateTime.now() |
|||
} |
|||
|
|||
val isExpired: Boolean |
|||
get() { |
|||
if (lastUpdatedOn == null || response == null) return true |
|||
val expiresInSecs = response!!.expires |
|||
return LocalDateTime.now().isAfter(lastUpdatedOn!!.plus(expiresInSecs, ChronoUnit.SECONDS)) |
|||
} |
|||
} |
@ -1,17 +1,12 @@ |
|||
package awais.instagrabber.utils; |
|||
package awais.instagrabber.utils |
|||
|
|||
import android.util.Pair; |
|||
import android.util.Pair |
|||
import java.io.Serializable |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
public class SerializablePair<F, S> extends Pair<F, S> implements Serializable { |
|||
/** |
|||
* Constructor for a Pair. |
|||
* |
|||
* @param first the first object in the Pair |
|||
* @param second the second object in the pair |
|||
*/ |
|||
public SerializablePair(final F first, final S second) { |
|||
super(first, second); |
|||
} |
|||
} |
|||
data class SerializablePair<F, S>(@JvmField val first: F, @JvmField val second: S) : Pair<F, S>(first, second), Serializable |
@ -1,38 +1,36 @@ |
|||
package awais.instagrabber.utils; |
|||
|
|||
import android.content.Context; |
|||
import android.content.DialogInterface; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
|
|||
import com.google.android.material.dialog.MaterialAlertDialogBuilder; |
|||
|
|||
import awais.instagrabber.BuildConfig; |
|||
import awais.instagrabber.R; |
|||
|
|||
import static awais.instagrabber.utils.Utils.settingsHelper; |
|||
|
|||
public final class UpdateCheckCommon { |
|||
|
|||
public static boolean shouldShowUpdateDialog(final boolean force, |
|||
@NonNull final String version) { |
|||
final String skippedVersion = settingsHelper.getString(Constants.SKIPPED_VERSION); |
|||
return force || (!BuildConfig.DEBUG && !skippedVersion.equals(version)); |
|||
@file:JvmName("UpdateCheckCommon") |
|||
|
|||
package awais.instagrabber.utils |
|||
|
|||
import android.content.Context |
|||
import android.content.DialogInterface |
|||
import awais.instagrabber.BuildConfig |
|||
import awais.instagrabber.R |
|||
import awais.instagrabber.utils.AppExecutors.mainThread |
|||
import com.google.android.material.dialog.MaterialAlertDialogBuilder |
|||
|
|||
fun shouldShowUpdateDialog( |
|||
force: Boolean, |
|||
version: String |
|||
): Boolean { |
|||
val skippedVersion = Utils.settingsHelper.getString(Constants.SKIPPED_VERSION) |
|||
return force || !BuildConfig.DEBUG && skippedVersion != version |
|||
} |
|||
|
|||
public static void showUpdateDialog(@NonNull final Context context, |
|||
@NonNull final String version, |
|||
@NonNull final DialogInterface.OnClickListener onDownloadClickListener) { |
|||
AppExecutors.INSTANCE.getMainThread().execute(() -> { |
|||
new MaterialAlertDialogBuilder(context) |
|||
.setTitle(context.getString(R.string.update_available, version)) |
|||
.setNeutralButton(R.string.skip_update, (dialog, which) -> { |
|||
settingsHelper.putString(Constants.SKIPPED_VERSION, version); |
|||
dialog.dismiss(); |
|||
}) |
|||
.setPositiveButton(R.string.action_download, onDownloadClickListener) |
|||
.setNegativeButton(R.string.cancel, null) |
|||
.show(); |
|||
}); |
|||
fun showUpdateDialog( |
|||
context: Context, |
|||
version: String, |
|||
onDownloadClickListener: DialogInterface.OnClickListener |
|||
) { |
|||
mainThread.execute { |
|||
MaterialAlertDialogBuilder(context).apply { |
|||
setTitle(context.getString(R.string.update_available, version)) |
|||
setNeutralButton(R.string.skip_update) { dialog: DialogInterface, which: Int -> |
|||
Utils.settingsHelper.putString(Constants.SKIPPED_VERSION, version) |
|||
dialog.dismiss() |
|||
} |
|||
setPositiveButton(R.string.action_download, onDownloadClickListener) |
|||
setNegativeButton(R.string.cancel, null) |
|||
}.show() |
|||
} |
|||
} |
@ -0,0 +1,12 @@ |
|||
package awais.instagrabber.utils.extensions |
|||
|
|||
val Any.TAG: String |
|||
get() { |
|||
return if (!javaClass.isAnonymousClass) { |
|||
val name = javaClass.simpleName |
|||
if (name.length <= 23) name else name.substring(0, 23) // first 23 chars |
|||
} else { |
|||
val name = javaClass.name |
|||
if (name.length <= 23) name else name.substring(name.length - 23, name.length) // last 23 chars |
|||
} |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue