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
-
226app/src/main/java/awais/instagrabber/utils/CombinedDrawable.kt
-
211app/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
-
154app/src/main/java/awais/instagrabber/utils/LocaleUtils.kt
-
3app/src/main/java/awais/instagrabber/utils/MediaUploadHelper.kt
-
85app/src/main/java/awais/instagrabber/utils/NullSafePair.kt
-
202app/src/main/java/awais/instagrabber/utils/NumberUtils.kt
-
88app/src/main/java/awais/instagrabber/utils/RankedRecipientsCache.kt
-
25app/src/main/java/awais/instagrabber/utils/SerializablePair.kt
-
68app/src/main/java/awais/instagrabber/utils/UpdateCheckCommon.kt
-
155app/src/main/java/awais/instagrabber/utils/UserAgentUtils.kt
-
210app/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. |
* 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); |
|
||||
|
iconDrawable.callback = this |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
public void setIconSize(int width, int height) { |
|
||||
iconWidth = width; |
|
||||
iconHeight = height; |
|
||||
} |
|
||||
|
|
||||
public void setCustomSize(int width, int height) { |
|
||||
backWidth = width; |
|
||||
backHeight = height; |
|
||||
|
fun setIconSize(width: Int, height: Int) { |
||||
|
iconWidth = width |
||||
|
iconHeight = height |
||||
} |
} |
||||
|
|
||||
public void setIconOffset(int x, int y) { |
|
||||
offsetX = x; |
|
||||
offsetY = y; |
|
||||
|
fun setCustomSize(width: Int, height: Int) { |
||||
|
backWidth = width |
||||
|
backHeight = height |
||||
} |
} |
||||
|
|
||||
public Drawable getIcon() { |
|
||||
return icon; |
|
||||
|
fun setIconOffset(x: Int, y: Int) { |
||||
|
offsetX = x |
||||
|
offsetY = y |
||||
} |
} |
||||
|
|
||||
public Drawable getBackground() { |
|
||||
return background; |
|
||||
|
fun setFullsize(value: Boolean) { |
||||
|
fullSize = value |
||||
} |
} |
||||
|
|
||||
public void setFullsize(boolean value) { |
|
||||
fullSize = value; |
|
||||
|
override fun setColorFilter(colorFilter: ColorFilter?) { |
||||
|
icon?.colorFilter = colorFilter |
||||
} |
} |
||||
|
|
||||
@Override |
|
||||
public void setColorFilter(ColorFilter colorFilter) { |
|
||||
icon.setColorFilter(colorFilter); |
|
||||
|
override fun isStateful(): Boolean { |
||||
|
return icon?.isStateful ?: false |
||||
} |
} |
||||
|
|
||||
@Override |
|
||||
public boolean isStateful() { |
|
||||
return icon.isStateful(); |
|
||||
|
override fun setState(stateSet: IntArray): Boolean { |
||||
|
icon?.state = stateSet |
||||
|
return true |
||||
} |
} |
||||
|
|
||||
@Override |
|
||||
public boolean setState(@NonNull int[] stateSet) { |
|
||||
icon.setState(stateSet); |
|
||||
return true; |
|
||||
|
override fun getState(): IntArray { |
||||
|
return icon?.state ?: super.getState() |
||||
} |
} |
||||
|
|
||||
@NonNull |
|
||||
@Override |
|
||||
public int[] getState() { |
|
||||
return icon.getState(); |
|
||||
|
override fun onStateChange(state: IntArray): Boolean { |
||||
|
return true |
||||
} |
} |
||||
|
|
||||
@Override |
|
||||
protected boolean onStateChange(int[] state) { |
|
||||
return true; |
|
||||
|
override fun jumpToCurrentState() { |
||||
|
icon?.jumpToCurrentState() |
||||
} |
} |
||||
|
|
||||
@Override |
|
||||
public void jumpToCurrentState() { |
|
||||
icon.jumpToCurrentState(); |
|
||||
|
override fun getConstantState(): ConstantState? { |
||||
|
return icon?.constantState |
||||
} |
} |
||||
|
|
||||
@Override |
|
||||
public ConstantState getConstantState() { |
|
||||
return icon.getConstantState(); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void draw(@NonNull Canvas canvas) { |
|
||||
background.setBounds(getBounds()); |
|
||||
background.draw(canvas); |
|
||||
if (icon != null) { |
|
||||
if (fullSize) { |
|
||||
android.graphics.Rect bounds = getBounds(); |
|
||||
if (left != 0) { |
|
||||
icon.setBounds(bounds.left + left, bounds.top + top, bounds.right - left, bounds.bottom - top); |
|
||||
} else { |
|
||||
icon.setBounds(bounds); |
|
||||
} |
|
||||
|
override fun draw(canvas: Canvas) { |
||||
|
background.bounds = bounds |
||||
|
background.draw(canvas) |
||||
|
if (icon == null) return |
||||
|
if (fullSize) { |
||||
|
val bounds = bounds |
||||
|
if (left != 0) { |
||||
|
icon.setBounds(bounds.left + left, bounds.top + top, bounds.right - left, bounds.bottom - top) |
||||
|
} else { |
||||
|
icon.bounds = bounds |
||||
|
} |
||||
|
} else { |
||||
|
val x: Int |
||||
|
val y: Int |
||||
|
if (iconWidth != 0) { |
||||
|
x = bounds.centerX() - iconWidth / 2 + left + offsetX |
||||
|
y = bounds.centerY() - iconHeight / 2 + top + offsetY |
||||
|
icon.setBounds(x, y, x + iconWidth, y + iconHeight) |
||||
} else { |
} else { |
||||
int x; |
|
||||
int y; |
|
||||
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); |
|
||||
} 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,139 +1,114 @@ |
|||||
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(); |
|
||||
if (cookieStore == null || TextUtils.isEmpty(cookieRaw)) { |
|
||||
return; |
|
||||
} |
|
||||
if (cookieRaw.equals("LOGOUT")) { |
|
||||
cookieStore.removeAll(); |
|
||||
return; |
|
||||
} |
|
||||
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); |
|
||||
} |
|
||||
|
fun setupCookies(cookieRaw: String) { |
||||
|
val cookieStore = NET_COOKIE_MANAGER.cookieStore |
||||
|
if (cookieStore == null || TextUtils.isEmpty(cookieRaw)) { |
||||
|
return |
||||
} |
} |
||||
|
|
||||
public static void removeAllAccounts(final Context context, final RepositoryCallback<Void> callback) { |
|
||||
final CookieStore cookieStore = NET_COOKIE_MANAGER.getCookieStore(); |
|
||||
if (cookieStore == null) return; |
|
||||
cookieStore.removeAll(); |
|
||||
try { |
|
||||
AccountRepository.getInstance(AccountDataSource.getInstance(context)) |
|
||||
.deleteAllAccounts(callback); |
|
||||
} catch (Exception e) { |
|
||||
Log.e(TAG, "setupCookies", e); |
|
||||
} |
|
||||
|
if (cookieRaw == "LOGOUT") { |
||||
|
cookieStore.removeAll() |
||||
|
return |
||||
} |
} |
||||
|
|
||||
public static long getUserIdFromCookie(final String cookies) { |
|
||||
final String dsUserId = getCookieValue(cookies, "ds_user_id"); |
|
||||
if (dsUserId == null) { |
|
||||
return 0; |
|
||||
|
try { |
||||
|
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) |
||||
} |
} |
||||
try { |
|
||||
return Long.parseLong(dsUserId); |
|
||||
} catch (NumberFormatException e) { |
|
||||
Log.e(TAG, "getUserIdFromCookie: ", e); |
|
||||
} |
|
||||
return 0; |
|
||||
|
} catch (e: URISyntaxException) { |
||||
|
Log.e(TAG, "", e) |
||||
} |
} |
||||
|
} |
||||
|
|
||||
@Nullable |
|
||||
public static String getCsrfTokenFromCookie(final String cookies) { |
|
||||
return getCookieValue(cookies, "csrftoken"); |
|
||||
|
fun removeAllAccounts(context: Context?, callback: RepositoryCallback<Void?>?) { |
||||
|
val cookieStore = NET_COOKIE_MANAGER.cookieStore ?: return |
||||
|
cookieStore.removeAll() |
||||
|
try { |
||||
|
AccountRepository.getInstance(AccountDataSource.getInstance(context!!)) |
||||
|
.deleteAllAccounts(callback) |
||||
|
} catch (e: Exception) { |
||||
|
Log.e(TAG, "setupCookies", e) |
||||
} |
} |
||||
|
} |
||||
|
|
||||
@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; |
|
||||
|
fun getUserIdFromCookie(cookies: String?): Long { |
||||
|
val dsUserId = getCookieValue(cookies, "ds_user_id") ?: return 0 |
||||
|
try { |
||||
|
return dsUserId.toLong() |
||||
|
} catch (e: NumberFormatException) { |
||||
|
Log.e(TAG, "getUserIdFromCookie: ", e) |
||||
} |
} |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
@Nullable |
|
||||
public static String getCookie(@Nullable final String webViewUrl) { |
|
||||
final List<String> domains = new ArrayList<>(Arrays.asList( |
|
||||
"https://instagram.com", |
|
||||
"https://instagram.com/", |
|
||||
"http://instagram.com", |
|
||||
"http://instagram.com", |
|
||||
"https://www.instagram.com", |
|
||||
"https://www.instagram.com/", |
|
||||
"http://www.instagram.com", |
|
||||
"http://www.instagram.com/" |
|
||||
)); |
|
||||
if (!TextUtils.isEmpty(webViewUrl)) { |
|
||||
domains.add(0, webViewUrl); |
|
||||
} |
|
||||
|
fun getCsrfTokenFromCookie(cookies: String?): String? { |
||||
|
return getCookieValue(cookies, "csrftoken") |
||||
|
} |
||||
|
|
||||
return getLongestCookie(domains); |
|
||||
} |
|
||||
|
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 |
|
||||
private static String getLongestCookie(final List<String> domains) { |
|
||||
int longestLength = 0; |
|
||||
String longestCookie = null; |
|
||||
|
fun getCookie(webViewUrl: String?): String? { |
||||
|
val domains: List<String> = listOfNotNull( |
||||
|
if (!TextUtils.isEmpty(webViewUrl)) webViewUrl else null, |
||||
|
"https://instagram.com", |
||||
|
"https://instagram.com/", |
||||
|
"http://instagram.com", |
||||
|
"http://instagram.com", |
||||
|
"https://www.instagram.com", |
||||
|
"https://www.instagram.com/", |
||||
|
"http://www.instagram.com", |
||||
|
"http://www.instagram.com/", |
||||
|
) |
||||
|
return getLongestCookie(domains) |
||||
|
} |
||||
|
|
||||
for (final String domain : domains) { |
|
||||
final String cookie = COOKIE_MANAGER.getCookie(domain); |
|
||||
if (cookie != null) { |
|
||||
final int cookieLength = cookie.length(); |
|
||||
if (cookieLength > longestLength) { |
|
||||
longestCookie = cookie; |
|
||||
longestLength = cookieLength; |
|
||||
} |
|
||||
|
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) { |
||||
|
val cookieLength = cookie.length |
||||
|
if (cookieLength > longestLength) { |
||||
|
longestCookie = cookie |
||||
|
longestLength = cookieLength |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
return longestCookie; |
|
||||
} |
} |
||||
|
return longestCookie |
||||
} |
} |
@ -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); |
|
||||
|
@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("")) |
||||
} |
} |
||||
} |
} |
||||
return null; |
|
||||
|
return null |
||||
} |
} |
||||
|
|
||||
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; |
|
||||
} |
|
||||
|
data class DeepLinkPattern(val patternText: String) { |
||||
|
val pattern: Pattern = Pattern.compile(patternText, Pattern.LITERAL) |
||||
} |
} |
||||
|
|
||||
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; |
|
||||
} |
|
||||
|
|
||||
public String getValue() { |
|
||||
return value; |
|
||||
} |
|
||||
|
|
||||
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 |
// 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()); |
|
||||
} |
|
||||
|
|
||||
public static Locale getCurrentLocale() { |
|
||||
return currentLocale; |
|
||||
} |
|
||||
|
|
||||
public static void updateConfig(final ContextThemeWrapper wrapper) { |
|
||||
if (currentLocale != null) { |
|
||||
final Configuration configuration = new Configuration(); |
|
||||
configuration.locale = currentLocale; |
|
||||
configuration.setLocale(currentLocale); |
|
||||
wrapper.applyOverrideConfiguration(configuration); |
|
||||
|
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]) |
||||
|
} |
||||
|
else -> Locale(lang) |
||||
|
} |
||||
|
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; |
|
||||
|
@JvmStatic |
||||
|
fun updateConfig(wrapper: ContextThemeWrapper) { |
||||
|
if (currentLocale == null) return |
||||
|
val configuration = Configuration() |
||||
|
// configuration.locale = currentLocale |
||||
|
configuration.setLocale(currentLocale) |
||||
|
wrapper.applyOverrideConfiguration(configuration) |
||||
|
} |
||||
|
|
||||
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"; |
|
||||
|
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; |
|
||||
|
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; |
|
||||
} |
|
||||
|
|
||||
public static int getResultingWidth(final int requiredHeight, final int height, final int width) { |
|
||||
return requiredHeight * width / height; |
|
||||
} |
|
||||
|
fun getResultingHeight(requiredWidth: Int, height: Int, width: Int): Int { |
||||
|
return requiredWidth * height / width |
||||
|
} |
||||
|
|
||||
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(); |
|
||||
} |
|
||||
return r; |
|
||||
} |
|
||||
|
fun getResultingWidth(requiredHeight: Int, height: Int, width: Int): Int { |
||||
|
return requiredHeight * width / height |
||||
|
} |
||||
|
|
||||
@NonNull |
|
||||
public static NullSafePair<Integer, Integer> calculateWidthHeight(final int height, final int width, final int maxHeight, final int maxWidth) { |
|
||||
if (width > maxWidth) { |
|
||||
int tempHeight = getResultingHeight(maxWidth, height, width); |
|
||||
int tempWidth = maxWidth; |
|
||||
if (tempHeight > maxHeight) { |
|
||||
tempWidth = getResultingWidth(maxHeight, tempHeight, tempWidth); |
|
||||
tempHeight = maxHeight; |
|
||||
|
// 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 |
||||
} |
} |
||||
return new NullSafePair<>(tempWidth, tempHeight); |
|
||||
|
r += origin |
||||
} |
} |
||||
if ((height < maxHeight && width < maxWidth) || (height > maxHeight)) { |
|
||||
int tempWidth = getResultingWidth(maxHeight, height, width); |
|
||||
int tempHeight = maxHeight; |
|
||||
if (tempWidth > maxWidth) { |
|
||||
tempHeight = getResultingHeight(maxWidth, tempHeight, tempWidth); |
|
||||
tempWidth = maxWidth; |
|
||||
} |
|
||||
return new NullSafePair<>(tempWidth, tempHeight); |
|
||||
|
else -> { |
||||
|
// range not representable as long |
||||
|
while (r < origin || r >= bound) r = random.nextLong() |
||||
} |
} |
||||
return new NullSafePair<>(width, height); |
|
||||
} |
} |
||||
|
return r |
||||
|
} |
||||
|
|
||||
public static float roundFloat2Decimals(final float value) { |
|
||||
return ((int) ((value + (value >= 0 ? 1 : -1) * 0.005f) * 100)) / 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) { |
|
||||
// adapted from https://stackoverflow.com/a/9769590/1436766 |
|
||||
int threshold = 1000; |
|
||||
boolean addSpace = false; |
|
||||
if (options != null) { |
|
||||
threshold = options.getThreshold(); |
|
||||
addSpace = options.addSpaceBeforePrefix(); |
|
||||
|
fun calculateWidthHeight(height: Int, width: Int, maxHeight: Int, maxWidth: Int): NullSafePair<Int, Int> { |
||||
|
if (width > maxWidth) { |
||||
|
var tempHeight = getResultingHeight(maxWidth, height, width) |
||||
|
var tempWidth = maxWidth |
||||
|
if (tempHeight > maxHeight) { |
||||
|
tempWidth = getResultingWidth(maxHeight, tempHeight, tempWidth) |
||||
|
tempHeight = maxHeight |
||||
} |
} |
||||
if (number < threshold) return "" + number; |
|
||||
int exp = (int) (Math.log(number) / Math.log(threshold)); |
|
||||
return String.format(Locale.US, |
|
||||
"%.1f%s%c", |
|
||||
number / Math.pow(threshold, exp), |
|
||||
addSpace ? " " : "", |
|
||||
"kMGTPE".charAt(exp - 1)); |
|
||||
|
return NullSafePair(tempWidth, tempHeight) |
||||
} |
} |
||||
|
|
||||
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; |
|
||||
|
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 |
||||
} |
} |
||||
|
return NullSafePair(tempWidth, tempHeight) |
||||
|
} |
||||
|
return NullSafePair(width, height) |
||||
|
} |
||||
|
|
||||
public int getThreshold() { |
|
||||
return threshold; |
|
||||
} |
|
||||
|
fun roundFloat2Decimals(value: Float): Float { |
||||
|
return ((value + (if (value >= 0) 1 else -1) * 0.005f) * 100).toInt() / 100f |
||||
|
} |
||||
|
|
||||
public boolean addSpaceBeforePrefix() { |
|
||||
return addSpaceBeforePrefix; |
|
||||
} |
|
||||
|
fun abbreviate(number: Long, options: AbbreviateOptions? = null): String { |
||||
|
// adapted from https://stackoverflow.com/a/9769590/1436766 |
||||
|
var threshold = 1000 |
||||
|
var addSpace = false |
||||
|
if (options != null) { |
||||
|
threshold = options.threshold |
||||
|
addSpace = options.addSpaceBeforePrefix |
||||
} |
} |
||||
|
if (number < threshold) return "" + number |
||||
|
val exp = (ln(number.toDouble()) / ln(threshold.toDouble())).toInt() |
||||
|
return String.format( |
||||
|
Locale.US, |
||||
|
"%.1f%s%c", |
||||
|
number / threshold.toDouble().pow(exp.toDouble()), |
||||
|
if (addSpace) " " else "", |
||||
|
"kMGTPE"[exp - 1] |
||||
|
) |
||||
} |
} |
||||
|
|
||||
|
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(); |
|
||||
|
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() |
||||
} |
} |
||||
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; |
|
||||
|
val isExpired: Boolean |
||||
|
get() { |
||||
|
if (lastUpdatedOn == null || response == null) return true |
||||
|
val expiresInSecs = response!!.expires |
||||
|
return LocalDateTime.now().isAfter(lastUpdatedOn!!.plus(expiresInSecs, ChronoUnit.SECONDS)) |
||||
} |
} |
||||
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; |
|
||||
} |
|
||||
} |
} |
@ -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); |
|
||||
} |
|
||||
} |
|
||||
|
/** |
||||
|
* Constructor for a Pair. |
||||
|
* |
||||
|
* @param first the first object in the Pair |
||||
|
* @param second the second object in the pair |
||||
|
*/ |
||||
|
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() |
||||
} |
} |
||||
} |
} |
@ -1,85 +1,82 @@ |
|||||
package awais.instagrabber.utils; |
|
||||
|
@file:JvmName("UserAgentUtils") |
||||
|
|
||||
import androidx.annotation.NonNull; |
|
||||
|
package awais.instagrabber.utils |
||||
|
|
||||
public class UserAgentUtils { |
|
||||
|
/* GraphQL user agents (which are just standard browser UA"s). |
||||
|
* Go to https://www.whatismybrowser.com/guides/the-latest-user-agent/ to update it |
||||
|
* Windows first (Assume win64 not wow64): Chrome, Firefox, Edge |
||||
|
* Then macOS: Chrome, Firefox, Safari |
||||
|
*/ |
||||
|
@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; 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 (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_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1 Safari/605.1.15" |
||||
|
) |
||||
|
|
||||
/* GraphQL user agents (which are just standard browser UA"s). |
|
||||
* Go to https://www.whatismybrowser.com/guides/the-latest-user-agent/ to update it |
|
||||
* Windows first (Assume win64 not wow64): Chrome, Firefox, Edge |
|
||||
* Then macOS: Chrome, Firefox, Safari |
|
||||
*/ |
|
||||
public static final String[] browsers = { |
|
||||
"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) 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; 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" |
|
||||
}; |
|
||||
// use APKpure, assume arm64-v8a |
|
||||
private static final String igVersion = "188.0.0.35.124"; |
|
||||
private static final String igVersionCode = "292080186"; |
|
||||
// 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 |
|
||||
"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; 480dpi; 2880x5884; Xiaomi; Redmi Note 4; nikel; mt6797", |
|
||||
"24/7.0; 480dpi; 2880x5884; Xiaomi/xiaomi; Redmi Note 4; mido; qcom", |
|
||||
"23/6.0; 480dpi; 2880x5884; Xiaomi; Redmi Note 4X; nikel; mt6797", |
|
||||
"27/8.1.0; 440dpi; 2880x5884; Xiaomi/xiaomi; Redmi Note 5; whyred; qcom", |
|
||||
"23/6.0.1; 480dpi; 2880x5884; Xiaomi; Redmi 4; markw; qcom", |
|
||||
"27/8.1.0; 440dpi; 2880x5884; Xiaomi/xiaomi; Redmi 5 Plus; vince; qcom", |
|
||||
"25/7.1.2; 440dpi; 2880x5884; Xiaomi/xiaomi; Redmi 5 Plus; vince; qcom", |
|
||||
"26/8.0.0; 480dpi; 2880x5884; Xiaomi; MI 5; gemini; qcom", |
|
||||
"27/8.1.0; 480dpi; 2880x5884; Xiaomi/xiaomi; Mi A1; tissot_sprout; qcom", |
|
||||
"26/8.0.0; 480dpi; 2880x5884; Xiaomi; MI 6; sagit; qcom", |
|
||||
"25/7.1.1; 440dpi; 2880x5884; Xiaomi; MI MAX 2; oxygen; qcom", |
|
||||
"24/7.0; 480dpi; 2880x5884; Xiaomi; MI 5s; capricorn; qcom", |
|
||||
"26/8.0.0; 480dpi; 2880x5884; samsung; SM-A520F; a5y17lte; samsungexynos7880", |
|
||||
"26/8.0.0; 480dpi; 2880x5884; samsung; SM-G950F; dreamlte; samsungexynos8895", |
|
||||
"26/8.0.0; 640dpi; 2880x5884; samsung; SM-G950F; dreamlte; samsungexynos8895", |
|
||||
"26/8.0.0; 420dpi; 2880x5884; samsung; SM-G955F; dream2lte; samsungexynos8895", |
|
||||
"26/8.0.0; 560dpi; 2880x5884; samsung; SM-G955F; dream2lte; samsungexynos8895", |
|
||||
"24/7.0; 480dpi; 2880x5884; samsung; SM-A510F; a5xelte; samsungexynos7580", |
|
||||
"26/8.0.0; 480dpi; 2880x5884; samsung; SM-G930F; herolte; samsungexynos8890", |
|
||||
"26/8.0.0; 480dpi; 2880x5884; samsung; SM-G935F; hero2lte; samsungexynos8890", |
|
||||
"26/8.0.0; 420dpi; 2880x5884; samsung; SM-G965F; star2lte; samsungexynos9810", |
|
||||
"26/8.0.0; 480dpi; 2880x5884; samsung; SM-A530F; jackpotlte; samsungexynos7885", |
|
||||
"24/7.0; 640dpi; 2880x5884; samsung; SM-G925F; zerolte; samsungexynos7420", |
|
||||
"26/8.0.0; 420dpi; 2880x5884; samsung; SM-A720F; a7y17lte; samsungexynos7880", |
|
||||
"24/7.0; 640dpi; 2880x5884; samsung; SM-G920F; zeroflte; samsungexynos7420", |
|
||||
"24/7.0; 420dpi; 2880x5884; samsung; SM-J730FM; j7y17lte; samsungexynos7870", |
|
||||
"26/8.0.0; 480dpi; 2880x5884; samsung; SM-G960F; starlte; samsungexynos9810", |
|
||||
"26/8.0.0; 420dpi; 2880x5884; samsung; SM-N950F; greatlte; samsungexynos8895", |
|
||||
"26/8.0.0; 420dpi; 2880x5884; samsung; SM-A730F; jackpot2lte; samsungexynos7885", |
|
||||
"26/8.0.0; 420dpi; 2880x5884; samsung; SM-A605FN; a6plte; qcom", |
|
||||
"26/8.0.0; 480dpi; 2880x5884; HUAWEI/HONOR; STF-L09; HWSTF; hi3660", |
|
||||
"27/8.1.0; 480dpi; 2880x5884; HUAWEI/HONOR; COL-L29; HWCOL; kirin970", |
|
||||
"26/8.0.0; 480dpi; 2880x5884; HUAWEI/HONOR; LLD-L31; HWLLD-H; hi6250", |
|
||||
"26/8.0.0; 480dpi; 2880x5884; HUAWEI; ANE-LX1; HWANE; 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", |
|
||||
"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; 640dpi; 2880x5884; LGE/lge; RS988; h1; h1", |
|
||||
"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; samsung; SM-G935F; hero2lte; samsungexynos8890", |
|
||||
"23/6.0.1; 640dpi; 2880x5884; samsung; SM-G930F; herolte; samsungexynos8890" |
|
||||
}; |
|
||||
|
// use APKpure, assume arm64-v8a |
||||
|
private const val igVersion = "188.0.0.35.124" |
||||
|
private const val igVersionCode = "292080186" |
||||
|
|
||||
@NonNull |
|
||||
public static String generateBrowserUA(final int code) { |
|
||||
return browsers[code]; |
|
||||
} |
|
||||
|
// you can pick *any* device as long as you LEAVE OUT the resolution for maximum download quality |
||||
|
// 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", |
||||
|
"23/6.0.1; 480dpi; 2880x5884; Xiaomi; Redmi Note 3; kenzo; qcom", |
||||
|
"23/6.0; 480dpi; 2880x5884; Xiaomi; Redmi Note 4; nikel; mt6797", |
||||
|
"24/7.0; 480dpi; 2880x5884; Xiaomi/xiaomi; Redmi Note 4; mido; qcom", |
||||
|
"23/6.0; 480dpi; 2880x5884; Xiaomi; Redmi Note 4X; nikel; mt6797", |
||||
|
"27/8.1.0; 440dpi; 2880x5884; Xiaomi/xiaomi; Redmi Note 5; whyred; qcom", |
||||
|
"23/6.0.1; 480dpi; 2880x5884; Xiaomi; Redmi 4; markw; qcom", |
||||
|
"27/8.1.0; 440dpi; 2880x5884; Xiaomi/xiaomi; Redmi 5 Plus; vince; qcom", |
||||
|
"25/7.1.2; 440dpi; 2880x5884; Xiaomi/xiaomi; Redmi 5 Plus; vince; qcom", |
||||
|
"26/8.0.0; 480dpi; 2880x5884; Xiaomi; MI 5; gemini; qcom", |
||||
|
"27/8.1.0; 480dpi; 2880x5884; Xiaomi/xiaomi; Mi A1; tissot_sprout; qcom", |
||||
|
"26/8.0.0; 480dpi; 2880x5884; Xiaomi; MI 6; sagit; qcom", |
||||
|
"25/7.1.1; 440dpi; 2880x5884; Xiaomi; MI MAX 2; oxygen; qcom", |
||||
|
"24/7.0; 480dpi; 2880x5884; Xiaomi; MI 5s; capricorn; qcom", |
||||
|
"26/8.0.0; 480dpi; 2880x5884; samsung; SM-A520F; a5y17lte; samsungexynos7880", |
||||
|
"26/8.0.0; 480dpi; 2880x5884; samsung; SM-G950F; dreamlte; samsungexynos8895", |
||||
|
"26/8.0.0; 640dpi; 2880x5884; samsung; SM-G950F; dreamlte; samsungexynos8895", |
||||
|
"26/8.0.0; 420dpi; 2880x5884; samsung; SM-G955F; dream2lte; samsungexynos8895", |
||||
|
"26/8.0.0; 560dpi; 2880x5884; samsung; SM-G955F; dream2lte; samsungexynos8895", |
||||
|
"24/7.0; 480dpi; 2880x5884; samsung; SM-A510F; a5xelte; samsungexynos7580", |
||||
|
"26/8.0.0; 480dpi; 2880x5884; samsung; SM-G930F; herolte; samsungexynos8890", |
||||
|
"26/8.0.0; 480dpi; 2880x5884; samsung; SM-G935F; hero2lte; samsungexynos8890", |
||||
|
"26/8.0.0; 420dpi; 2880x5884; samsung; SM-G965F; star2lte; samsungexynos9810", |
||||
|
"26/8.0.0; 480dpi; 2880x5884; samsung; SM-A530F; jackpotlte; samsungexynos7885", |
||||
|
"24/7.0; 640dpi; 2880x5884; samsung; SM-G925F; zerolte; samsungexynos7420", |
||||
|
"26/8.0.0; 420dpi; 2880x5884; samsung; SM-A720F; a7y17lte; samsungexynos7880", |
||||
|
"24/7.0; 640dpi; 2880x5884; samsung; SM-G920F; zeroflte; samsungexynos7420", |
||||
|
"24/7.0; 420dpi; 2880x5884; samsung; SM-J730FM; j7y17lte; samsungexynos7870", |
||||
|
"26/8.0.0; 480dpi; 2880x5884; samsung; SM-G960F; starlte; samsungexynos9810", |
||||
|
"26/8.0.0; 420dpi; 2880x5884; samsung; SM-N950F; greatlte; samsungexynos8895", |
||||
|
"26/8.0.0; 420dpi; 2880x5884; samsung; SM-A730F; jackpot2lte; samsungexynos7885", |
||||
|
"26/8.0.0; 420dpi; 2880x5884; samsung; SM-A605FN; a6plte; qcom", |
||||
|
"26/8.0.0; 480dpi; 2880x5884; HUAWEI/HONOR; STF-L09; HWSTF; hi3660", |
||||
|
"27/8.1.0; 480dpi; 2880x5884; HUAWEI/HONOR; COL-L29; HWCOL; kirin970", |
||||
|
"26/8.0.0; 480dpi; 2880x5884; HUAWEI/HONOR; LLD-L31; HWLLD-H; hi6250", |
||||
|
"26/8.0.0; 480dpi; 2880x5884; HUAWEI; ANE-LX1; HWANE; 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", |
||||
|
"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; 640dpi; 2880x5884; LGE/lge; RS988; h1; h1", |
||||
|
"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; samsung; SM-G935F; hero2lte; samsungexynos8890", |
||||
|
"23/6.0.1; 640dpi; 2880x5884; samsung; SM-G930F; herolte; samsungexynos8890" |
||||
|
) |
||||
|
|
||||
@NonNull |
|
||||
public static String generateAppUA(final int code, final String lang) { |
|
||||
return "Instagram " + igVersion + " Android (" + devices[code] + "; " + lang + "; " + igVersionCode + ")"; |
|
||||
} |
|
||||
|
fun generateBrowserUA(code: Int): String { |
||||
|
return browsers[code] |
||||
|
} |
||||
|
|
||||
|
fun generateAppUA(code: Int, lang: String): String { |
||||
|
return "Instagram " + igVersion + " Android (" + devices[code] + "; " + lang + "; " + igVersionCode + ")" |
||||
} |
} |
@ -1,121 +1,117 @@ |
|||||
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) { |
|
||||
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()); |
|
||||
} |
|
||||
|
fun measure(view: View, parent: View): Pair<Int, Int> { |
||||
|
view.measure( |
||||
|
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) |
||||
|
} |
||||
|
|
||||
/** |
|
||||
* Creates [SpringAnimation] for object. |
|
||||
* If finalPosition is not [Float.NaN] then create [SpringAnimation] with |
|
||||
* [SpringForce.mFinalPosition]. |
|
||||
* |
|
||||
* @param object Object |
|
||||
* @param property object's property to be animated. |
|
||||
* @param finalPosition [SpringForce.mFinalPosition] Final position of spring. |
|
||||
* @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); |
|
||||
} |
|
||||
|
/** |
||||
|
* Creates [SpringAnimation] for object. |
||||
|
* If finalPosition is not [Float.NaN] then create [SpringAnimation] with |
||||
|
* [SpringForce.mFinalPosition]. |
||||
|
* |
||||
|
* @param object Object |
||||
|
* @param property object's property to be animated. |
||||
|
* @param finalPosition [SpringForce.mFinalPosition] Final position of spring. |
||||
|
* @return [SpringAnimation] |
||||
|
*/ |
||||
|
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"); |
|
||||
if (Build.VERSION.SDK_INT >= 29) { |
|
||||
$this$suppressLayoutCompat.suppressLayout(suppress); |
|
||||
} else { |
|
||||
hiddenSuppressLayout($this$suppressLayoutCompat, suppress); |
|
||||
} |
|
||||
|
fun suppressLayoutCompat(`$this$suppressLayoutCompat`: ViewGroup, suppress: Boolean) { |
||||
|
Intrinsics.checkNotNullParameter(`$this$suppressLayoutCompat`, "\$this\$suppressLayoutCompat") |
||||
|
if (Build.VERSION.SDK_INT >= 29) { |
||||
|
`$this$suppressLayoutCompat`.suppressLayout(suppress) |
||||
|
} else { |
||||
|
hiddenSuppressLayout(`$this$suppressLayoutCompat`, suppress) |
||||
} |
} |
||||
|
} |
||||
|
|
||||
private static boolean tryHiddenSuppressLayout = true; |
|
||||
|
private var tryHiddenSuppressLayout = true |
||||
|
|
||||
@SuppressLint({"NewApi"}) |
|
||||
private static void hiddenSuppressLayout(ViewGroup group, boolean suppress) { |
|
||||
if (tryHiddenSuppressLayout) { |
|
||||
try { |
|
||||
group.suppressLayout(suppress); |
|
||||
} catch (NoSuchMethodError var3) { |
|
||||
tryHiddenSuppressLayout = false; |
|
||||
} |
|
||||
|
@SuppressLint("NewApi") |
||||
|
private fun hiddenSuppressLayout(group: ViewGroup, suppress: Boolean) { |
||||
|
if (tryHiddenSuppressLayout) { |
||||
|
try { |
||||
|
group.suppressLayout(suppress) |
||||
|
} catch (var3: NoSuchMethodError) { |
||||
|
tryHiddenSuppressLayout = false |
||||
} |
} |
||||
} |
} |
||||
} |
} |
@ -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