diff --git a/app/build.gradle b/app/build.gradle index 8d90e12b..e77a6ace 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' @@ -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" @@ -114,5 +114,8 @@ dependencies { implementation 'com.github.ammargitham:uCrop:2.3-native-beta-2' implementation 'com.github.ammargitham:android-gpuimage:2.1.1-beta4' + debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.6' + + testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1' } 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/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/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/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/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/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()); 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/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/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/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) { 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 2af8f045..f65ff8c9 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -472,4 +472,5 @@ Added keyword: %s to filter list Removed keyword: %s from filter list Marked as seen + Delete unsuccessful diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index f3d88b1c..e708b1c0 100755 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a93e645f..6e61ea74 100755 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,9 +1,6 @@ -#Sun Feb 28 01:04:23 JST 2021 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionSha256Sum=9af5c8e7e2cd1a3b0f694a4ac262b9f38c75262e74a9e8b5101af302a6beadd7 +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-all.zip -distributionSha256Sum=1433372d903ffba27496f8d5af24265310d2da0d78bf6b4e5138831d4fe066e9 -# https://gradle.org/releases/ -# https://gradle.org/release-checksums/ \ No newline at end of file diff --git a/gradlew b/gradlew index 2fe81a7d..4f906e0c 100755 --- a/gradlew +++ b/gradlew @@ -82,6 +82,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -129,6 +130,7 @@ fi if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath diff --git a/gradlew.bat b/gradlew.bat index 24467a14..ac1b06f9 100755 --- a/gradlew.bat +++ b/gradlew.bat @@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @@ -37,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 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 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 }