Browse Source

Convert some more classes to kotlin

renovate/org.robolectric-robolectric-4.x
Ammar Githam 4 years ago
parent
commit
17fb608c34
  1. 3
      app/src/main/java/awais/instagrabber/InstaGrabberApplication.java
  2. 2
      app/src/main/java/awais/instagrabber/customviews/FormattedNumberTextView.java
  3. 5
      app/src/main/java/awais/instagrabber/customviews/Tooltip.java
  4. 214
      app/src/main/java/awais/instagrabber/utils/CombinedDrawable.kt
  5. 175
      app/src/main/java/awais/instagrabber/utils/CookieUtils.kt
  6. 33
      app/src/main/java/awais/instagrabber/utils/CubicInterpolation.java
  7. 43
      app/src/main/java/awais/instagrabber/utils/DataBox.java
  8. 72
      app/src/main/java/awais/instagrabber/utils/DeepLinkParser.kt
  9. 150
      app/src/main/java/awais/instagrabber/utils/LocaleUtils.kt
  10. 3
      app/src/main/java/awais/instagrabber/utils/MediaUploadHelper.kt
  11. 61
      app/src/main/java/awais/instagrabber/utils/NullSafePair.kt
  12. 182
      app/src/main/java/awais/instagrabber/utils/NumberUtils.kt
  13. 92
      app/src/main/java/awais/instagrabber/utils/RankedRecipientsCache.kt
  14. 13
      app/src/main/java/awais/instagrabber/utils/SerializablePair.kt
  15. 66
      app/src/main/java/awais/instagrabber/utils/UpdateCheckCommon.kt
  16. 39
      app/src/main/java/awais/instagrabber/utils/UserAgentUtils.kt
  17. 162
      app/src/main/java/awais/instagrabber/utils/ViewUtils.kt
  18. 12
      app/src/main/java/awais/instagrabber/utils/extensions/AnyExtensions.kt
  19. 4
      app/src/main/java/awais/instagrabber/viewmodels/UserSearchViewModel.java

3
app/src/main/java/awais/instagrabber/InstaGrabberApplication.java

@ -27,9 +27,6 @@ import static awais.instagrabber.utils.Utils.clipboardManager;
import static awais.instagrabber.utils.Utils.datetimeParser; import static awais.instagrabber.utils.Utils.datetimeParser;
import static awais.instagrabber.utils.Utils.settingsHelper; import static awais.instagrabber.utils.Utils.settingsHelper;
//import awaisomereport.LogCollector;
//import static awais.instagrabber.utils.Utils.logCollector;
public final class InstaGrabberApplication extends Application { public final class InstaGrabberApplication extends Application {
private static final String TAG = "InstaGrabberApplication"; private static final String TAG = "InstaGrabberApplication";

2
app/src/main/java/awais/instagrabber/customviews/FormattedNumberTextView.java

@ -153,7 +153,7 @@ public class FormattedNumberTextView extends AppCompatTextView {
return; return;
} }
if (showAbbreviation) { if (showAbbreviation) {
setText(NumberUtils.abbreviate(number));
setText(NumberUtils.abbreviate(number, null));
return; return;
} }
setText(String.valueOf(number)); setText(String.valueOf(number));

5
app/src/main/java/awais/instagrabber/customviews/Tooltip.java

@ -16,6 +16,8 @@ import awais.instagrabber.utils.AppExecutors;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.ViewUtils; import awais.instagrabber.utils.ViewUtils;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
public class Tooltip extends AppCompatTextView { public class Tooltip extends AppCompatTextView {
private View anchor; private View anchor;
@ -40,8 +42,7 @@ public class Tooltip extends AppCompatTextView {
setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
setPadding(Utils.convertDpToPx(8), Utils.convertDpToPx(7), Utils.convertDpToPx(8), Utils.convertDpToPx(7)); setPadding(Utils.convertDpToPx(8), Utils.convertDpToPx(7), Utils.convertDpToPx(8), Utils.convertDpToPx(7));
setGravity(Gravity.CENTER_VERTICAL); setGravity(Gravity.CENTER_VERTICAL);
parentView.addView(this, ViewUtils.createFrame(
ViewUtils.WRAP_CONTENT, ViewUtils.WRAP_CONTENT, Gravity.START | Gravity.TOP, 5, 0, 5, 3));
parentView.addView(this, ViewUtils.createFrame(WRAP_CONTENT, WRAP_CONTENT, Gravity.START | Gravity.TOP, 5, 0, 5, 3));
setVisibility(GONE); setVisibility(GONE);
} }

214
app/src/main/java/awais/instagrabber/utils/CombinedDrawable.kt

@ -1,185 +1,157 @@
package awais.instagrabber.utils;/*
/*
* This is the source code of Telegram for Android v. 5.x.x. * This is the source code of Telegram for Android v. 5.x.x.
* It is licensed under GNU GPL v. 2 or later. * It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE). * You should have received a copy of the license in this archive (see LICENSE).
*
* <p>
* Copyright Nikolai Kudashov, 2013-2018. * 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) { 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) { 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) { if (fullSize) {
android.graphics.Rect bounds = getBounds();
val bounds = bounds
if (left != 0) { 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 { } else {
icon.setBounds(bounds);
icon.bounds = bounds
} }
} else { } else {
int x;
int y;
val x: Int
val y: Int
if (iconWidth != 0) { 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 { } 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)
} }
} }

175
app/src/main/java/awais/instagrabber/utils/CookieUtils.kt

@ -1,107 +1,90 @@
package awais.instagrabber.utils;
@file:JvmName("CookieUtils")
import android.content.Context;
import android.util.Log;
import android.webkit.CookieManager;
package awais.instagrabber.utils
import androidx.annotation.Nullable;
import android.content.Context
import android.util.Log
import android.webkit.CookieManager
import awais.instagrabber.db.datasources.AccountDataSource
import awais.instagrabber.db.repositories.AccountRepository
import awais.instagrabber.db.repositories.RepositoryCallback
import java.net.CookiePolicy
import java.net.HttpCookie
import java.net.URI
import java.net.URISyntaxException
import java.util.regex.Pattern
import java.net.CookiePolicy;
import java.net.CookieStore;
import java.net.HttpCookie;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
private const val TAG = "CookieUtils"
private val COOKIE_MANAGER = CookieManager.getInstance()
import awais.instagrabber.BuildConfig;
import awais.instagrabber.db.datasources.AccountDataSource;
import awais.instagrabber.db.repositories.AccountRepository;
import awais.instagrabber.db.repositories.RepositoryCallback;
//import awaisomereport.LogCollector;
@JvmField
val NET_COOKIE_MANAGER = java.net.CookieManager(null, CookiePolicy.ACCEPT_ALL)
public final class CookieUtils {
private static final String TAG = CookieUtils.class.getSimpleName();
public static final CookieManager COOKIE_MANAGER = CookieManager.getInstance();
public static final java.net.CookieManager NET_COOKIE_MANAGER = new java.net.CookieManager(null, CookiePolicy.ACCEPT_ALL);
public static void setupCookies(final String cookieRaw) {
final CookieStore cookieStore = NET_COOKIE_MANAGER.getCookieStore();
fun setupCookies(cookieRaw: String) {
val cookieStore = NET_COOKIE_MANAGER.cookieStore
if (cookieStore == null || TextUtils.isEmpty(cookieRaw)) { if (cookieStore == null || TextUtils.isEmpty(cookieRaw)) {
return;
return
} }
if (cookieRaw.equals("LOGOUT")) {
cookieStore.removeAll();
return;
if (cookieRaw == "LOGOUT") {
cookieStore.removeAll()
return
} }
try { try {
final URI uri1 = new URI("https://instagram.com");
final URI uri2 = new URI("https://instagram.com/");
final URI uri3 = new URI("https://i.instagram.com/");
for (final String cookie : cookieRaw.split("; ")) {
final String[] strings = cookie.split("=", 2);
final HttpCookie httpCookie = new HttpCookie(strings[0].trim(), strings[1].trim());
httpCookie.setDomain(".instagram.com");
httpCookie.setPath("/");
httpCookie.setVersion(0);
cookieStore.add(uri1, httpCookie);
cookieStore.add(uri2, httpCookie);
cookieStore.add(uri3, httpCookie);
}
} catch (final URISyntaxException e) {
// if (Utils.logCollector != null)
// Utils.logCollector.appendException(e, LogCollector.LogFile.UTILS, "setupCookies");
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
val uri1 = URI("https://instagram.com")
val uri2 = URI("https://instagram.com/")
val uri3 = URI("https://i.instagram.com/")
for (cookie in cookieRaw.split("; ")) {
val strings = cookie.split("=", limit = 2)
val httpCookie = HttpCookie(strings[0].trim { it <= ' ' }, strings[1].trim { it <= ' ' })
httpCookie.domain = ".instagram.com"
httpCookie.path = "/"
httpCookie.version = 0
cookieStore.add(uri1, httpCookie)
cookieStore.add(uri2, httpCookie)
cookieStore.add(uri3, httpCookie)
}
} catch (e: URISyntaxException) {
Log.e(TAG, "", e)
} }
} }
public static void removeAllAccounts(final Context context, final RepositoryCallback<Void> callback) {
final CookieStore cookieStore = NET_COOKIE_MANAGER.getCookieStore();
if (cookieStore == null) return;
cookieStore.removeAll();
fun removeAllAccounts(context: Context?, callback: RepositoryCallback<Void?>?) {
val cookieStore = NET_COOKIE_MANAGER.cookieStore ?: return
cookieStore.removeAll()
try { try {
AccountRepository.getInstance(AccountDataSource.getInstance(context))
.deleteAllAccounts(callback);
} catch (Exception e) {
Log.e(TAG, "setupCookies", e);
AccountRepository.getInstance(AccountDataSource.getInstance(context!!))
.deleteAllAccounts(callback)
} catch (e: Exception) {
Log.e(TAG, "setupCookies", e)
} }
} }
public static long getUserIdFromCookie(final String cookies) {
final String dsUserId = getCookieValue(cookies, "ds_user_id");
if (dsUserId == null) {
return 0;
}
fun getUserIdFromCookie(cookies: String?): Long {
val dsUserId = getCookieValue(cookies, "ds_user_id") ?: return 0
try { try {
return Long.parseLong(dsUserId);
} catch (NumberFormatException e) {
Log.e(TAG, "getUserIdFromCookie: ", e);
return dsUserId.toLong()
} catch (e: NumberFormatException) {
Log.e(TAG, "getUserIdFromCookie: ", e)
} }
return 0;
return 0
} }
@Nullable
public static String getCsrfTokenFromCookie(final String cookies) {
return getCookieValue(cookies, "csrftoken");
fun getCsrfTokenFromCookie(cookies: String?): String? {
return getCookieValue(cookies, "csrftoken")
} }
@Nullable
private static String getCookieValue(final String cookies, final String name) {
if (cookies == null) return null;
final Pattern pattern = Pattern.compile(name + "=(.+?);");
final Matcher matcher = pattern.matcher(cookies);
if (matcher.find()) {
return matcher.group(1);
}
return null;
private fun getCookieValue(cookies: String?, name: String): String? {
if (cookies == null) return null
val pattern = Pattern.compile("$name=(.+?);")
val matcher = pattern.matcher(cookies)
return if (matcher.find()) {
matcher.group(1)
} else null
} }
@Nullable
public static String getCookie(@Nullable final String webViewUrl) {
final List<String> domains = new ArrayList<>(Arrays.asList(
fun getCookie(webViewUrl: String?): String? {
val domains: List<String> = listOfNotNull(
if (!TextUtils.isEmpty(webViewUrl)) webViewUrl else null,
"https://instagram.com", "https://instagram.com",
"https://instagram.com/", "https://instagram.com/",
"http://instagram.com", "http://instagram.com",
@ -109,31 +92,23 @@ public final class CookieUtils {
"https://www.instagram.com", "https://www.instagram.com",
"https://www.instagram.com/", "https://www.instagram.com/",
"http://www.instagram.com", "http://www.instagram.com",
"http://www.instagram.com/"
));
if (!TextUtils.isEmpty(webViewUrl)) {
domains.add(0, webViewUrl);
}
return getLongestCookie(domains);
"http://www.instagram.com/",
)
return getLongestCookie(domains)
} }
@Nullable
private static String getLongestCookie(final List<String> domains) {
int longestLength = 0;
String longestCookie = null;
for (final String domain : domains) {
final String cookie = COOKIE_MANAGER.getCookie(domain);
private fun getLongestCookie(domains: List<String>): String? {
var longestLength = 0
var longestCookie: String? = null
for (domain in domains) {
val cookie = COOKIE_MANAGER.getCookie(domain)
if (cookie != null) { if (cookie != null) {
final int cookieLength = cookie.length();
val cookieLength = cookie.length
if (cookieLength > longestLength) { if (cookieLength > longestLength) {
longestCookie = cookie;
longestLength = cookieLength;
longestCookie = cookie
longestLength = cookieLength
} }
} }
} }
return longestCookie;
}
return longestCookie
} }

33
app/src/main/java/awais/instagrabber/utils/CubicInterpolation.java

@ -8,29 +8,6 @@ public class CubicInterpolation {
private final int tangentFactor; private final int tangentFactor;
private final int length; private final int length;
// constructor: (array, config) ->
// @array = array.slice 0 #copy the array
// @length = @array.length #cache length
// class CubicInterpolator extends AbstractInterpolator
// constructor: (array, config)->
// #clamp cubic tension to [0,1] range
// @tangentFactor = 1 - Math.max 0, Math.min 1, config.cubicTension
// super
//
// # Cardinal spline with tension 0.5)
// getTangent: (k) -> @tangentFactor*(@getClippedInput(k + 1) - @getClippedInput(k - 1))/2
//
// interpolate: (t) ->
// k = Math.floor t
// m = [(@getTangent k), (@getTangent k+1)] #get tangents
// p = [(@getClippedInput k), (@getClippedInput k+1)] #get points
// #Translate t to interpolate between k and k+1
// t -= k
// t2 = t*t #t^2
// t3 = t*t2 #t^3
// #Apply cubic hermite spline formula
// return (2*t3 - 3*t2 + 1)*p[0] + (t3 - 2*t2 + t)*m[0] + (-2*t3 + 3*t2)*p[1] + (t3 - t2)*m[1]
public CubicInterpolation(final float[] array, final int cubicTension) { public CubicInterpolation(final float[] array, final int cubicTension) {
this.array = Arrays.copyOf(array, array.length); this.array = Arrays.copyOf(array, array.length);
this.length = array.length; this.length = array.length;
@ -55,16 +32,6 @@ public class CubicInterpolation {
return (2 * t3 - 3 * t2 + 1) * p[0] + (t3 - 2 * t2 + t1) * m[0] + (-2 * t3 + 3 * t2) * p[1] + (t3 - t2) * m[1]; return (2 * t3 - 3 * t2 + 1) * p[0] + (t3 - 2 * t2 + t1) * m[0] + (-2 * t3 + 3 * t2) * p[1] + (t3 - t2) * m[1];
} }
// getClippedInput: (i) ->
// #Normal behavior for indexes within bounds
// if 0 <= i < @length
// @array[i]
// else
// @clipHelper i
//
// clipHelperClamp: (i) -> @array[clipClamp i, @length]
// clipClamp = (i, n) -> Math.max 0, Math.min i, n - 1
private float getClippedInput(int i) { private float getClippedInput(int i) {
if (i >= 0 && i < length) { if (i >= 0 && i < length) {
return array[i]; return array[i];

43
app/src/main/java/awais/instagrabber/utils/DataBox.java

@ -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));
}
}

72
app/src/main/java/awais/instagrabber/utils/DeepLinkParser.kt

@ -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
} }
} }
} }

150
app/src/main/java/awais/instagrabber/utils/LocaleUtils.kt

@ -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 // 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
} }
} }

3
app/src/main/java/awais/instagrabber/utils/MediaUploadHelper.kt

@ -7,6 +7,7 @@ import awais.instagrabber.models.UploadVideoOptions
import awais.instagrabber.models.enums.MediaItemType import awais.instagrabber.models.enums.MediaItemType
import org.json.JSONObject import org.json.JSONObject
import java.util.* import java.util.*
import kotlin.random.Random
private const val LOWER = 1000000000L private const val LOWER = 1000000000L
@ -109,7 +110,7 @@ fun generateUploadId(): String {
} }
fun generateName(uploadId: String): String { fun generateName(uploadId: String): String {
val random = NumberUtils.random(LOWER, UPPER + 1)
val random = Random.nextLong(LOWER, UPPER + 1)
return "${uploadId}_0_$random" return "${uploadId}_0_$random"
} }

61
app/src/main/java/awais/instagrabber/utils/NullSafePair.kt

@ -1,4 +1,4 @@
package awais.instagrabber.utils;
package awais.instagrabber.utils
/* /*
* Copyright (C) 2009 The Android Open Source Project * Copyright (C) 2009 The Android Open Source Project
@ -15,67 +15,19 @@ package awais.instagrabber.utils;
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.util.ObjectsCompat;
/** /**
* Container to ease passing around a tuple of two objects. This object provides a sensible * Container to ease passing around a tuple of two objects. This object provides a sensible
* implementation of equals(), returning true if equals() is true on each of the contained * implementation of equals(), returning true if equals() is true on each of the contained
* objects. * objects.
*/ */
public class NullSafePair<F, S> {
public final @NonNull
F first;
public final @NonNull
S second;
/** /**
* Constructor for a Pair. * Constructor for a Pair.
* *
* @param first the first object in the Pair * @param first the first object in the Pair
* @param second the second object in the pair * @param second the second object in the pair
*/ */
public NullSafePair(@NonNull F first, @NonNull S second) {
this.first = first;
this.second = second;
}
/**
* Checks the two objects for equality by delegating to their respective
* {@link Object#equals(Object)} methods.
*
* @param o the {@link androidx.core.util.Pair} to which this one is to be checked for equality
* @return true if the underlying objects of the Pair are both considered
* equal
*/
@Override
public boolean equals(Object o) {
if (!(o instanceof androidx.core.util.Pair)) {
return false;
}
androidx.core.util.Pair<?, ?> p = (androidx.core.util.Pair<?, ?>) o;
return ObjectsCompat.equals(p.first, first) && ObjectsCompat.equals(p.second, second);
}
/**
* Compute a hash code using the hash codes of the underlying objects
*
* @return a hashcode of the Pair
*/
@Override
public int hashCode() {
return first.hashCode() ^ second.hashCode();
}
@NonNull
@Override
public String toString() {
return "Pair{" + first + " " + second + "}";
}
data class NullSafePair<F, S>(@JvmField val first: F, @JvmField val second: S) {
companion object {
/** /**
* Convenience method for creating an appropriately typed pair. * Convenience method for creating an appropriately typed pair.
* *
@ -83,9 +35,8 @@ public class NullSafePair<F, S> {
* @param b the second object in the pair * @param b the second object in the pair
* @return a Pair that is templatized with the types of a and b * @return a Pair that is templatized with the types of a and b
*/ */
@NonNull
public static <A, B> androidx.core.util.Pair<A, B> create(@Nullable A a, @Nullable B b) {
return new androidx.core.util.Pair<A, B>(a, b);
fun <A, B> create(a: A, b: B): NullSafePair<A, B> {
return NullSafePair(a, b)
}
} }
} }

182
app/src/main/java/awais/instagrabber/utils/NumberUtils.kt

@ -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) { if (width > maxWidth) {
int tempHeight = getResultingHeight(maxWidth, height, width);
int tempWidth = maxWidth;
var tempHeight = getResultingHeight(maxWidth, height, width)
var tempWidth = maxWidth
if (tempHeight > maxHeight) { 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) { 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 // adapted from https://stackoverflow.com/a/9769590/1436766
int threshold = 1000;
boolean addSpace = false;
var threshold = 1000
var addSpace = false
if (options != null) { 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", "%.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)

92
app/src/main/java/awais/instagrabber/utils/RankedRecipientsCache.kt

@ -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))
} }
} }

13
app/src/main/java/awais/instagrabber/utils/SerializablePair.kt

@ -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. * Constructor for a Pair.
* *
* @param first the first object in the Pair * @param first the first object in the Pair
* @param second the second 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

66
app/src/main/java/awais/instagrabber/utils/UpdateCheckCommon.kt

@ -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()
} }
} }

39
app/src/main/java/awais/instagrabber/utils/UserAgentUtils.kt

@ -1,28 +1,30 @@
package awais.instagrabber.utils;
@file:JvmName("UserAgentUtils")
import androidx.annotation.NonNull;
public class UserAgentUtils {
package awais.instagrabber.utils
/* GraphQL user agents (which are just standard browser UA"s). /* GraphQL user agents (which are just standard browser UA"s).
* Go to https://www.whatismybrowser.com/guides/the-latest-user-agent/ to update it * Go to https://www.whatismybrowser.com/guides/the-latest-user-agent/ to update it
* Windows first (Assume win64 not wow64): Chrome, Firefox, Edge * Windows first (Assume win64 not wow64): Chrome, Firefox, Edge
* Then macOS: Chrome, Firefox, Safari * Then macOS: Chrome, Firefox, Safari
*/ */
public static final String[] browsers = {
@JvmField
val browsers = arrayOf(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36 Edg/90.0.818.62", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36 Edg/90.0.818.62",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 11_3_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_3_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 11.3; rv:88.0) Gecko/20100101 Firefox/88.0", "Mozilla/5.0 (Macintosh; Intel Mac OS X 11.3; rv:88.0) Gecko/20100101 Firefox/88.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 11_3_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1 Safari/605.1.15" "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_3_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1 Safari/605.1.15"
};
)
// use APKpure, assume arm64-v8a // use APKpure, assume arm64-v8a
private static final String igVersion = "188.0.0.35.124";
private static final String igVersionCode = "292080186";
private const val igVersion = "188.0.0.35.124"
private const val igVersionCode = "292080186"
// you can pick *any* device as long as you LEAVE OUT the resolution for maximum download quality // you can pick *any* device as long as you LEAVE OUT the resolution for maximum download quality
public static final String[] devices = {
// https://github.com/dilame/instagram-private-api/blob/master/src/samples/devices.json // https://github.com/dilame/instagram-private-api/blob/master/src/samples/devices.json
@JvmField
val devices = arrayOf(
"25/7.1.1; 440dpi; 2880x5884; Xiaomi; Mi Note 3; jason; qcom", "25/7.1.1; 440dpi; 2880x5884; Xiaomi; Mi Note 3; jason; qcom",
"23/6.0.1; 480dpi; 2880x5884; Xiaomi; Redmi Note 3; kenzo; qcom", "23/6.0.1; 480dpi; 2880x5884; Xiaomi; Redmi Note 3; kenzo; qcom",
"23/6.0; 480dpi; 2880x5884; Xiaomi; Redmi Note 4; nikel; mt6797", "23/6.0; 480dpi; 2880x5884; Xiaomi; Redmi Note 4; nikel; mt6797",
@ -62,24 +64,19 @@ public class UserAgentUtils {
"26/8.0.0; 480dpi; 2880x5884; HUAWEI; FIG-LX1; HWFIG-H; hi6250", "26/8.0.0; 480dpi; 2880x5884; HUAWEI; FIG-LX1; HWFIG-H; hi6250",
"27/8.1.0; 480dpi; 2880x5884; HUAWEI/HONOR; COL-L29; HWCOL; kirin970", "27/8.1.0; 480dpi; 2880x5884; HUAWEI/HONOR; COL-L29; HWCOL; kirin970",
"26/8.0.0; 480dpi; 2880x5884; HUAWEI/HONOR; BND-L21; HWBND-H; hi6250", "26/8.0.0; 480dpi; 2880x5884; HUAWEI/HONOR; BND-L21; HWBND-H; hi6250",
"23/6.0.1; 420dpi; 2880x5884; LeMobile/LeEco; Le X527; le_s2_ww; qcom",
// https://github.com/mimmi20/BrowserDetector/tree/master
"28/9; 560dpi; 2880x5884; samsung; SM-N960F; crownlte; samsungexynos9810",
// mgp25
"23/6.0.1; 420dpi; 2880x5884; LeMobile/LeEco; Le X527; le_s2_ww; qcom", // https://github.com/mimmi20/BrowserDetector/tree/master
"28/9; 560dpi; 2880x5884; samsung; SM-N960F; crownlte; samsungexynos9810", // mgp25
"23/6.0.1; 640dpi; 2880x5884; LGE/lge; RS988; h1; h1", "23/6.0.1; 640dpi; 2880x5884; LGE/lge; RS988; h1; h1",
"24/7.0; 640dpi; 2880x5884; HUAWEI; LON-L29; HWLON; hi3660", "24/7.0; 640dpi; 2880x5884; HUAWEI; LON-L29; HWLON; hi3660",
"23/6.0.1; 640dpi; 2880x5884; ZTE; ZTE A2017U; ailsa_ii; qcom", "23/6.0.1; 640dpi; 2880x5884; ZTE; ZTE A2017U; ailsa_ii; qcom",
"23/6.0.1; 640dpi; 2880x5884; samsung; SM-G935F; hero2lte; samsungexynos8890", "23/6.0.1; 640dpi; 2880x5884; samsung; SM-G935F; hero2lte; samsungexynos8890",
"23/6.0.1; 640dpi; 2880x5884; samsung; SM-G930F; herolte; samsungexynos8890" "23/6.0.1; 640dpi; 2880x5884; samsung; SM-G930F; herolte; samsungexynos8890"
};
)
@NonNull
public static String generateBrowserUA(final int code) {
return browsers[code];
fun generateBrowserUA(code: Int): String {
return browsers[code]
} }
@NonNull
public static String generateAppUA(final int code, final String lang) {
return "Instagram " + igVersion + " Android (" + devices[code] + "; " + lang + "; " + igVersionCode + ")";
}
fun generateAppUA(code: Int, lang: String): String {
return "Instagram " + igVersion + " Android (" + devices[code] + "; " + lang + "; " + igVersionCode + ")"
} }

162
app/src/main/java/awais/instagrabber/utils/ViewUtils.kt

@ -1,83 +1,79 @@
package awais.instagrabber.utils;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RoundRectShape;
import android.os.Build;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.res.ResourcesCompat;
import androidx.core.util.Pair;
import androidx.dynamicanimation.animation.FloatPropertyCompat;
import androidx.dynamicanimation.animation.SpringAnimation;
import org.jetbrains.annotations.NotNull;
import kotlin.jvm.internal.Intrinsics;
public final class ViewUtils {
public static final int MATCH_PARENT = -1;
public static final int WRAP_CONTENT = -2;
public static Drawable createRoundRectDrawableWithIcon(final Context context, int rad, int iconRes) {
ShapeDrawable defaultDrawable = new ShapeDrawable(new RoundRectShape(new float[]{rad, rad, rad, rad, rad, rad, rad, rad}, null, null));
defaultDrawable.getPaint().setColor(0xffffffff);
final Drawable d = ResourcesCompat.getDrawable(context.getResources(), iconRes, null);
if (d == null) return null;
Drawable drawable = d.mutate();
return new CombinedDrawable(defaultDrawable, drawable);
@file:JvmName("ViewUtils")
package awais.instagrabber.utils
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.ShapeDrawable
import android.graphics.drawable.shapes.RoundRectShape
import android.os.Build
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.core.content.res.ResourcesCompat
import androidx.core.util.Pair
import androidx.dynamicanimation.animation.FloatPropertyCompat
import androidx.dynamicanimation.animation.SpringAnimation
import kotlin.jvm.internal.Intrinsics
fun createRoundRectDrawableWithIcon(context: Context, rad: Int, iconRes: Int): Drawable? {
val defaultDrawable = ShapeDrawable(RoundRectShape(FloatArray(8) { rad.toFloat() }, null, null))
defaultDrawable.paint.color = -0x1
val d = ResourcesCompat.getDrawable(context.resources, iconRes, null) ?: return null
val drawable = d.mutate()
return CombinedDrawable(defaultDrawable, drawable)
} }
public static Drawable createRoundRectDrawable(int rad, int defaultColor) {
ShapeDrawable defaultDrawable = new ShapeDrawable(new RoundRectShape(new float[]{rad, rad, rad, rad, rad, rad, rad, rad}, null, null));
defaultDrawable.getPaint().setColor(defaultColor);
return defaultDrawable;
fun createRoundRectDrawable(rad: Int, defaultColor: Int): Drawable {
val defaultDrawable = ShapeDrawable(RoundRectShape(FloatArray(8) { rad.toFloat() }, null, null))
defaultDrawable.paint.color = defaultColor
return defaultDrawable
} }
public static FrameLayout.LayoutParams createFrame(int width,
float height,
int gravity,
float leftMargin,
float topMargin,
float rightMargin,
float bottomMargin) {
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(getSize(width), getSize(height), gravity);
layoutParams.setMargins(Utils.convertDpToPx(leftMargin), Utils.convertDpToPx(topMargin), Utils.convertDpToPx(rightMargin),
Utils.convertDpToPx(bottomMargin));
return layoutParams;
fun createFrame(
width: Int,
height: Float,
gravity: Int,
leftMargin: Float,
topMargin: Float,
rightMargin: Float,
bottomMargin: Float
): FrameLayout.LayoutParams {
val layoutParams = FrameLayout.LayoutParams(getSize(width.toFloat()), getSize(height), gravity)
layoutParams.setMargins(
Utils.convertDpToPx(leftMargin), Utils.convertDpToPx(topMargin), Utils.convertDpToPx(rightMargin),
Utils.convertDpToPx(bottomMargin)
)
return layoutParams
} }
public static GradientDrawable createGradientDrawable(final GradientDrawable.Orientation orientation,
@ColorInt final int[] colors) {
final GradientDrawable drawable = new GradientDrawable(orientation, colors);
drawable.setShape(GradientDrawable.RECTANGLE);
return drawable;
fun createGradientDrawable(
orientation: GradientDrawable.Orientation?,
@ColorInt colors: IntArray?
): GradientDrawable {
val drawable = GradientDrawable(orientation, colors)
drawable.shape = GradientDrawable.RECTANGLE
return drawable
} }
private static int getSize(float size) {
return (int) (size < 0 ? size : Utils.convertDpToPx(size));
private fun getSize(size: Float): Int {
return if (size < 0) size.toInt() else Utils.convertDpToPx(size)
} }
public static Pair<Integer, Integer> measure(@NonNull final View view, @NonNull final View parent) {
fun measure(view: View, parent: View): Pair<Int, Int> {
view.measure( view.measure(
View.MeasureSpec.makeMeasureSpec(parent.getWidth(), View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(parent.getHeight(), View.MeasureSpec.UNSPECIFIED)
);
return new Pair<>(view.getMeasuredHeight(), view.getMeasuredWidth());
View.MeasureSpec.makeMeasureSpec(parent.width, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(parent.height, View.MeasureSpec.UNSPECIFIED)
)
return Pair(view.measuredHeight, view.measuredWidth)
} }
public static float getTextViewValueWidth(final TextView textView, final String text) {
return textView.getPaint().measureText(text);
fun getTextViewValueWidth(textView: TextView, text: String?): Float {
return textView.paint.measureText(text)
} }
/** /**
@ -90,32 +86,32 @@ public final class ViewUtils {
* @param finalPosition [SpringForce.mFinalPosition] Final position of spring. * @param finalPosition [SpringForce.mFinalPosition] Final position of spring.
* @return [SpringAnimation] * @return [SpringAnimation]
*/ */
@NonNull
public static SpringAnimation springAnimationOf(final Object object,
final FloatPropertyCompat<Object> property,
@Nullable final Float finalPosition) {
return finalPosition == null ? new SpringAnimation(object, property) : new SpringAnimation(object, property, finalPosition);
fun springAnimationOf(
`object`: Any?,
property: FloatPropertyCompat<Any?>?,
finalPosition: Float?
): SpringAnimation {
return finalPosition?.let { SpringAnimation(`object`, property, it) } ?: SpringAnimation(`object`, property)
} }
public static void suppressLayoutCompat(@NotNull ViewGroup $this$suppressLayoutCompat, boolean suppress) {
Intrinsics.checkNotNullParameter($this$suppressLayoutCompat, "$this$suppressLayoutCompat");
fun suppressLayoutCompat(`$this$suppressLayoutCompat`: ViewGroup, suppress: Boolean) {
Intrinsics.checkNotNullParameter(`$this$suppressLayoutCompat`, "\$this\$suppressLayoutCompat")
if (Build.VERSION.SDK_INT >= 29) { if (Build.VERSION.SDK_INT >= 29) {
$this$suppressLayoutCompat.suppressLayout(suppress);
`$this$suppressLayoutCompat`.suppressLayout(suppress)
} else { } else {
hiddenSuppressLayout($this$suppressLayoutCompat, suppress);
hiddenSuppressLayout(`$this$suppressLayoutCompat`, suppress)
} }
} }
private static boolean tryHiddenSuppressLayout = true;
private var tryHiddenSuppressLayout = true
@SuppressLint({"NewApi"})
private static void hiddenSuppressLayout(ViewGroup group, boolean suppress) {
@SuppressLint("NewApi")
private fun hiddenSuppressLayout(group: ViewGroup, suppress: Boolean) {
if (tryHiddenSuppressLayout) { if (tryHiddenSuppressLayout) {
try { try {
group.suppressLayout(suppress);
} catch (NoSuchMethodError var3) {
tryHiddenSuppressLayout = false;
}
group.suppressLayout(suppress)
} catch (var3: NoSuchMethodError) {
tryHiddenSuppressLayout = false
} }
} }
} }

12
app/src/main/java/awais/instagrabber/utils/extensions/AnyExtensions.kt

@ -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
}
}

4
app/src/main/java/awais/instagrabber/viewmodels/UserSearchViewModel.java

@ -73,7 +73,7 @@ public class UserSearchViewModel extends ViewModel {
} }
userService = UserService.getInstance(); userService = UserService.getInstance();
directMessagesService = DirectMessagesService.getInstance(csrfToken, viewerId, deviceUuid); directMessagesService = DirectMessagesService.getInstance(csrfToken, viewerId, deviceUuid);
rankedRecipientsCache = RankedRecipientsCache.getInstance();
rankedRecipientsCache = RankedRecipientsCache.INSTANCE;
if ((rankedRecipientsCache.isFailed() || rankedRecipientsCache.isExpired()) && !rankedRecipientsCache.isUpdateInitiated()) { if ((rankedRecipientsCache.isFailed() || rankedRecipientsCache.isExpired()) && !rankedRecipientsCache.isUpdateInitiated()) {
updateRankedRecipientCache(); updateRankedRecipientCache();
} }
@ -113,7 +113,7 @@ public class UserSearchViewModel extends ViewModel {
continueSearchIfRequired(); continueSearchIfRequired();
return; return;
} }
rankedRecipientsCache.setRankedRecipientsResponse(response.body());
rankedRecipientsCache.setResponse(response.body());
rankedRecipientsCache.setUpdateInitiated(false); rankedRecipientsCache.setUpdateInitiated(false);
continueSearchIfRequired(); continueSearchIfRequired();
} }

Loading…
Cancel
Save