From f3028f5c5a41a29d3418fb6c053e24e2692417ea Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 20 Mar 2021 15:13:41 +0000 Subject: [PATCH 1/8] Update dependency com.google.android.exoplayer:exoplayer-core to v2.13.2 --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 2bfb4cf8..c890eba9 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -60,7 +60,7 @@ dependencies { def appcompat_version = "1.2.0" def nav_version = '2.3.4' - def exoplayer_version = '2.12.0' + def exoplayer_version = '2.13.2' implementation 'com.google.android.material:material:1.4.0-alpha01' From 7b578e0462088fddc1bfcca0e671e1b9e53e3e96 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Sat, 20 Mar 2021 12:02:44 -0400 Subject: [PATCH 2/8] 1 renovate pr per hour --- renovate.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/renovate.json b/renovate.json index 267698f2..d3133b37 100644 --- a/renovate.json +++ b/renovate.json @@ -2,5 +2,6 @@ "extends": [ "config:base" ], - "prConcurrentLimit": 5 + "prConcurrentLimit": 5, + "prHourlyLimit": 1 } From 839e30a4e56c268f3b5ce6bb4e3eae342fe34ed2 Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Sun, 21 Mar 2021 01:14:57 +0900 Subject: [PATCH 3/8] Add delete option in post view. Fixes 1 part of https://github.com/austinhuang0131/barinsta/issues/289 --- .../fragments/PostViewV2Fragment.java | 83 ++++++++++++++++--- .../fragments/main/ProfileFragment.java | 11 ++- .../repositories/MediaRepository.java | 12 +++ .../viewmodels/PostViewV2ViewModel.java | 36 ++++++++ .../webservices/MediaService.java | 32 +++++++ app/src/main/res/menu/post_view_menu.xml | 3 + app/src/main/res/values/strings.xml | 1 + 7 files changed, 162 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java b/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java index 5d0c2ead..0daf6561 100644 --- a/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java @@ -24,7 +24,6 @@ import android.util.Log; import android.view.GestureDetector; import android.view.Gravity; import android.view.LayoutInflater; -import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; @@ -132,8 +131,10 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment im private PostViewV2ViewModel viewModel; private PopupMenu optionsPopup; private EditTextDialogFragment editTextDialogFragment; - + private boolean wasDeleted; private MutableLiveData backStackSavedStateResultLiveData; + private OnDeleteListener onDeleteListener; + private final Observer backStackSavedStateObserver = result -> { if (result == null) return; if (result instanceof String) { @@ -196,6 +197,15 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment im this.onShowListener = onShowListener; } + public void setOnDeleteListener(final OnDeleteListener onDeleteListener) { + if (onDeleteListener == null) return; + this.onDeleteListener = onDeleteListener; + } + + public interface OnDeleteListener { + void onDelete(); + } + public static class Builder { private final Media feedModel; private View profilePicElement; @@ -540,7 +550,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment im viewModel.getSaved().observe(getViewLifecycleOwner(), this::setSavedResources); viewModel.getOptions().observe(getViewLifecycleOwner(), options -> binding.getRoot().post(() -> { setupOptions(options != null && !options.isEmpty()); - createOptionsPopupMenu(options); + createOptionsPopupMenu(); })); } @@ -1375,30 +1385,73 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment im }); } - private void createOptionsPopupMenu(final List options) { - if (options == null) return; + private void createOptionsPopupMenu() { if (optionsPopup == null) { final ContextThemeWrapper themeWrapper = new ContextThemeWrapper(context, R.style.popupMenuStyle); optionsPopup = new PopupMenu(themeWrapper, binding.options); + } else { + optionsPopup.getMenu().clear(); } optionsPopup.getMenuInflater().inflate(R.menu.post_view_menu, optionsPopup.getMenu()); - final Menu menu = optionsPopup.getMenu(); - final int size = menu.size(); - for (int i = 0; i < size; i++) { - final MenuItem item = menu.getItem(i); - if (item == null) continue; - if (options.contains(item.getItemId())) continue; - menu.removeItem(item.getItemId()); - } + // final Menu menu = optionsPopup.getMenu(); + // final int size = menu.size(); + // for (int i = 0; i < size; i++) { + // final MenuItem item = menu.getItem(i); + // if (item == null) continue; + // if (options.contains(item.getItemId())) continue; + // menu.removeItem(item.getItemId()); + // } optionsPopup.setOnMenuItemClickListener(item -> { int itemId = item.getItemId(); if (itemId == R.id.edit_caption) { showCaptionEditDialog(); + return true; + } + if (itemId == R.id.delete) { + item.setEnabled(false); + final LiveData> resourceLiveData = viewModel.delete(); + handleDeleteResource(resourceLiveData, item); } return true; }); } + private void handleDeleteResource(final LiveData> resourceLiveData, final MenuItem item) { + if (resourceLiveData == null) return; + resourceLiveData.observe(getViewLifecycleOwner(), new Observer>() { + @Override + public void onChanged(final Resource resource) { + try { + switch (resource.status) { + case SUCCESS: + wasDeleted = true; + if (onDeleteListener != null) { + onDeleteListener.onDelete(); + } + break; + case ERROR: + if (item != null) { + item.setEnabled(true); + } + final Snackbar snackbar = Snackbar.make(binding.getRoot(), + R.string.delete_unsuccessful, + Snackbar.LENGTH_INDEFINITE); + snackbar.setAction(R.string.ok, null); + snackbar.show(); + break; + case LOADING: + if (item != null) { + item.setEnabled(false); + } + break; + } + } finally { + resourceLiveData.removeObserver(this); + } + } + }); + } + private void showCaptionEditDialog() { final Caption caption = viewModel.getCaption().getValue(); final String captionText = caption != null ? caption.getText() : null; @@ -1561,4 +1614,8 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment im } return navController; } + + public boolean wasDeleted() { + return wasDeleted; + } } \ No newline at end of file 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 12a42ff6..161c8aa9 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java @@ -258,7 +258,12 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe builder.setSharedProfilePicElement(profilePicView) .setSharedMainPostElement(mainPostImage); } - builder.build().show(getChildFragmentManager(), "post_view"); + final PostViewV2Fragment postViewV2Fragment = builder.build(); + postViewV2Fragment.setOnDeleteListener(() -> { + postViewV2Fragment.dismiss(); + binding.postsRecyclerView.refresh(); + }); + postViewV2Fragment.show(getChildFragmentManager(), "post_view"); } }; private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() { @@ -1086,7 +1091,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe fragmentActivity.navigateToThread(thread.getThreadId(), profileModel.getUsername()); profileDetailsBinding.btnDM.setEnabled(true); }).execute(); - }); + }); profileDetailsBinding.mainProfileImage.setOnClickListener(v -> { if (!hasStories) { // show profile pic @@ -1160,7 +1165,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe } private void updateSwipeRefreshState() { - Log.d("austin_debug", "usrs: "+binding.postsRecyclerView.isFetching()); + Log.d("austin_debug", "usrs: " + binding.postsRecyclerView.isFetching()); binding.swipeRefreshLayout.setRefreshing(binding.postsRecyclerView.isFetching()); } diff --git a/app/src/main/java/awais/instagrabber/repositories/MediaRepository.java b/app/src/main/java/awais/instagrabber/repositories/MediaRepository.java index ea7d97bf..6c71040b 100644 --- a/app/src/main/java/awais/instagrabber/repositories/MediaRepository.java +++ b/app/src/main/java/awais/instagrabber/repositories/MediaRepository.java @@ -11,6 +11,7 @@ import retrofit2.http.GET; import retrofit2.http.Header; import retrofit2.http.POST; import retrofit2.http.Path; +import retrofit2.http.Query; import retrofit2.http.QueryMap; public interface MediaRepository { @@ -60,4 +61,15 @@ public interface MediaRepository { Call uploadFinish(@Header("retry_context") final String retryContext, @QueryMap Map queryParams, @FieldMap final Map signedForm); + + @FormUrlEncoded + @POST("/api/v1/media/{mediaId}/delete/") + Call delete(@Path("mediaId") final String mediaId, + @Query("media_type") final String mediaType, + @FieldMap final Map signedForm); + + @FormUrlEncoded + @POST("/api/v1/media/{mediaId}/archive/") + Call archive(@Path("mediaId") final String mediaId, + @FieldMap final Map signedForm); } diff --git a/app/src/main/java/awais/instagrabber/viewmodels/PostViewV2ViewModel.java b/app/src/main/java/awais/instagrabber/viewmodels/PostViewV2ViewModel.java index 41fd7930..7f53850f 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/PostViewV2ViewModel.java +++ b/app/src/main/java/awais/instagrabber/viewmodels/PostViewV2ViewModel.java @@ -24,6 +24,9 @@ import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.TextUtils; import awais.instagrabber.webservices.MediaService; import awais.instagrabber.webservices.ServiceCallback; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; import static awais.instagrabber.utils.Utils.settingsHelper; @@ -75,6 +78,7 @@ public class PostViewV2ViewModel extends ViewModel { final ImmutableList.Builder builder = ImmutableList.builder(); if (isLoggedIn && media.getUser() != null && media.getUser().getPk() == viewerId) { builder.add(R.id.edit_caption); + builder.add(R.id.delete); } options.postValue(builder.build()); } @@ -290,4 +294,36 @@ public class PostViewV2ViewModel extends ViewModel { public void setViewCount(final Long viewCount) { this.viewCount.postValue(viewCount); } + + public LiveData> delete() { + final MutableLiveData> data = new MutableLiveData<>(); + data.postValue(Resource.loading(null)); + final Call request = mediaService.delete(media.getId(), media.getMediaType()); + if (request == null) { + data.postValue(Resource.success(new Object())); + return data; + } + request.enqueue(new Callback() { + @Override + public void onResponse(@NonNull final Call call, @NonNull final Response response) { + if (!response.isSuccessful()) { + data.postValue(Resource.error(R.string.generic_null_response, null)); + return; + } + final String body = response.body(); + if (body == null) { + data.postValue(Resource.error(R.string.generic_null_response, null)); + return; + } + data.postValue(Resource.success(new Object())); + } + + @Override + public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { + Log.e(TAG, "onFailure: ", t); + data.postValue(Resource.error(t.getMessage(), null)); + } + }); + return data; + } } diff --git a/app/src/main/java/awais/instagrabber/webservices/MediaService.java b/app/src/main/java/awais/instagrabber/webservices/MediaService.java index 210c5b32..e1990e87 100644 --- a/app/src/main/java/awais/instagrabber/webservices/MediaService.java +++ b/app/src/main/java/awais/instagrabber/webservices/MediaService.java @@ -5,6 +5,7 @@ import android.util.Log; import androidx.annotation.NonNull; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import org.json.JSONException; @@ -17,6 +18,7 @@ import java.util.Map; import java.util.Objects; import java.util.UUID; +import awais.instagrabber.models.enums.MediaItemType; import awais.instagrabber.repositories.MediaRepository; import awais.instagrabber.repositories.requests.UploadFinishOptions; import awais.instagrabber.repositories.responses.LikersResponse; @@ -33,6 +35,9 @@ import retrofit2.Retrofit; public class MediaService extends BaseService { private static final String TAG = "MediaService"; + private static final List DELETABLE_ITEMS_TYPES = ImmutableList.of(MediaItemType.MEDIA_TYPE_IMAGE, + MediaItemType.MEDIA_TYPE_VIDEO, + MediaItemType.MEDIA_TYPE_SLIDER); private final MediaRepository repository; private final String deviceUuid, csrfToken; @@ -444,4 +449,31 @@ public class MediaService extends BaseService { final Map signedForm = Utils.sign(formBuilder.build()); return repository.uploadFinish(MediaUploadHelper.getRetryContextString(), queryMap, signedForm); } + + public Call delete(@NonNull final String postId, + @NonNull final MediaItemType type) { + if (!DELETABLE_ITEMS_TYPES.contains(type)) return null; + final Map form = new HashMap<>(); + form.put("_csrftoken", csrfToken); + form.put("_uid", userId); + form.put("_uuid", deviceUuid); + form.put("igtv_feed_preview", "false"); + form.put("media_id", postId); + final Map signedForm = Utils.sign(form); + final String mediaType; + switch (type) { + case MEDIA_TYPE_IMAGE: + mediaType = "PHOTO"; + break; + case MEDIA_TYPE_VIDEO: + mediaType = "VIDEO"; + break; + case MEDIA_TYPE_SLIDER: + mediaType = "CAROUSEL"; + break; + default: + return null; + } + return repository.delete(postId, mediaType, signedForm); + } } diff --git a/app/src/main/res/menu/post_view_menu.xml b/app/src/main/res/menu/post_view_menu.xml index d9a8ebc1..aca7a6cc 100644 --- a/app/src/main/res/menu/post_view_menu.xml +++ b/app/src/main/res/menu/post_view_menu.xml @@ -3,4 +3,7 @@ + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d67da637..b13df845 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -467,4 +467,5 @@ Response status is not ok! Request failed! Marked as seen + Delete unsuccessful From 6fec6f5c86e3150ef0d3f228134d2d1a60d1513c Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Sat, 20 Mar 2021 13:37:17 -0400 Subject: [PATCH 4/8] convert HashtagFetcher --- .../instagrabber/activities/MainActivity.java | 2 +- .../instagrabber/asyncs/HashtagFetcher.java | 102 ------------- .../asyncs/HashtagPostFetchService.java | 6 +- .../fragments/HashTagFragment.java | 135 +++++++----------- .../instagrabber/models/HashtagModel.java | 35 ----- .../models/enums/FollowingType.java | 35 +++++ .../repositories/GraphQLRepository.java | 3 + .../repositories/TagsRepository.java | 14 +- .../repositories/responses/Hashtag.java | 45 ++++++ .../webservices/GraphQLService.java | 42 ++++++ .../instagrabber/webservices/TagsService.java | 51 +++---- 11 files changed, 208 insertions(+), 262 deletions(-) delete mode 100644 app/src/main/java/awais/instagrabber/asyncs/HashtagFetcher.java delete mode 100755 app/src/main/java/awais/instagrabber/models/HashtagModel.java create mode 100755 app/src/main/java/awais/instagrabber/models/enums/FollowingType.java create mode 100755 app/src/main/java/awais/instagrabber/repositories/responses/Hashtag.java diff --git a/app/src/main/java/awais/instagrabber/activities/MainActivity.java b/app/src/main/java/awais/instagrabber/activities/MainActivity.java index fb56d027..df2369cc 100644 --- a/app/src/main/java/awais/instagrabber/activities/MainActivity.java +++ b/app/src/main/java/awais/instagrabber/activities/MainActivity.java @@ -714,7 +714,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage final NavController navController = currentNavControllerLiveData.getValue(); if (navController == null) return; final Bundle bundle = new Bundle(); - bundle.putString("hashtag", "#" + hashtag); + bundle.putString("hashtag", hashtag); navController.navigate(R.id.action_global_hashTagFragment, bundle); } diff --git a/app/src/main/java/awais/instagrabber/asyncs/HashtagFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/HashtagFetcher.java deleted file mode 100644 index 09630e6f..00000000 --- a/app/src/main/java/awais/instagrabber/asyncs/HashtagFetcher.java +++ /dev/null @@ -1,102 +0,0 @@ -package awais.instagrabber.asyncs; - -import android.os.AsyncTask; -import android.util.Log; - -import androidx.annotation.Nullable; - -import org.json.JSONArray; -import org.json.JSONObject; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.URL; - -import awais.instagrabber.BuildConfig; -import awais.instagrabber.interfaces.FetchListener; -import awais.instagrabber.models.HashtagModel; -import awais.instagrabber.utils.Constants; -import awais.instagrabber.utils.NetworkUtils; -//import awaisomereport.LogCollector; - -//import static awais.instagrabber.utils.Utils.logCollector; - -public final class HashtagFetcher extends AsyncTask { - private static final String TAG = "HashtagFetcher"; - - private final FetchListener fetchListener; - private final String hashtag; - - public HashtagFetcher(String hashtag, FetchListener fetchListener) { - this.hashtag = hashtag; - this.fetchListener = fetchListener; - } - - @Nullable - @Override - protected HashtagModel doInBackground(final Void... voids) { - HashtagModel result = null; - - try { - final HttpURLConnection conn = (HttpURLConnection) new URL("https://www.instagram.com/explore/tags/" + hashtag + "/?__a=1") - .openConnection(); - conn.setUseCaches(true); - conn.connect(); - - if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { - final JSONObject user = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("graphql") - .getJSONObject(Constants.EXTRAS_HASHTAG); - - final JSONObject timelineMedia = user.getJSONObject("edge_hashtag_to_media"); - if (timelineMedia.has("edges")) { - final JSONArray edges = timelineMedia.getJSONArray("edges"); - } - - result = new HashtagModel( - user.getString(Constants.EXTRAS_ID), - user.getString("name"), - user.getString("profile_pic_url"), - timelineMedia.getLong("count"), - user.optBoolean("is_following")); - } else { - BufferedReader bufferedReader = null; - try { - final InputStream responseInputStream = conn.getErrorStream(); - bufferedReader = new BufferedReader(new InputStreamReader(responseInputStream)); - final StringBuilder builder = new StringBuilder(); - for (String line = bufferedReader.readLine(); line != null; line = bufferedReader.readLine()) { - if (builder.length() != 0) { - builder.append("\n"); - } - builder.append(line); - } - Log.d(TAG, "doInBackground: " + builder.toString()); - } finally { - if (bufferedReader != null) { - try { - bufferedReader.close(); - } catch (IOException ignored) { - } - } - } - } - - conn.disconnect(); - } catch (final Exception e) { -// if (logCollector != null) -// logCollector.appendException(e, LogCollector.LogFile.ASYNC_HASHTAG_FETCHER, "doInBackground"); - if (BuildConfig.DEBUG) Log.e(TAG, "", e); - if (fetchListener != null) fetchListener.onFailure(e); - } - - return result; - } - - @Override - protected void onPostExecute(final HashtagModel result) { - if (fetchListener != null) fetchListener.onResult(result); - } -} diff --git a/app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java index 46e4f807..fa9f331e 100644 --- a/app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java +++ b/app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java @@ -4,7 +4,7 @@ import java.util.List; import awais.instagrabber.customviews.helpers.PostFetcher; import awais.instagrabber.interfaces.FetchListener; -import awais.instagrabber.models.HashtagModel; +import awais.instagrabber.repositories.responses.Hashtag; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.PostsFetchResponse; import awais.instagrabber.webservices.GraphQLService; @@ -14,12 +14,12 @@ import awais.instagrabber.webservices.TagsService; public class HashtagPostFetchService implements PostFetcher.PostFetchService { private final TagsService tagsService; private final GraphQLService graphQLService; - private final HashtagModel hashtagModel; + private final Hashtag hashtagModel; private String nextMaxId; private boolean moreAvailable; private final boolean isLoggedIn; - public HashtagPostFetchService(final HashtagModel hashtagModel, final boolean isLoggedIn) { + public HashtagPostFetchService(final Hashtag hashtagModel, final boolean isLoggedIn) { this.hashtagModel = hashtagModel; this.isLoggedIn = isLoggedIn; tagsService = isLoggedIn ? TagsService.getInstance() : null; diff --git a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java index d2b30803..d822587d 100644 --- a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java @@ -43,7 +43,6 @@ import java.util.Set; import awais.instagrabber.R; import awais.instagrabber.activities.MainActivity; import awais.instagrabber.adapters.FeedAdapterV2; -import awais.instagrabber.asyncs.HashtagFetcher; import awais.instagrabber.asyncs.HashtagPostFetchService; import awais.instagrabber.asyncs.PostFetcher; import awais.instagrabber.customviews.PrimaryActionModeCallback; @@ -54,12 +53,12 @@ import awais.instagrabber.db.entities.Favorite; import awais.instagrabber.db.repositories.FavoriteRepository; import awais.instagrabber.db.repositories.RepositoryCallback; import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment; -import awais.instagrabber.interfaces.FetchListener; -import awais.instagrabber.models.HashtagModel; import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.models.StoryModel; import awais.instagrabber.models.enums.FavoriteType; +import awais.instagrabber.models.enums.FollowingType; import awais.instagrabber.repositories.requests.StoryViewerOptions; +import awais.instagrabber.repositories.responses.Hashtag; import awais.instagrabber.repositories.responses.Location; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.utils.Constants; @@ -67,16 +66,18 @@ import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.DownloadUtils; import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.Utils; +import awais.instagrabber.webservices.GraphQLService; import awais.instagrabber.webservices.ServiceCallback; import awais.instagrabber.webservices.StoriesService; import awais.instagrabber.webservices.TagsService; -//import awaisomereport.LogCollector; import static androidx.core.content.PermissionChecker.checkSelfPermission; import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION; -//import static awais.instagrabber.utils.Utils.logCollector; import static awais.instagrabber.utils.Utils.settingsHelper; +//import awaisomereport.LogCollector; +//import static awais.instagrabber.utils.Utils.logCollector; + public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener { private static final String TAG = "HashTagFragment"; private static final int STORAGE_PERM_REQUEST_CODE = 8020; @@ -91,12 +92,13 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe private boolean hasStories = false; private boolean opening = false; private String hashtag; - private HashtagModel hashtagModel = null; + private Hashtag hashtagModel = null; private ActionMode actionMode; private StoriesService storiesService; private AsyncTask currentlyExecuting; private boolean isLoggedIn; private TagsService tagsService; + private GraphQLService graphQLService; private boolean storiesFetching; private Set selectedFeedModels; private Media downloadFeedModel; @@ -270,13 +272,29 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe } } }; + private final ServiceCallback cb = new ServiceCallback() { + @Override + public void onSuccess(final Hashtag result) { + hashtagModel = result; + binding.swipeRefreshLayout.setRefreshing(false); + setHashtagDetails(); + } + + @Override + public void onFailure(final Throwable t) { + setHashtagDetails(); + } + }; @Override public void onCreate(@Nullable final Bundle savedInstanceState) { super.onCreate(savedInstanceState); fragmentActivity = (MainActivity) requireActivity(); - tagsService = TagsService.getInstance(); - storiesService = StoriesService.getInstance(null, 0L, null); + final String cookie = settingsHelper.getString(Constants.COOKIE); + 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(); setHasOptionsMenu(true); } @@ -358,29 +376,15 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe private void init() { if (getArguments() == null) return; - final String cookie = settingsHelper.getString(Constants.COOKIE); - isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0; final HashTagFragmentArgs fragmentArgs = HashTagFragmentArgs.fromBundle(getArguments()); hashtag = fragmentArgs.getHashtag(); fetchHashtagModel(); } private void fetchHashtagModel() { - stopCurrentExecutor(); binding.swipeRefreshLayout.setRefreshing(true); - currentlyExecuting = new HashtagFetcher(hashtag.substring(1), new FetchListener() { - @Override - public void onResult(final HashtagModel result) { - hashtagModel = result; - binding.swipeRefreshLayout.setRefreshing(false); - setHashtagDetails(); - } - - @Override - public void onFailure(Throwable t) { - setHashtagDetails(); - } - }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + if (isLoggedIn) tagsService.fetch(hashtag, cb); + else graphQLService.fetchTag(hashtag, cb); } private void setupPosts() { @@ -409,8 +413,10 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe fetchStories(); if (isLoggedIn) { hashtagDetailsBinding.btnFollowTag.setVisibility(View.VISIBLE); - hashtagDetailsBinding.btnFollowTag.setText(hashtagModel.getFollowing() ? R.string.unfollow : R.string.follow); - hashtagDetailsBinding.btnFollowTag.setChipIconResource(hashtagModel.getFollowing() + hashtagDetailsBinding.btnFollowTag.setText(hashtagModel.getFollowing() == FollowingType.FOLLOWING + ? R.string.unfollow + : R.string.follow); + hashtagDetailsBinding.btnFollowTag.setChipIconResource(hashtagModel.getFollowing() == FollowingType.FOLLOWING ? R.drawable.ic_outline_person_add_disabled_24 : R.drawable.ic_outline_person_add_24); hashtagDetailsBinding.btnFollowTag.setOnClickListener(v -> { @@ -420,36 +426,12 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe final String deviceUuid = Utils.settingsHelper.getString(Constants.DEVICE_UUID); if (csrfToken != null && userId != 0 && deviceUuid != null) { hashtagDetailsBinding.btnFollowTag.setClickable(false); - if (!hashtagModel.getFollowing()) { - tagsService.follow(hashtag.substring(1), csrfToken, userId, deviceUuid, new ServiceCallback() { - @Override - public void onSuccess(final Boolean result) { - hashtagDetailsBinding.btnFollowTag.setClickable(true); - if (!result) { - Log.e(TAG, "onSuccess: result is false"); - Snackbar.make(root, R.string.downloader_unknown_error, BaseTransientBottomBar.LENGTH_LONG) - .show(); - return; - } - hashtagDetailsBinding.btnFollowTag.setText(R.string.unfollow); - hashtagDetailsBinding.btnFollowTag.setChipIconResource(R.drawable.ic_outline_person_add_disabled_24); - } - - @Override - public void onFailure(@NonNull final Throwable t) { - hashtagDetailsBinding.btnFollowTag.setClickable(true); - Log.e(TAG, "onFailure: ", t); - final String message = t.getMessage(); - Snackbar.make(root, - message != null ? message - : getString(R.string.downloader_unknown_error), - BaseTransientBottomBar.LENGTH_LONG) - .show(); - } - }); - return; - } - tagsService.unfollow(hashtag.substring(1), csrfToken, userId, deviceUuid, new ServiceCallback() { + tagsService.changeFollow(hashtagModel.getFollowing() == FollowingType.FOLLOWING ? "unfollow" : "follow", + hashtag, + csrfToken, + userId, + deviceUuid, + new ServiceCallback() { @Override public void onSuccess(final Boolean result) { hashtagDetailsBinding.btnFollowTag.setClickable(true); @@ -459,8 +441,8 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe .show(); return; } - hashtagDetailsBinding.btnFollowTag.setText(R.string.follow); - hashtagDetailsBinding.btnFollowTag.setChipIconResource(R.drawable.ic_outline_person_add_24); + hashtagDetailsBinding.btnFollowTag.setText(R.string.unfollow); + hashtagDetailsBinding.btnFollowTag.setChipIconResource(R.drawable.ic_outline_person_add_disabled_24); } @Override @@ -475,6 +457,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe .show(); } }); + return; } }); } else { @@ -482,15 +465,15 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe } hashtagDetailsBinding.favChip.setVisibility(View.VISIBLE); final FavoriteRepository favoriteRepository = FavoriteRepository.getInstance(FavoriteDataSource.getInstance(getContext())); - favoriteRepository.getFavorite(hashtag.substring(1), FavoriteType.HASHTAG, new RepositoryCallback() { + favoriteRepository.getFavorite(hashtag, FavoriteType.HASHTAG, new RepositoryCallback() { @Override public void onSuccess(final Favorite result) { favoriteRepository.insertOrUpdateFavorite(new Favorite( result.getId(), - hashtag.substring(1), + hashtag, FavoriteType.HASHTAG, hashtagModel.getName(), - hashtagModel.getSdProfilePic(), + hashtagModel.getProfilePicUrl(), result.getDateAdded() ), new RepositoryCallback() { @Override @@ -511,10 +494,10 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe } }); hashtagDetailsBinding.favChip.setOnClickListener( - v -> favoriteRepository.getFavorite(hashtag.substring(1), FavoriteType.HASHTAG, new RepositoryCallback() { + v -> favoriteRepository.getFavorite(hashtag, FavoriteType.HASHTAG, new RepositoryCallback() { @Override public void onSuccess(final Favorite result) { - favoriteRepository.deleteFavorite(hashtag.substring(1), FavoriteType.HASHTAG, new RepositoryCallback() { + favoriteRepository.deleteFavorite(hashtag, FavoriteType.HASHTAG, new RepositoryCallback() { @Override public void onSuccess(final Void result) { hashtagDetailsBinding.favChip.setText(R.string.add_to_favorites); @@ -531,10 +514,10 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe public void onDataNotAvailable() { favoriteRepository.insertOrUpdateFavorite(new Favorite( 0, - hashtag.substring(1), + hashtag, FavoriteType.HASHTAG, hashtagModel.getName(), - hashtagModel.getSdProfilePic(), + hashtagModel.getProfilePicUrl(), new Date() ), new RepositoryCallback() { @Override @@ -549,12 +532,12 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe }); } })); - hashtagDetailsBinding.mainHashtagImage.setImageURI(hashtagModel.getSdProfilePic()); - final String postCount = String.valueOf(hashtagModel.getPostCount()); + hashtagDetailsBinding.mainHashtagImage.setImageURI(hashtagModel.getProfilePicUrl()); + final String postCount = String.valueOf(hashtagModel.getMediaCount()); final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline, - hashtagModel.getPostCount() > 2000000000L + hashtagModel.getMediaCount() > 2000000000L ? 2000000000 - : hashtagModel.getPostCount().intValue(), + : hashtagModel.getMediaCount().intValue(), postCount)); span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0); span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0); @@ -602,23 +585,11 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe }); } - public void stopCurrentExecutor() { - if (currentlyExecuting != null) { - try { - currentlyExecuting.cancel(true); - } catch (final Exception e) { -// if (logCollector != null) -// logCollector.appendException(e, LogCollector.LogFile.MAIN_HELPER, "stopCurrentExecutor"); - Log.e(TAG, "", e); - } - } - } - private void setTitle() { final ActionBar actionBar = fragmentActivity.getSupportActionBar(); if (actionBar != null) { // Log.d(TAG, "setting title: " + hashtag); - actionBar.setTitle(hashtag); + actionBar.setTitle('#' + hashtag); // final Handler handler = new Handler(); // handler.postDelayed(() -> , 1000); } diff --git a/app/src/main/java/awais/instagrabber/models/HashtagModel.java b/app/src/main/java/awais/instagrabber/models/HashtagModel.java deleted file mode 100755 index 58ff7932..00000000 --- a/app/src/main/java/awais/instagrabber/models/HashtagModel.java +++ /dev/null @@ -1,35 +0,0 @@ -package awais.instagrabber.models; - -import java.io.Serializable; - -public final class HashtagModel implements Serializable { - private final boolean following; - private final long postCount; - private final String id; - private final String name; - private final String sdProfilePic; - - public HashtagModel(final String id, final String name, final String sdProfilePic, final long postCount, final boolean following) { - this.id = id; - this.name = name; - this.sdProfilePic = sdProfilePic; - this.postCount = postCount; - this.following = following; - } - - public String getId() { - return id; - } - - public String getName() { - return name; - } - - public String getSdProfilePic() { - return sdProfilePic; - } - - public Long getPostCount() { return postCount; } - - public boolean getFollowing() { return following; } -} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/models/enums/FollowingType.java b/app/src/main/java/awais/instagrabber/models/enums/FollowingType.java new file mode 100755 index 00000000..ae10ec7f --- /dev/null +++ b/app/src/main/java/awais/instagrabber/models/enums/FollowingType.java @@ -0,0 +1,35 @@ +package awais.instagrabber.models.enums; + +import com.google.gson.annotations.SerializedName; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +public enum FollowingType implements Serializable { + @SerializedName("1") + FOLLOWING(1), + @SerializedName("0") + NOT_FOLLOWING(0); + + private final int id; + private static final Map map = new HashMap<>(); + + static { + for (FollowingType type : FollowingType.values()) { + map.put(type.id, type); + } + } + + FollowingType(final int id) { + this.id = id; + } + + public int getId() { + return id; + } + + public static FollowingType valueOf(final int id) { + return map.get(id); + } +} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/repositories/GraphQLRepository.java b/app/src/main/java/awais/instagrabber/repositories/GraphQLRepository.java index 0165ad9e..a6246fe0 100644 --- a/app/src/main/java/awais/instagrabber/repositories/GraphQLRepository.java +++ b/app/src/main/java/awais/instagrabber/repositories/GraphQLRepository.java @@ -13,4 +13,7 @@ public interface GraphQLRepository { @GET("/{username}/?__a=1") Call getUser(@Path("username") String username); + + @GET("/explore/tags/{tag}/?__a=1") + Call getTag(@Path("tag") String tag); } diff --git a/app/src/main/java/awais/instagrabber/repositories/TagsRepository.java b/app/src/main/java/awais/instagrabber/repositories/TagsRepository.java index d5e7443e..a70ed369 100644 --- a/app/src/main/java/awais/instagrabber/repositories/TagsRepository.java +++ b/app/src/main/java/awais/instagrabber/repositories/TagsRepository.java @@ -2,6 +2,7 @@ package awais.instagrabber.repositories; import java.util.Map; +import awais.instagrabber.repositories.responses.Hashtag; import awais.instagrabber.repositories.responses.TagFeedResponse; import retrofit2.Call; import retrofit2.http.FieldMap; @@ -13,15 +14,14 @@ import retrofit2.http.Path; import retrofit2.http.QueryMap; public interface TagsRepository { - @FormUrlEncoded - @POST("/api/v1/tags/follow/{tag}/") - Call follow(@FieldMap final Map signedForm, - @Path("tag") String tag); + @GET("/api/v1/tags/{tag}/info/") + Call fetch(@Path("tag") final String tag); @FormUrlEncoded - @POST("/api/v1/tags/unfollow/{tag}/") - Call unfollow(@FieldMap final Map signedForm, - @Path("tag") String tag); + @POST("/api/v1/tags/{action}/{tag}/") + Call changeFollow(@FieldMap final Map signedForm, + @Path("action") String action, + @Path("tag") String tag); @GET("/api/v1/feed/tag/{tag}/") Call fetchPosts(@Path("tag") final String tag, diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/Hashtag.java b/app/src/main/java/awais/instagrabber/repositories/responses/Hashtag.java new file mode 100755 index 00000000..aca5daad --- /dev/null +++ b/app/src/main/java/awais/instagrabber/repositories/responses/Hashtag.java @@ -0,0 +1,45 @@ +package awais.instagrabber.repositories.responses; + +import java.io.Serializable; + +import awais.instagrabber.models.enums.FollowingType; + +public final class Hashtag implements Serializable { + private final FollowingType following; // 0 false 1 true + private final long mediaCount; + private final String id; + private final String name; + private final String profilePicUrl; // on app API this is always null (property exists) + + public Hashtag(final String id, + final String name, + final String profilePicUrl, + final long mediaCount, + final FollowingType following) { + this.id = id; + this.name = name; + this.profilePicUrl = profilePicUrl; + this.mediaCount = mediaCount; + this.following = following; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public String getProfilePicUrl() { + return profilePicUrl; + } + + public Long getMediaCount() { + return mediaCount; + } + + public FollowingType getFollowing() { + return following; + } +} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/webservices/GraphQLService.java b/app/src/main/java/awais/instagrabber/webservices/GraphQLService.java index c83ee008..52e91af1 100644 --- a/app/src/main/java/awais/instagrabber/webservices/GraphQLService.java +++ b/app/src/main/java/awais/instagrabber/webservices/GraphQLService.java @@ -14,9 +14,11 @@ 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.Media; import awais.instagrabber.repositories.responses.PostsFetchResponse; import awais.instagrabber.repositories.responses.User; @@ -359,4 +361,44 @@ public class GraphQLService extends BaseService { } }); } + + 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"), + body.getString("profile_pic_url"), + timelineMedia.getLong("count"), + body.optBoolean("is_following") ? FollowingType.FOLLOWING : FollowingType.NOT_FOLLOWING)); + } 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); + } + } + }); + } } diff --git a/app/src/main/java/awais/instagrabber/webservices/TagsService.java b/app/src/main/java/awais/instagrabber/webservices/TagsService.java index 61f31261..615d72af 100644 --- a/app/src/main/java/awais/instagrabber/webservices/TagsService.java +++ b/app/src/main/java/awais/instagrabber/webservices/TagsService.java @@ -13,6 +13,7 @@ import java.util.HashMap; import java.util.Map; import awais.instagrabber.repositories.TagsRepository; +import awais.instagrabber.repositories.responses.Hashtag; import awais.instagrabber.repositories.responses.PostsFetchResponse; import awais.instagrabber.repositories.responses.TagFeedResponse; import awais.instagrabber.utils.TextUtils; @@ -44,53 +45,39 @@ public class TagsService extends BaseService { return instance; } - public void follow(@NonNull final String tag, - @NonNull final String csrfToken, - @NonNull final long userId, - @NonNull final String deviceUuid, - final ServiceCallback callback) { - final Map form = new HashMap<>(3); - form.put("_csrftoken", csrfToken); - form.put("_uid", userId); - form.put("_uuid", deviceUuid); - final Map signedForm = Utils.sign(form); - final Call request = repository.follow(signedForm, tag); - request.enqueue(new Callback() { + public void fetch(@NonNull final String tag, + final ServiceCallback callback) { + final Call request = repository.fetch(tag); + request.enqueue(new Callback() { @Override - public void onResponse(@NonNull final Call call, @NonNull final Response response) { - final String body = response.body(); - if (body == null) { - callback.onFailure(new RuntimeException("body is null")); + public void onResponse(@NonNull final Call call, @NonNull final Response response) { + if (callback == null) { return; } - try { - final JSONObject jsonObject = new JSONObject(body); - final String status = jsonObject.optString("status"); - callback.onSuccess(status.equals("ok")); - } catch (JSONException e) { - Log.e(TAG, "onResponse: ", e); - } + callback.onSuccess(response.body()); } @Override - public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { - // Log.e(TAG, "onFailure: ", t); - callback.onFailure(t); + public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { + if (callback != null) { + callback.onFailure(t); + } } }); } - public void unfollow(@NonNull final String tag, - @NonNull final String csrfToken, - @NonNull final long userId, - @NonNull final String deviceUuid, - final ServiceCallback callback) { + public void changeFollow(@NonNull final String action, + @NonNull final String tag, + @NonNull final String csrfToken, + @NonNull final long userId, + @NonNull final String deviceUuid, + final ServiceCallback callback) { final Map form = new HashMap<>(3); form.put("_csrftoken", csrfToken); form.put("_uid", userId); form.put("_uuid", deviceUuid); final Map signedForm = Utils.sign(form); - final Call request = repository.unfollow(signedForm, tag); + final Call request = repository.changeFollow(signedForm, action, tag); request.enqueue(new Callback() { @Override public void onResponse(@NonNull final Call call, @NonNull final Response response) { From 46bf8d9191dc0ba485210bf3efb887f841f3d9c2 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 20 Mar 2021 18:29:42 +0000 Subject: [PATCH 5/8] Update dependency com.google.guava:guava to v27.1-jre --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index c890eba9..07d0546a 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -81,7 +81,7 @@ dependencies { implementation 'androidx.palette:palette:1.0.0' implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" - implementation 'com.google.guava:guava:27.0.1-android' + implementation 'com.google.guava:guava:27.1-jre' // Room def room_version = "2.2.6" From a64a5644fcd69946f9d35097b2f3f2a4c91e9856 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 20 Mar 2021 18:32:27 +0000 Subject: [PATCH 6/8] Update dependency gradle to v6.8.3 --- gradle/wrapper/gradle-wrapper.jar | Bin 58695 -> 59203 bytes gradle/wrapper/gradle-wrapper.properties | 7 ++----- gradlew | 2 ++ gradlew.bat | 25 +++++++---------------- 4 files changed, 11 insertions(+), 23 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index f3d88b1c2faf2fc91d853cd5d4242b5547257070..e708b1c023ec8b20f512888fe07c5bd3ff77bb8f 100755 GIT binary patch delta 12842 zcmY+q1ymhDmoSOmQr324hm`&0SZbS3R18r37J8Z0F-T>@gIK` zatajf2b1lO#&E_RnGDF51uk%8Wcxm3`%Yhe7Psx?*?eZ?8M9_+G(?L=s^OG1n#S(NP3gF?2Mr2^f5E7sM~iVC`Rn;(-^MZ zZu*ZXB;XmgvPls(e#)MMTObsEx9oNz-K?AmQ8pP&P7vqx*=5zxjU+ye_1R<%KSg1? z7H&Yh))(Ke!Pa+aVuWxPKa_~Qo_IH}*;tV8n~O*Xa?t3P^9=L%=wOL1=~{LVv}mU8Q#e6s>v}iV8cDP|EdY)`dp≶7^21 ziF~qst3+S0y_IcTmzBD?t^AL=8|hpx>4aXc#L1YriEI=T#&IZ=SoAEyLg|^3d~uWZ zL(@1$!3on^gfz^e5VdZe5qx_>I%?g|J-FS>NG7S8Uwqt9t6KDa`8Nu!bDng+bM`&i zd>s2#sQ2Dsh6c}3YYi}8DqsK)DG!%;@xqz(<#=W`C`X+!HhtF~r~9OsI`@n36>D}N zz^HjPst0d<*2#=afSFiYwBeNZDk>BahnaW;GkQDA235(RJ%j;vVg80O#gk|q<#+OO z!F(BArIYDQG-{DlHpf+F=!)yw08zWccjd6DKgR+zJ(0X3zS;mzg+Na{$2N+AhF7`& zXj`aBWy{YG#8s$C5=GZH$a@!+F42?=O~WoaIjO;k;0P0nE5|ma;I^@xN`kKvIjTQe z1!_si%O1V@BP`r(WwTpr7HN&p#_-)5!T z%!r5ZL79g`v%i29=J2rPglr;%LCc+ZSZeh71?CfOgZ&EJdacV35*58xwhWGhyMhx{ z5KAVHq&&zae)(vc?T~KB9rtcfzy#SAUvce5`+$`_U7}=j*;@5(PyBoTp#IwDtV?s% zQ%T#rekISAFx`AeHyBx6BP^4OtUo>VhbksSk&W=OkQIO#SJ13R8z6r|HNM}$TK=58 z^$>Cg`+P;E@||v&RXQ8dF?fqSS3;wKND5tF(tf3C`q!LEI9_~9LgscI=n#Q>Vl6%6 z^xQ<;f6C*>yStD8WZ4LPzJjmeuu1L`A4BDvEy6DgDMC)PB+3}KWft<^5DPgko{>P8 zJL=zIrDlQ3l54nAxi=;0*HF+cQ`|0Z;~#mt0NHndDI8Ft6^Gp+Lz!19<=L3-abvfX zelFvqpMs+)n2}tXR2j_UG99=i2A)GzpZxTtF=_i+PyVcT4m=oLbh0j3wb~T*1D(f! zOnvTcyI^VbldY>z*{sBnk&j3`-I6GqvB;Qa*bl<5YKpLMNKjDk-$VW9y)f6Qa?T73 z1=aTsLXIW~Xm4p^spI@Hmcn0=j#SgUrQ(LwQu{s6rO7cNL8G>CZWT(hIbdv%x(Jlp zoI&SgpA?j``5vR&m!53m5=zO&hWkznAAO#F&1pI^L3;~$fir#2Cha{-SD4F2dZ$Yj zNWSt;3dLNmPZ<;D0kQqC&yn>qqCMISL58?}bV(f=u%P^DVHARl?p=uptqCK6qHR%G znz@gHYqEnCEL=>78`c?7$>81*%RQ`@urhDyDti}_ZIXnVa)~U{)lq9bj?aBpb1|OX zQEOY8nV`I7nqbYP%pqaNpQZht6Jst`i`{B$ycuhg>p)3{T|=C)ZRx zwhOaI{+g~G@s-nQB66k4ZKP7Wk4v)bVT$sdEEvJj5EkX)2#Rp1J(m+pLGRGtgR}!C zJ1^uNmx6bMEDWh)dOtRzDkdg7lNs7AO6;LFpmezCp}|2dbseLD5M?D7VP+y`GysD~ zXb)?J3jG=5(Rn1_;i`Dqld zLN8F94c4{|1+YfvKa)vn+;*{ju_%uj`H`ke;KQ2P7DD5nGOQP(R8l=AL0{o9qc%9& z4e))*rFyxhsM%wgJC6S4tJLteds>&34_6tvv7a(#F`kk%031W1Aq<#&3|2ZN-Cqq`-l5Ajt zmAD72)g^6kQ@$3=wef)3tC4m)dsw?AxwR=`#N_`9Hd+t$4SzJ+Za8)malG?}{YbGV zxcLZ87Enlr@O~eE@6qx44m*uKyFE-L%FP1HxR_($c}_VqmXk%xb*nPsReTfvRCy#; zLY#`)G1RGkp=-|NJ^jIMW}3=(vjF6sXq}{QLw%AcwOIS4Xzu*SI~An6k)^=@T}{+b z;{p5VP*8g0P*4>A`R0;9;+Nh5H3o>@M5CSo@o)`_E?{vin&S{F5*+l|B+sN&hr~i^ zxo)Y1WCr~t-M*v{c=O$137j0hxQnsK3wkdHI@jz{r>wsxUt;$AWa$ls__3NTo)gRm zxs5xy_-19*m7b*fKPY(QViL^@bJ0u|)*rDFGM{5vt|In|Dbn>J8Y}9zMGS2hhAyEyX;#g( z3$svdx$kb`47#gDE}`MAb8TA7R|g7nS`|htx!e*OH3KH;-=d|O^tcqIH0d%+3iWA0 zc>|NUCIvSN=od!@4k5Y~-RqKc;Mj?P6lV=^P5w4Y*^K}?DsbbI!s~r})~$Z%6UvLI z5j+vg$jfj?7@8$a{2edF8S?`VQ@8Z4q4sv=4Npp2Rk!3}&cKMVgm2Y^BjZl#jZ?}) zxnI}B=kjh{W(VDN$z6XX19np0>bUe=C6Ih6_wN`?Vce#N4D9Rl3fX67_r(uM_$4H;FS0L^8S4SsUjic=MUP==s$OM)&NEvp)gDY#mLjhipsjL4`P4~y+`Ffxn){Z zM1N=8c5d!;9JDPTH^%wTa}r{{C6e<~q%Z-FCbp1aBH&ZH-)oNV1Fn^++vwDsdP6*} zaVhuu2m6!6^tlgaCy^m$Egs|YdX=V|Me#&Rq<3K`OoZI~O5Bm)H(OTPBblGUmJg0| z-izCVizZ(K;ct6*^BV0U#+S@wOf5Zixt#8bM^uTH0|NxC-~Y)n6Xq#4ROj%DVD)8= zBCj(Tvjoy@S#8(io3h46W>%(0?BH3|f~ zZq)Djpdf3=53UQ^^XbRF&r^wwiCCoP-$on0UIe_qQp8l(D;vgp5?-tOGOH$Gjwd~0 zP-c9qN8_Z?BDcLlJvT^o_QFSEa0&@kuTLnrD_uKDAQZ7!g`IO9R9fTT@7Imu&@^@m z?qLv2Y{YygNjB;sk7J*DU>$t@B2QBhPY|r*5cj8Z9cR1l3OW>>kyz_7VIbUnO1*T+ z9R_H!tG(65#gKrw8j9+gx+_FfNZzggvg8ut<>bLdeet7<#JLJ_x1f%XFklxkhk;Q& zlehT2JngNwJRkN<$!~!2&0Ypouxad+=bQtZ!X$UphLDQG_S5)O&__;c_f*|+lp2ZZ zb76mUyr3?H+16hMIi0xCNVPOzqbJg&7(w8MVCzHGtbS&j1f^eEw%Ax_x;-@du9i|; zY=1W0GG4sSZfnWW9v0COT!>0Kp4UDL2Hj+kovXh(BB?mhhdoSJ4=u}gXno7mDu=dC zoEbrTZk!pao1a3}xpEUS8VADYb;P9bB4H%rWa4Mx=Y$I85KhEnNeh!@&^4nf9H9Xm z!v|Iya%#6W8M4A#kbg|B7~F`1Ag0{=1FS6FcG-QC&M0#{?C~|i(mn~Fqr7Sf?Yse5 zuAfH&M+NU zr;=Ulc0TW}u9-^{O7RHY6OPIhyaTZE=(Nl&!jj2uVS5!ZQY2LBqP6e)7&F3Q_Hab_ zLrV(2QNJR@Q3@ySlY`qAJ9RW~ANP~kCZVc+(E_?xFf#1GdC0(ny@RWUMHZ%x#$)ve zcJ}OJcEnWXXK3lgvCS6;RcVVxAN!_E@w<4vAMGFa<$G1RE+wyj%X;u}&YuEpoFBLY zM0*$}OUKT3lDDD0O&TM_#1oBU&8>dbIXCK0$D*1#`;Df(2uT^Ys9whszl=P|xI zK4W*LaJ@*-8JDeOOjy3z`Q?(C2>>3>fNK3wAi&Px<>6wQKf}hc^znUVz-_hJ(=Wd4 z9IgSrq}Qf)hGfeb7E!z>^fAEWN>&Y|bLXLO1^4350UN=XN>k)gBATK}fRry2DzFf> zeI(W{Xb~dAwxAs=Iz=}3s2+eqikF2ZX30Fu3T?1I`cx!0rN1g2`c0fKmhEaZJ7nf# z7i(J~BWxF}HSK0xuHTjRBVugcLHr;P4EsClv;7N>sBvGKacB8^N>1Pj{+H4BO>brw z0Z=^L{Yk5nDlH1J>{YeU!Y6DDQyZmEqf3LCmI8@+&eAWLcdAks4e)z-%Fp|y7pkRL zh}ia&0kgEwac`26TUTAYnw`*P9-Q7OWaElwe(&9MbY7Am?<9OE zFl?kb79&+DH2+NM@b%G?f-GhaeE#IQrH%0x#4(vYW~7*gBkYlZ_UG$X<**g4C=s1F zetpyc6kF;#-gZ{o zo5%FNck~|JmFqChIa4HyzJ;9s^E2Q(q|ExN5GJ_+N&t=PR~t6#B$4n*`iuX z1ar~vtGu~kDGyTq9B@908w}v9v2?bp-!9ig&naSfi|YE`Z$sk-lBk*YOlN|43gU^lO_#1eF&9897yLT7 zYcHsfu2`qSC6AbNB}O{3wPcAu%$O;&a4co+Tp2_=;n%-!MlMGm-@306*aPxS&vEN4 zj(vxTGWQ6asMvUfb*p&*Fs`N}LjI4Z&xgki`R^F0>I4N9QCZ+RXKPC_CuR2&Y^#{2+ z!ilm<k;?lLILj?ya|l9 zOrF)I76r5|z+J+8 ziFg6yfg3{uQbl69k(zf@XXb1Y%n*+s5lv;=~4G5mx-nnZb5v}SQj%*ymKZCt1qOy+hkMR?8 z3gvp`aXHm=DrW6Ny_oLcLxJA%*=Qtx`2sd34-LI3R;| zOyl7p2TBbRhEzz#+0d|LY0{h(3twB_~^+8*w z(MzSza-w44?3^wDf(gH9>2>qWL&SogA z-~dj4T9o)~d0>{1_V$*al&)P&kJE z(;N?J$~(vn9K#c|HbCAO?f$k2SDJQWKaxirtifx|+UMwRCosQtaG|O>CaCtzh+1k_ zUN-Kl6?5rfCS-I>#mU)Ru z`~1=H;0sspAO7fElv->{+Q6+a2p>b zzWKwufce=pd5)@gn41XkS@m8%-2@asCj|(nG2jk~7Sq~Y$}9DXI}3OP5GhER=U&AP z46t6NH}f!7|Jn|2T{;w|BDLC1_ipdm=dMIyzuCbBc>%lXcp#a~kgzS0JDfa0u40m48`rku!q$3Z`?6tz{7*3^*p;uG^uikJU&oxP-l&}0XQc|~h7{Re)JHg*6b%(nxs+$7)^Z;BFV`}*L;>Ih zMs0u!*7d+jPeqM>>`JVZNg&G2h&w?{eiRg}{_C-q$%M!Li&?YZ(2o10ogN!NtSeNC zjIimtk-Li5J5$w6iCygi?yi5%rFx>QPLksnXoCOsKnj zWm@ShV5616Y;`R6(k1=$nob4SvJJy_(#wO@~}HOesm>Yt?QkErQn-m;~p50(~qBvdQO0Tqg#3yus1TtZVpt zez2NfwQCkO9BbTyZKb53b=pgfR5#)@qqG_jn;*jYd8%il*I7t~jol8g3`&N1tZZcU zP?@z6(Ef>6&(i8g=}|}YxuzVGL*SZ}6Xc?$r~90bt*|D*l?gqfI`mopjp%Mhfm?-x z^|Bt(sH}sqdH{8p-4YV~9?TQS?iq*C9y#_-a)8xJ9W+}0A{X$4W6q7yGx!# zioE>n)y?!4H)ge$jK${3)1^DbijD9)Tbm=idENin5;KJ7luQlufA)Y6ykK04aG;=A zS)icEA@z26kQp%oz|5ODGKAd$O^%$&Ocur*fL?wa7`4$$}c9uTHjTP0W!qq|U!ZTXG! zwem4Au0gAe-)dl%%kFQJdq!$^wL1&-O#7CA>qah-HDa=g!Cx@~#P%n-dWGatXH5pk zk`c+UtVD^6d52Jq=ezrLZC@~B>n!Jq_T)DrWX?LLT7^$JoeVtHsWP}AN(G+3&OWvB zI(4}i1CqC`HK;8cZQKq{oi2*sT2YnYWATa7K-%h5+xklmhKb%s_N9oPh`ac0p9$uY z3BUU*z1bEvEi|WFbJ12$SE@|f#%F2^r_OCT8feGbV{pP=MCN*PnKg5M^P+OlOCwFw z?nLdXdPg~8P&5Fp-3Vdn;7aG(I(LjNO-fY!2K-7a*I!t+riEn1v=>-bx$Ua~1Bj+a zAF(54&s&r(8NkRr+XfIwv~g$fxM7+tZw4*5%$~I-pnxQCI@xM9QOeJ=V*gIAP{Dz+)^?Hv zuwfLo(wQwMnKVpXPWE$dD^$WJxp!TtUGHsyrVj0({$@OqbjXyc$x-@JUf#=Uqqbi) zyT#X;Ww$0@Bjh~ATwOgraYm`5Q*M^y+45K`*XB2v+84RTvXBJ&h}vda&w?8Yc8AL~ z{E(zrVR*+rbokD+ihF5}qJC<_c=F%`_~1K77UV3r_yx=XH_jX`KJTEYkJ(jck3EZM zTN~|>DNocbL;@2$a9)Xe{WCc>MTv@bZh0NylBl(x3+y2upl3t7 zQ7zZDZ{h4a^raO-@xk1 z9TpSAw6VztodyVF+}N^t+`@ObqHijM>hLM9<5Cm$oZ4bByuMxEcs3k#se;Ob<;y`{ zqnfp+BI3w$bQh%Zm2*^j#r*Sx0PlHn=rDdx$O^$HD0=yY+DrI*2afM}3sKTZ@{v$O z-))`LxK!);ST-&LCmeR{K^H0?l-DmJlXHfv41D|tq6k}S-gm20i(Z{LNmI^n{J>+P zu zB%Yv;$Pk$%K`K`Vi`gYjjZS2Hnk3~gC}*QCLT;KZ1J--!f>fo73wVt^rrBk9PiE{s z5sNm%+$*S`pjQy9%J73s{&hyJYw8(v9${NeZ#8yu5JwA=ezl1Vbxl9)8ZkwGl362v z*~bq>^-=E|qukP$Mm0G&fvk051!m_ihTqYxyb#9dk=mbPNumYUbkIw!QlCGnl$smp z?Pd0FTDj-HYXak>3#oIM&8n5=wAs#4ma44Our{&10kl=!+tTyQsn+B5IFnLH52(CU zp=XS10t|z;9qb0~&oORiyw67 z*QlVQ@4qfLhFW%QrxT5z)7qI%;-Evg9T}+v->Wv};S)o;a`O4kH-|JI!P6(hWbVZG zu3klVR@UPg!(Xo~05p37>P17&(^+DK)UKQ`b{dp1+2xJ!9=|Yb*WH#q$;66Mks)~W zMmjG)HTiMckF|*bzs=3=`E#6i4GSb{!y+DjpmL|s`+4-nipJ;9kbFZlcL}WZ69mMM z*lyB1-adRRyA^*!a*OvREWFnBd;vdt72M0F!=GewE(`H9SOrwaiKba_ z2auy6$O9LMoP=?7=j@C+8xcc;GTrEwc&#fT#Z95R&vzyOQ7iT?n&nOXTC^koI=)GE z$(dmUqlI4kw;KE+!=tVz(wumguhX!82n+CZIFvnJSc=pG4Q+Hm)3Q${v6l(6TgRPinsg&fK*bQBHc%w@veNmwotb;NZVfgLKZ^#2% zIxyH5z3g|#kQU+yol=TUc44&Ouo4&~*(9|Mth4^Ziw`sodKfG@2tlkZMm|36g9<~Y zWE%=J!`Lbut!g;PM|kaKi#AKUdzP+35Vb)K>IU&~%mgGWmk;j8`q5!&vlSA*M98m8b9sZWplD(I~<0?+~bfN?)r{9JjUZ z9F80S7*a(lDl2}veqYj>63% z>lmBeOXGCiRh7V>Bp`H`v`l32Y2}3|2bcuDO3I%aV4mFBy!A{27_x7Pf07%n(i@eJ zR;Yiy>Te3UH)Hd}mmifLmhNs+H2nEEL;@_Gklm@~{2BQOgQS}Ml7W|PP7>0{&&k{$ zljJ&nLN>xFqFX!R1F;)1Bo1_UO)^yZ;j+GLMdOpbzcZA$gkpckam_KH&fmhifYSzO zXyrf^pR?N8CX{9MZ#qeAdo&4ccDL zQRt^{SOU{mZC}AFVtA~br7&%K74Xd4msMx`!k_TY(=~%unotUH5qP^Q(FPFP zWsL6l4qCNs?i`H^PmD~J^HdiW~& z8oZnipal~XC8Md&EVg)5YTnRVsLCM=1lC=n2CLdH4&Qq9?C;%;h6R4z=I{c9YshCl z-^V%dCZE?>&Oc!z5>c3XI7?70I}oL^kfu`~niM5Q717u8fQ~aR0+=|7Y4rV5i)WIH z##D{*zk(4@F~?nLSX+~$QI6gQ##Oj$#-+Hdval|TN)JY&*Ul_oT;1$iwmYIG5a343 zG39F~7hCJg=Fp^aJa2p@nK=N$fknb=DdHBp#YAiS$jQ*isIX!^gQ0Jpi&qy3%N9}& zizKTkS`I%fZwj;FxNq=0Gk+h3RDT}1QhQES^nt}{i9Kcf^v#X}JQbQ=Jf@tnF_@i+ z+Lft*f;}Hee=F=}=;EV0nWJ5*rct)4iL^MSZFn5Ag5&lp>6lnw@xB1ajN{L6R_qM4 z$pjP>Yo}e@ylmI>4p?1$-S1_~vxAlxv$$z}5|&ZNfedapJEN6Zj3DdFA92{Go#I;( zv?M4UCX=BGZav&L5)K+^iJQswQ_tmu!G;*f`}@{)Id8-lc@8jhrmROub7UL)MsDF@ ziQGSKsu*=wFjsu(zSIMC2piGz?zU_xSc&lxchH?N>8zu=r2Yv=2h-4(@NUorxw`Wr zzq+GpM{ZGOO(e;re{=X5qoJ7y9i^bowl|6+wc^Cgl*zu66P3W8n21l%(QyrVu}YD( z-7{+$7@bq06J75}CoE;~z_U!3ZK@z}zCAIBN#++i!M>BH{6!0VXz;Xff4PDxf%_bK;(#smOVP*Zp*&Gj(tN!!bR0zr6^3C$<+#<{n>3P@zCM zn5(D6FVLC`tmA!4x6lT&)GOh~CgDG}o z-tLW_Bd=~C#zF8Q4vbe*Ky9tB6@TM9u*k9i61T1s^KB;2o8bA{MSX9TfR#z

XiM^r_M1uFTw)(UPqqUJ zed7DJQvOpplYJoNl`A3a2Zr~v)Y}K|*%d7?8;dC*Af}0=(EXrk7hP8PM4v)ZawEv0 z5j5q_6vilv4*ppZ3Wke7%8b`odJu26#YseA?$sP8?@d?TpACTX`G^?%K=Hly9Y$Tj z5`i!}-WH0lQ3yt-&Pf&Z)OxjKfAZ3X`YI2)5o@9EiOA}?kX`^rk;yaOh^Li4VHcT& zd6ztJz^`H!8>cL)a%?Lnofzo0$WPrgkNcP#u@{%~EvA7g#go z+Q4$Q^o_MJTE0G_&9@cgp)WF>2zo>AG}C|(~E^_?ijc2EQ>> zFH1Lxc@i!gDHxvEsrfc+Kiz3Q6Z*NM+U6DH6*-Fv{YDY4>U&eegGH~Xmk~!sd6fw2 z!6^4(MZWhzf(xs6BHy=oS+dir0_JW(j*AIu$9&&p@}T;&6s7(yYidEdkp6pk9}Z(F zk6lG`d!HcJWP{7X)&P5FW;XWU6;zke2e+g*#1kW4L0Auj5pVA45Bhzt{8lj)XyMHu z0p;Q}t*NLnXbGQOTC9WdOQsX;_yLThOP$mzcEbqSBifmDecV;Fqhtm#KxfJTMhY!K zw{1L9za2QtuIWXu0ZYm@Uy72(9^vYajuP$(VAKn+&Jp z!%S;#BqO;02)n~6pFYK8oRC6wXx@kyb?VENM7l?NFg)%^=q4aZ* zwVCZxA8lalF&n`vk#gxD@!9~Aktc+h2UUUax3q2XKQLuL@CpVEpx2IyiH3bALY+Oi zDydtacE1Z|$z5qsCG=%F{}8_ozwieWuM4VcDesuu+wX&YjHm^Ryx)pVtiSLpch7<` zIw9QM%I;(VG9~#Rw2JNt+;?_=O2lA#YSudpgsM`e&(}1 zGdj7U)St4`+Cj#Vn>a;Bj~Q3S1G54;gzq)`b_Hh~ip(`BK#GMkI*RUcK+6ycjl?QN zP%sH5*R$$6A&8j8O4iFJhuHMXLJm|drH+=!_{`60&+tC8*m3Z=YsR@~%eaZyWQI|W zie7;BRL6`LR+4B{%FY~;-^*1GGTCLp!U6VoS36GUAWv$Rcc#|a7DD01{QPB=VyT)? z+1ZV>P=bP1;>v-+j0;BwenO`EKcIbmB3$r*@a~=?5#KB_R$3bTg zOjEutZmWuylIJQEM?28D!4uA#0CZEJHar0TFAW|NwP;WL|EIb_L2>;}e*N!K9E61S zH&u@m!n#CH{C_j}{#ybCRU8z7`Cs{b>@bxSkp3lOm`qAYgB>npnw%V>w}t_+S_Z)s zFhGKq^e%m@=^@sD0CJdAK=`Upbr}?}N zf;snqpt#dOwhxFg82(%Tw=ND+@`O0JGeOWd7-3R8A%Yu%FhiaYXD>p?t2+o%_1CKE z{g(>=g%}X(O%M!}KZK%$7-F<34wD-24`y$WLDv6z?ty=_b)Oi*x&?v}3j0f`AV3HL zWPAYw!XNs-1EmZ9=d=$6LAJISVJ4&gQM5=bh{!f0OmFNz8oMnGgOl_RK5VPP3@87C zpLS$muCo5YTmOslSjFLb`AXIJ@1|O{pm5r z9MxUb-8HMe*|TpFa%dE?+7}MVQ-A-N8wvtq85ROU2%KYt4etC52X0%W08haQgGSU| zvw=K$djTYSLy@4My_Te_8JcZp8Oozg{-ey>*Nm7BkGrX(M~HU6N16U>DSj<`;cy`u zR?0B23zx|*o1V=#fLZ=wd6*NfWjC`pqA^mt>DTZjGBeX`0ZK>Qgxz+37Dyb#NT4WT zl@7Lmh{WdY*h%e_bo6(oXKw=`(9=mft10fOR4< zCOUungtJZPAM~qw4Ydy9PhvYTi1ZRqR5nY)?OX2O3FJ6VXy&`&H%^U|d>N>@f zQ}Cd;DV}_bNiVTW8HcUJDRvXCUwGx|(r2-KbXY>jpd!~jmt(

p*eWM!w;MI3hb2}DlKZV3$r+FsPh`1u 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 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -51,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -61,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell From c10d41528eedae44ec83fd444e9ce35aaa511b24 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 20 Mar 2021 22:22:12 +0000 Subject: [PATCH 7/8] Update dependency org.junit.jupiter:junit-jupiter to v5.7.1 --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index c890eba9..f2095280 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -116,5 +116,5 @@ dependencies { debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.6' - testImplementation 'org.junit.jupiter:junit-jupiter:5.7.0' + testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1' } From 16ac573859d8d38a7a898a404a4cbd00a5bc06d6 Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Sun, 21 Mar 2021 22:41:06 +0900 Subject: [PATCH 8/8] Fix compile error after pull --- .../java/awais/instagrabber/utils/ExportImportUtils.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java b/app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java index 9c24db69..ca5ca21c 100755 --- a/app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java +++ b/app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java @@ -16,7 +16,6 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; -import org.checkerframework.checker.nullness.compatqual.NullableDecl; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -41,11 +40,12 @@ import awais.instagrabber.db.repositories.RepositoryCallback; import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.models.enums.FavoriteType; import awais.instagrabber.utils.PasswordUtils.IncorrectPasswordException; -//import awaisomereport.LogCollector.LogFile; -//import static awais.instagrabber.utils.Utils.logCollector; import static awais.instagrabber.utils.Utils.settingsHelper; +//import awaisomereport.LogCollector.LogFile; +//import static awais.instagrabber.utils.Utils.logCollector; + public final class ExportImportUtils { private static final String TAG = "ExportImportUtils"; @@ -283,7 +283,7 @@ public final class ExportImportUtils { final ListenableFuture> allFutures = Futures.allAsList(futures.build()); Futures.addCallback(allFutures, new FutureCallback>() { @Override - public void onSuccess(@NullableDecl final List result) { + public void onSuccess(final List result) { final JSONObject jsonObject = new JSONObject(); if (result == null) { callback.onCreated(jsonObject.toString());