Browse Source
Break down Utils into separate utility classes as Utils was getting bloated
renovate/org.robolectric-robolectric-4.x
Break down Utils into separate utility classes as Utils was getting bloated
renovate/org.robolectric-robolectric-4.x
89 changed files with 1649 additions and 1484 deletions
-
2app/src/main/java/awais/instagrabber/InstaGrabberApplication.java
-
11app/src/main/java/awais/instagrabber/activities/Login.java
-
11app/src/main/java/awais/instagrabber/activities/MainActivity.java
-
5app/src/main/java/awais/instagrabber/activities/NotificationsViewer.java
-
4app/src/main/java/awais/instagrabber/adapters/CommentsAdapter.java
-
13app/src/main/java/awais/instagrabber/adapters/FollowAdapter.java
-
9app/src/main/java/awais/instagrabber/adapters/viewholder/PostViewerViewHolder.java
-
7app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageItemViewHolder.java
-
10app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageLinkViewHolder.java
-
4app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageRavenMediaViewHolder.java
-
6app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageReelShareViewHolder.java
-
4app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageStoryShareViewHolder.java
-
6app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageTextViewHolder.java
-
6app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageVoiceMediaViewHolder.java
-
20app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedItemViewHolder.java
-
3app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedPhotoViewHolder.java
-
3app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedVideoViewHolder.java
-
6app/src/main/java/awais/instagrabber/asyncs/CommentAction.java
-
19app/src/main/java/awais/instagrabber/asyncs/CommentsFetcher.java
-
14app/src/main/java/awais/instagrabber/asyncs/DiscoverFetcher.java
-
6app/src/main/java/awais/instagrabber/asyncs/DownloadAsync.java
-
12app/src/main/java/awais/instagrabber/asyncs/FeedFetcher.java
-
4app/src/main/java/awais/instagrabber/asyncs/FeedStoriesFetcher.java
-
6app/src/main/java/awais/instagrabber/asyncs/FollowFetcher.java
-
11app/src/main/java/awais/instagrabber/asyncs/GetActivityAsyncTask.java
-
4app/src/main/java/awais/instagrabber/asyncs/HashtagFetcher.java
-
4app/src/main/java/awais/instagrabber/asyncs/HighlightsFetcher.java
-
7app/src/main/java/awais/instagrabber/asyncs/ImageUploader.java
-
6app/src/main/java/awais/instagrabber/asyncs/LocationFetcher.java
-
38app/src/main/java/awais/instagrabber/asyncs/NotificationsFetcher.java
-
23app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java
-
9app/src/main/java/awais/instagrabber/asyncs/PostsFetcher.java
-
7app/src/main/java/awais/instagrabber/asyncs/ProfileFetcher.java
-
14app/src/main/java/awais/instagrabber/asyncs/ProfilePictureFetcher.java
-
3app/src/main/java/awais/instagrabber/asyncs/QuizAction.java
-
3app/src/main/java/awais/instagrabber/asyncs/RespondAction.java
-
4app/src/main/java/awais/instagrabber/asyncs/SeenAction.java
-
4app/src/main/java/awais/instagrabber/asyncs/SuggestionsFetcher.java
-
4app/src/main/java/awais/instagrabber/asyncs/UsernameFetcher.java
-
12app/src/main/java/awais/instagrabber/asyncs/direct_messages/DirectMessageInboxThreadFetcher.java
-
5app/src/main/java/awais/instagrabber/asyncs/direct_messages/DirectThreadBroadcaster.java
-
10app/src/main/java/awais/instagrabber/asyncs/direct_messages/InboxFetcher.java
-
22app/src/main/java/awais/instagrabber/asyncs/i/iLikedFetcher.java
-
24app/src/main/java/awais/instagrabber/asyncs/i/iPostFetcher.java
-
7app/src/main/java/awais/instagrabber/asyncs/i/iStoryStatusFetcher.java
-
4app/src/main/java/awais/instagrabber/asyncs/i/iTopicFetcher.java
-
4app/src/main/java/awais/instagrabber/customviews/RamboTextView.java
-
4app/src/main/java/awais/instagrabber/customviews/helpers/ImageResizingControllerListener.java
-
7app/src/main/java/awais/instagrabber/dialogs/AboutDialog.java
-
9app/src/main/java/awais/instagrabber/dialogs/ProfilePicDialogFragment.java
-
15app/src/main/java/awais/instagrabber/dialogs/QuickAccessDialog.java
-
15app/src/main/java/awais/instagrabber/dialogs/SettingsDialog.java
-
6app/src/main/java/awais/instagrabber/dialogs/TimeSettingsDialog.java
-
17app/src/main/java/awais/instagrabber/directdownload/DirectDownload.java
-
4app/src/main/java/awais/instagrabber/directdownload/MultiDirectDialog.java
-
24app/src/main/java/awais/instagrabber/fragments/CommentsViewerFragment.java
-
4app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java
-
16app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java
-
25app/src/main/java/awais/instagrabber/fragments/LocationFragment.java
-
30app/src/main/java/awais/instagrabber/fragments/PostViewFragment.java
-
14app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java
-
33app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java
-
4app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java
-
3app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java
-
13app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java
-
9app/src/main/java/awais/instagrabber/fragments/main/DiscoverFragment.java
-
26app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java
-
55app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java
-
18app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java
-
8app/src/main/java/awais/instagrabber/fragments/settings/SettingsPreferencesFragment.java
-
3app/src/main/java/awais/instagrabber/models/CommentModel.java
-
3app/src/main/java/awais/instagrabber/models/NotificationModel.java
-
3app/src/main/java/awais/instagrabber/models/direct_messages/DirectItemModel.java
-
33app/src/main/java/awais/instagrabber/models/enums/NotificationType.java
-
2app/src/main/java/awais/instagrabber/services/MediaService.java
-
4app/src/main/java/awais/instagrabber/services/StoriesService.java
-
3app/src/main/java/awais/instagrabber/utils/Constants.java
-
148app/src/main/java/awais/instagrabber/utils/CookieUtils.java
-
8app/src/main/java/awais/instagrabber/utils/DataBox.java
-
8app/src/main/java/awais/instagrabber/utils/DirectoryChooser.java
-
218app/src/main/java/awais/instagrabber/utils/DownloadUtils.java
-
14app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java
-
4app/src/main/java/awais/instagrabber/utils/LocaleUtils.java
-
50app/src/main/java/awais/instagrabber/utils/NetworkUtils.java
-
56app/src/main/java/awais/instagrabber/utils/NumberUtils.java
-
562app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java
-
112app/src/main/java/awais/instagrabber/utils/TextUtils.java
-
2app/src/main/java/awais/instagrabber/utils/UpdateChecker.java
-
1121app/src/main/java/awais/instagrabber/utils/Utils.java
@ -18,6 +18,7 @@ import androidx.appcompat.widget.LinearLayoutCompat; |
|||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment; |
|||
|
|||
import awais.instagrabber.R; |
|||
import awais.instagrabber.utils.TextUtils; |
|||
import awais.instagrabber.utils.Utils; |
|||
|
|||
public final class AboutDialog extends BottomSheetDialogFragment { |
|||
@ -36,7 +37,7 @@ public final class AboutDialog extends BottomSheetDialogFragment { |
|||
final Intent intent = new Intent(Intent.ACTION_VIEW); |
|||
if (v == btnTelegram) { |
|||
intent.setData(Uri.parse("https://t.me/grabber_app")); |
|||
if (!Utils.isEmpty(Utils.telegramPackage)) |
|||
if (!TextUtils.isEmpty(Utils.telegramPackage)) |
|||
intent.setPackage(Utils.telegramPackage); |
|||
} |
|||
else if (v == btnMatrix) { |
|||
@ -50,7 +51,7 @@ public final class AboutDialog extends BottomSheetDialogFragment { |
|||
btnMatrix.setOnClickListener(onClickListener); |
|||
|
|||
final String description = getString(R.string.description); |
|||
if (!Utils.isEmpty(description)) { |
|||
if (!TextUtils.isEmpty(description)) { |
|||
final SpannableStringBuilder descriptionText = new SpannableStringBuilder(description, 0, description.length()); |
|||
|
|||
int lastIndex = descriptionText.length() / 2; |
|||
@ -72,7 +73,7 @@ public final class AboutDialog extends BottomSheetDialogFragment { |
|||
} |
|||
} |
|||
|
|||
lastIndex = Utils.indexOfChar(descriptionText, '@', lastIndex) - 12; |
|||
lastIndex = TextUtils.indexOfChar(descriptionText, '@', lastIndex) - 12; |
|||
descriptionText.setSpan(new URLSpan("mailto:[email protected]"), lastIndex, lastIndex + 27, 0); |
|||
|
|||
final TextView textView = (TextView) infoContainer.getChildAt(0); |
|||
|
@ -1,11 +1,34 @@ |
|||
package awais.instagrabber.models.enums; |
|||
|
|||
import java.io.Serializable; |
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
|
|||
public enum NotificationType implements Serializable { |
|||
LIKE, |
|||
FOLLOW, |
|||
COMMENT, |
|||
MENTION, |
|||
REQUEST |
|||
LIKE("GraphLikeAggregatedStory"), |
|||
FOLLOW("GraphFollowAggregatedStory"), |
|||
COMMENT("GraphCommentMediaStory"), |
|||
MENTION("GraphMentionStory"), |
|||
REQUEST("REQUEST"); |
|||
|
|||
private final String itemType; |
|||
private static Map<String, NotificationType> map = new HashMap<>(); |
|||
|
|||
static { |
|||
for (NotificationType type : NotificationType.values()) { |
|||
map.put(type.itemType, type); |
|||
} |
|||
} |
|||
|
|||
NotificationType(final String itemType) { |
|||
this.itemType = itemType; |
|||
} |
|||
|
|||
public String getItemType() { |
|||
return itemType; |
|||
} |
|||
|
|||
public static NotificationType valueOfType(final String itemType) { |
|||
return map.get(itemType); |
|||
} |
|||
} |
@ -0,0 +1,148 @@ |
|||
package awais.instagrabber.utils; |
|||
|
|||
import android.util.Log; |
|||
import android.webkit.CookieManager; |
|||
|
|||
import androidx.annotation.Nullable; |
|||
|
|||
import java.net.CookiePolicy; |
|||
import java.net.CookieStore; |
|||
import java.net.HttpCookie; |
|||
import java.net.URI; |
|||
import java.net.URISyntaxException; |
|||
|
|||
import awais.instagrabber.BuildConfig; |
|||
import awaisomereport.LogCollector; |
|||
|
|||
public final class CookieUtils { |
|||
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(); |
|||
Utils.dataBox.deleteAllUserCookies(); |
|||
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("AWAISKING_APP", "", e); |
|||
} |
|||
} |
|||
|
|||
@Nullable |
|||
public static String getUserIdFromCookie(final String cookie) { |
|||
if (!TextUtils.isEmpty(cookie)) { |
|||
final int uidIndex = cookie.indexOf("ds_user_id="); |
|||
if (uidIndex > 0) { |
|||
String uid = cookie.split("ds_user_id=")[1].split(";")[0]; |
|||
return !TextUtils.isEmpty(uid) ? uid : null; |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
public static String getCsrfTokenFromCookie(final String cookie) { |
|||
if (cookie == null) { |
|||
return null; |
|||
} |
|||
return cookie.split("csrftoken=")[1].split(";")[0]; |
|||
} |
|||
|
|||
@Nullable |
|||
public static String getCookie(@Nullable final String webViewUrl) { |
|||
int lastLongestCookieLength = 0; |
|||
String mainCookie = null; |
|||
|
|||
String cookie; |
|||
if (!TextUtils.isEmpty(webViewUrl)) { |
|||
cookie = COOKIE_MANAGER.getCookie(webViewUrl); |
|||
if (cookie != null) { |
|||
final int cookieLen = cookie.length(); |
|||
if (cookieLen > lastLongestCookieLength) { |
|||
mainCookie = cookie; |
|||
lastLongestCookieLength = cookieLen; |
|||
} |
|||
} |
|||
} |
|||
cookie = COOKIE_MANAGER.getCookie("https://instagram.com"); |
|||
if (cookie != null) { |
|||
final int cookieLen = cookie.length(); |
|||
if (cookieLen > lastLongestCookieLength) { |
|||
mainCookie = cookie; |
|||
lastLongestCookieLength = cookieLen; |
|||
} |
|||
} |
|||
cookie = COOKIE_MANAGER.getCookie("https://instagram.com/"); |
|||
if (cookie != null) { |
|||
final int cookieLen = cookie.length(); |
|||
if (cookieLen > lastLongestCookieLength) { |
|||
mainCookie = cookie; |
|||
lastLongestCookieLength = cookieLen; |
|||
} |
|||
} |
|||
cookie = COOKIE_MANAGER.getCookie("http://instagram.com"); |
|||
if (cookie != null) { |
|||
final int cookieLen = cookie.length(); |
|||
if (cookieLen > lastLongestCookieLength) { |
|||
mainCookie = cookie; |
|||
lastLongestCookieLength = cookieLen; |
|||
} |
|||
} |
|||
cookie = COOKIE_MANAGER.getCookie("http://instagram.com/"); |
|||
if (cookie != null) { |
|||
final int cookieLen = cookie.length(); |
|||
if (cookieLen > lastLongestCookieLength) { |
|||
mainCookie = cookie; |
|||
lastLongestCookieLength = cookieLen; |
|||
} |
|||
} |
|||
cookie = COOKIE_MANAGER.getCookie("https://www.instagram.com"); |
|||
if (cookie != null) { |
|||
final int cookieLen = cookie.length(); |
|||
if (cookieLen > lastLongestCookieLength) { |
|||
mainCookie = cookie; |
|||
lastLongestCookieLength = cookieLen; |
|||
} |
|||
} |
|||
cookie = COOKIE_MANAGER.getCookie("https://www.instagram.com/"); |
|||
if (cookie != null) { |
|||
final int cookieLen = cookie.length(); |
|||
if (cookieLen > lastLongestCookieLength) { |
|||
mainCookie = cookie; |
|||
lastLongestCookieLength = cookieLen; |
|||
} |
|||
} |
|||
cookie = COOKIE_MANAGER.getCookie("http://www.instagram.com"); |
|||
if (cookie != null) { |
|||
final int cookieLen = cookie.length(); |
|||
if (cookieLen > lastLongestCookieLength) { |
|||
mainCookie = cookie; |
|||
lastLongestCookieLength = cookieLen; |
|||
} |
|||
} |
|||
cookie = COOKIE_MANAGER.getCookie("http://www.instagram.com/"); |
|||
if (cookie != null && cookie.length() > lastLongestCookieLength) mainCookie = cookie; |
|||
|
|||
return mainCookie; |
|||
} |
|||
} |
@ -0,0 +1,218 @@ |
|||
package awais.instagrabber.utils; |
|||
|
|||
import android.Manifest; |
|||
import android.app.Activity; |
|||
import android.content.Context; |
|||
import android.content.pm.PackageManager; |
|||
import android.os.AsyncTask; |
|||
import android.os.Environment; |
|||
import android.util.Log; |
|||
import android.util.Pair; |
|||
import android.widget.Toast; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.annotation.Nullable; |
|||
import androidx.core.app.ActivityCompat; |
|||
import androidx.core.content.ContextCompat; |
|||
|
|||
import java.io.File; |
|||
import java.io.FilenameFilter; |
|||
import java.util.List; |
|||
import java.util.regex.Pattern; |
|||
|
|||
import awais.instagrabber.BuildConfig; |
|||
import awais.instagrabber.R; |
|||
import awais.instagrabber.asyncs.DownloadAsync; |
|||
import awais.instagrabber.asyncs.PostFetcher; |
|||
import awais.instagrabber.models.BasePostModel; |
|||
import awais.instagrabber.models.StoryModel; |
|||
import awais.instagrabber.models.direct_messages.DirectItemModel; |
|||
import awais.instagrabber.models.enums.DownloadMethod; |
|||
import awais.instagrabber.models.enums.MediaItemType; |
|||
import awaisomereport.LogCollector; |
|||
|
|||
import static awais.instagrabber.utils.Constants.FOLDER_PATH; |
|||
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO; |
|||
|
|||
public final class DownloadUtils { |
|||
public static final String[] PERMS = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}; |
|||
|
|||
public static void batchDownload(@NonNull final Context context, @Nullable String username, final DownloadMethod method, |
|||
final List<? extends BasePostModel> itemsToDownload) { |
|||
if (Utils.settingsHelper == null) Utils.settingsHelper = new SettingsHelper(context); |
|||
|
|||
if (itemsToDownload == null || itemsToDownload.size() < 1) return; |
|||
|
|||
if (username != null && username.charAt(0) == '@') username = username.substring(1); |
|||
|
|||
if (ContextCompat.checkSelfPermission(context, PERMS[0]) == PackageManager.PERMISSION_GRANTED) |
|||
batchDownloadImpl(context, username, method, itemsToDownload); |
|||
else if (context instanceof Activity) |
|||
ActivityCompat.requestPermissions((Activity) context, PERMS, 8020); |
|||
} |
|||
|
|||
private static void batchDownloadImpl(@NonNull final Context context, |
|||
@Nullable final String username, |
|||
final DownloadMethod method, |
|||
final List<? extends BasePostModel> itemsToDownload) { |
|||
File dir = new File(Environment.getExternalStorageDirectory(), "Download"); |
|||
|
|||
if (Utils.settingsHelper.getBoolean(FOLDER_SAVE_TO)) { |
|||
final String customPath = Utils.settingsHelper.getString(FOLDER_PATH); |
|||
if (!TextUtils.isEmpty(customPath)) dir = new File(customPath); |
|||
} |
|||
|
|||
if (Utils.settingsHelper.getBoolean(Constants.DOWNLOAD_USER_FOLDER) && !TextUtils.isEmpty(username)) |
|||
dir = new File(dir, username); |
|||
|
|||
if (!dir.exists() && !dir.mkdirs()) { |
|||
Toast.makeText(context, R.string.error_creating_folders, Toast.LENGTH_SHORT).show(); |
|||
return; |
|||
} |
|||
boolean checkEachPost = false; |
|||
switch (method) { |
|||
case DOWNLOAD_SAVED: |
|||
case DOWNLOAD_MAIN: |
|||
checkEachPost = true; |
|||
break; |
|||
case DOWNLOAD_FEED: |
|||
checkEachPost = false; |
|||
break; |
|||
} |
|||
final int itemsToDownloadSize = itemsToDownload.size(); |
|||
for (int i = 0; i < itemsToDownloadSize; i++) { |
|||
final BasePostModel selectedItem = itemsToDownload.get(i); |
|||
if (!checkEachPost) { |
|||
final boolean isSlider = itemsToDownloadSize > 1; |
|||
final File saveFile = getDownloadSaveFile(dir, selectedItem, isSlider ? "_slide_" + (i + 1) : ""); |
|||
new DownloadAsync(context, |
|||
selectedItem.getDisplayUrl(), |
|||
saveFile, |
|||
file -> selectedItem.setDownloaded(true)) |
|||
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
|||
} else { |
|||
final File finalDir = dir; |
|||
new PostFetcher(selectedItem.getShortCode(), result -> { |
|||
if (result != null) { |
|||
final int resultsSize = result.length; |
|||
final boolean multiResult = resultsSize > 1; |
|||
for (int j = 0; j < resultsSize; j++) { |
|||
final BasePostModel model = result[j]; |
|||
final File saveFile = getDownloadSaveFile(finalDir, model, multiResult ? "_slide_" + (j + 1) : ""); |
|||
new DownloadAsync(context, |
|||
model.getDisplayUrl(), |
|||
saveFile, |
|||
file -> model.setDownloaded(true)) |
|||
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
|||
} |
|||
} |
|||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public static void dmDownload(@NonNull final Context context, @Nullable final String username, final DownloadMethod method, |
|||
final DirectItemModel.DirectItemMediaModel itemsToDownload) { |
|||
if (Utils.settingsHelper == null) Utils.settingsHelper = new SettingsHelper(context); |
|||
|
|||
if (itemsToDownload == null) return; |
|||
|
|||
if (ContextCompat.checkSelfPermission(context, PERMS[0]) == PackageManager.PERMISSION_GRANTED) |
|||
dmDownloadImpl(context, username, method, itemsToDownload); |
|||
else if (context instanceof Activity) |
|||
ActivityCompat.requestPermissions((Activity) context, PERMS, 8020); |
|||
} |
|||
|
|||
private static void dmDownloadImpl(@NonNull final Context context, @Nullable final String username, |
|||
final DownloadMethod method, final DirectItemModel.DirectItemMediaModel selectedItem) { |
|||
File dir = new File(Environment.getExternalStorageDirectory(), "Download"); |
|||
|
|||
if (Utils.settingsHelper.getBoolean(FOLDER_SAVE_TO)) { |
|||
final String customPath = Utils.settingsHelper.getString(FOLDER_PATH); |
|||
if (!TextUtils.isEmpty(customPath)) dir = new File(customPath); |
|||
} |
|||
|
|||
if (Utils.settingsHelper.getBoolean(Constants.DOWNLOAD_USER_FOLDER) && !TextUtils.isEmpty(username)) |
|||
dir = new File(dir, username); |
|||
|
|||
if (dir.exists() || dir.mkdirs()) { |
|||
new DownloadAsync(context, |
|||
selectedItem.getMediaType() == MediaItemType.MEDIA_TYPE_VIDEO ? selectedItem.getVideoUrl() : selectedItem.getThumbUrl(), |
|||
getDownloadSaveFileDm(dir, selectedItem, ""), |
|||
null).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
|||
} else |
|||
Toast.makeText(context, R.string.error_creating_folders, Toast.LENGTH_SHORT).show(); |
|||
} |
|||
|
|||
@NonNull |
|||
private static File getDownloadSaveFile(final File finalDir, @NonNull final BasePostModel model, final String sliderPrefix) { |
|||
final String displayUrl = model.getDisplayUrl(); |
|||
return new File(finalDir, model.getPostId() + '_' + model.getPosition() + sliderPrefix + |
|||
getExtensionFromModel(displayUrl, model)); |
|||
} |
|||
|
|||
@NonNull |
|||
private static File getDownloadSaveFileDm(final File finalDir, |
|||
@NonNull final DirectItemModel.DirectItemMediaModel model, |
|||
final String sliderPrefix) { |
|||
final String displayUrl = model.getMediaType() == MediaItemType.MEDIA_TYPE_VIDEO ? model.getVideoUrl() : model.getThumbUrl(); |
|||
return new File(finalDir, model.getId() + sliderPrefix + |
|||
getExtensionFromModel(displayUrl, model)); |
|||
} |
|||
|
|||
@NonNull |
|||
public static String getExtensionFromModel(@NonNull final String url, final Object model) { |
|||
final String extension; |
|||
final int index = url.indexOf('?'); |
|||
|
|||
if (index != -1) extension = url.substring(index - 4, index); |
|||
else { |
|||
final boolean isVideo; |
|||
if (model instanceof StoryModel) |
|||
isVideo = ((StoryModel) model).getItemType() == MediaItemType.MEDIA_TYPE_VIDEO; |
|||
else if (model instanceof BasePostModel) |
|||
isVideo = ((BasePostModel) model).getItemType() == MediaItemType.MEDIA_TYPE_VIDEO; |
|||
else |
|||
isVideo = false; |
|||
extension = isVideo || url.contains(".mp4") ? ".mp4" : ".jpg"; |
|||
} |
|||
|
|||
return extension; |
|||
} |
|||
|
|||
public static void checkExistence(final File downloadDir, final File customDir, final boolean isSlider, |
|||
@NonNull final BasePostModel model) { |
|||
boolean exists = false; |
|||
|
|||
try { |
|||
final String displayUrl = model.getDisplayUrl(); |
|||
int index = displayUrl.indexOf('?'); |
|||
if (index < 0) { |
|||
return; |
|||
} |
|||
final String fileName = model.getPostId() + '_'; |
|||
final String extension = displayUrl.substring(index - 4, index); |
|||
|
|||
final String fileWithoutPrefix = fileName + '0' + extension; |
|||
exists = new File(downloadDir, fileWithoutPrefix).exists(); |
|||
if (!exists) { |
|||
final String fileWithPrefix = fileName + "[\\d]+(|_slide_[\\d]+)(\\.mp4|\\" + extension + ")"; |
|||
final FilenameFilter filenameFilter = (dir, name) -> Pattern.matches(fileWithPrefix, name); |
|||
|
|||
File[] files = downloadDir.listFiles(filenameFilter); |
|||
if ((files == null || files.length < 1) && customDir != null) |
|||
files = customDir.listFiles(filenameFilter); |
|||
|
|||
if (files != null && files.length >= 1) exists = true; |
|||
} |
|||
} catch (final Exception e) { |
|||
if (Utils.logCollector != null) |
|||
Utils.logCollector.appendException(e, LogCollector.LogFile.UTILS, "checkExistence", |
|||
new Pair<>("isSlider", isSlider), |
|||
new Pair<>("model", model)); |
|||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); |
|||
} |
|||
|
|||
model.setDownloaded(exists); |
|||
} |
|||
} |
@ -0,0 +1,50 @@ |
|||
package awais.instagrabber.utils; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
|
|||
import java.io.BufferedReader; |
|||
import java.io.InputStreamReader; |
|||
import java.net.HttpURLConnection; |
|||
import java.util.Map; |
|||
import java.util.Set; |
|||
|
|||
public final class NetworkUtils { |
|||
@NonNull |
|||
public static String readFromConnection(@NonNull final HttpURLConnection conn) throws Exception { |
|||
final StringBuilder sb = new StringBuilder(); |
|||
try (final BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()))) { |
|||
String line; |
|||
while ((line = br.readLine()) != null) sb.append(line).append('\n'); |
|||
} |
|||
return sb.toString(); |
|||
} |
|||
|
|||
public static void setConnectionHeaders(final HttpURLConnection connection, final Map<String, String> headers) { |
|||
if (connection == null || headers == null || headers.isEmpty()) { |
|||
return; |
|||
} |
|||
for (Map.Entry<String, String> header : headers.entrySet()) { |
|||
connection.setRequestProperty(header.getKey(), header.getValue()); |
|||
} |
|||
} |
|||
|
|||
public static String getQueryString(final Map<String, String> queryParamsMap) { |
|||
if (queryParamsMap == null || queryParamsMap.isEmpty()) { |
|||
return ""; |
|||
} |
|||
final Set<Map.Entry<String, String>> params = queryParamsMap.entrySet(); |
|||
final StringBuilder builder = new StringBuilder(); |
|||
for (final Map.Entry<String, String> param : params) { |
|||
if (TextUtils.isEmpty(param.getKey())) { |
|||
continue; |
|||
} |
|||
if (builder.length() != 0) { |
|||
builder.append("&"); |
|||
} |
|||
builder.append(param.getKey()); |
|||
builder.append("="); |
|||
builder.append(param.getValue() != null ? param.getValue() : ""); |
|||
} |
|||
return builder.toString(); |
|||
} |
|||
} |
@ -0,0 +1,56 @@ |
|||
package awais.instagrabber.utils; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
|
|||
import java.util.Random; |
|||
|
|||
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; |
|||
} |
|||
|
|||
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; |
|||
} |
|||
} |
@ -0,0 +1,562 @@ |
|||
package awais.instagrabber.utils; |
|||
|
|||
import android.util.Log; |
|||
import android.util.Pair; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.annotation.Nullable; |
|||
|
|||
import org.json.JSONArray; |
|||
import org.json.JSONObject; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.Locale; |
|||
|
|||
import awais.instagrabber.BuildConfig; |
|||
import awais.instagrabber.models.ProfileModel; |
|||
import awais.instagrabber.models.direct_messages.DirectItemModel; |
|||
import awais.instagrabber.models.direct_messages.InboxThreadModel; |
|||
import awais.instagrabber.models.enums.DirectItemType; |
|||
import awais.instagrabber.models.enums.InboxReadState; |
|||
import awais.instagrabber.models.enums.MediaItemType; |
|||
import awais.instagrabber.models.enums.RavenExpiringMediaType; |
|||
import awais.instagrabber.models.enums.RavenMediaViewType; |
|||
import awaisomereport.LogCollector; |
|||
|
|||
public final class ResponseBodyUtils { |
|||
// isI: true if the content was requested from i.instagram.com instead of graphql |
|||
@Nullable |
|||
public static String getHighQualityPost(final JSONArray resources, final boolean isVideo, final boolean isI, final boolean low) { |
|||
try { |
|||
final int resourcesLen = resources.length(); |
|||
|
|||
final String[] sources = new String[resourcesLen]; |
|||
int lastResMain = low ? 1000000 : 0, lastIndexMain = -1; |
|||
int lastResBase = low ? 1000000 : 0, lastIndexBase = -1; |
|||
for (int i = 0; i < resourcesLen; ++i) { |
|||
final JSONObject item = resources.getJSONObject(i); |
|||
if (item != null && (!isVideo || item.has(Constants.EXTRAS_PROFILE) || isI)) { |
|||
sources[i] = item.getString(isI ? "url" : "src"); |
|||
final int currRes = item.getInt(isI ? "width" : "config_width") * item.getInt(isI ? "height" : "config_height"); |
|||
|
|||
final String profile = isVideo ? item.optString(Constants.EXTRAS_PROFILE) : null; |
|||
|
|||
if (!isVideo || "MAIN".equals(profile)) { |
|||
if (currRes > lastResMain && !low) { |
|||
lastResMain = currRes; |
|||
lastIndexMain = i; |
|||
} else if (currRes < lastResMain && low) { |
|||
lastResMain = currRes; |
|||
lastIndexMain = i; |
|||
} |
|||
} else { |
|||
if (currRes > lastResBase && !low) { |
|||
lastResBase = currRes; |
|||
lastIndexBase = i; |
|||
} else if (currRes < lastResBase && low) { |
|||
lastResBase = currRes; |
|||
lastIndexBase = i; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (lastIndexMain >= 0) return sources[lastIndexMain]; |
|||
else if (lastIndexBase >= 0) return sources[lastIndexBase]; |
|||
} catch (final Exception e) { |
|||
if (Utils.logCollector != null) |
|||
Utils.logCollector.appendException(e, LogCollector.LogFile.UTILS, "getHighQualityPost", |
|||
new Pair<>("resourcesNull", resources == null), |
|||
new Pair<>("isVideo", isVideo)); |
|||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
public static String getHighQualityImage(final JSONObject resources) { |
|||
String src = null; |
|||
try { |
|||
if (resources.has("display_resources")) |
|||
src = getHighQualityPost(resources.getJSONArray("display_resources"), false, false, false); |
|||
else if (resources.has("image_versions2")) |
|||
src = getHighQualityPost(resources.getJSONObject("image_versions2").getJSONArray("candidates"), false, true, false); |
|||
if (src == null) return resources.getString("display_url"); |
|||
} catch (final Exception e) { |
|||
if (Utils.logCollector != null) |
|||
Utils.logCollector.appendException(e, LogCollector.LogFile.UTILS, "getHighQualityImage", |
|||
new Pair<>("resourcesNull", resources == null)); |
|||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); |
|||
} |
|||
return src; |
|||
} |
|||
|
|||
public static String getLowQualityImage(final JSONObject resources) { |
|||
String src = null; |
|||
try { |
|||
src = getHighQualityPost(resources.getJSONObject("image_versions2").getJSONArray("candidates"), false, true, true); |
|||
} catch (final Exception e) { |
|||
if (Utils.logCollector != null) |
|||
Utils.logCollector.appendException(e, LogCollector.LogFile.UTILS, "getLowQualityImage", |
|||
new Pair<>("resourcesNull", resources == null)); |
|||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); |
|||
} |
|||
return src; |
|||
} |
|||
|
|||
public static String getItemThumbnail(@NonNull final JSONArray jsonArray) { |
|||
String thumbnail = null; |
|||
final int imageResLen = jsonArray.length(); |
|||
|
|||
for (int i = 0; i < imageResLen; ++i) { |
|||
final JSONObject imageResource = jsonArray.optJSONObject(i); |
|||
try { |
|||
final int width = imageResource.getInt("width"); |
|||
final int height = imageResource.getInt("height"); |
|||
final float ratio = Float.parseFloat(String.format(Locale.ENGLISH, "%.2f", (float) height / width)); |
|||
if (ratio >= 0.95f && ratio <= 1.0f) { |
|||
thumbnail = imageResource.getString("url"); |
|||
break; |
|||
} |
|||
} catch (final Exception e) { |
|||
if (Utils.logCollector != null) |
|||
Utils.logCollector.appendException(e, LogCollector.LogFile.UTILS, "getItemThumbnail"); |
|||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); |
|||
thumbnail = null; |
|||
} |
|||
} |
|||
|
|||
if (TextUtils.isEmpty(thumbnail)) thumbnail = jsonArray.optJSONObject(0).optString("url"); |
|||
|
|||
return thumbnail; |
|||
} |
|||
|
|||
@Nullable |
|||
public static String getThumbnailUrl(@NonNull final JSONObject mediaObj, final MediaItemType mediaType) throws Exception { |
|||
String thumbnail = null; |
|||
|
|||
if (mediaType == MediaItemType.MEDIA_TYPE_IMAGE || mediaType == MediaItemType.MEDIA_TYPE_VIDEO) { |
|||
final JSONObject imageVersions = mediaObj.optJSONObject("image_versions2"); |
|||
if (imageVersions != null) |
|||
thumbnail = getItemThumbnail(imageVersions.getJSONArray("candidates")); |
|||
|
|||
} else if (mediaType == MediaItemType.MEDIA_TYPE_SLIDER) { |
|||
final JSONArray carouselMedia = mediaObj.optJSONArray("carousel_media"); |
|||
if (carouselMedia != null) |
|||
thumbnail = getItemThumbnail(carouselMedia.getJSONObject(0) |
|||
.getJSONObject("image_versions2").getJSONArray("candidates")); |
|||
} |
|||
|
|||
return thumbnail; |
|||
} |
|||
|
|||
public static String getVideoUrl(@NonNull final JSONObject mediaObj) throws Exception { |
|||
String thumbnail = null; |
|||
|
|||
final JSONArray imageVersions = mediaObj.optJSONArray("video_versions"); |
|||
if (imageVersions != null) |
|||
thumbnail = getItemThumbnail(imageVersions); |
|||
|
|||
return thumbnail; |
|||
} |
|||
|
|||
@Nullable |
|||
public static MediaItemType getMediaItemType(final int mediaType) { |
|||
if (mediaType == 1) return MediaItemType.MEDIA_TYPE_IMAGE; |
|||
if (mediaType == 2) return MediaItemType.MEDIA_TYPE_VIDEO; |
|||
if (mediaType == 8) return MediaItemType.MEDIA_TYPE_SLIDER; |
|||
if (mediaType == 11) return MediaItemType.MEDIA_TYPE_VOICE; |
|||
return null; |
|||
} |
|||
|
|||
public static DirectItemModel.DirectItemMediaModel getDirectMediaModel(final JSONObject mediaObj) throws Exception { |
|||
final DirectItemModel.DirectItemMediaModel mediaModel; |
|||
if (mediaObj == null) mediaModel = null; |
|||
else { |
|||
final JSONObject userObj = mediaObj.optJSONObject("user"); |
|||
|
|||
ProfileModel user = null; |
|||
if (userObj != null) { |
|||
user = new ProfileModel( |
|||
userObj.getBoolean("is_private"), |
|||
false, |
|||
userObj.optBoolean("is_verified"), |
|||
String.valueOf(userObj.get("pk")), |
|||
userObj.getString("username"), |
|||
userObj.getString("full_name"), |
|||
null, null, |
|||
userObj.getString("profile_pic_url"), |
|||
null, 0, 0, 0, false, false, false, false); |
|||
} |
|||
|
|||
final MediaItemType mediaType = getMediaItemType(mediaObj.optInt("media_type", -1)); |
|||
|
|||
String id = mediaObj.optString("id"); |
|||
if (TextUtils.isEmpty(id)) id = null; |
|||
|
|||
mediaModel = new DirectItemModel.DirectItemMediaModel(mediaType, |
|||
mediaObj.optLong("expiring_at"), |
|||
mediaObj.optLong("pk"), |
|||
id, |
|||
getThumbnailUrl(mediaObj, mediaType), |
|||
mediaType == MediaItemType.MEDIA_TYPE_VIDEO ? getVideoUrl(mediaObj) : null, |
|||
user, |
|||
mediaObj.optString("code")); |
|||
} |
|||
return mediaModel; |
|||
} |
|||
|
|||
private static DirectItemType getDirectItemType(final String itemType) { |
|||
if ("placeholder".equals(itemType)) return DirectItemType.PLACEHOLDER; |
|||
if ("media".equals(itemType)) return DirectItemType.MEDIA; |
|||
if ("link".equals(itemType)) return DirectItemType.LINK; |
|||
if ("like".equals(itemType)) return DirectItemType.LIKE; |
|||
if ("reel_share".equals(itemType)) return DirectItemType.REEL_SHARE; |
|||
if ("media_share".equals(itemType)) return DirectItemType.MEDIA_SHARE; |
|||
if ("action_log".equals(itemType)) return DirectItemType.ACTION_LOG; |
|||
if ("raven_media".equals(itemType)) return DirectItemType.RAVEN_MEDIA; |
|||
if ("profile".equals(itemType)) return DirectItemType.PROFILE; |
|||
if ("video_call_event".equals(itemType)) return DirectItemType.VIDEO_CALL_EVENT; |
|||
if ("animated_media".equals(itemType)) return DirectItemType.ANIMATED_MEDIA; |
|||
if ("voice_media".equals(itemType)) return DirectItemType.VOICE_MEDIA; |
|||
if ("story_share".equals(itemType)) return DirectItemType.STORY_SHARE; |
|||
if ("clip".equals(itemType)) return DirectItemType.CLIP; |
|||
return DirectItemType.TEXT; |
|||
} |
|||
|
|||
@NonNull |
|||
public static InboxThreadModel createInboxThreadModel(@NonNull final JSONObject data, final boolean inThreadView) throws Exception { |
|||
final InboxReadState readState = data.getInt("read_state") == 0 ? InboxReadState.STATE_READ : InboxReadState.STATE_UNREAD; |
|||
final String threadType = data.getString("thread_type"); // they're all "private", group is identified by boolean "is_group" |
|||
|
|||
final String threadId = data.getString("thread_id"); |
|||
final String threadV2Id = data.getString("thread_v2_id"); |
|||
final String threadTitle = data.getString("thread_title"); |
|||
|
|||
final String threadNewestCursor = data.getString("newest_cursor"); |
|||
final String threadOldestCursor = data.getString("oldest_cursor"); |
|||
final String threadNextCursor = data.has("next_cursor") ? data.getString("next_cursor") : null; |
|||
final String threadPrevCursor = data.has("prev_cursor") ? data.getString("prev_cursor") : null; |
|||
|
|||
final boolean threadHasOlder = data.getBoolean("has_older"); |
|||
final long unreadCount = data.optLong("read_state", 0); |
|||
|
|||
final long lastActivityAt = data.optLong("last_activity_at"); |
|||
final boolean named = data.optBoolean("named"); |
|||
final boolean muted = data.optBoolean("muted"); |
|||
final boolean isPin = data.optBoolean("is_pin"); |
|||
final boolean isSpam = data.optBoolean("is_spam"); |
|||
final boolean isGroup = data.optBoolean("is_group"); |
|||
final boolean pending = data.optBoolean("pending"); |
|||
final boolean archived = data.optBoolean("archived"); |
|||
final boolean canonical = data.optBoolean("canonical"); |
|||
|
|||
final JSONArray users = data.getJSONArray("users"); |
|||
final int usersLen = users.length(); |
|||
final JSONArray leftusers = data.getJSONArray("left_users"); |
|||
final int leftusersLen = leftusers.length(); |
|||
final JSONArray admins = data.getJSONArray("admin_user_ids"); |
|||
final int adminsLen = admins.length(); |
|||
|
|||
final ProfileModel[] userModels = new ProfileModel[usersLen]; |
|||
for (int j = 0; j < usersLen; ++j) { |
|||
final JSONObject userObject = users.getJSONObject(j); |
|||
userModels[j] = new ProfileModel(userObject.getBoolean("is_private"), |
|||
false, |
|||
userObject.optBoolean("is_verified"), |
|||
String.valueOf(userObject.get("pk")), |
|||
userObject.getString("username"), |
|||
userObject.getString("full_name"), |
|||
null, null, |
|||
userObject.getString("profile_pic_url"), |
|||
null, 0, 0, 0, false, false, false, false); |
|||
} |
|||
|
|||
final ProfileModel[] leftuserModels = new ProfileModel[leftusersLen]; |
|||
for (int j = 0; j < leftusersLen; ++j) { |
|||
final JSONObject userObject = leftusers.getJSONObject(j); |
|||
leftuserModels[j] = new ProfileModel(userObject.getBoolean("is_private"), |
|||
false, |
|||
userObject.optBoolean("is_verified"), |
|||
String.valueOf(userObject.get("pk")), |
|||
userObject.getString("username"), |
|||
userObject.getString("full_name"), |
|||
null, null, |
|||
userObject.getString("profile_pic_url"), |
|||
null, 0, 0, 0, false, false, false, false); |
|||
} |
|||
|
|||
final Long[] adminIDs = new Long[adminsLen]; |
|||
for (int j = 0; j < adminsLen; ++j) { |
|||
adminIDs[j] = admins.getLong(j); |
|||
} |
|||
|
|||
final JSONArray items = data.getJSONArray("items"); |
|||
final int itemsLen = items.length(); |
|||
|
|||
final ArrayList<DirectItemModel> itemModels = new ArrayList<>(itemsLen); |
|||
for (int i = 0; i < itemsLen; ++i) { |
|||
final JSONObject itemObject = items.getJSONObject(i); |
|||
|
|||
CharSequence text = null; |
|||
ProfileModel profileModel = null; |
|||
DirectItemModel.DirectItemLinkModel linkModel = null; |
|||
DirectItemModel.DirectItemMediaModel directMedia = null; |
|||
DirectItemModel.DirectItemReelShareModel reelShareModel = null; |
|||
DirectItemModel.DirectItemActionLogModel actionLogModel = null; |
|||
DirectItemModel.DirectItemAnimatedMediaModel animatedMediaModel = null; |
|||
DirectItemModel.DirectItemVoiceMediaModel voiceMediaModel = null; |
|||
DirectItemModel.DirectItemRavenMediaModel ravenMediaModel = null; |
|||
DirectItemModel.DirectItemVideoCallEventModel videoCallEventModel = null; |
|||
|
|||
final DirectItemType itemType = getDirectItemType(itemObject.getString("item_type")); |
|||
switch (itemType) { |
|||
case ANIMATED_MEDIA: { |
|||
final JSONObject animatedMedia = itemObject.getJSONObject("animated_media"); |
|||
final JSONObject stickerImage = animatedMedia.getJSONObject("images").getJSONObject("fixed_height"); |
|||
|
|||
animatedMediaModel = new DirectItemModel.DirectItemAnimatedMediaModel(animatedMedia.getBoolean("is_random"), |
|||
animatedMedia.getBoolean("is_sticker"), |
|||
animatedMedia.getString("id"), |
|||
stickerImage.getString("url"), |
|||
stickerImage.optString("webp"), |
|||
stickerImage.optString("mp4"), |
|||
stickerImage.getInt("height"), |
|||
stickerImage.getInt("width")); |
|||
} |
|||
break; |
|||
|
|||
case VOICE_MEDIA: { |
|||
final JSONObject voiceMedia = itemObject.getJSONObject("voice_media").getJSONObject("media"); |
|||
final JSONObject audio = voiceMedia.getJSONObject("audio"); |
|||
|
|||
int[] waveformData = null; |
|||
final JSONArray waveformDataArray = audio.optJSONArray("waveform_data"); |
|||
if (waveformDataArray != null) { |
|||
final int waveformDataLen = waveformDataArray.length(); |
|||
waveformData = new int[waveformDataLen]; |
|||
// 0.011775206 |
|||
for (int j = 0; j < waveformDataLen; ++j) { |
|||
waveformData[j] = (int) (waveformDataArray.optDouble(j) * 10); |
|||
} |
|||
} |
|||
|
|||
voiceMediaModel = new DirectItemModel.DirectItemVoiceMediaModel(voiceMedia.getString("id"), |
|||
audio.getString("audio_src"), audio.getLong("duration"), |
|||
waveformData); |
|||
} |
|||
break; |
|||
|
|||
case LINK: { |
|||
final JSONObject linkObj = itemObject.getJSONObject("link"); |
|||
|
|||
DirectItemModel.DirectItemLinkContext itemLinkContext = null; |
|||
final JSONObject linkContext = linkObj.optJSONObject("link_context"); |
|||
if (linkContext != null) { |
|||
itemLinkContext = new DirectItemModel.DirectItemLinkContext( |
|||
linkContext.getString("link_url"), |
|||
linkContext.optString("link_title"), |
|||
linkContext.optString("link_summary"), |
|||
linkContext.optString("link_image_url") |
|||
); |
|||
} |
|||
|
|||
linkModel = new DirectItemModel.DirectItemLinkModel(linkObj.getString("text"), |
|||
linkObj.getString("client_context"), |
|||
linkObj.optString("mutation_token"), |
|||
itemLinkContext); |
|||
} |
|||
break; |
|||
|
|||
case REEL_SHARE: { |
|||
final JSONObject reelShare = itemObject.getJSONObject("reel_share"); |
|||
reelShareModel = new DirectItemModel.DirectItemReelShareModel( |
|||
reelShare.optBoolean("is_reel_persisted"), |
|||
reelShare.getLong("reel_owner_id"), |
|||
reelShare.getJSONObject("media").getJSONObject("user").getString("username"), |
|||
reelShare.getString("text"), |
|||
reelShare.getString("type"), |
|||
reelShare.getString("reel_type"), |
|||
reelShare.optString("reel_name"), |
|||
reelShare.optString("reel_id"), |
|||
getDirectMediaModel(reelShare.optJSONObject("media"))); |
|||
} |
|||
break; |
|||
|
|||
case RAVEN_MEDIA: { |
|||
final JSONObject visualMedia = itemObject.getJSONObject("visual_media"); |
|||
|
|||
final JSONArray seenUserIdsArray = visualMedia.getJSONArray("seen_user_ids"); |
|||
final int seenUsersLen = seenUserIdsArray.length(); |
|||
final String[] seenUserIds = new String[seenUsersLen]; |
|||
for (int j = 0; j < seenUsersLen; j++) |
|||
seenUserIds[j] = seenUserIdsArray.getString(j); |
|||
|
|||
DirectItemModel.RavenExpiringMediaActionSummaryModel expiringSummaryModel = null; |
|||
final JSONObject actionSummary = visualMedia.optJSONObject("expiring_media_action_summary"); |
|||
if (actionSummary != null) |
|||
expiringSummaryModel = new DirectItemModel.RavenExpiringMediaActionSummaryModel( |
|||
actionSummary.getLong("timestamp"), actionSummary.getInt("count"), |
|||
getExpiringMediaType(actionSummary.getString("type"))); |
|||
|
|||
final RavenMediaViewType viewType; |
|||
final String viewMode = visualMedia.getString("view_mode"); |
|||
switch (viewMode) { |
|||
case "replayable": |
|||
viewType = RavenMediaViewType.REPLAYABLE; |
|||
break; |
|||
case "permanent": |
|||
viewType = RavenMediaViewType.PERMANENT; |
|||
break; |
|||
case "once": |
|||
default: |
|||
viewType = RavenMediaViewType.ONCE; |
|||
} |
|||
|
|||
ravenMediaModel = new DirectItemModel.DirectItemRavenMediaModel( |
|||
visualMedia.optLong(viewType == RavenMediaViewType.PERMANENT ? "url_expire_at_secs" : "replay_expiring_at_us"), |
|||
visualMedia.optInt("playback_duration_secs"), |
|||
visualMedia.getInt("seen_count"), |
|||
seenUserIds, |
|||
viewType, |
|||
getDirectMediaModel(visualMedia.optJSONObject("media")), |
|||
expiringSummaryModel); |
|||
|
|||
} |
|||
break; |
|||
|
|||
case VIDEO_CALL_EVENT: { |
|||
final JSONObject videoCallEvent = itemObject.getJSONObject("video_call_event"); |
|||
videoCallEventModel = new DirectItemModel.DirectItemVideoCallEventModel(videoCallEvent.getLong("vc_id"), |
|||
videoCallEvent.optBoolean("thread_has_audio_only_call"), |
|||
videoCallEvent.getString("action"), |
|||
videoCallEvent.getString("description")); |
|||
} |
|||
break; |
|||
|
|||
case PROFILE: { |
|||
final JSONObject profile = itemObject.getJSONObject("profile"); |
|||
profileModel = new ProfileModel(profile.getBoolean("is_private"), |
|||
false, |
|||
profile.getBoolean("is_verified"), |
|||
Long.toString(profile.getLong("pk")), |
|||
profile.getString("username"), |
|||
profile.getString("full_name"), |
|||
null, null, |
|||
profile.getString("profile_pic_url"), |
|||
null, 0, 0, 0, false, false, false, false); |
|||
} |
|||
break; |
|||
|
|||
case PLACEHOLDER: |
|||
final JSONObject placeholder = itemObject.getJSONObject("placeholder"); |
|||
text = placeholder.getString("title") + "<br><small>" + placeholder.getString("message") + "</small>"; |
|||
break; |
|||
|
|||
case ACTION_LOG: |
|||
if (inThreadView && itemObject.optInt("hide_in_thread", 0) != 0) |
|||
continue; |
|||
final JSONObject actionLog = itemObject.getJSONObject("action_log"); |
|||
String desc = actionLog.getString("description"); |
|||
JSONArray bold = actionLog.getJSONArray("bold"); |
|||
for (int q = 0; q < bold.length(); ++q) { |
|||
JSONObject boldItem = bold.getJSONObject(q); |
|||
desc = desc.substring(0, boldItem.getInt("start") + q * 7) + "<b>" |
|||
+ desc.substring(boldItem.getInt("start") + q * 7, boldItem.getInt("end") + q * 7) |
|||
+ "</b>" + desc.substring(boldItem.getInt("end") + q * 7); |
|||
} |
|||
actionLogModel = new DirectItemModel.DirectItemActionLogModel(desc); |
|||
break; |
|||
|
|||
case MEDIA_SHARE: |
|||
directMedia = getDirectMediaModel(itemObject.getJSONObject("media_share")); |
|||
break; |
|||
|
|||
case CLIP: |
|||
directMedia = getDirectMediaModel(itemObject.getJSONObject("clip").getJSONObject("clip")); |
|||
break; |
|||
|
|||
case MEDIA: |
|||
directMedia = getDirectMediaModel(itemObject.optJSONObject("media")); |
|||
break; |
|||
|
|||
case LIKE: |
|||
text = itemObject.getString("like"); |
|||
break; |
|||
|
|||
case STORY_SHARE: |
|||
final JSONObject storyShare = itemObject.getJSONObject("story_share"); |
|||
if (!storyShare.has("media")) |
|||
text = "<small>" + storyShare.optString("message") + "</small>"; |
|||
else { |
|||
reelShareModel = new DirectItemModel.DirectItemReelShareModel( |
|||
storyShare.optBoolean("is_reel_persisted"), |
|||
storyShare.getJSONObject("media").getJSONObject("user").getLong("pk"), |
|||
storyShare.getJSONObject("media").getJSONObject("user").getString("username"), |
|||
storyShare.getString("text"), |
|||
storyShare.getString("story_share_type"), |
|||
storyShare.getString("reel_type"), |
|||
storyShare.optString("reel_name"), |
|||
storyShare.optString("reel_id"), |
|||
getDirectMediaModel(storyShare.optJSONObject("media"))); |
|||
} |
|||
break; |
|||
|
|||
case TEXT: |
|||
if (!itemObject.has("text")) |
|||
Log.d("AWAISKING_APP", "itemObject: " + itemObject); // todo |
|||
text = itemObject.optString("text"); |
|||
break; |
|||
} |
|||
|
|||
String[] liked = null; |
|||
if (!itemObject.isNull("reactions") && !itemObject.getJSONObject("reactions").isNull("likes")) { |
|||
JSONArray rawLiked = itemObject.getJSONObject("reactions").getJSONArray("likes"); |
|||
liked = new String[rawLiked.length()]; |
|||
for (int l = 0; l < rawLiked.length(); ++l) { |
|||
liked[l] = String.valueOf(rawLiked.getJSONObject(l).getLong("sender_id")); |
|||
} |
|||
} |
|||
|
|||
itemModels.add(new DirectItemModel( |
|||
itemObject.getLong("user_id"), |
|||
itemObject.getLong("timestamp"), |
|||
itemObject.getString("item_id"), |
|||
liked, |
|||
itemType, |
|||
text, |
|||
linkModel, |
|||
profileModel, |
|||
reelShareModel, |
|||
directMedia, |
|||
actionLogModel, |
|||
voiceMediaModel, |
|||
ravenMediaModel, |
|||
videoCallEventModel, |
|||
animatedMediaModel)); |
|||
} |
|||
|
|||
itemModels.trimToSize(); |
|||
|
|||
return new InboxThreadModel(readState, threadId, threadV2Id, threadType, threadTitle, |
|||
threadNewestCursor, threadOldestCursor, threadNextCursor, threadPrevCursor, |
|||
null, // todo |
|||
userModels, leftuserModels, adminIDs, |
|||
itemModels.toArray(new DirectItemModel[0]), |
|||
muted, isPin, named, canonical, |
|||
pending, threadHasOlder, unreadCount, isSpam, isGroup, archived, lastActivityAt); |
|||
} |
|||
|
|||
private static RavenExpiringMediaType getExpiringMediaType(final String type) { |
|||
if ("raven_sent".equals(type)) return RavenExpiringMediaType.RAVEN_SENT; |
|||
if ("raven_opened".equals(type)) return RavenExpiringMediaType.RAVEN_OPENED; |
|||
if ("raven_blocked".equals(type)) return RavenExpiringMediaType.RAVEN_BLOCKED; |
|||
if ("raven_sending".equals(type)) return RavenExpiringMediaType.RAVEN_SENDING; |
|||
if ("raven_replayed".equals(type)) return RavenExpiringMediaType.RAVEN_REPLAYED; |
|||
if ("raven_delivered".equals(type)) return RavenExpiringMediaType.RAVEN_DELIVERED; |
|||
if ("raven_suggested".equals(type)) return RavenExpiringMediaType.RAVEN_SUGGESTED; |
|||
if ("raven_screenshot".equals(type)) return RavenExpiringMediaType.RAVEN_SCREENSHOT; |
|||
if ("raven_cannot_deliver".equals(type)) return RavenExpiringMediaType.RAVEN_CANNOT_DELIVER; |
|||
//if ("raven_unknown".equals(type)) [default?] |
|||
return RavenExpiringMediaType.RAVEN_UNKNOWN; |
|||
} |
|||
} |
@ -0,0 +1,112 @@ |
|||
package awais.instagrabber.utils; |
|||
|
|||
import android.text.SpannableString; |
|||
import android.text.SpannableStringBuilder; |
|||
import android.text.Spanned; |
|||
import android.text.style.URLSpan; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
|
|||
import awais.instagrabber.customviews.CommentMentionClickSpan; |
|||
|
|||
public final class TextUtils { |
|||
@NonNull |
|||
public static CharSequence getMentionText(@NonNull final CharSequence text) { |
|||
final int commentLength = text.length(); |
|||
final SpannableStringBuilder stringBuilder = new SpannableStringBuilder(text, 0, commentLength); |
|||
|
|||
for (int i = 0; i < commentLength; ++i) { |
|||
char currChar = text.charAt(i); |
|||
|
|||
if (currChar == '@' || currChar == '#') { |
|||
final int startLen = i; |
|||
|
|||
do { |
|||
if (++i == commentLength) break; |
|||
currChar = text.charAt(i); |
|||
|
|||
if (currChar == '.' && i + 1 < commentLength) { |
|||
final char nextChar = text.charAt(i + 1); |
|||
if (nextChar == '.' || nextChar == ' ' || nextChar == '#' || nextChar == '@' || nextChar == '/' |
|||
|| nextChar == '\r' || nextChar == '\n') { |
|||
break; |
|||
} |
|||
} else if (currChar == '.') |
|||
break; |
|||
|
|||
// for merged hashtags |
|||
if (currChar == '#') { |
|||
--i; |
|||
break; |
|||
} |
|||
} while (currChar != ' ' && currChar != '\r' && currChar != '\n' && currChar != '>' && currChar != '<' |
|||
&& currChar != ':' && currChar != ';' && currChar != '\'' && currChar != '"' && currChar != '[' |
|||
&& currChar != ']' && currChar != '\\' && currChar != '=' && currChar != '-' && currChar != '!' |
|||
&& currChar != '$' && currChar != '%' && currChar != '^' && currChar != '&' && currChar != '*' |
|||
&& currChar != '(' && currChar != ')' && currChar != '{' && currChar != '}' && currChar != '/' |
|||
&& currChar != '|' && currChar != '?' && currChar != '`' && currChar != '~' |
|||
); |
|||
|
|||
final int endLen = currChar != '#' ? i : i + 1; // for merged hashtags |
|||
stringBuilder.setSpan(new CommentMentionClickSpan(), startLen, |
|||
Math.min(commentLength, endLen), // fixed - crash when end index is greater than comment length ( @kernoeb ) |
|||
Spanned.SPAN_EXCLUSIVE_INCLUSIVE); |
|||
} |
|||
} |
|||
|
|||
return stringBuilder; |
|||
} |
|||
|
|||
// extracted from String class |
|||
public static int indexOfChar(@NonNull final CharSequence sequence, final int ch, final int startIndex) { |
|||
final int max = sequence.length(); |
|||
if (startIndex < max) { |
|||
if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) { |
|||
for (int i = startIndex; i < max; i++) if (sequence.charAt(i) == ch) return i; |
|||
} else if (Character.isValidCodePoint(ch)) { |
|||
final char hi = (char) ((ch >>> 10) + (Character.MIN_HIGH_SURROGATE - (Character.MIN_SUPPLEMENTARY_CODE_POINT >>> 10))); |
|||
final char lo = (char) ((ch & 0x3ff) + Character.MIN_LOW_SURROGATE); |
|||
for (int i = startIndex; i < max; i++) |
|||
if (sequence.charAt(i) == hi && sequence.charAt(i + 1) == lo) return i; |
|||
} |
|||
} |
|||
return -1; |
|||
} |
|||
|
|||
public static boolean hasMentions(final CharSequence text) { |
|||
if (isEmpty(text)) return false; |
|||
return indexOfChar(text, '@', 0) != -1 || indexOfChar(text, '#', 0) != -1; |
|||
} |
|||
|
|||
public static CharSequence getSpannableUrl(final String url) { |
|||
if (isEmpty(url)) return url; |
|||
final int httpIndex = url.indexOf("http:"); |
|||
final int httpsIndex = url.indexOf("https:"); |
|||
if (httpIndex == -1 && httpsIndex == -1) return url; |
|||
|
|||
final int length = url.length(); |
|||
|
|||
final int startIndex = httpIndex != -1 ? httpIndex : httpsIndex; |
|||
final int spaceIndex = url.indexOf(' ', startIndex + 1); |
|||
|
|||
final int endIndex = (spaceIndex != -1 ? spaceIndex : length); |
|||
|
|||
final String extractUrl = url.substring(startIndex, Math.min(length, endIndex)); |
|||
|
|||
final SpannableString spannableString = new SpannableString(url); |
|||
spannableString.setSpan(new URLSpan(extractUrl), startIndex, endIndex, 0); |
|||
|
|||
return spannableString; |
|||
} |
|||
|
|||
public static boolean isEmpty(final CharSequence charSequence) { |
|||
if (charSequence == null || charSequence.length() < 1) return true; |
|||
if (charSequence instanceof String) { |
|||
String str = (String) charSequence; |
|||
if ("".equals(str) || "null".equals(str) || str.isEmpty()) return true; |
|||
str = str.trim(); |
|||
return "".equals(str) || "null".equals(str) || str.isEmpty(); |
|||
} |
|||
return "null".contentEquals(charSequence) || "".contentEquals(charSequence) || charSequence.length() < 1; |
|||
} |
|||
} |
1121
app/src/main/java/awais/instagrabber/utils/Utils.java
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Write
Preview
Loading…
Cancel
Save
Reference in new issue