diff --git a/.all-contributorsrc b/.all-contributorsrc index 7a9958b9..a39de36f 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -60,16 +60,6 @@ "bug" ] }, - { - "login": "vojta-horanek", - "name": "Vojtěch Hořánek", - "avatar_url": "https://avatars.githubusercontent.com/u/12630566?v=4", - "profile": "https://vojtechh.eu/", - "contributions": [ - "code", - "translation" - ] - }, { "login": "MeLlamoPablo", "name": "Pablo Rodríguez", @@ -79,6 +69,33 @@ "code" ] }, + { + "login": "tcely", + "name": "tcely", + "avatar_url": "https://avatars.githubusercontent.com/u/138864?v=4", + "profile": "https://github.com/tcely", + "contributions": [ + "code" + ] + }, + { + "login": "Vonter", + "name": "Vonter", + "avatar_url": "https://avatars.githubusercontent.com/u/25414711?v=4", + "profile": "https://github.com/Vonter", + "contributions": [ + "code" + ] + }, + { + "login": "raniapl", + "name": "Rania Pilioura", + "avatar_url": "https://avatars.githubusercontent.com/u/56370534?v=4", + "profile": "https://github.com/raniapl", + "contributions": [ + "code" + ] + }, { "login": "stamatiap", "name": "Stamatia Papageorgiou", @@ -90,19 +107,29 @@ ] }, { - "login": "The-EDev", - "name": "Farook Al-Sammarraie", - "avatar_url": "https://avatars.githubusercontent.com/u/60552923?v=4", - "profile": "https://github.com/The-EDev", + "login": "Zopieux", + "name": "Alexandre Macabies", + "avatar_url": "https://avatars.githubusercontent.com/u/81353?v=4", + "profile": "https://github.com/Zopieux", "contributions": [ "code" ] }, { - "login": "Zopieux", - "name": "Alexandre Macabies", - "avatar_url": "https://avatars.githubusercontent.com/u/81353?v=4", - "profile": "https://github.com/Zopieux", + "login": "vojta-horanek", + "name": "Vojtěch Hořánek", + "avatar_url": "https://avatars.githubusercontent.com/u/12630566?v=4", + "profile": "https://vojtechh.eu/", + "contributions": [ + "code", + "translation" + ] + }, + { + "login": "The-EDev", + "name": "Farook Al-Sammarraie", + "avatar_url": "https://avatars.githubusercontent.com/u/60552923?v=4", + "profile": "https://github.com/The-EDev", "contributions": [ "code" ] @@ -135,6 +162,17 @@ "financial" ] }, + { + "login": "Martin5001", + "name": "Martin Krejčí", + "avatar_url": "https://avatars.githubusercontent.com/u/35201200?v=4", + "profile": "https://github.com/Martin5001", + "contributions": [ + "bug", + "ideas", + "translation" + ] + }, { "login": "Shadowspear123", "name": "Shadowspear123", @@ -266,6 +304,15 @@ "translation" ] }, + { + "login": "Lego8486", + "name": "Ten_Lego", + "avatar_url": "https://avatars1.githubusercontent.com/u/47414485", + "profile": "https://github.com/Lego8486", + "contributions": [ + "translation" + ] + }, { "login": "MoaufmKlo", "name": "MoaufmKlo", @@ -338,6 +385,15 @@ "translation" ] }, + { + "login": "rex07", + "name": "Rex_sa", + "avatar_url": "https://avatars.githubusercontent.com/u/13156001?v=4", + "profile": "https://github.com/rex07", + "contributions": [ + "translation" + ] + }, { "login": "rikishi0071", "name": "rikishi0071", @@ -375,10 +431,10 @@ ] }, { - "login": "Lego8486", - "name": "Ten_Lego", - "avatar_url": "https://avatars1.githubusercontent.com/u/47414485", - "profile": "https://github.com/Lego8486", + "login": "Umeaboy", + "name": "Kristoffer Grundström", + "avatar_url": "https://avatars.githubusercontent.com/u/714473?v=4", + "profile": "https://github.com/Umeaboy", "contributions": [ "translation" ] @@ -418,6 +474,15 @@ "contributions": [ "translation" ] + }, + { + "login": "2hot2exist", + "name": "2hot2exist", + "avatar_url": "https://avatars.githubusercontent.com/u/84233003?v=4", + "profile": "https://github.com/2hot2exist", + "contributions": [ + "translation" + ] } ], "contributorsPerLine": 6, diff --git a/README.md b/README.md index 7b374c7b..48491466 100755 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://makeapullrequest.com) [![GPLv3 license](https://img.shields.io/badge/License-GPLv3-blue.svg)](./LICENSE) [![GitHub stars](https://img.shields.io/github/stars/austinhuang0131/instagrabber.svg?style=social&label=Star)](https://GitHub.com/austinhuang0131/barinsta/stargazers/) -[![All Contributors](https://img.shields.io/badge/all_contributors-44-orange.svg)](#contributors) +[![All Contributors](https://img.shields.io/badge/all_contributors-51-orange.svg)](#contributors) Instagram client; previously known as InstaGrabber. @@ -59,59 +59,68 @@ Prominent contributors are listed here in the [all-contributors](https://allcont
Zerrium

💻
Chua Jun Hui

💻
Anderson Mesquita

💻 🐛 +
Pablo Rodríguez

💻 + + +
tcely

💻 +
Vonter

💻 +
Rania Pilioura

💻 +
Stamatia Papageorgiou

💻 🌍 +
Alexandre Macabies

💻
Vojtěch Hořánek

💻 🌍 -
Pablo Rodríguez

💻 -
Stamatia Papageorgiou

💻 🌍
Farook Al-Sammarraie

💻 -
Alexandre Macabies

💻
Stefan Najdovski

🎨 🌍
CrazyMarvin

💵 +
Kevin Thomas

💵 +
Martin Krejčí

🐛 🤔 🌍 +
Shadowspear123

📝 🐛 🤔 💬 -
Kevin Thomas

💵 -
Shadowspear123

📝 🐛 🤔 💬
Ricardo

🐛 🌍
Akrai

🤔 🌍
avtkal

🌍
Cézar Augusto

🌍 - -
Dimitris T

🌍
farzadx

🌍 + +
Fatih Aydın

🌍
fouze555

🌍
Galang23

🌍
Initdebugs

🌍 - -
Jakub Janek

🌍
GenosseFlosse

🌍 + +
kernoeb

🌍 +
Ten_Lego

🌍
MoaufmKlo

🌍
nalinalini

🌍
peterge1998

🌍 +
PierreM0

🌍 -
PierreM0

🌍
Pyrobauve

🌍
RAMAR-RAR

🌍
rohang02

🌍
retiolus

🌍 +
Rex_sa

🌍
rikishi0071

🌍
Alexey Peschany

🌍
Sitavi

🌍
Still Hsu

🌍 -
Ten_Lego

🌍 +
Kristoffer Grundström

🌍
wagnim

🌍
wokija

🌍
ysakamoto

🌍
ZDVokoun

🌍 +
2hot2exist

🌍 diff --git a/app/build.gradle b/app/build.gradle index 6de63314..f184bcc6 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -180,6 +180,7 @@ dependencies { implementation "androidx.constraintlayout:constraintlayout:2.0.4" implementation "androidx.preference:preference:1.1.1" implementation 'androidx.palette:palette:1.0.0' + implementation 'androidx.documentfile:documentfile:1.0.1' implementation 'com.google.guava:guava:27.0.1-android' diff --git a/app/src/main/java/awais/instagrabber/activities/CameraActivity.kt b/app/src/main/java/awais/instagrabber/activities/CameraActivity.kt index 1eca69d8..1ea124b6 100644 --- a/app/src/main/java/awais/instagrabber/activities/CameraActivity.kt +++ b/app/src/main/java/awais/instagrabber/activities/CameraActivity.kt @@ -52,7 +52,7 @@ class CameraActivity : BaseLanguageActivity() { setContentView(binding.root) Utils.transparentStatusBar(this, true, false) displayManager = getSystemService(DISPLAY_SERVICE) as DisplayManager - outputDirectory = DownloadUtils.getCameraDir() + outputDirectory = DownloadUtils.cameraDir cameraExecutor = Executors.newSingleThreadExecutor() displayManager.registerDisplayListener(displayListener, null) binding.viewFinder.post { diff --git a/app/src/main/java/awais/instagrabber/adapters/FeedAdapterV2.java b/app/src/main/java/awais/instagrabber/adapters/FeedAdapterV2.java index ac94b02c..102640a6 100644 --- a/app/src/main/java/awais/instagrabber/adapters/FeedAdapterV2.java +++ b/app/src/main/java/awais/instagrabber/adapters/FeedAdapterV2.java @@ -226,7 +226,7 @@ public final class FeedAdapterV2 extends ListAdapter extends - ListAdapter { - - private boolean isSelecting; - private OnItemClickListener internalOnItemClickListener; - private OnItemLongClickListener internalOnLongItemClickListener; - - private final List selectedItems = new ArrayList<>(); - - protected MultiSelectListAdapter(@NonNull final DiffUtil.ItemCallback diffCallback, - final OnItemClickListener clickListener, - final OnItemLongClickListener longClickListener) { - super(diffCallback); - internalOnItemClickListener = (item, position) -> { - if (isSelecting) { - toggleSelection(item, position); - } - if (clickListener == null) { - return; - } - clickListener.onItemClick(item, position); - }; - internalOnLongItemClickListener = (item, position) -> { - if (!isSelecting) { - isSelecting = true; - } - toggleSelection(item, position); - if (longClickListener == null) { - return true; - } - return longClickListener.onItemLongClick(item, position); - }; - } - - public OnItemClickListener getInternalOnItemClickListener() { - return internalOnItemClickListener; - } - - public OnItemLongClickListener getInternalOnLongItemClickListener() { - return internalOnLongItemClickListener; - } - - private void toggleSelection(final T item, final int position) { - if (item == null) { - return; - } - if (selectedItems.size() >= 100) { - // Toast.makeText(mainActivity, R.string.downloader_too_many, Toast.LENGTH_SHORT); - return; - } - if (item.isSelected()) { - item.setSelected(false); - selectedItems.remove(item); - } else { - item.setSelected(true); - selectedItems.add(item); - } - if (selectedItems.size() == 0) { - isSelecting = false; - } - notifyItemChanged(position); - } - - public boolean isSelecting() { - return isSelecting; - } - - public List getSelectedModels() { - return selectedItems; - } - - public void clearSelection() { - for (final T item : selectedItems) { - item.setSelected(false); - } - selectedItems.clear(); - isSelecting = false; - notifyDataSetChanged(); - } - - public interface Selectable { - boolean isSelected(); - - void setSelected(boolean selected); - } - - public interface OnItemClickListener { - void onItemClick(T item, int position); - } - - public interface OnItemLongClickListener { - boolean onItemLongClick(T item, int position); - } -} diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/CommentViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/CommentViewHolder.java index 0643f7cd..bb1d3e03 100644 --- a/app/src/main/java/awais/instagrabber/adapters/viewholder/CommentViewHolder.java +++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/CommentViewHolder.java @@ -122,7 +122,6 @@ public final class CommentViewHolder extends RecyclerView.ViewHolder { } private void setLikes(@NonNull final Comment comment, final boolean isReply) { - // final String likesString = itemView.getResources().getQuantityString(R.plurals.likes_count, likes, likes); binding.likes.setText(String.valueOf(comment.getCommentLikeCount())); binding.likes.setOnLongClickListener(v -> { if (commentCallback == null) return false; @@ -150,7 +149,6 @@ public final class CommentViewHolder extends RecyclerView.ViewHolder { final int replies = comment.getChildCommentCount(); binding.replies.setVisibility(View.VISIBLE); final String text = isReply ? "" : String.valueOf(replies); - // final String string = itemView.getResources().getQuantityString(R.plurals.replies_count, replies, replies); binding.replies.setText(text); binding.replies.setOnClickListener(v -> { if (commentCallback == null) return; diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedItemViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedItemViewHolder.java index 44a3fcae..806faab5 100644 --- a/app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedItemViewHolder.java +++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedItemViewHolder.java @@ -47,7 +47,9 @@ public abstract class FeedItemViewHolder extends RecyclerView.ViewHolder { setupComments(media); setupCaption(media); if (media.getType() != MediaItemType.MEDIA_TYPE_SLIDER) { - bottomBinding.btnDownload.setOnClickListener(v -> feedItemCallback.onDownloadClick(media, -1)); + bottomBinding.btnDownload.setOnClickListener(v -> + feedItemCallback.onDownloadClick(media, -1, null) + ); } bindItem(media); } diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedSliderViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedSliderViewHolder.java index 6931c851..66902340 100644 --- a/app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedSliderViewHolder.java +++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedSliderViewHolder.java @@ -59,11 +59,15 @@ public class FeedSliderViewHolder extends FeedItemViewHolder { final String text = (position + 1) + "/" + sliderItemLen; binding.mediaCounter.setText(text); setDimensions(binding.mediaList, sliderItems.get(position)); - binding.itemFeedBottom.btnDownload.setOnClickListener(v -> feedItemCallback.onDownloadClick(feedModel, position)); + binding.itemFeedBottom.btnDownload.setOnClickListener(v -> + feedItemCallback.onDownloadClick(feedModel, position, binding.itemFeedBottom.btnDownload) + ); } }); setDimensions(binding.mediaList, sliderItems.get(0)); - binding.itemFeedBottom.btnDownload.setOnClickListener(v -> feedItemCallback.onDownloadClick(feedModel, 0)); + binding.itemFeedBottom.btnDownload.setOnClickListener(v -> + feedItemCallback.onDownloadClick(feedModel, 0, binding.itemFeedBottom.btnDownload) + ); adapter.submitList(sliderItems); } diff --git a/app/src/main/java/awais/instagrabber/fragments/CollectionPostsFragment.java b/app/src/main/java/awais/instagrabber/fragments/CollectionPostsFragment.java index 77e55bbe..f69ebcc9 100644 --- a/app/src/main/java/awais/instagrabber/fragments/CollectionPostsFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/CollectionPostsFragment.java @@ -126,10 +126,10 @@ public class CollectionPostsFragment extends Fragment implements SwipeRefreshLay } @Override - public void onDownloadClick(final Media feedModel, final int childPosition) { + public void onDownloadClick(final Media feedModel, final int childPosition, final View popupLocation) { final Context context = getContext(); if (context == null) return; - DownloadUtils.showDownloadDialog(context, feedModel, childPosition); + DownloadUtils.showDownloadDialog(context, feedModel, childPosition, popupLocation); } @Override diff --git a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java index d04c9389..189820f6 100644 --- a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java @@ -145,10 +145,10 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe } @Override - public void onDownloadClick(final Media feedModel, final int childPosition) { + public void onDownloadClick(final Media feedModel, final int childPosition, final View popupLocation) { final Context context = getContext(); if (context == null) return; - DownloadUtils.showDownloadDialog(context, feedModel, childPosition); + DownloadUtils.showDownloadDialog(context, feedModel, childPosition, popupLocation); } @Override diff --git a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java index 6e18f5f0..17f1f1b9 100644 --- a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java @@ -139,10 +139,10 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR } @Override - public void onDownloadClick(final Media feedModel, final int childPosition) { + public void onDownloadClick(final Media feedModel, final int childPosition, final View popupLocation) { final Context context = getContext(); if (context == null) return; - DownloadUtils.showDownloadDialog(context, feedModel, childPosition); + DownloadUtils.showDownloadDialog(context, feedModel, childPosition, popupLocation); } @Override diff --git a/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java b/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java index fd6d1ee5..9a987981 100644 --- a/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java @@ -446,7 +446,7 @@ public class PostViewV2Fragment extends Fragment implements EditTextDialogFragme private void setupDownload() { bottom.download.setOnClickListener(v -> { - DownloadUtils.showDownloadDialog(context, viewModel.getMedia(), sliderPosition); + DownloadUtils.showDownloadDialog(context, viewModel.getMedia(), sliderPosition, bottom.download); }); TooltipCompat.setTooltipText(bottom.download, getString(R.string.action_download)); } @@ -744,12 +744,6 @@ public class PostViewV2Fragment extends Fragment implements EditTextDialogFragme final Media media = viewModel.getMedia(); final User profileModel = media.getUser(); if (profileModel == null) return; - final boolean isPrivate = profileModel.isPrivate(); - if (isPrivate) { - final Context context = getContext(); - if (context == null) return; -// Toast.makeText(context, R.string.share_private_post, Toast.LENGTH_LONG).show(); - } if (viewModel.isLoggedIn()) { final Context context = getContext(); if (context == null) return; @@ -776,7 +770,7 @@ public class PostViewV2Fragment extends Fragment implements EditTextDialogFragme } return true; } else if (itemId == R.id.share) { - shareLink(media, isPrivate); + shareLink(media, profileModel.isPrivate()); return true; } return false; @@ -784,7 +778,7 @@ public class PostViewV2Fragment extends Fragment implements EditTextDialogFragme popupMenu.show(); return; } - shareLink(media, isPrivate); + shareLink(media, false); }); } diff --git a/app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java index 71d517b9..57c40b85 100644 --- a/app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java @@ -109,10 +109,10 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL } @Override - public void onDownloadClick(final Media feedModel, final int childPosition) { + public void onDownloadClick(final Media feedModel, final int childPosition, final View popupLocation) { final Context context = getContext(); if (context == null) return; - DownloadUtils.showDownloadDialog(context, feedModel, childPosition); + DownloadUtils.showDownloadDialog(context, feedModel, childPosition, popupLocation); } @Override diff --git a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java index 59346a17..d6400c7e 100644 --- a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java @@ -473,11 +473,11 @@ public class StoryViewerFragment extends Fragment { poll = (PollSticker) tag; final List tallies = poll.getTallies(); final String[] choices = tallies.stream() - .map(t -> (poll.getViewerVote() == tallies.indexOf(t) ? "√ " : "") + .map(t -> (poll.getViewerVote() != null && poll.getViewerVote() == tallies.indexOf(t) ? "√ " : "") + t.getText() + " (" + t.getCount() + ")" ) .toArray(String[]::new); final ArrayAdapter adapter = new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, choices); - if (poll.getViewerVote() > -1) { + if (poll.getViewerVote() != null) { new AlertDialog.Builder(context) .setTitle(R.string.voted_story_poll) .setAdapter(adapter, null) @@ -577,16 +577,17 @@ public class StoryViewerFragment extends Fragment { .setPositiveButton(R.string.cancel, null) .show(); } else if (tag instanceof QuizSticker) { + quiz = (QuizSticker) tag; final List tallies = quiz.getTallies(); final String[] choices = tallies.stream().map( - t -> (quiz.getViewerAnswer() == tallies.indexOf(t) ? "√ " : "") + + t -> (quiz.getViewerAnswer() != null && quiz.getViewerAnswer() == tallies.indexOf(t) ? "√ " : "") + (quiz.getCorrectAnswer() == tallies.indexOf(t) ? "*** " : "") + t.getText() + " (" + t.getCount() + ")" ).toArray(String[]::new); new AlertDialog.Builder(context) - .setTitle(quiz.getViewerAnswer() > -1 ? getString(R.string.story_quizzed) : quiz.getQuestion()) + .setTitle(quiz.getViewerAnswer() != null ? getString(R.string.story_quizzed) : quiz.getQuestion()) .setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, choices), (d, w) -> { - if (quiz.getViewerAnswer() == -1) { + if (quiz.getViewerAnswer() == null) { sticking = true; storiesRepository.respondToQuiz( csrfToken, @@ -901,7 +902,7 @@ public class StoryViewerFragment extends Fragment { } lastSlidePos = slidePos; - final MediaItemType itemType = currentStory.getMediaType(); + final MediaItemType itemType = currentStory.getType(); url = itemType == MediaItemType.MEDIA_TYPE_IMAGE ? ResponseBodyUtils.getImageUrl(currentStory) @@ -966,7 +967,7 @@ public class StoryViewerFragment extends Fragment { } if (currentStory.getStoryCta() != null) { - final StoryCta swipeUp = currentStory.getStoryCta().get(0).getLinks(); + final StoryCta swipeUp = currentStory.getStoryCta().get(0).getLinks().get(0); binding.swipeUp.setVisibility(View.VISIBLE); binding.swipeUp.setText(currentStory.getLinkText()); final String swipeUpUrl = swipeUp.getWebUri(); diff --git a/app/src/main/java/awais/instagrabber/fragments/TopicPostsFragment.java b/app/src/main/java/awais/instagrabber/fragments/TopicPostsFragment.java index bba645a7..acbc2eef 100644 --- a/app/src/main/java/awais/instagrabber/fragments/TopicPostsFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/TopicPostsFragment.java @@ -120,10 +120,10 @@ public class TopicPostsFragment extends Fragment implements SwipeRefreshLayout.O } @Override - public void onDownloadClick(final Media feedModel, final int childPosition) { + public void onDownloadClick(final Media feedModel, final int childPosition, final View popupLocation) { final Context context = getContext(); if (context == null) return; - DownloadUtils.showDownloadDialog(context, feedModel, childPosition); + DownloadUtils.showDownloadDialog(context, feedModel, childPosition, popupLocation); } @Override diff --git a/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java b/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java index ae4e9b6b..593ea96f 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java @@ -116,10 +116,10 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre } @Override - public void onDownloadClick(final Media feedModel, final int childPosition) { + public void onDownloadClick(final Media feedModel, final int childPosition, final View popupLocation) { final Context context = getContext(); if (context == null) return; - DownloadUtils.showDownloadDialog(context, feedModel, childPosition); + DownloadUtils.showDownloadDialog(context, feedModel, childPosition, popupLocation); } @Override diff --git a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.kt b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.kt index 82d6a078..9c2ef9ae 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.kt +++ b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.kt @@ -127,8 +127,8 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall NavHostFragment.findNavController(this@ProfileFragment).navigate(commentsAction) } - override fun onDownloadClick(media: Media?, childPosition: Int) { - DownloadUtils.showDownloadDialog(context ?: return, media ?: return, childPosition) + override fun onDownloadClick(media: Media?, childPosition: Int, popupLocation: View) { + DownloadUtils.showDownloadDialog(context ?: return, media ?: return, childPosition, popupLocation) } override fun onEmailClick(emailId: String?) { diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/stories/PollSticker.kt b/app/src/main/java/awais/instagrabber/repositories/responses/stories/PollSticker.kt index d95e1329..537a3d21 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/stories/PollSticker.kt +++ b/app/src/main/java/awais/instagrabber/repositories/responses/stories/PollSticker.kt @@ -6,8 +6,8 @@ import awais.instagrabber.repositories.responses.Location import awais.instagrabber.repositories.responses.User data class PollSticker( - val pollId: Long?, + val pollId: Long, val question: String?, - val tallies: List?, - var viewerVote: Int = -1 + val tallies: List, + var viewerVote: Int? ) : Serializable \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/stories/QuestionSticker.kt b/app/src/main/java/awais/instagrabber/repositories/responses/stories/QuestionSticker.kt index 7b9f1bf8..7dfec43c 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/stories/QuestionSticker.kt +++ b/app/src/main/java/awais/instagrabber/repositories/responses/stories/QuestionSticker.kt @@ -7,6 +7,6 @@ import awais.instagrabber.repositories.responses.User data class QuestionSticker( val questionType: String?, - val questionId: Long?, - val question: String? + val questionId: Long, + val question: String ) : Serializable \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/stories/QuizSticker.kt b/app/src/main/java/awais/instagrabber/repositories/responses/stories/QuizSticker.kt index 5a82a88f..45f237f2 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/stories/QuizSticker.kt +++ b/app/src/main/java/awais/instagrabber/repositories/responses/stories/QuizSticker.kt @@ -8,7 +8,7 @@ import awais.instagrabber.repositories.responses.User data class QuizSticker( val quizId: Long?, val question: String?, - val tallies: List?, - var viewerAnswer: Int? = -1, - val correctAnswer: Int? + val tallies: List, + var viewerAnswer: Int?, + val correctAnswer: Int ) : Serializable \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/stories/StoryMedia.kt b/app/src/main/java/awais/instagrabber/repositories/responses/stories/StoryMedia.kt index 47af8625..f9559fd0 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/stories/StoryMedia.kt +++ b/app/src/main/java/awais/instagrabber/repositories/responses/stories/StoryMedia.kt @@ -1,6 +1,7 @@ package awais.instagrabber.repositories.responses.stories import awais.instagrabber.models.enums.MediaItemType +import awais.instagrabber.models.enums.MediaItemType.Companion.valueOf import awais.instagrabber.repositories.responses.ImageVersions2 import awais.instagrabber.repositories.responses.MediaCandidate import awais.instagrabber.repositories.responses.User @@ -9,36 +10,36 @@ import java.io.Serializable data class StoryMedia( // inherited from Media - val pk: String? = null, - val id: String? = null, - val takenAt: Long = -1, - val user: User? = null, - val canReshare: Boolean = false, - val imageVersions2: ImageVersions2? = null, - val originalWidth: Int = 0, - val originalHeight: Int = 0, - val mediaType: MediaItemType? = null, - val isReelMedia: Boolean = false, - val videoVersions: List? = null, - val hasAudio: Boolean = false, - val videoDuration: Double = 0.0, - val viewCount: Long = 0, - val title: String? = null, - // story-specific - val canReply: Boolean = false, - val linkText: String? = null, // required for story_cta - // stickers - val reelMentions: List? = null, - val storyHashtags: List? = null, - val storyLocations: List? = null, - val storyFeedMedia: List? = null, - val storyPolls: List? = null, - val storyQuestions: List? = null, - val storyQuizs: List? = null, - val storyCta: List? = null, - val storySliders: List? = null, - // spotify/soundcloud button, not a sticker - val storyAppAttribution: StoryAppAttribution? = null + val pk: String? = null, + val id: String? = null, + val takenAt: Long = -1, + val user: User? = null, + val canReshare: Boolean = false, + val imageVersions2: ImageVersions2? = null, + val originalWidth: Int = 0, + val originalHeight: Int = 0, + val mediaType: Int = 0, + val isReelMedia: Boolean = false, + val videoVersions: List? = null, + val hasAudio: Boolean = false, + val videoDuration: Double = 0.0, + val viewCount: Long = 0, + val title: String? = null, + // story-specific + val canReply: Boolean = false, + val linkText: String? = null, // required for story_cta + // stickers + val reelMentions: List? = null, + val storyHashtags: List? = null, + val storyLocations: List? = null, + val storyFeedMedia: List? = null, + val storyPolls: List? = null, + val storyQuestions: List? = null, + val storyQuizs: List? = null, + val storyCta: List? = null, + val storySliders: List? = null, + // spotify/soundcloud button, not a sticker + val storyAppAttribution: StoryAppAttribution? = null ) : Serializable { private var dateString: String? = null var position = 0 @@ -48,6 +49,9 @@ data class StoryMedia( // val date: String by lazy { // if (takenAt <= 0) "" else Utils.datetimeParser.format(Date(takenAt * 1000L)) // } + val type: MediaItemType? + get() = valueOf(mediaType) + val date: String get() { if (takenAt <= 0) return "" diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/stories/StorySticker.kt b/app/src/main/java/awais/instagrabber/repositories/responses/stories/StorySticker.kt index 7f40f94a..1f154e30 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/stories/StorySticker.kt +++ b/app/src/main/java/awais/instagrabber/repositories/responses/stories/StorySticker.kt @@ -14,6 +14,6 @@ data class StorySticker( val pollSticker: PollSticker?, // story_polls val questionSticker: QuestionSticker?, // story_questions val quizSticker: QuizSticker?, // story_quizs - val links: StoryCta?, // story_cta, requires link_text from the story + val links: List?, // story_cta, requires link_text from the story val sliderSticker: SliderSticker? // story_sliders ) : Serializable \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/utils/DownloadUtils.kt b/app/src/main/java/awais/instagrabber/utils/DownloadUtils.kt index d5f3eebb..14410239 100644 --- a/app/src/main/java/awais/instagrabber/utils/DownloadUtils.kt +++ b/app/src/main/java/awais/instagrabber/utils/DownloadUtils.kt @@ -1,14 +1,16 @@ package awais.instagrabber.utils import android.content.Context -import android.content.DialogInterface import android.content.UriPermission import android.net.Uri import android.provider.DocumentsContract import android.util.Log -import android.widget.Toast -import androidx.appcompat.app.AlertDialog -import androidx.core.util.Pair +import android.view.MenuItem +import android.view.View +import androidx.appcompat.view.ContextThemeWrapper +import androidx.appcompat.widget.PopupMenu +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat import androidx.documentfile.provider.DocumentFile import androidx.work.* import awais.instagrabber.R @@ -16,6 +18,7 @@ import awais.instagrabber.fragments.settings.PreferenceKeys import awais.instagrabber.models.enums.MediaItemType import awais.instagrabber.repositories.responses.Media import awais.instagrabber.repositories.responses.stories.StoryMedia +import awais.instagrabber.utils.AppExecutors.tasksThread import awais.instagrabber.utils.TextUtils.isEmpty import awais.instagrabber.workers.DownloadWorker import com.google.gson.Gson @@ -24,6 +27,7 @@ import java.io.IOException import java.io.OutputStreamWriter import java.util.* import java.util.regex.Pattern +import kotlin.math.abs object DownloadUtils { @@ -36,6 +40,8 @@ object DownloadUtils { private const val DIR_RECORDINGS = "Sent Recordings" private const val DIR_TEMP = "Temp" private const val DIR_BACKUPS = "Backups" + private const val MIME_DIR = DocumentsContract.Document.MIME_TYPE_DIR + private val dirMap: MutableMap = mutableMapOf() private var root: DocumentFile? = null @JvmStatic @Throws(ReselectDocumentTreeException::class) @@ -48,18 +54,15 @@ object DownloadUtils { } val uri = Uri.parse(barinstaDirUri) if (!barinstaDirUri!!.startsWith("content://com.android.externalstorage.documents")) { - // reselect the folder in selector view throw ReselectDocumentTreeException(uri) } val existingPermissions = context.contentResolver.persistedUriPermissions if (existingPermissions.isEmpty()) { - // reselect the folder in selector view throw ReselectDocumentTreeException(uri) } val anyMatch = existingPermissions.stream() .anyMatch { uriPermission: UriPermission -> uriPermission.uri == uri } if (!anyMatch) { - // reselect the folder in selector view throw ReselectDocumentTreeException(uri) } root = DocumentFile.fromTreeUri(context, uri) @@ -68,155 +71,152 @@ object DownloadUtils { throw ReselectDocumentTreeException(uri) } Utils.settingsHelper.putString(PreferenceKeys.PREF_BARINSTA_DIR_URI, uri.toString()) + // set up directories + val dirKeys = mapOf( + DIR_DOWNLOADS to MIME_DIR, + DIR_CAMERA to MIME_DIR, + DIR_EDIT to MIME_DIR, + DIR_RECORDINGS to MIME_DIR, + DIR_TEMP to MIME_DIR, + DIR_BACKUPS to MIME_DIR + ) + dirMap.putAll(checkFiles(context, root, dirKeys, true)) } fun destroy() { root = null + dirMap.clear() } - fun getDownloadDir(vararg dirs: String?): DocumentFile? { - if (root == null) { - return null + fun checkFiles(context: Context, + parent: DocumentFile?, + queries: Map, // + create: Boolean + ): Map { + // first we'll find existing ones + val result: MutableMap = mutableMapOf() + if (root == null || parent == null || !parent.isDirectory) return result.toMap() + val docId = DocumentsContract.getDocumentId(parent.uri) + val docUri = DocumentsContract.buildChildDocumentsUriUsingTree(root!!.uri, docId) + val docCursor = context.contentResolver.query( + docUri, arrayOf( + DocumentsContract.Document.COLUMN_DISPLAY_NAME, + DocumentsContract.Document.COLUMN_DOCUMENT_ID, + DocumentsContract.Document.COLUMN_MIME_TYPE + ), null, null, null + ) + if (docCursor == null) return result.toMap() + while (docCursor.moveToNext()) { + val q = queries.get(docCursor.getString(0)) + if (q == null || !docCursor.getString(2).equals(q)) continue + val fileUri = DocumentsContract.buildDocumentUriUsingTree(parent.uri, docCursor.getString(1)) + val dir = if (q.equals(MIME_DIR)) DocumentFile.fromTreeUri(context, fileUri) + else DocumentFile.fromSingleUri(context, fileUri) + result.put(docCursor.getString(0), dir) + if (result.size >= queries.size) break } - var subDir = root - for (dir in dirs) { - if (subDir == null || isEmpty(dir)) continue - val subDirFile = subDir.findFile(dir!!) - val exists = subDirFile != null && subDirFile.exists() - subDir = if (exists) subDirFile else subDir.createDirectory(dir) + docCursor.close() + // next we'll create inexistent ones, if necessary + if (create) { + for (k in queries) { + if (result.get(k.key) == null) { + result.put(k.key, if (MIME_DIR.equals(k.value)) parent.createDirectory(k.key) + else parent.createFile(k.value, k.key)) + } + } } - return subDir + return result.toMap() + } + + fun getRootDir(dir: String): DocumentFile? { + if (root == null) return null + return dirMap.get(dir) } @JvmStatic val downloadDir: DocumentFile? - get() = getDownloadDir(DIR_DOWNLOADS) + get() = getRootDir(DIR_DOWNLOADS) @JvmStatic - fun getCameraDir(): DocumentFile? { - return getDownloadDir(DIR_CAMERA) + val cameraDir: DocumentFile? + get() = getRootDir(DIR_CAMERA) + + @JvmStatic + fun getImageEditDir(sessionId: String?, context: Context): DocumentFile? { + val editRoot = getRootDir(DIR_EDIT) + if (sessionId == null) return editRoot + return checkFiles(context, + editRoot, + mapOf(sessionId to MIME_DIR), + true).get(sessionId) } @JvmStatic - fun getImageEditDir(sessionId: String?): DocumentFile? { - return getDownloadDir(DIR_EDIT, sessionId) - } - - fun getRecordingsDir(): DocumentFile? { - return getDownloadDir(DIR_RECORDINGS) - } + val recordingsDir: DocumentFile? + get() = getRootDir(DIR_RECORDINGS) @JvmStatic - fun getBackupsDir(): DocumentFile? { - return getDownloadDir(DIR_BACKUPS) - } + val backupsDir: DocumentFile? + get() = getRootDir(DIR_BACKUPS) - // @Nullable - // private static DocumentFile getDownloadDir(@NonNull final Context context, @Nullable final String username) { - // return getDownloadDir(context, username, false); - // } private fun getDownloadDir( - context: Context?, - username: String? + context: Context, + username: String?, + shouldCreate: Boolean ): DocumentFile? { - val userFolderPaths: List = getSubPathForUserFolder(username) - var dir = root - for (dirName in userFolderPaths) { - val file = dir!!.findFile(dirName) - if (file != null) { - dir = file - continue - } - dir = dir.createDirectory(dirName) - if (dir == null) break - } - // final String joined = android.text.TextUtils.join("/", userFolderPaths); - // final Uri userFolderUri = DocumentsContract.buildDocumentUriUsingTree(root.getUri(), joined); - // final DocumentFile userFolder = DocumentFile.fromSingleUri(context, userFolderUri); - if (context != null && (dir == null || !dir.exists())) { - Toast.makeText(context, R.string.error_creating_folders, Toast.LENGTH_SHORT).show() - return null - } - return dir + if (!Utils.settingsHelper.getBoolean(PreferenceKeys.DOWNLOAD_USER_FOLDER) || username.isNullOrEmpty()) + return downloadDir + return checkFiles(context, + downloadDir, + mapOf(username to MIME_DIR), + shouldCreate).get(username) } - private fun getSubPathForUserFolder(username: String?): MutableList { - val list: MutableList = ArrayList() - if (!Utils.settingsHelper.getBoolean(PreferenceKeys.DOWNLOAD_USER_FOLDER) || - username.isNullOrEmpty()) { - list.add(DIR_DOWNLOADS) - return list - } - val finalUsername = if (username.startsWith("@")) username.substring(1) else username - list.add(DIR_DOWNLOADS) - list.add(finalUsername) - return list - } - - private fun getTempDir(): DocumentFile? { - var file = root!!.findFile(DIR_TEMP) - if (file == null) { - file = root!!.createDirectory(DIR_TEMP) - } - return file - } + private val tempDir: DocumentFile? + get() = getRootDir(DIR_TEMP) private fun getDownloadSavePaths( - paths: MutableList, postId: String?, displayUrl: String? - ): Pair, String?>? { - return getDownloadSavePaths(paths, postId, "", displayUrl, "") + ): Pair { + return getDownloadFileName(postId, "", displayUrl, "") } private fun getDownloadSavePaths( - paths: MutableList, postId: String?, displayUrl: String, username: String - ): Pair, String?>? { - return getDownloadSavePaths(paths, postId, "", displayUrl, username) + ): Pair { + return getDownloadFileName(postId, "", displayUrl, username) } private fun getDownloadChildSavePaths( - paths: MutableList, postId: String?, childPosition: Int, url: String?, username: String - ): Pair, String?>? { + ): Pair { val sliderPostfix = "_slide_$childPosition" - return getDownloadSavePaths(paths, postId, sliderPostfix, url, username) + return getDownloadFileName(postId, sliderPostfix, url, username) } - private fun getDownloadSavePaths( - paths: MutableList?, + private fun getDownloadFileName( postId: String?, sliderPostfix: String, displayUrl: String?, username: String - ): Pair, String?>? { - if (paths == null) return null + ): Pair { val extension = getFileExtensionFromUrl(displayUrl) val usernamePrepend = if (isEmpty(username)) "" else username + "_" val fileName = usernamePrepend + postId + sliderPostfix + extension - // return new File(finalDir, fileName); - // DocumentFile file = finalDir.findFile(fileName); - // if (file == null) { val mimeType = Utils.mimeTypeMap.getMimeTypeFromExtension( if (extension.startsWith(".")) extension.substring(1) else extension ) - // file = finalDir.createFile(mimeType, fileName); - // } - paths.add(fileName) - return Pair(paths, mimeType) + return Pair(fileName, mimeType!!) } - // public static DocumentFile getTempFile() { - // return getTempFile(null, null); - // } fun getTempFile(fileName: String?, extension: String): DocumentFile? { - val dir = getTempDir() + val dir = tempDir var name = fileName if (isEmpty(name)) { name = UUID.randomUUID().toString() @@ -284,26 +284,23 @@ object DownloadUtils { if (user != null) { username = user.username } - val userFolderPaths: List = getSubPathForUserFolder(username) + val userFolder = getDownloadDir(context, username, false) + if (userFolder == null) return checkList when (media.type) { MediaItemType.MEDIA_TYPE_IMAGE, MediaItemType.MEDIA_TYPE_VIDEO -> { val url = if (media.type == MediaItemType.MEDIA_TYPE_VIDEO) ResponseBodyUtils.getVideoUrl( media ) else ResponseBodyUtils.getImageUrl(media) - val file = getDownloadSavePaths(ArrayList(userFolderPaths), media.code, url, "") - val fileExists = file!!.first != null && checkPathExists(file.first, context) - var usernameFileExists = false - if (!fileExists) { - val usernameFile = getDownloadSavePaths( - ArrayList(userFolderPaths), media.code, url, username - ) - usernameFileExists = usernameFile!!.first != null && checkPathExists(usernameFile.first, context) - } - checkList.add(fileExists || usernameFileExists) + val fileName = getDownloadSavePaths(media.code, url) + val fileNameWithUser = getDownloadSavePaths(media.code, url, username) + val files = checkFiles(context, userFolder, mapOf(fileName, fileNameWithUser), false) + checkList.add(files.size > 0) } MediaItemType.MEDIA_TYPE_SLIDER -> { val sliderItems = media.carouselMedia + val fileNames: MutableMap = mutableMapOf() + val filePairs: MutableMap = mutableMapOf() var i = 0 while (i < sliderItems!!.size) { val child = sliderItems[i] @@ -311,20 +308,17 @@ object DownloadUtils { if (child.type == MediaItemType.MEDIA_TYPE_VIDEO) ResponseBodyUtils.getVideoUrl( child ) else ResponseBodyUtils.getImageUrl(child) - val file = getDownloadChildSavePaths( - ArrayList(userFolderPaths), media.code, i + 1, url, "" - ) - val fileExists = file!!.first != null && checkPathExists(file.first, context) - var usernameFileExists = false - if (!fileExists) { - val usernameFile = getDownloadChildSavePaths( - ArrayList(userFolderPaths), media.code, i + 1, url, username - ) - usernameFileExists = usernameFile!!.first != null && checkPathExists(usernameFile.first, context) - } - checkList.add(fileExists || usernameFileExists) + val fileName = getDownloadChildSavePaths(media.code, i+1, url, "") + val fileNameWithUser = getDownloadChildSavePaths(media.code, i+1, url, username) + fileNames.put(fileName.first, fileName.second) + fileNames.put(fileNameWithUser.first, fileNameWithUser.second) + filePairs.put(fileName.first, fileNameWithUser.first) i++ } + val files = checkFiles(context, userFolder, fileNames, false) + for (p in filePairs) { + checkList.add(files.get(p.key) != null || files.get(p.value) != null) + } } else -> { } @@ -332,61 +326,32 @@ object DownloadUtils { return checkList } - private fun checkPathExists(paths: List, context: Context): Boolean { - if (root == null) return false - val uri = root!!.uri - var found = false - var docId = DocumentsContract.getTreeDocumentId(uri) - for (path in paths) { - val docUri = DocumentsContract.buildChildDocumentsUriUsingTree(uri, docId) - val docCursor = context.contentResolver.query( - docUri, arrayOf( - DocumentsContract.Document.COLUMN_DISPLAY_NAME, - DocumentsContract.Document.COLUMN_DOCUMENT_ID - ), null, null, null - ) - if (docCursor == null) return false - while (docCursor.moveToNext() && !found) { - if (path.equals(docCursor.getString(0))) { - docId = docCursor.getString(1) - found = true - } - } - docCursor.close() - if (!found) return false - found = false - } - return true - } - @JvmStatic fun showDownloadDialog( context: Context, feedModel: Media, - childPosition: Int + childPosition: Int, + popupLocation: View? ) { - if (childPosition >= 0) { - val clickListener = - DialogInterface.OnClickListener { dialog: DialogInterface, which: Int -> - when (which) { - 0 -> download(context, feedModel, childPosition) - 1 -> download(context, feedModel) - DialogInterface.BUTTON_NEGATIVE -> dialog.dismiss() - else -> dialog.dismiss() - } - } - val items = arrayOf( - context.getString(R.string.post_viewer_download_current), - context.getString(R.string.post_viewer_download_album) - ) - AlertDialog.Builder(context) - .setTitle(R.string.post_viewer_download_dialog_title) - .setItems(items, clickListener) - .setNegativeButton(R.string.cancel, null) - .show() + if (childPosition == -1 || popupLocation == null) { + download(context, feedModel) return } - download(context, feedModel) + val themeWrapper = ContextThemeWrapper(context, R.style.popupMenuStyle) + val popupMenu = PopupMenu(themeWrapper, popupLocation) + val menu = popupMenu.menu + menu.add(0, R.id.download_current, 0, R.string.post_viewer_download_current) + menu.add(0, R.id.download_all, 1, R.string.post_viewer_download_album) + popupMenu.setOnMenuItemClickListener { item: MenuItem -> + val itemId = item.itemId + if (itemId == R.id.download_current) { + download(context, feedModel, childPosition) + } else if (itemId == R.id.download_all) { + download(context, feedModel) + } + false + } + popupMenu.show() } @JvmStatic @@ -394,27 +359,19 @@ object DownloadUtils { context: Context, storyModel: StoryMedia ) { - val downloadDir = getDownloadDir(context, storyModel.user?.username) ?: return + val downloadDir = getDownloadDir(context, storyModel.user?.username, true) ?: return val url = - if (storyModel.mediaType == MediaItemType.MEDIA_TYPE_VIDEO) ResponseBodyUtils.getVideoUrl(storyModel) + if (storyModel.type == MediaItemType.MEDIA_TYPE_VIDEO) ResponseBodyUtils.getVideoUrl(storyModel) else ResponseBodyUtils.getImageUrl(storyModel) val extension = getFileExtensionFromUrl(url) - val baseFileName = (storyModel.id + "_" - + storyModel.takenAt + extension) + val mimeType = Utils.mimeTypeMap.getMimeTypeFromExtension(extension) + val baseFileName = storyModel.id + "_" + storyModel.takenAt + extension val usernamePrepend = if (Utils.settingsHelper.getBoolean(PreferenceKeys.DOWNLOAD_PREPEND_USER_NAME) && storyModel.user?.username != null ) storyModel.user.username + "_" else "" val fileName = usernamePrepend + baseFileName - var saveFile = downloadDir.findFile(fileName) - if (saveFile == null) { - val mimeType = Utils.mimeTypeMap.getMimeTypeFromExtension( - if (extension.startsWith(".")) extension.substring(1) else extension - ) - ?: return - saveFile = downloadDir.createFile(mimeType, fileName) - } - // final File saveFile = new File(downloadDir, fileName); + var saveFile = checkFiles(context, downloadDir, mapOf(fileName to mimeType!!), true).get(fileName) download(context, url, saveFile) } @@ -428,12 +385,28 @@ object DownloadUtils { download(context, listOf(feedModel), position) } + // this must be used for bulk download, but ONLY bulk download @JvmStatic fun download( context: Context, feedModels: List ) { - download(context, feedModels, -1) + val builder = NotificationCompat.Builder(context, Constants.DOWNLOAD_CHANNEL_ID) + .setCategory(NotificationCompat.CATEGORY_PROGRESS) + .setSmallIcon(R.drawable.ic_download) + .setOngoing(true) + .setProgress(1, 0, true) + .setAutoCancel(false) + .setOnlyAlertOnce(true) + .setContentTitle(context.getString(R.string.downloader_preparing)) + val notification = builder.build() + val nid = abs(UUID.randomUUID().hashCode()) + val nManager = NotificationManagerCompat.from(context.applicationContext) + nManager.notify(nid, notification) + tasksThread.execute { + download(context, feedModels, -1) + nManager.cancel(nid) + } } private fun download( @@ -441,11 +414,12 @@ object DownloadUtils { feedModels: List, childPositionIfSingle: Int ) { - val map: MutableMap = HashMap() + val map: MutableMap> = HashMap() + val fileMap: MutableMap = HashMap() for (media in feedModels) { val mediaUser = media.user val username = mediaUser?.username ?: "" - val userFolderPaths = getSubPathForUserFolder(username) + val dir = getDownloadDir(context, username, true) when (media.type) { MediaItemType.MEDIA_TYPE_IMAGE, MediaItemType.MEDIA_TYPE_VIDEO -> { val url = getUrlOfType(media) @@ -459,9 +433,8 @@ object DownloadUtils { fileName = mediaUser.username + "_" + fileName } } - val pair = getDownloadSavePaths(userFolderPaths, fileName, url) - val file = createFile(pair!!) ?: continue - map[url!!] = file + val pair = getDownloadSavePaths(fileName, url) + map[url!!] = pair } MediaItemType.MEDIA_TYPE_VOICE -> { val url = getUrlOfType(media) @@ -469,9 +442,8 @@ object DownloadUtils { if (mediaUser != null) { fileName = mediaUser.username + "_" + fileName } - val pair = getDownloadSavePaths(userFolderPaths, fileName, url) - val file = createFile(pair!!) ?: continue - map[url!!] = file + val pair = getDownloadSavePaths(fileName, url) + map[url!!] = pair } MediaItemType.MEDIA_TYPE_SLIDER -> { val sliderItems = media.carouselMedia @@ -485,43 +457,18 @@ object DownloadUtils { val url = getUrlOfType(child) val usernamePrepend = if (Utils.settingsHelper.getBoolean(PreferenceKeys.DOWNLOAD_PREPEND_USER_NAME) && mediaUser != null) mediaUser.username else "" - val pair = getDownloadChildSavePaths( - ArrayList(userFolderPaths), media.code, i + 1, url, usernamePrepend - ) - val file = createFile(pair!!) - if (file == null) { - i++ - continue - } - map[url!!] = file + val pair = getDownloadChildSavePaths(media.code, i + 1, url, usernamePrepend) + map[url!!] = pair i++ } } } + fileMap.putAll(checkFiles(context, dir, map.values.toMap(), true)) } - if (map.isEmpty()) return - download(context, map) - } - - private fun createFile(pair: Pair, String?>): DocumentFile? { - if (root == null) return null - if (pair.first == null || pair.second == null) return null - var dir = root - val first = pair.first - for (i in first.indices) { - val name = first[i] - val file = dir!!.findFile(name) - if (file != null) { - dir = file - continue - } - dir = if (i == first.size - 1) dir.createFile( - pair.second!!, - name - ) else dir.createDirectory(name) - if (dir == null) break - } - return dir + if (map.isEmpty() || fileMap.isEmpty()) return + val resultMap: MutableMap = mutableMapOf() + map.mapValuesTo(resultMap) { fileMap.get(it.value.first) } + download(context, resultMap) } private fun getUrlOfType(media: Media): String? { @@ -559,7 +506,7 @@ object DownloadUtils { download(context, Collections.singletonMap(url!!, filePath)) } - private fun download(context: Context?, urlFilePathMap: Map) { + private fun download(context: Context?, urlFilePathMap: Map) { if (context == null) return val constraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) diff --git a/app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.kt b/app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.kt index 200f4deb..479ce01b 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.kt +++ b/app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.kt @@ -34,7 +34,7 @@ class DirectThreadViewModel( // private static final String ERROR_INVALID_THREAD = "Invalid thread"; private val contentResolver: ContentResolver = application.contentResolver - private val recordingsDir: DocumentFile? = DownloadUtils.getRecordingsDir() + private val recordingsDir: DocumentFile? = DownloadUtils.recordingsDir private var voiceRecorder: VoiceRecorder? = null private lateinit var threadManager: ThreadManager diff --git a/app/src/main/java/awais/instagrabber/viewmodels/ImageEditViewModel.java b/app/src/main/java/awais/instagrabber/viewmodels/ImageEditViewModel.java index ebbfc2f6..11c8f15a 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/ImageEditViewModel.java +++ b/app/src/main/java/awais/instagrabber/viewmodels/ImageEditViewModel.java @@ -59,7 +59,7 @@ public class ImageEditViewModel extends AndroidViewModel { public ImageEditViewModel(final Application application) { super(application); sessionId = LocalDateTime.now().format(SIMPLE_DATE_FORMAT); - outputDir = DownloadUtils.getImageEditDir(sessionId); + outputDir = DownloadUtils.getImageEditDir(sessionId, application); destinationFile = outputDir.createFile(MIME_TYPE, RESULT + ".jpg"); destinationUri = destinationFile.getUri(); cropDestinationUri = outputDir.createFile(MIME_TYPE, CROP + ".jpg").getUri(); diff --git a/app/src/main/java/awais/instagrabber/workers/DownloadWorker.kt b/app/src/main/java/awais/instagrabber/workers/DownloadWorker.kt index d499c1b2..e0c80664 100644 --- a/app/src/main/java/awais/instagrabber/workers/DownloadWorker.kt +++ b/app/src/main/java/awais/instagrabber/workers/DownloadWorker.kt @@ -393,9 +393,10 @@ class DownloadWorker(context: Context, workerParams: WorkerParameters) : Corouti class Builder { private var urlToFilePathMap: MutableMap = mutableMapOf() - fun setUrlToFilePathMap(urlToFilePathMap: Map): Builder { + fun setUrlToFilePathMap(urlToFilePathMap: Map): Builder { this.urlToFilePathMap = urlToFilePathMap - .mapValues { it.value.uri.toString() } + .filter{ it.value != null } + .mapValues { it.value!!.uri.toString() } .toMutableMap() return this } diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 5505bf1c..cccace7d 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -40,8 +40,6 @@ حفظ في مجلد مخصص تحديد مجلد السمة - فقط يؤثر على المستخدمين المسجلين: - يؤثر فقط على مستخدمين مجهولين: اللغة %s\nPosts @@ -125,10 +123,7 @@ تم الحفظ مشار إليها الرسالة - اعجاب - إلغاء الإعجاب Bookmark - Unbookmark Follow Unfollow Favorite @@ -156,7 +151,6 @@ Failed to export! Refresh Get cookies - Desktop Mode Use custom format Separator Time Format @@ -223,14 +217,8 @@ Error creating folder! Error downloading file You can only download 100 posts at a time. Don\'t be too greedy! - Copy comment - View comment likers - Reply to comment - Like comment - Unlike comment Translate comment Delete comment - No empty comments! Do you want to search the username? Do you want to search the hashtag? Followers diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 91d7f9f2..9fb0f75c 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -40,8 +40,6 @@ Desa a una carpeta personalitzada Selecciona la carpeta Tema - Només afecta als usuaris amb sessió iniciada: - Només afecta als usuaris anònims: Llengua %s\nPublicació @@ -109,10 +107,7 @@ Desat Etiquetat Missatge - M\'agrada - Desfer \"m\'agrada\" Marcador - Desfer marcador Seguir Deixar de seguir Preferit @@ -140,7 +135,6 @@ No s\'ha pogut exportar! Actualitzar Obtenir cookies - Mode escriptori Utilitza un format personalitzat Separador Format d\'hora @@ -207,14 +201,8 @@ S\'ha produït un error en crear la carpeta! S\'ha produït un error en descarregar l\'arxiu Només pots descarregar 100 publicacions alhora. No siguis massa cobdiciós! - Copiar el comentari - Veure m\'agrades del comentari - Respondre al comentari - Donar \"m\'agrada\" al comentari - Treure \"m\'agrada\" al comentari Traduir comentari Esborrar comentari - No als comentaris buits! Vols cercar el nom d\'usuari? Vols cercar l\'etiqueta? Seguidors diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index ab3d1865..bc7b3db6 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -40,8 +40,6 @@ Uložit do vlastní složky Vybrat složku Motiv - Týká se pouze přihlášených uživatelů: - Týká se pouze anonymních uživatelů: Jazyk %s\nPříspěvek @@ -117,10 +115,7 @@ Uložené Označení Zpráva - To se mi líbí - Už se mi to nelíbí Přidat do záložek - Odebrat záložku Sledovat Přestat sledovat Oblíbené @@ -148,7 +143,6 @@ Export se nezdařil! Načíst znovu Získat cookies - Režim počítače Použít vlastní formát Oddělovač Formát času @@ -215,14 +209,8 @@ Chyba při vytváření složky! Chyba při stahování souboru Můžete stahovat pouze 100 příspěvků najednou. Nebuďte příliš chamtiví! - Kopírovat komentář - Zobrazit, komu se komentář líbí - Odpovědět na komentář - Označit komentář lajkem - Odebrat lajk z komentáře Přeložit komentář Odstranit komentář - Žádné prázdné komentáře! Chcete vyhledat uživatelské jméno? Chcete vyhledat hashtag? Sledující diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 31c1ea3d..93f78af7 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -40,8 +40,6 @@ In benutzerdefiniertem Ordner speichern Ordner auswählen Design - Betrifft nur angemeldete Benutzer: - Betrifft nur anonyme Benutzer: Sprache %s\nBeitrag @@ -109,10 +107,7 @@ Gespeichert Markiert Nachricht - Gefällt mir - Gefällt mir nicht mehr Lesezeichen - Lesezeichen entfernen Folgen Nicht mehr folgen Favorit @@ -140,7 +135,6 @@ Export fehlgeschlagen! Aktualisieren Cookies abrufen - Desktop Modus Eigenes Format verwenden Trennzeichen Zeiformat @@ -207,14 +201,8 @@ Fehler beim Erstellen des Ordners! Fehler beim Dateidownload Du kannst nur 100 Beiträge gleichzeitig herunterladen. Sei nicht zu gierig! - Kommentar kopieren - Kommentar-Liker ansehen - Auf Kommentar antworten - Kommentar gefällt mir - Kommentar gefällt mir nicht mehr Kommentar übersetzen Kommentar löschen - Keine leeren Kommentare! Möchtest du nach dem Benutzernamen suchen? Möchtest du nach dem Hashtag suchen? Abonnenten diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 3451b8d6..55810e73 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -40,8 +40,6 @@ Αποθήκευση σε προσαρμοσμένο φάκελο Επιλογή φακέλου Θέμα - Επηρεάζει μόνο τους συνδεδεμένους χρήστες: - Επηρεάζει μόνο τους ανώνυμους χρήστες: Γλώσσα %s\nΔημοσίευση @@ -109,10 +107,7 @@ Αποθηκευμένα Ετικέτες Μήνυμα - Μου αρέσει - Δε μου αρέσει Αποθήκευση σαν σελιδοδείκτης - Διαγραφή σελιδοδείκτη Ακολουθήστε Να μην ακολουθώ Αγαπημένα @@ -140,7 +135,6 @@ Η εξαγωγή απέτυχε! Ανανέωση Λήψη cookies - Λειτουργία Επιφάνειας Εργασίας Χρήση προσαρμοσμένης μορφής Διαχωριστικό Μορφή Ώρας @@ -207,14 +201,8 @@ Σφάλμα κατά τη δημιουργία φακέλου! Σφάλμα λήψης αρχείου Μπορείτε να καταφορτώσετε μόνο 100 δημοσιεύσεις τη φορά. Μην είστε τόσο άπληστος! - Αντιγραφή σχολίου - Προβολή όσων δήλωσαν ότι τους αρέσει το σχόλιο - Απάντηση στο σχόλιο - Επισήμανση ως «Μου Αρέσει» - Κατάργηση του «Μου Αρέσει» Μετάφραση σχολίου Διαγραφή σχολίου - Όχι κενά σχόλια! Θέλετε να αναζητήσετε το όνομα χρήστη; Θέλετε να αναζητήσετε το hashtag; Ακόλουθοι diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 9f10ea79..04e9345a 100755 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -40,8 +40,6 @@ Descargar en carpeta personalizada Seleccionar carpeta Tema - Sólo afecta a cuentas que han iniciado sesión: - Sólo afecta a usuarios anónimos: Idioma %s\nPublicación @@ -109,10 +107,7 @@ Guardado Etiquetado Mensaje - Me gusta - No me gusta Añadir marcador - Quitar marcador Seguir Dejar de seguir Agregar a favoritos @@ -140,7 +135,6 @@ ¡Falló la exportación! Actualizar Obtener cookies - Modo escritorio Usar formato personalizado Separador Formato de tiempo @@ -207,14 +201,8 @@ ¡Error al crear carpeta! Error al descargar archivo Sólo puedes descargar 100 publicaciones a la vez. ¡No seas abaricioso! - Copiar comentario - Ver me gustas del comentario - Responder a comentario - Dar me gusta a comentario - Quitar me gusta a comentario Traducir comentario Eliminar comentario - ¡No hay comentarios vacíos! ¿Quieres buscar el nombre de usuario? ¿Quieres buscar el hashtag? Seguidores diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 7ce2f4df..4888ab74 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -40,8 +40,6 @@ Gorde pertsonalizatutako karpetan Hautatu karpeta Gaia - Saioa hasi duten erabiltzaileei soilik eragiten die: - Erabiltzaile anonimoei soilik eragiten die: Hizkuntza %s\nPost @@ -109,10 +107,7 @@ Gordeta Etiketatuta Mezua - Atsegitea - Ez dut atsegin Laster-marka - Laster-marka kendu Jarraitu Jarraitzeari utzi Gogokoa @@ -140,7 +135,6 @@ Esportatzeak huts egin du! Freskatu Eskuratu cookieak - Mahaigaineko modua Erabili pertsonalizatutako formatua Bereizlea Ordu-formatua @@ -207,14 +201,8 @@ Errorea karpeta sortzean! Errorea fitxategia deskargatzean 100 bidalketa deskargatu ditzakezu une berean. Ez izan gutiziatsu! - Kopiatu iruzkina - Ikusi iruzkinen atsegiteak - Erantzun iruzkinari - Atsegin iruzkina - Ez dut atsegin Itzuli iruzkina Delete comment - Iruzkin hutsik ez! Erabiltzaile-izena bilatu nahi duzu? Traol-hitza bilatu nahi duzu? Jarraitzaileak diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index c0047461..152614d0 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -40,8 +40,6 @@ ذخیره در پوشه سفارشی انتخاب پوشه پوسته - فقط بر کاربران وارد شده اثر دارد: - فقط بر کاربران ناشناس اثر دارد: زبان %s\n پست @@ -110,10 +108,7 @@ ذخیره شد تگ شده پیام - پسند - ناپسند نشانک - حذف نشانک پی‌گرفتن پایان پی‌گرفتن برگزیده‌ @@ -141,7 +136,6 @@ پشتبان گیری با موفقیت نبود! تازه سازی گرفتن کوکی ها - حالت دسکتاپ استفاده از فرمت سفارشی تفکیک کننده فرمت زمان @@ -208,14 +202,8 @@ خطا در ایجاد پوشه! خطا در دانلود فایل شما فقط 100 پست در یک زمان میتوانید دانلود کنید. خیلی حریص نباشید! - کپی دیدگاه - View comment likers - پاسخ به دیدگاه - پسندیدن دیدگاه - نپسندیدن دیدگاه گرداندن دیدگاه Delete comment - بدون دیدگاه تهی! ایا میخواهید نام کاربری جستوجو کنید ؟ آیا میخواهید هشتگ جستوجو کنید؟ پیگیران diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 23c0b358..d1f0fe83 100755 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -40,8 +40,6 @@ Enregistrer dans un dossier personnalisé Sélectionnez un dossier Thème - N\'affecte que les utilisateurs connectés : - N\'affecte que les utilisateurs anonymes: Langues %s\nPublication @@ -109,10 +107,7 @@ Sauvegardé Taggé Message - J\'aime - Ne plus aimer Ajouter aux marque-pages - Retirer des marque-pages Suivre Ne plus suivre Mettre en favoris @@ -140,7 +135,6 @@ Erreur d\'exportation! Actualiser Récupérer les cookies - Mode desktop Utiliser un format personnalisé Séparateur Format de l\'heure @@ -207,14 +201,8 @@ Erreur de création de dossier ! Erreur de téléchargement du fichier Vous ne pouvez télécharger que 100 publications à la fois. Ne soyez pas trop gourmands ! - Copier le commentaire - Voir les \"j\'aime\" du commentaire - Répondre au commentaire - Aimer le commentaire - Retirer le j\'aime du commentaire Traduire le commentaire Supprimer le commentaire - Aucun commentaire vide ! Souhaitez-vous faire une recherche sur le nom d\'utilisateur ? Souhaitez-vous faire une recherch sur le hashtag ? Abonnés diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index ff0253dc..b74f6145 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -40,8 +40,6 @@ खुद के द्बारा निर्धारित फोल्डर पे रखें फोल्डर का चयन करें थीम - सिर्फ लग इन किए हुए ब्यबहारकारी के लिये - गुमनामी लोगों के लिये भाषा %s\nपोस्ट @@ -110,10 +108,7 @@ सेव किया टैग किये संदेश - पसन्द - नापसंद बुकमार्क - बुकमार्क न करें अनुसरण करे अनुसरण ना करें पसंदीदा @@ -141,7 +136,6 @@ निर्यात करने में विफल! ताज़ा करें: कुकिज पायें - डेस्कटप मोड करें कस्टम फर्माट ब्यबहार करें सेपरेटर समय का स्बरुप @@ -208,14 +202,8 @@ फोलडर बनाने में त्रुटि! फ़ाइल डाउनलोड करने में त्रुटि आप सिर्फ १०० पोस्टस एक बार में डाउनलोड कर सकते हैं. ज्यादा लोभी बनना हानिकारक हैं! - टिप्पणी कॉपी करें - View comment likers - टिप्पणी का जवाब दें - टिप्पणी को पसन्द करें - टिप्पणी को पसन्द न करें टिप्पणी का अनुवाद करें टिप्पणी हटाएं - रिक्त टिप्पणी न करें! युज़रनेम को ढूंढ़ना चाहते हैं? हैशटैग को ढूंढ़ना चाहते हैं? अनुयायी diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 85a94637..84ab0264 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -40,8 +40,6 @@ Simpan ke folder khusus Pilih folder Tema - Hanya memengaruhi pengguna yang sudah masuk: - Hanya memengaruhi pengguna anonim: Bahasa %s\nKiriman @@ -105,10 +103,7 @@ Tersimpan Ditandai Pesan - Suka - Batal Suka Markah - Batalkan Markah Ikuti Batal Ikuti Favorit @@ -136,7 +131,6 @@ Gagal mengekspor! Muat ulang Dapatkan cookies - Desktop Mode\nuntuk 2FA Gunakan format suaian Pemisah Format Jam @@ -203,14 +197,8 @@ Galat membuat folder! Galat mengunduh berkas Anda hanya dapat mengunduh 100 kiriman saat bersamaan. Jangan serakah-serakah! - Salin komentar - Lihat penyuka komentar - Balas komentar - Suka komentar - Batal suka komentar Terjemahkan komentar Delete comment - Ups, komentar kosong! Apakah anda ingin mencari nama pengguna ini? Apakah anda ingin mencari tagar ini? Pengikut diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 551a3cfb..43d0d418 100755 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -40,8 +40,6 @@ Salva alla cartella personalizzata Seleziona cartella Tema - Influenza solo gli utenti connessi: - Influenza solo gli utenti anonimi: Lingua %s\nPubblicazione @@ -109,10 +107,7 @@ Salvati Taggati Messaggio - Mi Piace - Non mi Piace Contrassegna - Rimuovi dai preferiti Segui Non Seguire Più Preferito @@ -140,7 +135,6 @@ Impossibile esportare! Ricarica Ottieni cookie - Modalità Desktop Usa formato personalizzato Separatore Formato Ora @@ -207,14 +201,8 @@ Errore creando la cartella! Errore scaricando il file Puoi scaricare solo 100 post per volta. Non essere troppo avido! - Copia commento - Visualizza i mi piace del commento - Rispondi al commento - Mi Piace il commento - Il commento Non mi Piace Più Traduci commento Elimina commento - Nessun commento vuoto! Vuoi cercare il nome utente? Vuoi cercare l\'hashtag? Seguaci diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 5b71db6a..3b3575b9 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -40,8 +40,6 @@ カスタムフォルダーに保存 フォルダーを選択 テーマ - ログインユーザーにのみ影響します: - 匿名ユーザーにのみ影響します: 言語 %s\n件 @@ -105,10 +103,7 @@ 保存 タグ付き メッセージ - いいね! - いいね!を取り消す ブックマーク - ブックマークを解除する フォローする フォローを解除 お気に入り @@ -136,7 +131,6 @@ エクスポートに失敗しました! 更新 Cookie を取得する - デスクトップモード カスタム形式を使用する 区切り記号 時刻形式 @@ -203,14 +197,8 @@ フォルダの作成中にエラーが発生しました! ダウンロード中にエラーが発生しました 一度に100個までの投稿しかダウンロードできません。取り過ぎ注意! - コメントをコピー - コメントにいいね!したユーザーを表示 - コメントに返信 - コメントに いいね! - コメントのいいね!を取り消す コメントを翻訳 コメントを削除 - メッセージが未入力です! ユーザー名を検索しますか? ハッシュタグを検索しますか? フォロワー diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 06abdaa8..1f8ccbf7 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -40,8 +40,6 @@ Save to custom folder 폴더 선택 테마 - Only affects logged-in users: - Only affects anonymous users: 언어 %s\n게시물 @@ -105,10 +103,7 @@ 저장됨 태그됨 메시지 - 좋아요 - 좋아요 취소 북마크 하기 - 북마크에서 제거 팔로우 팔로우 취소 즐겨찾기 @@ -136,7 +131,6 @@ Failed to export! 새로고침 Get cookies - Desktop Mode Use custom format Separator 시간 형식 @@ -203,14 +197,8 @@ Error creating folder! Error downloading file You can only download 100 posts at a time. Don\'t be too greedy! - 댓글 복사 - 좋아요 보기 - 답글 달기 - 댓글 좋아요 - 댓글 좋아요 삭제 댓글 번역 댓글 삭제 - 빈 댓글이 안 됍니다. Do you want to search the username? Do you want to search the hashtag? 팔로워 diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 5e1d1a60..8999792c 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -40,8 +40,6 @@ Зачувај во друга папка Одбери папка Тема - Влијае само на корисниците кои се најавени: - Влијае на анонимните корисници: Јазик %s\nОбјава @@ -109,10 +107,7 @@ Зачувано Означен Порака - Лајк - Дислајк Обележи - Одобележи Следи Одследи Омилен @@ -140,7 +135,6 @@ Неуспешно експортиање! Обнови Превземи колачиња - Десктоп изглед Користи свој формат Разделувач Формат на време @@ -207,14 +201,8 @@ Фатална грешка при креирање папка! Фатална грешка при превзамање фајл Можете само 100 постови наеднаш да превземите. Не бидете алчни! - Копирај Коментар - Прегледај лајкови на коментар - Одговори на коментар - Лајкни коментар - Одлајкни коментар Преведи коментар Избриши коментар - Не смее празни коментари! Дали сакате да го пребарате ова корисничко име? Дали сакате да го пребарате овај хаштаг? Следачи diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index e56ff754..7bbfc94f 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -40,8 +40,6 @@ Opslaan in aangepaste map Selecteer map Thema - Heeft alleen invloed op ingelogde gebruikers: - Heeft alleen invloed op anonieme gebruikers: Taal %s\nPost @@ -109,10 +107,7 @@ Opgeslagen Getagd Bericht - Vind ik leuk - Vind ik niet meer leuk Toevoegen aan favorieten - Verwijderen uit favorieten Volg Ontvolg Toevoegen aan favorieten @@ -140,7 +135,6 @@ Exporteren mislukt! Ververs Cookies ophalen - Desktop Modus Aangepaste indeling gebruiken Scheidingsteken Tijdnotatie @@ -207,14 +201,8 @@ Fout bij aanmaken map! Fout bij downloaden bestand Je kunt slechts 100 berichten per keer downloaden. Wees niet te hebberig! - Kopieer reactie - Bekijk reacties likers - Reageer op opmerking - Vind reactie leuk - Vind reactie niet meer leuk Reactie vertalen Verwijder reactie - Geen lege reacties! Wil je de gebruikersnaam zoeken? Wil je de hashtag zoeken? Volgers diff --git a/app/src/main/res/values-or/strings.xml b/app/src/main/res/values-or/strings.xml index 1ed04f22..ea04c162 100644 --- a/app/src/main/res/values-or/strings.xml +++ b/app/src/main/res/values-or/strings.xml @@ -40,8 +40,6 @@ ନିଜେ ସ୍ଥିର କରିଥିବା ଫୋଲଡର ରେ ରଖ। ଫୋଲ୍‌ଡର୍‌ ଚୟନ କରନ୍ତୁ ଥିମ - କେବଳ ଲଗ ଇନ ହୋଇଥିବା ବ୍ୟବହାରକାରୀଙ୍କ ପାଇଁ: - କେବଳ ଲଗ ଇନ ହୋଇନଥିବା ବ୍ୟବହାରକାରୀଙ୍କ ପାଇଁ: ଭାଷା %s\nPost @@ -109,10 +107,7 @@ ସଞ୍ଚୟ ହେଲା ଟ୍ୟାଗ୍ କରିଛନ୍ତି ସନ୍ଦେଶ - ପସନ୍ଦ କରନ୍ତୁ - Unlike Bookmark - Unbookmark Follow Unfollow Favorite @@ -140,7 +135,6 @@ Failed to export! Refresh Get cookies - Desktop Mode Use custom format Separator Time Format @@ -207,14 +201,8 @@ Error creating folder! Error downloading file You can only download 100 posts at a time. Don\'t be too greedy! - Copy comment - View comment likers - Reply to comment - Like comment - Unlike comment Translate comment Delete comment - No empty comments! Do you want to search the username? Do you want to search the hashtag? Followers diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index b44c5e65..217dce32 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -40,8 +40,6 @@ Zapisz w folderze niestandardowym Wybierz folder Motyw - Dotyczy tylko zalogowanych użytkowników: - Dotyczy tylko anonimowych użytkowników: Język %s\npost @@ -117,10 +115,7 @@ Zapisane Oznaczono Message - Polub - Cofnij polubienie Zapisz - Usuń z zapisanych Obserwuj Przestań obserwować Ulubiony @@ -148,7 +143,6 @@ Eksportowanie nie powiodło się! Odśwież Pobierz ciasteczka - Tryb PC Niestandardowy format Separator Format czasu @@ -215,14 +209,8 @@ Błąd podczas tworzenia folderu! Błąd podczas pobierania pliku Możesz pobrać tylko 100 postów jednocześnie. Nie bądź zbyt chciwy! - Skopiuj komentarz - Wyświetl polubienia komentarzy - Odpowiedz na komentarz - Polub komentarz - Usuń polubienie komentarza Tłumacz komentarz Usuń komentarz - Brak komentarzy! Czy chcesz wyszukać przy pomocy nazwy użytkownika? Czy chcesz wyszukać przy pomocy hashtag\'a? Obserwujący diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 5bb5b425..092fdffc 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -40,8 +40,6 @@ Salvar em pasta personalizada Selecionar pasta Tema - Afeta apenas usuários logados: - Afeta apenas usuários anônimos: Idioma %s\nPublicação @@ -109,10 +107,7 @@ Salvo Marcado Mensagem - Curtir - Descurtir Salvar - Remover Seguir Deixar de seguir Favorito @@ -140,7 +135,6 @@ Falha ao exportar! Atualizar Obter cookies - Modo Desktop Usar formato personalizado Separador Formato de hora @@ -207,14 +201,8 @@ Erro ao criar pasta! Erro ao baixar arquivo Você só pode baixar 100 publicações por vez. Não seja tão ganancioso! - Copiar comentário - Ver quem curtiu o comentário - Responder comentário - Curtir comentário - Descurtir comentário Traduzir comentário Excluir comentário - Comentário em branco! Você quer buscar o nome de usuário? Você quer buscar a hashtag? Seguidores diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 214356c6..4c71160a 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -40,8 +40,6 @@ Сохранить в пользовательскую папку Выбрать папку Тема - Затрагивает только авторизованных пользователей: - Затрагивает только анонимных пользователей: Язык %s\nПубликация @@ -117,10 +115,7 @@ Сохранено Отмечено Сообщение - Нравится - Не нравится Закладка - Снять закладку Подписаться Отписаться В избранное @@ -148,7 +143,6 @@ Не удалось экспортировать! Обновить Получить файлы cookie - Режим настольного ПК Использовать пользовательский формат Разделитель Формат времени @@ -215,14 +209,8 @@ Ошибка при создании папки! Ошибка при скачивании файла Вы можете скачать только 100 сообщений за раз. Не будьте слишком жадным! - Копировать комментарий - Просмотр симпатий комментария - Ответить на комментарий - Мне нравится комментарий - Не нравится комментарий Перевести комментарий Удалить комментарий - Нет пустых комментариев! Вы хотите найти имя пользователя? Вы хотите найти хештэг? Подписчики diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 0abc87de..36722b21 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -40,8 +40,6 @@ Uložiť do vlastného priečinku Vybrať priečinok Téma - Platí iba pre prihlásených užívateľov: - Platí iba pre neprihlásených užívateľov: Jazyk %s\nPríspevok @@ -117,10 +115,7 @@ Uložené Označené Správa - Páči sa - Nepáči sa Záložka - Zrušiť záložku Sledovať Zrušiť sledovanie Obľúbené @@ -148,7 +143,6 @@ Export zlyhal! Obnoviť Získať cookies - Režim desktop Použiť vlastný formát Oddeľovač Formát času @@ -215,14 +209,8 @@ Chyba pri vytváraní priečinka! Chyba pri sťahovaní súboru Môžeš stiahnuť len 100 príspevkov v jeden čas. Nebuď chamtivý/á! - Kopírovať komentár - Ľudia čo označili \"páči sa mi\" - Odpovedať na komentár - Páči sa mi - Nepáči sa mi Preložiť komentár Delete comment - Komentár je prázdny! Chcete vyhľadať používeteľské meno? Chcete vyhľadať hashtag? Sledovatelia diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index bb43f425..5b3302b8 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -40,8 +40,6 @@ Spara till en anpassad mapp Välj mapp Tema - Påverkar endast inloggade användare: - Påverkar endast anonyma användare: Språk %s\nPost @@ -109,10 +107,7 @@ Sparade Taggade Meddelande - Gilla - Sluta gilla Bokmärk - Ta bort bokmärke Följ Sluta följa Favorit @@ -140,7 +135,6 @@ Misslyckades med exportering! Uppdatera Hämta kakor - Skrivbordsläge Använd anpassat format Avskiljare Tidsformat @@ -207,14 +201,8 @@ Error creating folder! Error downloading file Du kan endast ladda ner 100 inlägg åt gången. Var inte för girig! - Kopiera kommentaren - Visa gillare av kommentaren - Svara till kommentaren - Gilla kommentaren - Sluta gilla kommentaren Översätt kommentaren Delete comment - Inga tomma kommentarer! Vill du söka på användarnamnet? Vill du söka på hash-taggen? Följare diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 2dda9e6a..ca6cf993 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -40,8 +40,6 @@ Şu klasöre kaydet: Klasörü seçin Tema - Sadece giriş yapılmış hesapları etkiler: - Sadece anonim hesapları etkiler: Dil %s\nGönderi @@ -109,10 +107,7 @@ Kaydedilenler Etiketlenilenler Mesaj - Beğen - Beğenmekten Vazgeç Yer İşareti Koy - Yer İşaretini Kaldır Takip Et Takipten Çık Favorilere Ekle @@ -140,7 +135,6 @@ Dışa aktarılamadı! Yenile Çerezleri al - Masaüstü Modu Özel biçim kullan Ayırıcı Zaman Biçimi @@ -207,14 +201,8 @@ Klasör oluşturma hatası! Dosya indirme hatası Tek seferde en fazla 100 gönderi indirebilirsin, açgözlü olma! - Yorumu kopyala - Yorumu beğenenleri gör - Yoruma cevap ver - Yorumu beğen - Yorum beğenisini geri al Yorumu çevir Delete comment - Boş yorum yapılamaz! Kullanıcı adını aratmak istiyor musun? Etiketi aratmak istiyor musun? Takipçiler diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index c5655dd4..747dcabe 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -40,8 +40,6 @@ Lưu vào thư mục tùy chọn Chọn thư mục Giao diện - Chỉ ảnh hưởng người dùng đã đăng nhập: - Chỉ ảnh hưởng người dùng ẩn danh: Ngôn ngữ %s\nBài đăng @@ -105,10 +103,7 @@ Đã lưu Đã gắn thẻ Tin nhắn - Thích - Bỏ thích Dấu trang - Gỡ dấu trang Theo dõi Bỏ theo dõi Yêu thích @@ -136,7 +131,6 @@ Xuất dữ liệu thất bại! Làm mới Lấy cookies - Chế độ máy tính Dùng định dạng tuỳ chỉnh Phân cách Định dạng Thời gian @@ -203,14 +197,8 @@ Lỗi khi tạo thư mục! Lỗi khi tải xuống tệp Bạn chỉ có thể tải xuống 100 bài cùng một lúc. Đừng tham lam quá! - Sao chép bình luận - Xem những người đã thích bình luận - Trả lời bình luận - Thích bình luận - Bỏ thích bình luận Dịch bình luận Xóa bình luận - Không được để trống bình luận! Bạn có muốn tìm tên người dùng không? Bạn có muốn tìm hashtag không? Người theo dõi diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 7534b8f6..076f9ebf 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -40,8 +40,6 @@ 保存至自定义文件夹 选择文件夹 主题 - 仅影响登录用户: - 仅影响匿名用户: 语言 %s\n帖子 @@ -105,10 +103,7 @@ 已保存 已标记 消息 - - 取消点赞 加入收藏 - 解除收藏 关注 脱粉 加入收藏 @@ -136,7 +131,6 @@ 导出失败! 刷新 获取 cookies - 桌面版 使用自定义格式 分界 时间格式 @@ -203,14 +197,8 @@ 创建文件夹时出错! 下载文件时出错 您一次只能下载100个帖子。切勿贪得无厌! - 复制评论 - 查看评论赞 - 回复评论 - 赞评论 - 取消赞 翻译评论 删除评论 - 评论要写字的! 要搜索用户名吗? 要搜索标签吗? 粉丝 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 356086f6..a23a0f16 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -40,8 +40,6 @@ 儲存到自定義資料夾 選擇資料夾 主題 - 僅影響已登入用戶: - 僅影響匿名用戶: 語言 %s\n貼文 @@ -105,10 +103,7 @@ 已儲存 已標記 訊息 - - 收回讚 書籤 - 自書籤中移除 追蹤 取消追蹤 收藏 @@ -136,7 +131,6 @@ 匯出失敗! 重新整理 取得 Cookies - 桌面模式 使用自定義格式 分隔 時間格式 @@ -203,14 +197,8 @@ 建立資料夾時出錯! 下載檔案時出錯 您一次只能下載100個文章。不要太貪心! - 複製評論 - 查看為此評論點讚的用戶 - 回覆評論 - 對此評論說讚 - 收回評論的讚 翻譯評論 刪除評論 - 無空評論! 您要搜尋用戶名嗎? 您要搜尋主題標籤嗎? 追蹤者 diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml index d94430a5..29c6fecc 100644 --- a/app/src/main/res/values/ids.xml +++ b/app/src/main/res/values/ids.xml @@ -6,4 +6,6 @@ + + \ 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 e264fd28..c74b7e82 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5,8 +5,6 @@ Direct Messages Settings Download - GitHub - F-Droid Search username… Compare Error copying text @@ -44,8 +42,6 @@ Save to custom folder Select folder Theme - Only affects logged-in users: - Only affects anonymous users: Language %s\nPost @@ -73,7 +69,7 @@ Be patient! View Post View Post - Spotify + Spotify Vote Vote successful! You have already voted! @@ -114,10 +110,7 @@ Saved Tagged Message - Like - Unlike Bookmark - Unbookmark Follow Unfollow Favorite @@ -145,7 +138,6 @@ Failed to export! Refresh Get cookies - Desktop Mode Use custom format Separator Time Format @@ -205,6 +197,7 @@ Downloads posts directly to the phone! Fetching post(s) Download completed + Preparing to download… Downloading post… Downloading media Downloading profile picture @@ -212,14 +205,8 @@ Error creating folder! Error downloading file You can only download 100 posts at a time. Don\'t be too greedy! - Copy comment - View comment likers - Reply to comment - Like comment - Unlike comment Translate comment Delete comment - No empty comments! Do you want to search the username? Do you want to search the hashtag? Followers