diff --git a/app/src/main/java/awais/instagrabber/activities/MainActivity.kt b/app/src/main/java/awais/instagrabber/activities/MainActivity.kt index c72be55e..dc528ac4 100644 --- a/app/src/main/java/awais/instagrabber/activities/MainActivity.kt +++ b/app/src/main/java/awais/instagrabber/activities/MainActivity.kt @@ -49,7 +49,6 @@ import awais.instagrabber.models.IntentModel import awais.instagrabber.models.Resource import awais.instagrabber.models.Tab import awais.instagrabber.models.enums.IntentModelType -import awais.instagrabber.repositories.responses.Media import awais.instagrabber.services.ActivityCheckerService import awais.instagrabber.services.DMSyncAlarmReceiver import awais.instagrabber.utils.* @@ -61,7 +60,6 @@ import awais.instagrabber.viewmodels.AppStateViewModel import awais.instagrabber.viewmodels.DirectInboxViewModel import awais.instagrabber.webservices.GraphQLService import awais.instagrabber.webservices.MediaService -import awais.instagrabber.webservices.ServiceCallback import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout.ScrollingViewBehavior import com.google.android.material.appbar.CollapsingToolbarLayout @@ -71,6 +69,7 @@ import com.google.common.collect.ImmutableList import com.google.common.collect.Iterators import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.util.* import java.util.stream.Collectors @@ -92,7 +91,6 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL var currentTabs: List = emptyList() private set private var showBottomViewDestinations: List = emptyList() - private var graphQLService: GraphQLService? = null private val serviceConnection: ServiceConnection = object : ServiceConnection { override fun onServiceConnected(name: ComponentName, service: IBinder) { @@ -633,39 +631,32 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL .setCancelable(false) .setView(R.layout.dialog_opening_post) .create() - if (graphQLService == null) graphQLService = GraphQLService.getInstance() - val postCb: ServiceCallback = object : ServiceCallback { - override fun onSuccess(feedModel: Media?) { - if (feedModel != null) { - val currentNavControllerLiveData = currentNavControllerLiveData ?: return + alertDialog.show() + lifecycleScope.launch(Dispatchers.IO) { + try { + val media = if (isLoggedIn) MediaService.fetch(shortcodeToId(shortCode)) else GraphQLService.fetchPost(shortCode) + withContext(Dispatchers.Main) { + if (media == null) { + Toast.makeText(applicationContext, R.string.post_not_found, Toast.LENGTH_SHORT).show() + return@withContext + } + val currentNavControllerLiveData = currentNavControllerLiveData ?: return@withContext val navController = currentNavControllerLiveData.value val bundle = Bundle() - bundle.putSerializable(PostViewV2Fragment.ARG_MEDIA, feedModel) + bundle.putSerializable(PostViewV2Fragment.ARG_MEDIA, media) try { navController?.navigate(R.id.action_global_post_view, bundle) } catch (e: Exception) { Log.e(TAG, "showPostView: ", e) } - } else Toast.makeText(applicationContext, R.string.post_not_found, Toast.LENGTH_SHORT).show() - alertDialog.dismiss() - } - - override fun onFailure(t: Throwable) { - alertDialog.dismiss() - } - } - alertDialog.show() - if (isLoggedIn) { - lifecycleScope.launch(Dispatchers.IO) { - try { - val media = MediaService.fetch(shortcodeToId(shortCode)) - postCb.onSuccess(media) - } catch (e: Exception) { - postCb.onFailure(e) + } + } catch (e: Exception) { + Log.e(TAG, "showPostView: ", e) + } finally { + withContext(Dispatchers.Main) { + alertDialog.dismiss() } } - } else { - graphQLService?.fetchPost(shortCode, postCb) } } diff --git a/app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java index fa9f331e..0a47e895 100644 --- a/app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java +++ b/app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java @@ -7,9 +7,11 @@ import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.repositories.responses.Hashtag; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.PostsFetchResponse; +import awais.instagrabber.utils.CoroutineUtilsKt; import awais.instagrabber.webservices.GraphQLService; import awais.instagrabber.webservices.ServiceCallback; import awais.instagrabber.webservices.TagsService; +import kotlinx.coroutines.Dispatchers; public class HashtagPostFetchService implements PostFetcher.PostFetchService { private final TagsService tagsService; @@ -23,7 +25,7 @@ public class HashtagPostFetchService implements PostFetcher.PostFetchService { this.hashtagModel = hashtagModel; this.isLoggedIn = isLoggedIn; tagsService = isLoggedIn ? TagsService.getInstance() : null; - graphQLService = isLoggedIn ? null : GraphQLService.getInstance(); + graphQLService = isLoggedIn ? null : GraphQLService.INSTANCE; } @Override @@ -48,7 +50,17 @@ public class HashtagPostFetchService implements PostFetcher.PostFetchService { } }; if (isLoggedIn) tagsService.fetchPosts(hashtagModel.getName().toLowerCase(), nextMaxId, cb); - else graphQLService.fetchHashtagPosts(hashtagModel.getName().toLowerCase(), nextMaxId, cb); + else graphQLService.fetchHashtagPosts( + hashtagModel.getName().toLowerCase(), + nextMaxId, + CoroutineUtilsKt.getContinuation((postsFetchResponse, throwable) -> { + if (throwable != null) { + cb.onFailure(throwable); + return; + } + cb.onSuccess(postsFetchResponse); + }, Dispatchers.getIO()) + ); } @Override diff --git a/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java index 274b2314..e7410a64 100644 --- a/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java +++ b/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java @@ -7,9 +7,11 @@ import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.repositories.responses.Location; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.PostsFetchResponse; +import awais.instagrabber.utils.CoroutineUtilsKt; import awais.instagrabber.webservices.GraphQLService; import awais.instagrabber.webservices.LocationService; import awais.instagrabber.webservices.ServiceCallback; +import kotlinx.coroutines.Dispatchers; public class LocationPostFetchService implements PostFetcher.PostFetchService { private final LocationService locationService; @@ -23,7 +25,7 @@ public class LocationPostFetchService implements PostFetcher.PostFetchService { this.locationModel = locationModel; this.isLoggedIn = isLoggedIn; locationService = isLoggedIn ? LocationService.getInstance() : null; - graphQLService = isLoggedIn ? null : GraphQLService.getInstance(); + graphQLService = isLoggedIn ? null : GraphQLService.INSTANCE; } @Override @@ -48,7 +50,17 @@ public class LocationPostFetchService implements PostFetcher.PostFetchService { } }; if (isLoggedIn) locationService.fetchPosts(locationModel.getPk(), nextMaxId, cb); - else graphQLService.fetchLocationPosts(locationModel.getPk(), nextMaxId, cb); + else graphQLService.fetchLocationPosts( + locationModel.getPk(), + nextMaxId, + CoroutineUtilsKt.getContinuation((postsFetchResponse, throwable) -> { + if (throwable != null) { + cb.onFailure(throwable); + return; + } + cb.onSuccess(postsFetchResponse); + }, Dispatchers.getIO()) + ); } @Override diff --git a/app/src/main/java/awais/instagrabber/asyncs/ProfilePostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/ProfilePostFetchService.java index 02e0e27a..f6bc3423 100644 --- a/app/src/main/java/awais/instagrabber/asyncs/ProfilePostFetchService.java +++ b/app/src/main/java/awais/instagrabber/asyncs/ProfilePostFetchService.java @@ -7,9 +7,11 @@ import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.PostsFetchResponse; import awais.instagrabber.repositories.responses.User; +import awais.instagrabber.utils.CoroutineUtilsKt; import awais.instagrabber.webservices.GraphQLService; import awais.instagrabber.webservices.ProfileService; import awais.instagrabber.webservices.ServiceCallback; +import kotlinx.coroutines.Dispatchers; public class ProfilePostFetchService implements PostFetcher.PostFetchService { private static final String TAG = "ProfilePostFetchService"; @@ -23,7 +25,7 @@ public class ProfilePostFetchService implements PostFetcher.PostFetchService { public ProfilePostFetchService(final User profileModel, final boolean isLoggedIn) { this.profileModel = profileModel; this.isLoggedIn = isLoggedIn; - graphQLService = isLoggedIn ? null : GraphQLService.getInstance(); + graphQLService = isLoggedIn ? null : GraphQLService.INSTANCE; profileService = isLoggedIn ? ProfileService.getInstance() : null; } @@ -49,7 +51,19 @@ public class ProfilePostFetchService implements PostFetcher.PostFetchService { } }; if (isLoggedIn) profileService.fetchPosts(profileModel.getPk(), nextMaxId, cb); - else graphQLService.fetchProfilePosts(profileModel.getPk(), 30, nextMaxId, profileModel, cb); + else graphQLService.fetchProfilePosts( + profileModel.getPk(), + 30, + nextMaxId, + profileModel, + CoroutineUtilsKt.getContinuation((postsFetchResponse, throwable) -> { + if (throwable != null) { + cb.onFailure(throwable); + return; + } + cb.onSuccess(postsFetchResponse); + }, Dispatchers.getIO()) + ); } @Override diff --git a/app/src/main/java/awais/instagrabber/asyncs/SavedPostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/SavedPostFetchService.java index 9b3511a9..efc4eaa0 100644 --- a/app/src/main/java/awais/instagrabber/asyncs/SavedPostFetchService.java +++ b/app/src/main/java/awais/instagrabber/asyncs/SavedPostFetchService.java @@ -7,9 +7,11 @@ import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.models.enums.PostItemType; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.PostsFetchResponse; +import awais.instagrabber.utils.CoroutineUtilsKt; import awais.instagrabber.webservices.GraphQLService; import awais.instagrabber.webservices.ProfileService; import awais.instagrabber.webservices.ServiceCallback; +import kotlinx.coroutines.Dispatchers; public class SavedPostFetchService implements PostFetcher.PostFetchService { private final ProfileService profileService; @@ -27,7 +29,7 @@ public class SavedPostFetchService implements PostFetcher.PostFetchService { this.type = type; this.isLoggedIn = isLoggedIn; this.collectionId = collectionId; - graphQLService = isLoggedIn ? null : GraphQLService.getInstance(); + graphQLService = isLoggedIn ? null : GraphQLService.INSTANCE; profileService = isLoggedIn ? ProfileService.getInstance() : null; } @@ -58,7 +60,18 @@ public class SavedPostFetchService implements PostFetcher.PostFetchService { break; case TAGGED: if (isLoggedIn) profileService.fetchTagged(profileId, nextMaxId, callback); - else graphQLService.fetchTaggedPosts(profileId, 30, nextMaxId, callback); + else graphQLService.fetchTaggedPosts( + profileId, + 30, + nextMaxId, + CoroutineUtilsKt.getContinuation((postsFetchResponse, throwable) -> { + if (throwable != null) { + callback.onFailure(throwable); + return; + } + callback.onSuccess(postsFetchResponse); + }, Dispatchers.getIO()) + ); break; case COLLECTION: case SAVED: diff --git a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java index 6aa2ae20..ce3d6dd7 100644 --- a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java @@ -63,8 +63,10 @@ import awais.instagrabber.repositories.responses.Hashtag; import awais.instagrabber.repositories.responses.Location; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.User; +import awais.instagrabber.utils.AppExecutors; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; +import awais.instagrabber.utils.CoroutineUtilsKt; import awais.instagrabber.utils.DownloadUtils; import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.Utils; @@ -72,6 +74,7 @@ import awais.instagrabber.webservices.GraphQLService; import awais.instagrabber.webservices.ServiceCallback; import awais.instagrabber.webservices.StoriesService; import awais.instagrabber.webservices.TagsService; +import kotlinx.coroutines.Dispatchers; import static androidx.core.content.PermissionChecker.checkSelfPermission; import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION; @@ -218,20 +221,15 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe if (TextUtils.isEmpty(user.getUsername())) { // this only happens for anons opening = true; - graphQLService.fetchPost(feedModel.getCode(), new ServiceCallback() { - @Override - public void onSuccess(final Media newFeedModel) { - opening = false; - if (newFeedModel == null) return; - openPostDialog(newFeedModel, profilePicView, mainPostImage, position); + graphQLService.fetchPost(feedModel.getCode(), CoroutineUtilsKt.getContinuation((media, throwable) -> { + opening = false; + if (throwable != null) { + Log.e(TAG, "Error", throwable); + return; } - - @Override - public void onFailure(final Throwable t) { - opening = false; - Log.e(TAG, "Error", t); - } - }); + if (media == null) return; + AppExecutors.INSTANCE.getMainThread().execute(() -> openPostDialog(media, profilePicView, mainPostImage, position)); + }, Dispatchers.getIO())); return; } opening = true; @@ -304,7 +302,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0; tagsService = isLoggedIn ? TagsService.getInstance() : null; storiesService = isLoggedIn ? StoriesService.getInstance(null, 0L, null) : null; - graphQLService = isLoggedIn ? null : GraphQLService.getInstance(); + graphQLService = isLoggedIn ? null : GraphQLService.INSTANCE; setHasOptionsMenu(true); } @@ -385,7 +383,13 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe private void fetchHashtagModel() { binding.swipeRefreshLayout.setRefreshing(true); if (isLoggedIn) tagsService.fetch(hashtag, cb); - else graphQLService.fetchTag(hashtag, cb); + else graphQLService.fetchTag(hashtag, CoroutineUtilsKt.getContinuation((hashtag1, throwable) -> { + if (throwable != null) { + cb.onFailure(throwable); + return; + } + AppExecutors.INSTANCE.getMainThread().execute(() -> cb.onSuccess(hashtag1)); + }, Dispatchers.getIO())); } private void setupPosts() { diff --git a/app/src/main/java/awais/instagrabber/fragments/LikesViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/LikesViewerFragment.java index 38daa085..57ca0761 100644 --- a/app/src/main/java/awais/instagrabber/fragments/LikesViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/LikesViewerFragment.java @@ -113,7 +113,7 @@ public final class LikesViewerFragment extends BottomSheetDialogFragment impleme final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie); if (csrfToken == null) return; mediaService = isLoggedIn ? MediaService.INSTANCE : null; - graphQLService = isLoggedIn ? null : GraphQLService.getInstance(); + graphQLService = isLoggedIn ? null : GraphQLService.INSTANCE; // setHasOptionsMenu(true); } @@ -135,7 +135,17 @@ public final class LikesViewerFragment extends BottomSheetDialogFragment impleme public void onRefresh() { if (isComment && !isLoggedIn) { lazyLoader.resetState(); - graphQLService.fetchCommentLikers(postId, null, anonCb); + graphQLService.fetchCommentLikers( + postId, + null, + CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { + if (throwable != null) { + anonCb.onFailure(throwable); + return; + } + anonCb.onSuccess(response); + }), Dispatchers.getIO()) + ); } else { mediaService.fetchLikes( postId, @@ -164,8 +174,19 @@ public final class LikesViewerFragment extends BottomSheetDialogFragment impleme binding.rvLikes.setLayoutManager(layoutManager); binding.rvLikes.addItemDecoration(new DividerItemDecoration(getContext(), DividerItemDecoration.HORIZONTAL)); lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> { - if (!TextUtils.isEmpty(endCursor)) - graphQLService.fetchCommentLikers(postId, endCursor, anonCb); + if (!TextUtils.isEmpty(endCursor)) { + graphQLService.fetchCommentLikers( + postId, + endCursor, + CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { + if (throwable != null) { + anonCb.onFailure(throwable); + return; + } + anonCb.onSuccess(response); + }), Dispatchers.getIO()) + ); + } endCursor = null; }); binding.rvLikes.addOnScrollListener(lazyLoader); diff --git a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java index fd050345..0ac2571b 100644 --- a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java @@ -59,8 +59,10 @@ import awais.instagrabber.repositories.requests.StoryViewerOptions; import awais.instagrabber.repositories.responses.Location; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.User; +import awais.instagrabber.utils.AppExecutors; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; +import awais.instagrabber.utils.CoroutineUtilsKt; import awais.instagrabber.utils.DownloadUtils; import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.Utils; @@ -208,20 +210,18 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR if (user == null) return; if (TextUtils.isEmpty(user.getUsername())) { opening = true; - graphQLService.fetchPost(feedModel.getCode(), new ServiceCallback() { - @Override - public void onSuccess(final Media newFeedModel) { - opening = false; - if (newFeedModel == null) return; - openPostDialog(newFeedModel, profilePicView, mainPostImage, position); - } - - @Override - public void onFailure(final Throwable t) { - opening = false; - Log.e(TAG, "Error", t); - } - }); + graphQLService.fetchPost( + feedModel.getCode(), + CoroutineUtilsKt.getContinuation((media, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { + opening = false; + if (throwable != null) { + Log.e(TAG, "Error", throwable); + return; + } + if (media == null) return; + openPostDialog(media, profilePicView, mainPostImage, position); + })) + ); return; } opening = true; @@ -294,7 +294,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0; locationService = isLoggedIn ? LocationService.getInstance() : null; storiesService = StoriesService.getInstance(null, 0L, null); - graphQLService = isLoggedIn ? null : GraphQLService.getInstance(); + graphQLService = isLoggedIn ? null : GraphQLService.INSTANCE; setHasOptionsMenu(true); } @@ -402,7 +402,16 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR private void fetchLocationModel() { binding.swipeRefreshLayout.setRefreshing(true); if (isLoggedIn) locationService.fetch(locationId, cb); - else graphQLService.fetchLocation(locationId, cb); + else graphQLService.fetchLocation( + locationId, + CoroutineUtilsKt.getContinuation((location, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { + if (throwable != null) { + cb.onFailure(throwable); + return; + } + cb.onSuccess(location); + })) + ); } private void setupLocationDetails() { diff --git a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java index f6028715..43beb577 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java @@ -339,7 +339,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe storiesService = isLoggedIn ? StoriesService.getInstance(null, 0L, null) : null; mediaService = isLoggedIn ? MediaService.INSTANCE : null; userService = isLoggedIn ? UserService.INSTANCE : null; - graphQLService = isLoggedIn ? null : GraphQLService.getInstance(); + graphQLService = isLoggedIn ? null : GraphQLService.INSTANCE; final Context context = getContext(); if (context == null) return; accountRepository = AccountRepository.getInstance(AccountDataSource.getInstance(context)); @@ -618,25 +618,19 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe ); return; } - graphQLService.fetchUser(usernameTemp, new ServiceCallback() { - @Override - public void onSuccess(final User user) { - profileModel = user; - setProfileDetails(); - } - - @Override - public void onFailure(final Throwable t) { - Log.e(TAG, "Error fetching profile", t); - final Context context = getContext(); - try { - if (t == null) - Toast.makeText(context, R.string.error_loading_profile, Toast.LENGTH_LONG).show(); - else Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show(); - } catch (final Throwable ignored) { - } - } - }); + graphQLService.fetchUser( + usernameTemp, + CoroutineUtilsKt.getContinuation((user, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { + if (throwable != null) { + Log.e(TAG, "Error fetching profile", throwable); + final Context context = getContext(); + if (context == null) return; + Toast.makeText(context, throwable.getMessage(), Toast.LENGTH_SHORT).show(); + } + profileModel = user; + setProfileDetails(); + })) + ); } private void setProfileDetails() { diff --git a/app/src/main/java/awais/instagrabber/repositories/GraphQLRepository.kt b/app/src/main/java/awais/instagrabber/repositories/GraphQLRepository.kt index 199e44b4..483537e2 100644 --- a/app/src/main/java/awais/instagrabber/repositories/GraphQLRepository.kt +++ b/app/src/main/java/awais/instagrabber/repositories/GraphQLRepository.kt @@ -1,25 +1,22 @@ -package awais.instagrabber.repositories; +package awais.instagrabber.repositories -import java.util.Map; +import retrofit2.http.GET +import retrofit2.http.Path +import retrofit2.http.QueryMap -import retrofit2.Call; -import retrofit2.http.GET; -import retrofit2.http.Path; -import retrofit2.http.QueryMap; - -public interface GraphQLRepository { +interface GraphQLRepository { @GET("/graphql/query/") - Call fetch(@QueryMap(encoded = true) Map queryParams); + suspend fun fetch(@QueryMap(encoded = true) queryParams: Map): String @GET("/{username}/?__a=1") - Call getUser(@Path("username") String username); + suspend fun getUser(@Path("username") username: String): String @GET("/p/{shortcode}/?__a=1") - Call getPost(@Path("shortcode") String shortcode); + suspend fun getPost(@Path("shortcode") shortcode: String): String @GET("/explore/tags/{tag}/?__a=1") - Call getTag(@Path("tag") String tag); + suspend fun getTag(@Path("tag") tag: String): String @GET("/explore/locations/{locationId}/?__a=1") - Call getLocation(@Path("locationId") long locationId); -} + suspend fun getLocation(@Path("locationId") locationId: Long): String +} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/viewmodels/CommentsViewerViewModel.java b/app/src/main/java/awais/instagrabber/viewmodels/CommentsViewerViewModel.java index 431497f0..d8e0481e 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/CommentsViewerViewModel.java +++ b/app/src/main/java/awais/instagrabber/viewmodels/CommentsViewerViewModel.java @@ -30,13 +30,13 @@ import awais.instagrabber.repositories.responses.CommentsFetchResponse; import awais.instagrabber.repositories.responses.User; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; +import awais.instagrabber.utils.CoroutineUtilsKt; import awais.instagrabber.utils.Utils; import awais.instagrabber.webservices.CommentService; import awais.instagrabber.webservices.GraphQLService; import awais.instagrabber.webservices.ServiceCallback; -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; +import kotlin.coroutines.Continuation; +import kotlinx.coroutines.Dispatchers; import static awais.instagrabber.utils.Utils.settingsHelper; @@ -113,7 +113,7 @@ public class CommentsViewerViewModel extends ViewModel { }; public CommentsViewerViewModel() { - graphQLService = GraphQLService.getInstance(); + graphQLService = GraphQLService.INSTANCE; final String cookie = settingsHelper.getString(Constants.COOKIE); final String deviceUuid = Utils.settingsHelper.getString(Constants.DEVICE_UUID); final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie); @@ -165,8 +165,12 @@ public class CommentsViewerViewModel extends ViewModel { commentService.fetchComments(postId, rootCursor, ccb); return; } - final Call request = graphQLService.fetchComments(shortCode, true, rootCursor); - enqueueRequest(request, true, shortCode, ccb); + graphQLService.fetchComments( + shortCode, + true, + rootCursor, + enqueueRequest(true, shortCode, ccb) + ); } public void fetchReplies() { @@ -190,54 +194,49 @@ public class CommentsViewerViewModel extends ViewModel { commentService.fetchChildComments(postId, commentId, repliesCursor, rcb); return; } - final Call request = graphQLService.fetchComments(commentId, false, repliesCursor); - enqueueRequest(request, false, commentId, rcb); + graphQLService.fetchComments(commentId, false, repliesCursor, enqueueRequest(false, commentId, rcb)); } - private void enqueueRequest(@NonNull final Call request, - final boolean root, - final String shortCodeOrCommentId, - final ServiceCallback callback) { - request.enqueue(new Callback() { - @Override - public void onResponse(@NonNull final Call call, @NonNull final Response response) { - final String rawBody = response.body(); - if (rawBody == null) { - Log.e(TAG, "Error occurred while fetching gql comments of " + shortCodeOrCommentId); - callback.onSuccess(null); - return; - } - try { - final JSONObject body = root ? new JSONObject(rawBody).getJSONObject("data") - .getJSONObject("shortcode_media") - .getJSONObject("edge_media_to_parent_comment") - : new JSONObject(rawBody).getJSONObject("data") - .getJSONObject("comment") - .getJSONObject("edge_threaded_comments"); - final int count = body.optInt("count"); - final JSONObject pageInfo = body.getJSONObject("page_info"); - final boolean hasNextPage = pageInfo.getBoolean("has_next_page"); - final String endCursor = pageInfo.isNull("end_cursor") || !hasNextPage ? null : pageInfo.optString("end_cursor"); - final JSONArray commentsJsonArray = body.getJSONArray("edges"); - final ImmutableList.Builder builder = ImmutableList.builder(); - for (int i = 0; i < commentsJsonArray.length(); i++) { - final Comment commentModel = getComment(commentsJsonArray.getJSONObject(i).getJSONObject("node"), root); - builder.add(commentModel); - } - callback.onSuccess(root ? - new CommentsFetchResponse(count, endCursor, builder.build()) : - new ChildCommentsFetchResponse(count, endCursor, builder.build())); - } catch (Exception e) { - Log.e(TAG, "onResponse", e); - callback.onFailure(e); - } + private Continuation enqueueRequest(final boolean root, + final String shortCodeOrCommentId, + @SuppressWarnings("rawtypes") final ServiceCallback callback) { + return CoroutineUtilsKt.getContinuation((response, throwable) -> { + if (throwable != null) { + callback.onFailure(throwable); + return; } - - @Override - public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { - callback.onFailure(t); + if (response == null) { + Log.e(TAG, "Error occurred while fetching gql comments of " + shortCodeOrCommentId); + //noinspection unchecked + callback.onSuccess(null); + return; } - }); + try { + final JSONObject body = root ? new JSONObject(response).getJSONObject("data") + .getJSONObject("shortcode_media") + .getJSONObject("edge_media_to_parent_comment") + : new JSONObject(response).getJSONObject("data") + .getJSONObject("comment") + .getJSONObject("edge_threaded_comments"); + final int count = body.optInt("count"); + final JSONObject pageInfo = body.getJSONObject("page_info"); + final boolean hasNextPage = pageInfo.getBoolean("has_next_page"); + final String endCursor = pageInfo.isNull("end_cursor") || !hasNextPage ? null : pageInfo.optString("end_cursor"); + final JSONArray commentsJsonArray = body.getJSONArray("edges"); + final ImmutableList.Builder builder = ImmutableList.builder(); + for (int i = 0; i < commentsJsonArray.length(); i++) { + final Comment commentModel = getComment(commentsJsonArray.getJSONObject(i).getJSONObject("node"), root); + builder.add(commentModel); + } + final Object result = root ? new CommentsFetchResponse(count, endCursor, builder.build()) + : new ChildCommentsFetchResponse(count, endCursor, builder.build()); + //noinspection unchecked + callback.onSuccess(result); + } catch (Exception e) { + Log.e(TAG, "onResponse", e); + callback.onFailure(e); + } + }, Dispatchers.getIO()); } @NonNull diff --git a/app/src/main/java/awais/instagrabber/webservices/GraphQLService.kt b/app/src/main/java/awais/instagrabber/webservices/GraphQLService.kt index 5e43ca1f..ac6e881e 100644 --- a/app/src/main/java/awais/instagrabber/webservices/GraphQLService.kt +++ b/app/src/main/java/awais/instagrabber/webservices/GraphQLService.kt @@ -1,483 +1,266 @@ -package awais.instagrabber.webservices; +package awais.instagrabber.webservices -import android.util.Log; +import android.util.Log +import awais.instagrabber.models.enums.FollowingType +import awais.instagrabber.repositories.GraphQLRepository +import awais.instagrabber.repositories.responses.* +import awais.instagrabber.utils.Constants +import awais.instagrabber.utils.ResponseBodyUtils +import awais.instagrabber.utils.extensions.TAG +import awais.instagrabber.webservices.RetrofitFactory.retrofitWeb +import org.json.JSONException +import org.json.JSONObject +import java.util.* -import androidx.annotation.NonNull; - -import com.google.common.collect.ImmutableMap; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import awais.instagrabber.models.enums.FollowingType; -import awais.instagrabber.repositories.GraphQLRepository; -import awais.instagrabber.repositories.responses.FriendshipStatus; -import awais.instagrabber.repositories.responses.GraphQLUserListFetchResponse; -import awais.instagrabber.repositories.responses.Hashtag; -import awais.instagrabber.repositories.responses.Location; -import awais.instagrabber.repositories.responses.Media; -import awais.instagrabber.repositories.responses.PostsFetchResponse; -import awais.instagrabber.repositories.responses.User; -import awais.instagrabber.utils.Constants; -import awais.instagrabber.utils.ResponseBodyUtils; -import awais.instagrabber.utils.TextUtils; -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; - -public class GraphQLService extends BaseService { - private static final String TAG = "GraphQLService"; - - private final GraphQLRepository repository; - - private static GraphQLService instance; - - private GraphQLService() { - repository = RetrofitFactory.INSTANCE - .getRetrofitWeb() - .create(GraphQLRepository.class); - } - - public static GraphQLService getInstance() { - if (instance == null) { - instance = new GraphQLService(); - } - return instance; - } +object GraphQLService : BaseService() { + private val repository: GraphQLRepository = retrofitWeb.create(GraphQLRepository::class.java) // TODO convert string response to a response class - private void fetch(final String queryHash, - final String variables, - final String arg1, - final String arg2, - final User backup, - final ServiceCallback callback) { - final Map queryMap = new HashMap<>(); - queryMap.put("query_hash", queryHash); - queryMap.put("variables", variables); - final Call request = repository.fetch(queryMap); - request.enqueue(new Callback() { - @Override - public void onResponse(@NonNull final Call call, @NonNull final Response response) { - try { - // Log.d(TAG, "onResponse: body: " + response.body()); - final PostsFetchResponse postsFetchResponse = parsePostResponse(response, arg1, arg2, backup); - if (callback != null) { - callback.onSuccess(postsFetchResponse); - } - } catch (JSONException e) { - Log.e(TAG, "onResponse", e); - if (callback != null) { - callback.onFailure(e); - } - } - } - - @Override - public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { - if (callback != null) { - callback.onFailure(t); - } - } - }); + private suspend fun fetch( + queryHash: String, + variables: String, + arg1: String, + arg2: String, + backup: User?, + ): PostsFetchResponse { + val queryMap = mapOf( + "query_hash" to queryHash, + "variables" to variables, + ) + val response = repository.fetch(queryMap) + return parsePostResponse(response, arg1, arg2, backup) } - public void fetchLocationPosts(final long locationId, - final String maxId, - final ServiceCallback callback) { - fetch("36bd0f2bf5911908de389b8ceaa3be6d", - "{\"id\":\"" + locationId + "\"," + - "\"first\":25," + - "\"after\":\"" + (maxId == null ? "" : maxId) + "\"}", - Constants.EXTRAS_LOCATION, - "edge_location_to_media", - null, - callback); - } + suspend fun fetchLocationPosts( + locationId: Long, + maxId: String?, + ): PostsFetchResponse = fetch( + "36bd0f2bf5911908de389b8ceaa3be6d", + "{\"id\":\"" + locationId + "\"," + "\"first\":25," + "\"after\":\"" + (maxId ?: "") + "\"}", + Constants.EXTRAS_LOCATION, + "edge_location_to_media", + null + ) - public void fetchHashtagPosts(@NonNull final String tag, - final String maxId, - final ServiceCallback callback) { - fetch("9b498c08113f1e09617a1703c22b2f32", - "{\"tag_name\":\"" + tag + "\"," + - "\"first\":25," + - "\"after\":\"" + (maxId == null ? "" : maxId) + "\"}", - Constants.EXTRAS_HASHTAG, - "edge_hashtag_to_media", - null, - callback); - } + suspend fun fetchHashtagPosts( + tag: String, + maxId: String?, + ): PostsFetchResponse = fetch( + "9b498c08113f1e09617a1703c22b2f32", + "{\"tag_name\":\"" + tag + "\"," + "\"first\":25," + "\"after\":\"" + (maxId ?: "") + "\"}", + Constants.EXTRAS_HASHTAG, + "edge_hashtag_to_media", + null, + ) - public void fetchProfilePosts(final long profileId, - final int postsPerPage, - final String maxId, - final User backup, - final ServiceCallback callback) { - fetch("02e14f6a7812a876f7d133c9555b1151", - "{\"id\":\"" + profileId + "\"," + - "\"first\":" + postsPerPage + "," + - "\"after\":\"" + (maxId == null ? "" : maxId) + "\"}", - Constants.EXTRAS_USER, - "edge_owner_to_timeline_media", - backup, - callback); - } + suspend fun fetchProfilePosts( + profileId: Long, + postsPerPage: Int, + maxId: String?, + backup: User?, + ): PostsFetchResponse = fetch( + "02e14f6a7812a876f7d133c9555b1151", + "{\"id\":\"" + profileId + "\"," + "\"first\":" + postsPerPage + "," + "\"after\":\"" + (maxId ?: "") + "\"}", + Constants.EXTRAS_USER, + "edge_owner_to_timeline_media", + backup, + ) - public void fetchTaggedPosts(final long profileId, - final int postsPerPage, - final String maxId, - final ServiceCallback callback) { - fetch("31fe64d9463cbbe58319dced405c6206", - "{\"id\":\"" + profileId + "\"," + - "\"first\":" + postsPerPage + "," + - "\"after\":\"" + (maxId == null ? "" : maxId) + "\"}", - Constants.EXTRAS_USER, - "edge_user_to_photos_of_you", - null, - callback); - } + suspend fun fetchTaggedPosts( + profileId: Long, + postsPerPage: Int, + maxId: String?, + ): PostsFetchResponse = fetch( + "31fe64d9463cbbe58319dced405c6206", + "{\"id\":\"" + profileId + "\"," + "\"first\":" + postsPerPage + "," + "\"after\":\"" + (maxId ?: "") + "\"}", + Constants.EXTRAS_USER, + "edge_user_to_photos_of_you", + null, + ) - @NonNull - private PostsFetchResponse parsePostResponse(@NonNull final Response response, - @NonNull final String arg1, - @NonNull final String arg2, - final User backup) - throws JSONException { - if (TextUtils.isEmpty(response.body())) { - Log.e(TAG, "parseResponse: feed response body is empty with status code: " + response.code()); - return new PostsFetchResponse(Collections.emptyList(), false, null); + @Throws(JSONException::class) + private fun parsePostResponse( + response: String, + arg1: String, + arg2: String, + backup: User?, + ): PostsFetchResponse { + if (response.isBlank()) { + Log.e(TAG, "parseResponse: feed response body is empty") + return PostsFetchResponse(emptyList(), false, null) } - return parseResponseBody(response.body(), arg1, arg2, backup); + return parseResponseBody(response, arg1, arg2, backup) } - @NonNull - private PostsFetchResponse parseResponseBody(@NonNull final String body, - @NonNull final String arg1, - @NonNull final String arg2, - final User backup) - throws JSONException { - final List items = new ArrayList<>(); - final JSONObject timelineFeed = new JSONObject(body) - .getJSONObject("data") - .getJSONObject(arg1) - .getJSONObject(arg2); - final String endCursor; - final boolean hasNextPage; - - final JSONObject pageInfo = timelineFeed.getJSONObject("page_info"); + @Throws(JSONException::class) + private fun parseResponseBody( + body: String, + arg1: String, + arg2: String, + backup: User?, + ): PostsFetchResponse { + val items: MutableList = ArrayList() + val timelineFeed = JSONObject(body) + .getJSONObject("data") + .getJSONObject(arg1) + .getJSONObject(arg2) + val endCursor: String? + val hasNextPage: Boolean + val pageInfo = timelineFeed.getJSONObject("page_info") if (pageInfo.has("has_next_page")) { - hasNextPage = pageInfo.getBoolean("has_next_page"); - endCursor = hasNextPage ? pageInfo.getString("end_cursor") : null; + hasNextPage = pageInfo.getBoolean("has_next_page") + endCursor = if (hasNextPage) pageInfo.getString("end_cursor") else null } else { - hasNextPage = false; - endCursor = null; + hasNextPage = false + endCursor = null } - - final JSONArray feedItems = timelineFeed.getJSONArray("edges"); - - for (int i = 0; i < feedItems.length(); ++i) { - final JSONObject itemJson = feedItems.optJSONObject(i); - if (itemJson == null) { - continue; - } - final Media media = ResponseBodyUtils.parseGraphQLItem(itemJson, backup); + val feedItems = timelineFeed.getJSONArray("edges") + for (i in 0 until feedItems.length()) { + val itemJson = feedItems.optJSONObject(i) ?: continue + val media = ResponseBodyUtils.parseGraphQLItem(itemJson, backup) if (media != null) { - items.add(media); + items.add(media) } } - return new PostsFetchResponse(items, hasNextPage, endCursor); + return PostsFetchResponse(items, hasNextPage, endCursor) } // TODO convert string response to a response class - public void fetchCommentLikers(final String commentId, - final String endCursor, - final ServiceCallback callback) { - final Map queryMap = new HashMap<>(); - queryMap.put("query_hash", "5f0b1f6281e72053cbc07909c8d154ae"); - queryMap.put("variables", "{\"comment_id\":\"" + commentId + "\"," + - "\"first\":30," + - "\"after\":\"" + (endCursor == null ? "" : endCursor) + "\"}"); - final Call request = repository.fetch(queryMap); - request.enqueue(new Callback() { - @Override - public void onResponse(@NonNull final Call call, @NonNull final Response response) { - final String rawBody = response.body(); - if (rawBody == null) { - Log.e(TAG, "Error occurred while fetching gql comment likes of " + commentId); - callback.onSuccess(null); - return; - } - try { - final JSONObject body = new JSONObject(rawBody); - final String status = body.getString("status"); - final JSONObject data = body.getJSONObject("data").getJSONObject("comment").getJSONObject("edge_liked_by"); - final JSONObject pageInfo = data.getJSONObject("page_info"); - final String endCursor = pageInfo.getBoolean("has_next_page") ? pageInfo.getString("end_cursor") : null; - final JSONArray users = data.getJSONArray("edges"); - final int usersLen = users.length(); - final List userModels = new ArrayList<>(); - for (int j = 0; j < usersLen; ++j) { - final JSONObject userObject = users.getJSONObject(j).getJSONObject("node"); - userModels.add(new User( - userObject.getLong("id"), - userObject.getString("username"), - userObject.optString("full_name"), - userObject.optBoolean("is_private"), - userObject.getString("profile_pic_url"), - userObject.optBoolean("is_verified") - )); - // userModels.add(new ProfileModel(userObject.optBoolean("is_private"), - // false, - // userObject.optBoolean("is_verified"), - // userObject.getString("id"), - // userObject.getString("username"), - // userObject.optString("full_name"), - // null, null, - // userObject.getString("profile_pic_url"), - // null, 0, 0, 0, false, false, false, false, false)); - } - callback.onSuccess(new GraphQLUserListFetchResponse(endCursor, status, userModels)); - } catch (JSONException e) { - Log.e(TAG, "onResponse", e); - if (callback != null) { - callback.onFailure(e); - } - } - } - - @Override - public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { - if (callback != null) { - callback.onFailure(t); - } - } - }); + suspend fun fetchCommentLikers( + commentId: String, + endCursor: String?, + ): GraphQLUserListFetchResponse { + val queryMap = mapOf( + "query_hash" to "5f0b1f6281e72053cbc07909c8d154ae", + "variables" to "{\"comment_id\":\"" + commentId + "\"," + "\"first\":30," + "\"after\":\"" + (endCursor ?: "") + "\"}" + ) + val response = repository.fetch(queryMap) + val body = JSONObject(response) + val status = body.getString("status") + val data = body.getJSONObject("data").getJSONObject("comment").getJSONObject("edge_liked_by") + val pageInfo = data.getJSONObject("page_info") + val newEndCursor = if (pageInfo.getBoolean("has_next_page")) pageInfo.getString("end_cursor") else null + val users = data.getJSONArray("edges") + val usersLen = users.length() + val userModels: MutableList = ArrayList() + for (j in 0 until usersLen) { + val userObject = users.getJSONObject(j).getJSONObject("node") + userModels.add(User( + userObject.getLong("id"), + userObject.getString("username"), + userObject.optString("full_name"), + userObject.optBoolean("is_private"), + userObject.getString("profile_pic_url"), + userObject.optBoolean("is_verified") + )) + } + return GraphQLUserListFetchResponse(newEndCursor, status, userModels) } - public Call fetchComments(final String shortCodeOrCommentId, - final boolean root, - final String cursor) { - final Map queryMap = new HashMap<>(); - queryMap.put("query_hash", root ? "bc3296d1ce80a24b1b6e40b1e72903f5" : "51fdd02b67508306ad4484ff574a0b62"); - final Map variables = ImmutableMap.of( - root ? "shortcode" : "comment_id", shortCodeOrCommentId, - "first", 50, - "after", cursor == null ? "" : cursor - ); - queryMap.put("variables", new JSONObject(variables).toString()); - return repository.fetch(queryMap); + suspend fun fetchComments( + shortCodeOrCommentId: String?, + root: Boolean, + cursor: String?, + ): String { + val variables = mapOf( + (if (root) "shortcode" else "comment_id") to shortCodeOrCommentId, + "first" to 50, + "after" to (cursor ?: "") + ) + val queryMap = mapOf( + "query_hash" to if (root) "bc3296d1ce80a24b1b6e40b1e72903f5" else "51fdd02b67508306ad4484ff574a0b62", + "variables" to JSONObject(variables).toString() + ) + return repository.fetch(queryMap) } // TODO convert string response to a response class - public void fetchUser(final String username, - final ServiceCallback callback) { - final Call request = repository.getUser(username); - request.enqueue(new Callback() { - @Override - public void onResponse(@NonNull final Call call, @NonNull final Response response) { - final String rawBody = response.body(); - if (rawBody == null) { - Log.e(TAG, "Error occurred while fetching gql user of " + username); - callback.onSuccess(null); - return; - } - try { - final JSONObject body = new JSONObject(rawBody); - final JSONObject userJson = body.getJSONObject("graphql") - .getJSONObject(Constants.EXTRAS_USER); - - boolean isPrivate = userJson.getBoolean("is_private"); - final long id = userJson.optLong(Constants.EXTRAS_ID, 0); - final JSONObject timelineMedia = userJson.getJSONObject("edge_owner_to_timeline_media"); - // if (timelineMedia.has("edges")) { - // final JSONArray edges = timelineMedia.getJSONArray("edges"); - // } - - String url = userJson.optString("external_url"); - if (TextUtils.isEmpty(url)) url = null; - - callback.onSuccess(new User( - id, - username, - userJson.getString("full_name"), - isPrivate, - userJson.getString("profile_pic_url_hd"), - userJson.getBoolean("is_verified"), - null, - new FriendshipStatus( - userJson.optBoolean("followed_by_viewer"), - userJson.optBoolean("follows_viewer"), - userJson.optBoolean("blocked_by_viewer"), - false, - isPrivate, - userJson.optBoolean("has_requested_viewer"), - userJson.optBoolean("requested_by_viewer"), - false, - userJson.optBoolean("restricted_by_viewer"), - false - ), - false, - false, - false, - false, - false, - null, - null, - timelineMedia.getLong("count"), - userJson.getJSONObject("edge_followed_by").getLong("count"), - userJson.getJSONObject("edge_follow").getLong("count"), - 0, - userJson.getString("biography"), - url, - 0, - null, - null, - null, - null, - null, - null)); - } catch (JSONException e) { - Log.e(TAG, "onResponse", e); - if (callback != null) { - callback.onFailure(e); - } - } - } - - @Override - public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { - if (callback != null) { - callback.onFailure(t); - } - } - }); + suspend fun fetchUser( + username: String, + ): User { + val response = repository.getUser(username) + val body = JSONObject(response) + val userJson = body.getJSONObject("graphql").getJSONObject(Constants.EXTRAS_USER) + val isPrivate = userJson.getBoolean("is_private") + val id = userJson.optLong(Constants.EXTRAS_ID, 0) + val timelineMedia = userJson.getJSONObject("edge_owner_to_timeline_media") + // if (timelineMedia.has("edges")) { + // final JSONArray edges = timelineMedia.getJSONArray("edges"); + // } + var url: String? = userJson.optString("external_url") + if (url.isNullOrBlank()) url = null + return User( + id, + username, + userJson.getString("full_name"), + isPrivate, + userJson.getString("profile_pic_url_hd"), + userJson.getBoolean("is_verified"), + friendshipStatus = FriendshipStatus( + userJson.optBoolean("followed_by_viewer"), + userJson.optBoolean("follows_viewer"), + userJson.optBoolean("blocked_by_viewer"), + false, + isPrivate, + userJson.optBoolean("has_requested_viewer"), + userJson.optBoolean("requested_by_viewer"), + false, + userJson.optBoolean("restricted_by_viewer"), + false + ), + mediaCount = timelineMedia.getLong("count"), + followerCount = userJson.getJSONObject("edge_followed_by").getLong("count"), + followingCount = userJson.getJSONObject("edge_follow").getLong("count"), + biography = userJson.getString("biography"), + externalUrl = url, + ) } // TODO convert string response to a response class - public void fetchPost(final String shortcode, - final ServiceCallback callback) { - final Call request = repository.getPost(shortcode); - request.enqueue(new Callback() { - @Override - public void onResponse(@NonNull final Call call, @NonNull final Response response) { - final String rawBody = response.body(); - if (rawBody == null) { - Log.e(TAG, "Error occurred while fetching gql post of " + shortcode); - callback.onSuccess(null); - return; - } - try { - final JSONObject body = new JSONObject(rawBody); - final JSONObject media = body.getJSONObject("graphql") - .getJSONObject("shortcode_media"); - callback.onSuccess(ResponseBodyUtils.parseGraphQLItem(media, null)); - } catch (JSONException e) { - Log.e(TAG, "onResponse", e); - if (callback != null) { - callback.onFailure(e); - } - } - } - - @Override - public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { - if (callback != null) { - callback.onFailure(t); - } - } - }); + suspend fun fetchPost( + shortcode: String, + ): Media { + val response = repository.getPost(shortcode) + val body = JSONObject(response) + val media = body.getJSONObject("graphql").getJSONObject("shortcode_media") + return ResponseBodyUtils.parseGraphQLItem(media, null) } // TODO convert string response to a response class - public void fetchTag(final String tag, - final ServiceCallback callback) { - final Call request = repository.getTag(tag); - request.enqueue(new Callback() { - @Override - public void onResponse(@NonNull final Call call, @NonNull final Response response) { - final String rawBody = response.body(); - if (rawBody == null) { - Log.e(TAG, "Error occurred while fetching gql tag of " + tag); - callback.onSuccess(null); - return; - } - try { - final JSONObject body = new JSONObject(rawBody) - .getJSONObject("graphql") - .getJSONObject(Constants.EXTRAS_HASHTAG); - final JSONObject timelineMedia = body.getJSONObject("edge_hashtag_to_media"); - callback.onSuccess(new Hashtag( - body.getString(Constants.EXTRAS_ID), - body.getString("name"), - timelineMedia.getLong("count"), - body.optBoolean("is_following") ? FollowingType.FOLLOWING : FollowingType.NOT_FOLLOWING, - null)); - } catch (JSONException e) { - Log.e(TAG, "onResponse", e); - if (callback != null) { - callback.onFailure(e); - } - } - } - - @Override - public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { - if (callback != null) { - callback.onFailure(t); - } - } - }); + suspend fun fetchTag( + tag: String, + ): Hashtag { + val response = repository.getTag(tag) + val body = JSONObject(response) + .getJSONObject("graphql") + .getJSONObject(Constants.EXTRAS_HASHTAG) + val timelineMedia = body.getJSONObject("edge_hashtag_to_media") + return Hashtag( + body.getString(Constants.EXTRAS_ID), + body.getString("name"), + timelineMedia.getLong("count"), + if (body.optBoolean("is_following")) FollowingType.FOLLOWING else FollowingType.NOT_FOLLOWING, + null) } // TODO convert string response to a response class - public void fetchLocation(final long locationId, - final ServiceCallback callback) { - final Call request = repository.getLocation(locationId); - request.enqueue(new Callback() { - @Override - public void onResponse(@NonNull final Call call, @NonNull final Response response) { - final String rawBody = response.body(); - if (rawBody == null) { - Log.e(TAG, "Error occurred while fetching gql location of " + locationId); - callback.onSuccess(null); - return; - } - try { - final JSONObject body = new JSONObject(rawBody) - .getJSONObject("graphql") - .getJSONObject(Constants.EXTRAS_LOCATION); - final JSONObject timelineMedia = body.getJSONObject("edge_location_to_media"); - final JSONObject address = new JSONObject(body.getString("address_json")); - callback.onSuccess(new Location( - body.getLong(Constants.EXTRAS_ID), - body.getString("slug"), - body.getString("name"), - address.optString("street_address"), - address.optString("city_name"), - body.optDouble("lng", 0d), - body.optDouble("lat", 0d) - )); - } catch (JSONException e) { - Log.e(TAG, "onResponse", e); - if (callback != null) { - callback.onFailure(e); - } - } - } - - @Override - public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { - if (callback != null) { - callback.onFailure(t); - } - } - }); + suspend fun fetchLocation( + locationId: Long, + ): Location { + val response = repository.getLocation(locationId) + val body = JSONObject(response) + .getJSONObject("graphql") + .getJSONObject(Constants.EXTRAS_LOCATION) + // val timelineMedia = body.getJSONObject("edge_location_to_media") + val address = JSONObject(body.getString("address_json")) + return Location( + body.getLong(Constants.EXTRAS_ID), + body.getString("slug"), + body.getString("name"), + address.optString("street_address"), + address.optString("city_name"), + body.optDouble("lng", 0.0), + body.optDouble("lat", 0.0) + ) } -} +} \ No newline at end of file