diff --git a/LICENSE b/LICENSE index 9b65409a..e587591e 100755 --- a/LICENSE +++ b/LICENSE @@ -618,44 +618,4 @@ an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - InstaGrabber - Copyright (C) 2019 AWAiS - Copyright (C) 2020 Austin Huang - Ammar Githam - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - -[we're not terminal program, redacted] - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. + END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ff0a5084..f2047dbe 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -169,7 +169,7 @@ + + + + + diff --git a/app/src/main/java/awais/instagrabber/activities/CommentsViewer.java b/app/src/main/java/awais/instagrabber/activities/CommentsViewer.java index ac49e521..62f497bc 100755 --- a/app/src/main/java/awais/instagrabber/activities/CommentsViewer.java +++ b/app/src/main/java/awais/instagrabber/activities/CommentsViewer.java @@ -13,13 +13,11 @@ import android.view.inputmethod.InputMethodManager; import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.view.WindowManager; import android.widget.ArrayAdapter; import android.widget.Toast; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.SearchView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; @@ -39,8 +37,6 @@ import awais.instagrabber.models.ProfileModel; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Utils; -import static awais.instagrabber.utils.Utils.settingsHelper; - public final class CommentsViewer extends BaseLanguageActivity implements SwipeRefreshLayout.OnRefreshListener { private CommentsAdapter commentsAdapter; private CommentModel commentModel; @@ -117,7 +113,7 @@ public final class CommentsViewer extends BaseLanguageActivity implements SwipeR if (which == 0) { searchUsername(profileModel.getUsername()); } else if (which == 1) { - startActivity(new Intent(this, ProfileViewer.class).putExtra(Constants.EXTRAS_PROFILE, profileModel)); + startActivity(new Intent(this, ProfilePicViewer.class).putExtra(Constants.EXTRAS_PROFILE, profileModel)); } else if (which == 2) { Utils.copyText(this, profileModel.getUsername()); } else if (which == 3) { @@ -211,11 +207,10 @@ public final class CommentsViewer extends BaseLanguageActivity implements SwipeR }; private void searchUsername(final String text) { - if (Main.scanHack != null) { - Main.scanHack.onResult(text); - setResult(6969); - finish(); - } + startActivity( + new Intent(getApplicationContext(), ProfileViewer.class) + .putExtra(Constants.EXTRAS_USERNAME, text) + ); } @Override diff --git a/app/src/main/java/awais/instagrabber/activities/FollowViewer.java b/app/src/main/java/awais/instagrabber/activities/FollowViewer.java index 6fa159f8..69facbeb 100755 --- a/app/src/main/java/awais/instagrabber/activities/FollowViewer.java +++ b/app/src/main/java/awais/instagrabber/activities/FollowViewer.java @@ -77,8 +77,10 @@ public final class FollowViewer extends BaseLanguageActivity implements SwipeRef final Object tag = v.getTag(); if (tag instanceof FollowModel) { model = (FollowModel) tag; - Main.scanHack.onResult(model.getUsername()); - finish(); + startActivity( + new Intent(getApplicationContext(), ProfileViewer.class) + .putExtra(Constants.EXTRAS_USERNAME, model.getUsername()) + ); } }; diff --git a/app/src/main/java/awais/instagrabber/activities/Main.java b/app/src/main/java/awais/instagrabber/activities/Main.java index 73140cca..11b4193d 100755 --- a/app/src/main/java/awais/instagrabber/activities/Main.java +++ b/app/src/main/java/awais/instagrabber/activities/Main.java @@ -195,7 +195,7 @@ public final class Main extends BaseLanguageActivity { profileDialogListener = (dialog, which) -> { final Intent intent; if (which == 0 || storyModels == null || storyModels.length < 1) { - intent = new Intent(this, ProfileViewer.class).putExtra( + intent = new Intent(this, ProfilePicViewer.class).putExtra( ((hashtagModel != null) ? Constants.EXTRAS_HASHTAG : (locationModel != null ? Constants.EXTRAS_LOCATION : Constants.EXTRAS_PROFILE)), ((hashtagModel != null) ? hashtagModel : (locationModel != null ? locationModel : profileModel))); } diff --git a/app/src/main/java/awais/instagrabber/activities/PostViewer.java b/app/src/main/java/awais/instagrabber/activities/PostViewer.java index dafb8e7e..86146885 100755 --- a/app/src/main/java/awais/instagrabber/activities/PostViewer.java +++ b/app/src/main/java/awais/instagrabber/activities/PostViewer.java @@ -122,7 +122,7 @@ public final class PostViewer extends BaseLanguageActivity { if (which == 0) { searchUsername(username); } else if (profileModel != null && which == 1) { - startActivity(new Intent(this, ProfileViewer.class) + startActivity(new Intent(this, ProfilePicViewer.class) .putExtra(Constants.EXTRAS_PROFILE, profileModel)); } }; @@ -432,13 +432,10 @@ public final class PostViewer extends BaseLanguageActivity { } private void searchUsername(final String text) { - if (Main.scanHack != null) { - Main.scanHack.onResult(text); - setResult(6969); - Intent intent = new Intent(getApplicationContext(), Main.class); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - } + startActivity( + new Intent(getApplicationContext(), ProfileViewer.class) + .putExtra(Constants.EXTRAS_USERNAME, text) + ); } private void setupVideo() { diff --git a/app/src/main/java/awais/instagrabber/activities/ProfilePicViewer.java b/app/src/main/java/awais/instagrabber/activities/ProfilePicViewer.java new file mode 100755 index 00000000..dbf3fbee --- /dev/null +++ b/app/src/main/java/awais/instagrabber/activities/ProfilePicViewer.java @@ -0,0 +1,214 @@ +package awais.instagrabber.activities; + +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Environment; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.Toast; + +import androidx.annotation.Nullable; +import androidx.fragment.app.FragmentManager; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.RequestManager; +import com.bumptech.glide.load.DataSource; +import com.bumptech.glide.load.engine.GlideException; +import com.bumptech.glide.request.RequestListener; +import com.bumptech.glide.request.target.Target; + +import java.io.File; + +import awais.instagrabber.R; +import awais.instagrabber.asyncs.DownloadAsync; +import awais.instagrabber.asyncs.ProfilePictureFetcher; +import awais.instagrabber.databinding.ActivityProfilepicBinding; +import awais.instagrabber.interfaces.FetchListener; +import awais.instagrabber.models.HashtagModel; +import awais.instagrabber.models.LocationModel; +import awais.instagrabber.models.ProfileModel; +import awais.instagrabber.utils.Constants; +import awais.instagrabber.utils.Utils; + +public final class ProfilePicViewer extends BaseLanguageActivity { + private ActivityProfilepicBinding profileBinding; + private ProfileModel profileModel; + private HashtagModel hashtagModel; + private LocationModel locationModel; + private MenuItem menuItemDownload; + private String profilePicUrl; + private FragmentManager fragmentManager; + private FetchListener fetchListener; + private boolean errorHandled = false; + private boolean fallbackToProfile = false; + private boolean destroyed = false; + + @Override + protected void onCreate(@Nullable final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + profileBinding = ActivityProfilepicBinding.inflate(getLayoutInflater()); + setContentView(profileBinding.getRoot()); + + setSupportActionBar(profileBinding.toolbar.toolbar); + + final Intent intent = getIntent(); + if (intent == null || (!intent.hasExtra(Constants.EXTRAS_PROFILE) && !intent.hasExtra(Constants.EXTRAS_HASHTAG) && !intent.hasExtra(Constants.EXTRAS_LOCATION)) + || ((profileModel = (ProfileModel) intent.getSerializableExtra(Constants.EXTRAS_PROFILE)) == null + && (hashtagModel = (HashtagModel) intent.getSerializableExtra(Constants.EXTRAS_HASHTAG)) == null + && (locationModel = (LocationModel) intent.getSerializableExtra(Constants.EXTRAS_LOCATION)) == null)) { + Utils.errorFinish(this); + return; + } + + fragmentManager = getSupportFragmentManager(); + + final String id = hashtagModel != null ? hashtagModel.getId() : (locationModel != null ? locationModel.getId() : profileModel.getId()); + final String username = hashtagModel != null ? hashtagModel.getName() : (locationModel != null ? locationModel.getName() : profileModel.getUsername()); + + profileBinding.toolbar.toolbar.setTitle(username); + + profileBinding.progressView.setVisibility(View.VISIBLE); + profileBinding.imageViewer.setVisibility(View.VISIBLE); + + profileBinding.imageViewer.setZoomable(true); + profileBinding.imageViewer.setZoomTransitionDuration(420); + profileBinding.imageViewer.setMaximumScale(7.2f); + + fetchListener = profileUrl -> { + profilePicUrl = profileUrl; + + if (!fallbackToProfile && Utils.isEmpty(profilePicUrl)) { + fallbackToProfile = true; + new ProfilePictureFetcher(username, id, fetchListener, profilePicUrl, (hashtagModel != null || locationModel != null)).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + return; + } + + if (errorHandled && fallbackToProfile || Utils.isEmpty(profilePicUrl)) + profilePicUrl = hashtagModel != null ? hashtagModel.getSdProfilePic() : (locationModel != null ? locationModel.getSdProfilePic() : profileModel.getHdProfilePic()); + + if (destroyed == true) return; + + final RequestManager glideRequestManager = Glide.with(this); + + glideRequestManager.load(profilePicUrl).addListener(new RequestListener() { + @Override + public boolean onLoadFailed(@Nullable final GlideException e, final Object model, final Target target, final boolean isFirstResource) { + fallbackToProfile = true; + if (!errorHandled) { + errorHandled = true; + new ProfilePictureFetcher(username, id, fetchListener, profilePicUrl, (hashtagModel != null || locationModel != null)) + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } else { + glideRequestManager.load(profilePicUrl).into(profileBinding.imageViewer); + showImageInfo(); + } + profileBinding.progressView.setVisibility(View.GONE); + return false; + } + + @Override + public boolean onResourceReady(final Drawable resource, final Object model, final Target target, final DataSource dataSource, final boolean isFirstResource) { + if (menuItemDownload != null) menuItemDownload.setEnabled(true); + showImageInfo(); + profileBinding.progressView.setVisibility(View.GONE); + return false; + } + + private void showImageInfo() { + final Drawable drawable = profileBinding.imageViewer.getDrawable(); + if (drawable != null) { + final StringBuilder info = new StringBuilder(getString(R.string.profile_viewer_imageinfo, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight())); + if (drawable instanceof BitmapDrawable) { + final Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap(); + if (bitmap != null) { + final String colorDepthPrefix = getString(R.string.profile_viewer_colordepth_prefix); + switch (bitmap.getConfig()) { + case ALPHA_8: + info.append(colorDepthPrefix).append(" 8-bits(A)"); + break; + case RGB_565: + info.append(colorDepthPrefix).append(" 16-bits-A"); + break; + case ARGB_4444: + info.append(colorDepthPrefix).append(" 16-bits+A"); + break; + case ARGB_8888: + info.append(colorDepthPrefix).append(" 32-bits+A"); + break; + case RGBA_F16: + info.append(colorDepthPrefix).append(" 64-bits+A"); + break; + case HARDWARE: + info.append(colorDepthPrefix).append(" auto"); + break; + } + } + } + profileBinding.imageInfo.setText(info); + profileBinding.imageInfo.setVisibility(View.VISIBLE); + } + } + }).into(profileBinding.imageViewer); + }; + + new ProfilePictureFetcher(username, id, fetchListener, profilePicUrl, (hashtagModel != null || locationModel != null)) + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + private void downloadProfilePicture() { + int error = 0; + + if (profileModel != null) { + final File dir = new File(Environment.getExternalStorageDirectory(), "Download"); + if (dir.exists() || dir.mkdirs()) { + + final File saveFile = new File(dir, profileModel.getUsername() + '_' + System.currentTimeMillis() + + Utils.getExtensionFromModel(profilePicUrl, profileModel)); + + new DownloadAsync(this, + profilePicUrl, + saveFile, + result -> { + final int toastRes = result != null && result.exists() ? + R.string.downloader_downloaded_in_folder : R.string.downloader_error_download_file; + Toast.makeText(this, toastRes, Toast.LENGTH_SHORT).show(); + }).setItems(null, profileModel.getUsername()).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } else error = 1; + } else error = 2; + + if (error == 1) Toast.makeText(this, R.string.downloader_error_creating_folder, Toast.LENGTH_SHORT).show(); + else if (error == 2) Toast.makeText(this, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + getDelegate().onDestroy(); + destroyed = true; + } + + @Override + public boolean onCreateOptionsMenu(final Menu menu) { + getMenuInflater().inflate(R.menu.menu, menu); + + final MenuItem.OnMenuItemClickListener menuItemClickListener = item -> { + if (item == menuItemDownload) { + downloadProfilePicture(); + } + return true; + }; + + menu.findItem(R.id.action_search).setVisible(false); + menuItemDownload = menu.findItem(R.id.action_download); + menuItemDownload.setVisible(true); + menuItemDownload.setEnabled(false); + menuItemDownload.setOnMenuItemClickListener(menuItemClickListener); + + return true; + } +} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/activities/ProfileViewer.java b/app/src/main/java/awais/instagrabber/activities/ProfileViewer.java index 287294f7..f15a4b13 100755 --- a/app/src/main/java/awais/instagrabber/activities/ProfileViewer.java +++ b/app/src/main/java/awais/instagrabber/activities/ProfileViewer.java @@ -1,214 +1,878 @@ package awais.instagrabber.activities; import android.content.Intent; +import android.content.res.ColorStateList; +import android.content.res.Resources; import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; +import android.graphics.BitmapFactory; +import android.graphics.Typeface; +import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; -import android.os.Environment; -import android.view.Menu; +import android.text.SpannableStringBuilder; +import android.text.method.LinkMovementMethod; +import android.text.style.RelativeSizeSpan; +import android.text.style.StyleSpan; +import android.util.Log; +import android.util.TypedValue; import android.view.MenuItem; import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.fragment.app.FragmentManager; +import androidx.appcompat.app.AlertDialog; +import androidx.core.content.ContextCompat; +import androidx.core.view.GravityCompat; +import androidx.core.widget.ImageViewCompat; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; -import com.bumptech.glide.Glide; -import com.bumptech.glide.RequestManager; -import com.bumptech.glide.load.DataSource; -import com.bumptech.glide.load.engine.GlideException; -import com.bumptech.glide.request.RequestListener; -import com.bumptech.glide.request.target.Target; - -import java.io.File; +import java.io.DataOutputStream; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import awais.instagrabber.BuildConfig; import awais.instagrabber.R; -import awais.instagrabber.asyncs.DownloadAsync; -import awais.instagrabber.asyncs.ProfilePictureFetcher; +import awais.instagrabber.adapters.HighlightsAdapter; +import awais.instagrabber.adapters.PostsAdapter; +import awais.instagrabber.asyncs.HashtagFetcher; +import awais.instagrabber.asyncs.HighlightsFetcher; +import awais.instagrabber.asyncs.LocationFetcher; +import awais.instagrabber.asyncs.PostsFetcher; +import awais.instagrabber.asyncs.ProfileFetcher; +import awais.instagrabber.asyncs.i.iStoryStatusFetcher; +import awais.instagrabber.customviews.MouseDrawer; +import awais.instagrabber.customviews.RamboTextView; +import awais.instagrabber.customviews.helpers.GridAutofitLayoutManager; +import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration; +import awais.instagrabber.customviews.helpers.RecyclerLazyLoader; +import awais.instagrabber.customviews.helpers.VideoAwareRecyclerScroller; import awais.instagrabber.databinding.ActivityProfileBinding; import awais.instagrabber.interfaces.FetchListener; +import awais.instagrabber.interfaces.MentionClickListener; +import awais.instagrabber.models.BasePostModel; import awais.instagrabber.models.HashtagModel; +import awais.instagrabber.models.HighlightModel; import awais.instagrabber.models.LocationModel; +import awais.instagrabber.models.PostModel; import awais.instagrabber.models.ProfileModel; +import awais.instagrabber.models.StoryModel; +import awais.instagrabber.models.enums.ItemGetType; import awais.instagrabber.utils.Constants; +import awais.instagrabber.utils.DataBox; import awais.instagrabber.utils.Utils; +import awaisomereport.LogCollector; -public final class ProfileViewer extends BaseLanguageActivity { - private ActivityProfileBinding profileBinding; +import static awais.instagrabber.utils.Constants.AUTOLOAD_POSTS; +import static awais.instagrabber.utils.Constants.BOTTOM_TOOLBAR; +import static awais.instagrabber.utils.Utils.logCollector; + +public final class ProfileViewer extends BaseLanguageActivity implements SwipeRefreshLayout.OnRefreshListener { + private final ArrayList allItems = new ArrayList<>(), selectedItems = new ArrayList<>(); + private static AsyncTask currentlyExecuting; + private final boolean autoloadPosts = Utils.settingsHelper.getBoolean(AUTOLOAD_POSTS); + private boolean hasNextPage = false; + private View collapsingToolbar; + private String endCursor = null; private ProfileModel profileModel; private HashtagModel hashtagModel; private LocationModel locationModel; - private MenuItem menuItemDownload; - private String profilePicUrl; - private FragmentManager fragmentManager; - private FetchListener fetchListener; - private boolean errorHandled = false; - private boolean fallbackToProfile = false; - private boolean destroyed = false; + private StoryModel[] storyModels; + private MenuItem downloadAction; + private final FetchListener postsFetchListener = new FetchListener() { + @Override + public void onResult(final PostModel[] result) { + if (result != null) { + final int oldSize = allItems.size(); + allItems.addAll(Arrays.asList(result)); + + postsAdapter.notifyItemRangeInserted(oldSize, result.length); + + profileBinding.profileView.mainPosts.post(() -> { + profileBinding.profileView.mainPosts.setNestedScrollingEnabled(true); + profileBinding.profileView.mainPosts.setVisibility(View.VISIBLE); + }); + + if (isHashtag) + profileBinding.toolbar.toolbar.setTitle(userQuery); + else if (isLocation) + profileBinding.toolbar.toolbar.setTitle(locationModel.getName()); + else profileBinding.toolbar.toolbar.setTitle("@"+profileModel.getUsername()); + + final PostModel model = result[result.length - 1]; + if (model != null) { + endCursor = model.getEndCursor(); + hasNextPage = model.hasNextPage(); + if (autoloadPosts && hasNextPage) + currentlyExecuting = new PostsFetcher( + profileModel != null ? profileModel.getId() + : (hashtagModel != null ? ("#"+hashtagModel.getName()) : locationModel.getId()), endCursor, this) + .setUsername((isLocation || isHashtag) ? null : profileModel.getUsername()) + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + else { + profileBinding.profileView.swipeRefreshLayout.setRefreshing(false); + } + model.setPageCursor(false, null); + } + } + else { + profileBinding.profileView.swipeRefreshLayout.setRefreshing(false); + profileBinding.profileView.privatePage1.setImageResource(R.drawable.ic_cancel); + profileBinding.profileView.privatePage2.setText(R.string.empty_acc); + profileBinding.profileView.privatePage.setVisibility(View.VISIBLE); + } + } + }; + private final MentionClickListener mentionClickListener = new MentionClickListener() { + @Override + public void onClick(final RamboTextView view, final String text, final boolean isHashtag) { + new AlertDialog.Builder(ProfileViewer.this).setMessage(isHashtag ? R.string.comment_view_mention_hash_search : R.string.comment_view_mention_user_search) + .setTitle(text).setNegativeButton(R.string.cancel, null).setPositiveButton(R.string.ok, (dialog, which) -> { + //if (scanHack != null) scanHack.onResult(text); + }).show(); + } + }; + public final HighlightsAdapter highlightsAdapter = new HighlightsAdapter(null, new View.OnClickListener() { + @Override + public void onClick(final View v) { + final Object tag = v.getTag(); + if (tag instanceof HighlightModel) { + final HighlightModel highlightModel = (HighlightModel) tag; + new iStoryStatusFetcher(highlightModel.getId(), null, false, false, + (!isLoggedIn && Utils.settingsHelper.getBoolean(Constants.STORIESIG)), true, result -> { + if (result != null && result.length > 0) + startActivity(new Intent(ProfileViewer.this, StoryViewer.class) + //.putExtra(Constants.EXTRAS_USERNAME, userQuery.replace("@", "")) + .putExtra(Constants.EXTRAS_HIGHLIGHT, highlightModel.getTitle()) + .putExtra(Constants.EXTRAS_STORIES, result) + ); + else Toast.makeText(ProfileViewer.this, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); + }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + }); + private Resources resources; + private RecyclerLazyLoader lazyLoader; + private boolean isHashtag, isUser, isLocation; + private PostsAdapter postsAdapter; + private String cookie = Utils.settingsHelper.getString(Constants.COOKIE), userQuery; + public boolean isLoggedIn = !Utils.isEmpty(cookie); + private ActivityProfileBinding profileBinding; - @Override protected void onCreate(@Nullable final Bundle savedInstanceState) { + stopCurrentExecutor(); super.onCreate(savedInstanceState); - profileBinding = ActivityProfileBinding.inflate(getLayoutInflater()); - setContentView(profileBinding.getRoot()); - - setSupportActionBar(profileBinding.toolbar.toolbar); final Intent intent = getIntent(); - if (intent == null || (!intent.hasExtra(Constants.EXTRAS_PROFILE) && !intent.hasExtra(Constants.EXTRAS_HASHTAG) && !intent.hasExtra(Constants.EXTRAS_LOCATION)) - || ((profileModel = (ProfileModel) intent.getSerializableExtra(Constants.EXTRAS_PROFILE)) == null - && (hashtagModel = (HashtagModel) intent.getSerializableExtra(Constants.EXTRAS_HASHTAG)) == null - && (locationModel = (LocationModel) intent.getSerializableExtra(Constants.EXTRAS_LOCATION)) == null)) { + if (intent == null || !intent.hasExtra(Constants.EXTRAS_USERNAME) + || Utils.isEmpty((userQuery = intent.getStringExtra(Constants.EXTRAS_USERNAME)))) { Utils.errorFinish(this); return; } - fragmentManager = getSupportFragmentManager(); + userQuery = (userQuery.contains("/") || userQuery.startsWith("#") || userQuery.startsWith("@")) ? userQuery : ("@"+userQuery); - final String id = hashtagModel != null ? hashtagModel.getId() : (locationModel != null ? locationModel.getId() : profileModel.getId()); - final String username = hashtagModel != null ? hashtagModel.getName() : (locationModel != null ? locationModel.getName() : profileModel.getUsername()); + profileBinding = ActivityProfileBinding.inflate(getLayoutInflater()); + setContentView(profileBinding.getRoot()); - profileBinding.toolbar.toolbar.setTitle(username); + resources = getResources(); - profileBinding.progressView.setVisibility(View.VISIBLE); - profileBinding.imageViewer.setVisibility(View.VISIBLE); + profileBinding.profileView.swipeRefreshLayout.setOnRefreshListener(this); + profileBinding.profileView.mainUrl.setMovementMethod(new LinkMovementMethod()); - profileBinding.imageViewer.setZoomable(true); - profileBinding.imageViewer.setZoomTransitionDuration(420); - profileBinding.imageViewer.setMaximumScale(7.2f); + isLoggedIn = !Utils.isEmpty(cookie); - fetchListener = profileUrl -> { - profilePicUrl = profileUrl; + collapsingToolbar = profileBinding.profileView.appBarLayout.getChildAt(0); - if (!fallbackToProfile && Utils.isEmpty(profilePicUrl)) { - fallbackToProfile = true; - new ProfilePictureFetcher(username, id, fetchListener, profilePicUrl, (hashtagModel != null || locationModel != null)).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - return; + profileBinding.profileView.mainPosts.setNestedScrollingEnabled(false); + profileBinding.profileView.highlightsList.setLayoutManager( + new LinearLayoutManager(getApplicationContext(), LinearLayoutManager.HORIZONTAL, false)); + profileBinding.profileView.highlightsList.setAdapter(highlightsAdapter); + + setSupportActionBar(profileBinding.toolbar.toolbar); + + // change the next number to adjust grid + final GridAutofitLayoutManager layoutManager = new GridAutofitLayoutManager(ProfileViewer.this, Utils.convertDpToPx(110)); + profileBinding.profileView.mainPosts.setLayoutManager(layoutManager); + profileBinding.profileView.mainPosts.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(4))); + profileBinding.profileView.mainPosts.setAdapter(postsAdapter = new PostsAdapter(allItems, v -> { + final Object tag = v.getTag(); + if (tag instanceof PostModel) { + final PostModel postModel = (PostModel) tag; + + if (postsAdapter.isSelecting) toggleSelection(postModel); + else startActivity(new Intent(ProfileViewer.this, PostViewer.class) + .putExtra(Constants.EXTRAS_INDEX, postModel.getPosition()) + .putExtra(Constants.EXTRAS_POST, postModel) + //.putExtra(Constants.EXTRAS_USER, userQuery) + .putExtra(Constants.EXTRAS_TYPE, ItemGetType.MAIN_ITEMS)); } - - if (errorHandled && fallbackToProfile || Utils.isEmpty(profilePicUrl)) - profilePicUrl = hashtagModel != null ? hashtagModel.getSdProfilePic() : (locationModel != null ? locationModel.getSdProfilePic() : profileModel.getHdProfilePic()); - - if (destroyed == true) return; - - final RequestManager glideRequestManager = Glide.with(this); - - glideRequestManager.load(profilePicUrl).addListener(new RequestListener() { - @Override - public boolean onLoadFailed(@Nullable final GlideException e, final Object model, final Target target, final boolean isFirstResource) { - fallbackToProfile = true; - if (!errorHandled) { - errorHandled = true; - new ProfilePictureFetcher(username, id, fetchListener, profilePicUrl, (hashtagModel != null || locationModel != null)) - .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } else { - glideRequestManager.load(profilePicUrl).into(profileBinding.imageViewer); - showImageInfo(); - } - profileBinding.progressView.setVisibility(View.GONE); - return false; - } - - @Override - public boolean onResourceReady(final Drawable resource, final Object model, final Target target, final DataSource dataSource, final boolean isFirstResource) { - if (menuItemDownload != null) menuItemDownload.setEnabled(true); - showImageInfo(); - profileBinding.progressView.setVisibility(View.GONE); - return false; - } - - private void showImageInfo() { - final Drawable drawable = profileBinding.imageViewer.getDrawable(); - if (drawable != null) { - final StringBuilder info = new StringBuilder(getString(R.string.profile_viewer_imageinfo, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight())); - if (drawable instanceof BitmapDrawable) { - final Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap(); - if (bitmap != null) { - final String colorDepthPrefix = getString(R.string.profile_viewer_colordepth_prefix); - switch (bitmap.getConfig()) { - case ALPHA_8: - info.append(colorDepthPrefix).append(" 8-bits(A)"); - break; - case RGB_565: - info.append(colorDepthPrefix).append(" 16-bits-A"); - break; - case ARGB_4444: - info.append(colorDepthPrefix).append(" 16-bits+A"); - break; - case ARGB_8888: - info.append(colorDepthPrefix).append(" 32-bits+A"); - break; - case RGBA_F16: - info.append(colorDepthPrefix).append(" 64-bits+A"); - break; - case HARDWARE: - info.append(colorDepthPrefix).append(" auto"); - break; - } - } - } - profileBinding.imageInfo.setText(info); - profileBinding.imageInfo.setVisibility(View.VISIBLE); - } - } - }).into(profileBinding.imageViewer); - }; - - new ProfilePictureFetcher(username, id, fetchListener, profilePicUrl, (hashtagModel != null || locationModel != null)) - .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - - private void downloadProfilePicture() { - int error = 0; - - if (profileModel != null) { - final File dir = new File(Environment.getExternalStorageDirectory(), "Download"); - if (dir.exists() || dir.mkdirs()) { - - final File saveFile = new File(dir, profileModel.getUsername() + '_' + System.currentTimeMillis() - + Utils.getExtensionFromModel(profilePicUrl, profileModel)); - - new DownloadAsync(this, - profilePicUrl, - saveFile, - result -> { - final int toastRes = result != null && result.exists() ? - R.string.downloader_downloaded_in_folder : R.string.downloader_error_download_file; - Toast.makeText(this, toastRes, Toast.LENGTH_SHORT).show(); - }).setItems(null, profileModel.getUsername()).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } else error = 1; - } else error = 2; - - if (error == 1) Toast.makeText(this, R.string.downloader_error_creating_folder, Toast.LENGTH_SHORT).show(); - else if (error == 2) Toast.makeText(this, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - getDelegate().onDestroy(); - destroyed = true; - } - - @Override - public boolean onCreateOptionsMenu(final Menu menu) { - getMenuInflater().inflate(R.menu.menu, menu); - - final MenuItem.OnMenuItemClickListener menuItemClickListener = item -> { - if (item == menuItemDownload) { - downloadProfilePicture(); + }, v -> { // long click listener + final Object tag = v.getTag(); + if (tag instanceof PostModel) { + postsAdapter.isSelecting = true; + toggleSelection((PostModel) tag); } return true; - }; + })); - menu.findItem(R.id.action_search).setVisible(false); - menuItemDownload = menu.findItem(R.id.action_download); - menuItemDownload.setVisible(true); - menuItemDownload.setEnabled(false); - menuItemDownload.setOnMenuItemClickListener(menuItemClickListener); + this.lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> { + if ((!autoloadPosts || isHashtag) && hasNextPage) { + profileBinding.profileView.swipeRefreshLayout.setRefreshing(true); + stopCurrentExecutor(); + currentlyExecuting = new PostsFetcher(profileModel != null ? profileModel.getId() + : (hashtagModel != null ? ("#"+hashtagModel.getName()) : locationModel.getId()), endCursor, postsFetchListener) + .setUsername((isHashtag || isLocation) ? null : profileModel.getUsername()) + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + endCursor = null; + } + }); + profileBinding.profileView.mainPosts.addOnScrollListener(lazyLoader); + this.onRefresh(); + } + + @Override + public void onRefresh() { + if (lazyLoader != null) lazyLoader.resetState(); + stopCurrentExecutor(); + allItems.clear(); + selectedItems.clear(); + if (postsAdapter != null) { + postsAdapter.isSelecting = false; + postsAdapter.notifyDataSetChanged(); + } + profileBinding.profileView.appBarLayout.setExpanded(true, true); + profileBinding.profileView.privatePage.setVisibility(View.GONE); + profileBinding.profileView.privatePage2.setTextSize(28); + profileBinding.profileView.mainProfileImage.setImageBitmap(null); + profileBinding.profileView.mainHashtagImage.setImageBitmap(null); + profileBinding.profileView.mainLocationImage.setImageBitmap(null); + profileBinding.profileView.mainUrl.setText(null); + profileBinding.profileView.locationUrl.setText(null); + profileBinding.profileView.mainFullName.setText(null); + profileBinding.profileView.locationFullName.setText(null); + profileBinding.profileView.mainPostCount.setText(null); + profileBinding.profileView.mainLocPostCount.setText(null); + profileBinding.profileView.mainTagPostCount.setText(null); + profileBinding.profileView.mainFollowers.setText(null); + profileBinding.profileView.mainFollowing.setText(null); + profileBinding.profileView.mainBiography.setText(null); + profileBinding.profileView.locationBiography.setText(null); + profileBinding.profileView.mainBiography.setEnabled(false); + profileBinding.profileView.locationBiography.setEnabled(false); + profileBinding.profileView.mainProfileImage.setEnabled(false); + profileBinding.profileView.mainLocationImage.setEnabled(false); + profileBinding.profileView.mainHashtagImage.setEnabled(false); + profileBinding.profileView.mainBiography.setMentionClickListener(null); + profileBinding.profileView.locationBiography.setMentionClickListener(null); + profileBinding.profileView.mainUrl.setVisibility(View.GONE); + profileBinding.profileView.locationUrl.setVisibility(View.GONE); + profileBinding.profileView.isVerified.setVisibility(View.GONE); + profileBinding.profileView.btnFollow.setVisibility(View.GONE); + profileBinding.profileView.btnRestrict.setVisibility(View.GONE); + profileBinding.profileView.btnBlock.setVisibility(View.GONE); + profileBinding.profileView.btnSaved.setVisibility(View.GONE); + profileBinding.profileView.btnLiked.setVisibility(View.GONE); + profileBinding.profileView.btnTagged.setVisibility(View.GONE); + profileBinding.profileView.btnMap.setVisibility(View.GONE); + + profileBinding.profileView.btnFollow.setOnClickListener(profileActionListener); + profileBinding.profileView.btnRestrict.setOnClickListener(profileActionListener); + profileBinding.profileView.btnBlock.setOnClickListener(profileActionListener); + profileBinding.profileView.btnSaved.setOnClickListener(profileActionListener); + profileBinding.profileView.btnLiked.setOnClickListener(profileActionListener); + profileBinding.profileView.btnTagged.setOnClickListener(profileActionListener); + profileBinding.profileView.btnFollowTag.setOnClickListener(profileActionListener); + + profileBinding.profileView.infoContainer.setVisibility(View.GONE); + profileBinding.profileView.tagInfoContainer.setVisibility(View.GONE); + profileBinding.profileView.locInfoContainer.setVisibility(View.GONE); + + profileBinding.profileView.mainPosts.setNestedScrollingEnabled(false); + profileBinding.profileView.highlightsList.setVisibility(View.GONE); + collapsingToolbar.setVisibility(View.GONE); + highlightsAdapter.setData(null); + + profileBinding.profileView.swipeRefreshLayout.setRefreshing(userQuery != null); + if (userQuery == null) { + profileBinding.toolbar.toolbar.setTitle(R.string.app_name); + return; + } + + isHashtag = userQuery.charAt(0) == '#'; + isUser = userQuery.charAt(0) == '@'; + isLocation = userQuery.contains("/"); + collapsingToolbar.setVisibility(isUser ? View.VISIBLE : View.GONE); + + if (isHashtag) { + profileModel = null; + locationModel = null; + profileBinding.toolbar.toolbar.setTitle(userQuery); + profileBinding.profileView.tagInfoContainer.setVisibility(View.VISIBLE); + profileBinding.profileView.btnFollowTag.setVisibility(View.GONE); + + currentlyExecuting = new HashtagFetcher(userQuery.substring(1), result -> { + hashtagModel = result; + + if (hashtagModel == null) { + profileBinding.profileView.swipeRefreshLayout.setRefreshing(false); + Toast.makeText(ProfileViewer.this, R.string.error_loading_profile, Toast.LENGTH_SHORT).show(); + profileBinding.toolbar.toolbar.setTitle(R.string.app_name); + return; + } + + currentlyExecuting = new PostsFetcher(userQuery, postsFetchListener) + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + + profileBinding.profileView.btnFollowTag.setVisibility(View.VISIBLE); + + if (isLoggedIn) { + new iStoryStatusFetcher(hashtagModel.getName(), null, false, true, false, false, stories -> { + storyModels = stories; + if (stories != null && stories.length > 0) profileBinding.profileView.mainHashtagImage.setStoriesBorder(); + }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + + if (hashtagModel.getFollowing() == true) { + profileBinding.profileView.btnFollowTag.setText(R.string.unfollow); + profileBinding.profileView.btnFollowTag.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + ProfileViewer.this, R.color.btn_purple_background))); + } + else { + profileBinding.profileView.btnFollowTag.setText(R.string.follow); + profileBinding.profileView.btnFollowTag.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + ProfileViewer.this, R.color.btn_pink_background))); + } + } else { + if (Utils.dataBox.getFavorite(userQuery) != null) { + profileBinding.profileView.btnFollowTag.setText(R.string.unfavorite_short); + profileBinding.profileView.btnFollowTag.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + ProfileViewer.this, R.color.btn_purple_background))); + } + else { + profileBinding.profileView.btnFollowTag.setText(R.string.favorite_short); + profileBinding.profileView.btnFollowTag.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + ProfileViewer.this, R.color.btn_pink_background))); + } + } + + profileBinding.profileView.mainHashtagImage.setEnabled(false); + new MyTask().execute(); + profileBinding.profileView.mainHashtagImage.setEnabled(true); + + final String postCount = String.valueOf(hashtagModel.getPostCount()); + + SpannableStringBuilder span = new SpannableStringBuilder(resources.getString(R.string.main_posts_count, postCount)); + span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0); + span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0); + profileBinding.profileView.mainTagPostCount.setText(span); + profileBinding.profileView.mainTagPostCount.setVisibility(View.VISIBLE); + }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } else if (isUser) { + hashtagModel = null; + locationModel = null; + profileBinding.toolbar.toolbar.setTitle(userQuery); + profileBinding.profileView.infoContainer.setVisibility(View.VISIBLE); + profileBinding.profileView.btnFollowTag.setVisibility(View.GONE); + + currentlyExecuting = new ProfileFetcher(userQuery.substring(1), result -> { + profileModel = result; + + if (profileModel == null) { + profileBinding.profileView.swipeRefreshLayout.setRefreshing(false); + Toast.makeText(ProfileViewer.this, R.string.error_loading_profile, Toast.LENGTH_SHORT).show(); + profileBinding.toolbar.toolbar.setTitle(R.string.app_name); + return; + } + + profileBinding.profileView.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE); + final String profileId = profileModel.getId(); + + if (isLoggedIn || Utils.settingsHelper.getBoolean(Constants.STORIESIG)) { + new iStoryStatusFetcher(profileId, profileModel.getUsername(), false, false, + (!isLoggedIn && Utils.settingsHelper.getBoolean(Constants.STORIESIG)), false, + stories -> { + storyModels = stories; + if (stories != null && stories.length > 0) profileBinding.profileView.mainProfileImage.setStoriesBorder(); + }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + + new HighlightsFetcher(profileId, (!isLoggedIn && Utils.settingsHelper.getBoolean(Constants.STORIESIG)), hls -> { + if (hls != null && hls.length > 0) { + profileBinding.profileView.highlightsList.setVisibility(View.VISIBLE); + highlightsAdapter.setData(hls); + } + else profileBinding.profileView.highlightsList.setVisibility(View.GONE); + }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + if (isLoggedIn) { + final String myId = Utils.getUserIdFromCookie(cookie); + if (!profileId.equals(myId)) { + profileBinding.profileView.btnTagged.setVisibility(View.GONE); + profileBinding.profileView.btnSaved.setVisibility(View.GONE); + profileBinding.profileView.btnLiked.setVisibility(View.GONE); + profileBinding.profileView.btnFollow.setVisibility(View.VISIBLE); + if (profileModel.getFollowing() == true) { + profileBinding.profileView.btnFollow.setText(R.string.unfollow); + profileBinding.profileView.btnFollow.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + ProfileViewer.this, R.color.btn_purple_background))); + } + else if (profileModel.getRequested() == true) { + profileBinding.profileView.btnFollow.setText(R.string.cancel); + profileBinding.profileView.btnFollow.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + ProfileViewer.this, R.color.btn_purple_background))); + } + else { + profileBinding.profileView.btnFollow.setText(R.string.follow); + profileBinding.profileView.btnFollow.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + ProfileViewer.this, R.color.btn_pink_background))); + } + profileBinding.profileView.btnRestrict.setVisibility(View.VISIBLE); + if (profileModel.getRestricted() == true) { + profileBinding.profileView.btnRestrict.setText(R.string.unrestrict); + profileBinding.profileView.btnRestrict.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + ProfileViewer.this, R.color.btn_green_background))); + } + else { + profileBinding.profileView.btnRestrict.setText(R.string.restrict); + profileBinding.profileView.btnRestrict.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + ProfileViewer.this, R.color.btn_orange_background))); + } + if (profileModel.isReallyPrivate()) { + profileBinding.profileView.btnBlock.setVisibility(View.VISIBLE); + profileBinding.profileView.btnTagged.setVisibility(View.GONE); + if (profileModel.getBlocked() == true) { + profileBinding.profileView.btnBlock.setText(R.string.unblock); + profileBinding.profileView.btnBlock.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + ProfileViewer.this, R.color.btn_green_background))); + } else { + profileBinding.profileView.btnBlock.setText(R.string.block); + profileBinding.profileView.btnBlock.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + ProfileViewer.this, R.color.btn_red_background))); + } + } else { + profileBinding.profileView.btnBlock.setVisibility(View.GONE); + profileBinding.profileView.btnSaved.setVisibility(View.VISIBLE); + profileBinding.profileView.btnTagged.setVisibility(View.VISIBLE); + if (profileModel.getBlocked() == true) { + profileBinding.profileView.btnSaved.setText(R.string.unblock); + profileBinding.profileView.btnSaved.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + ProfileViewer.this, R.color.btn_green_background))); + } else { + profileBinding.profileView.btnSaved.setText(R.string.block); + profileBinding.profileView.btnSaved.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + ProfileViewer.this, R.color.btn_red_background))); + } + } + } + else { + profileBinding.profileView.btnTagged.setVisibility(View.VISIBLE); + profileBinding.profileView.btnSaved.setVisibility(View.VISIBLE); + profileBinding.profileView.btnLiked.setVisibility(View.VISIBLE); + profileBinding.profileView.btnSaved.setText(R.string.saved); + profileBinding.profileView.btnSaved.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + ProfileViewer.this, R.color.btn_orange_background))); + } + } else { + if (Utils.dataBox.getFavorite(userQuery) != null) { + profileBinding.profileView.btnFollow.setText(R.string.unfavorite_short); + profileBinding.profileView.btnFollow.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + ProfileViewer.this, R.color.btn_purple_background))); + } + else { + profileBinding.profileView.btnFollow.setText(R.string.favorite_short); + profileBinding.profileView.btnFollow.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + ProfileViewer.this, R.color.btn_pink_background))); + } + profileBinding.profileView.btnFollow.setVisibility(View.VISIBLE); + if (!profileModel.isReallyPrivate()) { + profileBinding.profileView.btnRestrict.setVisibility(View.VISIBLE); + profileBinding.profileView.btnRestrict.setText(R.string.tagged); + profileBinding.profileView.btnRestrict.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + ProfileViewer.this, R.color.btn_blue_background))); + } + } + + profileBinding.profileView.mainProfileImage.setEnabled(false); + new MyTask().execute(); + profileBinding.profileView.mainProfileImage.setEnabled(true); + + final long followersCount = profileModel.getFollowersCount(); + final long followingCount = profileModel.getFollowingCount(); + + final String postCount = String.valueOf(profileModel.getPostCount()); + + SpannableStringBuilder span = new SpannableStringBuilder(resources.getString(R.string.main_posts_count, postCount)); + span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0); + span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0); + profileBinding.profileView.mainPostCount.setText(span); + + final String followersCountStr = String.valueOf(followersCount); + final int followersCountStrLen = followersCountStr.length(); + span = new SpannableStringBuilder(resources.getString(R.string.main_posts_followers, followersCountStr)); + span.setSpan(new RelativeSizeSpan(1.2f), 0, followersCountStrLen, 0); + span.setSpan(new StyleSpan(Typeface.BOLD), 0, followersCountStrLen, 0); + profileBinding.profileView.mainFollowers.setText(span); + + final String followingCountStr = String.valueOf(followingCount); + final int followingCountStrLen = followingCountStr.length(); + span = new SpannableStringBuilder(resources.getString(R.string.main_posts_following, followingCountStr)); + span.setSpan(new RelativeSizeSpan(1.2f), 0, followingCountStrLen, 0); + span.setSpan(new StyleSpan(Typeface.BOLD), 0, followingCountStrLen, 0); + profileBinding.profileView.mainFollowing.setText(span); + + profileBinding.profileView.mainFullName.setText(Utils.isEmpty(profileModel.getName()) ? profileModel.getUsername() : profileModel.getName()); + + CharSequence biography = profileModel.getBiography(); + profileBinding.profileView.mainBiography.setCaptionIsExpandable(true); + profileBinding.profileView.mainBiography.setCaptionIsExpanded(true); + if (Utils.hasMentions(biography)) { + biography = Utils.getMentionText(biography); + profileBinding.profileView.mainBiography.setText(biography, TextView.BufferType.SPANNABLE); + profileBinding.profileView.mainBiography.setMentionClickListener(mentionClickListener); + } else { + profileBinding.profileView.mainBiography.setText(biography); + profileBinding.profileView.mainBiography.setMentionClickListener(null); + } + + final String url = profileModel.getUrl(); + if (Utils.isEmpty(url)) { + profileBinding.profileView.mainUrl.setVisibility(View.GONE); + } else { + profileBinding.profileView.mainUrl.setVisibility(View.VISIBLE); + profileBinding.profileView.mainUrl.setText(Utils.getSpannableUrl(url)); + } + + profileBinding.profileView.mainFullName.setSelected(true); + profileBinding.profileView.mainBiography.setEnabled(true); + + if (!profileModel.isReallyPrivate()) { + profileBinding.profileView.mainFollowing.setClickable(true); + profileBinding.profileView.mainFollowers.setClickable(true); + + if (isLoggedIn) { + final View.OnClickListener followClickListener = v -> startActivity(new Intent(ProfileViewer.this, FollowViewer.class) + .putExtra(Constants.EXTRAS_FOLLOWERS, v == profileBinding.profileView.mainFollowers) + .putExtra(Constants.EXTRAS_NAME, profileModel.getUsername()) + .putExtra(Constants.EXTRAS_ID, profileId)); + + profileBinding.profileView.mainFollowers.setOnClickListener(followersCount > 0 ? followClickListener : null); + profileBinding.profileView.mainFollowing.setOnClickListener(followingCount > 0 ? followClickListener : null); + } + + if (profileModel.getPostCount() == 0) { + profileBinding.profileView.swipeRefreshLayout.setRefreshing(false); + profileBinding.profileView.privatePage1.setImageResource(R.drawable.ic_cancel); + profileBinding.profileView.privatePage2.setText(R.string.empty_acc); + profileBinding.profileView.privatePage.setVisibility(View.VISIBLE); + } + else { + profileBinding.profileView.swipeRefreshLayout.setRefreshing(true); + profileBinding.profileView.mainPosts.setVisibility(View.VISIBLE); + currentlyExecuting = new PostsFetcher(profileId, postsFetchListener).setUsername(profileModel.getUsername()) + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } else { + profileBinding.profileView.mainFollowers.setClickable(false); + profileBinding.profileView.mainFollowing.setClickable(false); + profileBinding.profileView.swipeRefreshLayout.setRefreshing(false); + // error + profileBinding.profileView.privatePage1.setImageResource(R.drawable.lock); + profileBinding.profileView.privatePage2.setText(R.string.priv_acc); + profileBinding.profileView.privatePage.setVisibility(View.VISIBLE); + profileBinding.profileView.mainPosts.setVisibility(View.GONE); + } + } + ).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + else if (isLocation) { + profileModel = null; + hashtagModel = null; + profileBinding.toolbar.toolbar.setTitle(userQuery); + profileBinding.profileView.locInfoContainer.setVisibility(View.VISIBLE); + + currentlyExecuting = new LocationFetcher(userQuery.split("/")[0], result -> { + locationModel = result; + + if (locationModel == null) { + profileBinding.profileView.swipeRefreshLayout.setRefreshing(false); + Toast.makeText(ProfileViewer.this, R.string.error_loading_profile, Toast.LENGTH_SHORT).show(); + profileBinding.toolbar.toolbar.setTitle(R.string.app_name); + return; + } + profileBinding.toolbar.toolbar.setTitle(locationModel.getName()); + + final String profileId = locationModel.getId(); + + if (isLoggedIn) { + new iStoryStatusFetcher(profileId.split("/")[0], null, true, false, false, false, stories -> { + storyModels = stories; + if (stories != null && stories.length > 0) profileBinding.profileView.mainLocationImage.setStoriesBorder(); + }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + profileBinding.profileView.mainLocationImage.setEnabled(false); + new MyTask().execute(); + profileBinding.profileView.mainLocationImage.setEnabled(true); + + final String postCount = String.valueOf(locationModel.getPostCount()); + + SpannableStringBuilder span = new SpannableStringBuilder(resources.getString(R.string.main_posts_count, postCount)); + span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0); + span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0); + profileBinding.profileView.mainLocPostCount.setText(span); + + profileBinding.profileView.locationFullName.setText(locationModel.getName()); + + CharSequence biography = locationModel.getBio(); + profileBinding.profileView.locationBiography.setCaptionIsExpandable(true); + profileBinding.profileView.locationBiography.setCaptionIsExpanded(true); + + if (Utils.isEmpty(biography)) { + profileBinding.profileView.locationBiography.setVisibility(View.GONE); + } + else if (Utils.hasMentions(biography)) { + profileBinding.profileView.locationBiography.setVisibility(View.VISIBLE); + biography = Utils.getMentionText(biography); + profileBinding.profileView.locationBiography.setText(biography, TextView.BufferType.SPANNABLE); + profileBinding.profileView.locationBiography.setMentionClickListener(mentionClickListener); + } else { + profileBinding.profileView.locationBiography.setVisibility(View.VISIBLE); + profileBinding.profileView.locationBiography.setText(biography); + profileBinding.profileView.locationBiography.setMentionClickListener(null); + } + + if (!locationModel.getGeo().startsWith("geo:0.0,0.0?z=17")) { + profileBinding.profileView.btnMap.setVisibility(View.VISIBLE); + profileBinding.profileView.btnMap.setOnClickListener(v -> { + final Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(locationModel.getGeo())); + startActivity(intent); + }); + } + else { + profileBinding.profileView.btnMap.setVisibility(View.GONE); + profileBinding.profileView.btnMap.setOnClickListener(null); + } + + final String url = locationModel.getUrl(); + if (Utils.isEmpty(url)) { + profileBinding.profileView.locationUrl.setVisibility(View.GONE); + } else if (!url.startsWith("http")) { + profileBinding.profileView.locationUrl.setVisibility(View.VISIBLE); + profileBinding.profileView.locationUrl.setText(Utils.getSpannableUrl("http://"+url)); + } else { + profileBinding.profileView.locationUrl.setVisibility(View.VISIBLE); + profileBinding.profileView.locationUrl.setText(Utils.getSpannableUrl(url)); + } + + profileBinding.profileView.locationFullName.setSelected(true); + profileBinding.profileView.locationBiography.setEnabled(true); + + if (locationModel.getPostCount() == 0) { + profileBinding.profileView.swipeRefreshLayout.setRefreshing(false); + profileBinding.profileView.privatePage1.setImageResource(R.drawable.ic_cancel); + profileBinding.profileView.privatePage2.setText(R.string.empty_acc); + profileBinding.profileView.privatePage.setVisibility(View.VISIBLE); + } + else { + profileBinding.profileView.swipeRefreshLayout.setRefreshing(true); + profileBinding.profileView.mainPosts.setVisibility(View.VISIBLE); + currentlyExecuting = new PostsFetcher(profileId, postsFetchListener) + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + ).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + + public static void stopCurrentExecutor() { + if (currentlyExecuting != null) { + try { + currentlyExecuting.cancel(true); + } catch (final Exception e) { + if (logCollector != null) + logCollector.appendException(e, LogCollector.LogFile.MAIN_HELPER, "stopCurrentExecutor"); + if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); + } + } + } + + private void toggleSelection(final PostModel postModel) { + if (postModel != null && postsAdapter != null) { + if (postModel.isSelected()) selectedItems.remove(postModel); + else selectedItems.add(postModel); + postModel.setSelected(!postModel.isSelected()); + notifyAdapter(postModel); + } + } + + private void notifyAdapter(final PostModel postModel) { + if (selectedItems.size() < 1) postsAdapter.isSelecting = false; + if (postModel.getPosition() < 0) postsAdapter.notifyDataSetChanged(); + else postsAdapter.notifyItemChanged(postModel.getPosition(), postModel); + + if (downloadAction != null) downloadAction.setVisible(postsAdapter.isSelecting); + } + + public boolean isSelectionCleared() { + if (postsAdapter != null && postsAdapter.isSelecting) { + for (final PostModel postModel : selectedItems) postModel.setSelected(false); + selectedItems.clear(); + postsAdapter.isSelecting = false; + postsAdapter.notifyDataSetChanged(); + if (downloadAction != null) downloadAction.setVisible(false); + return false; + } return true; } + + public void deselectSelection(final BasePostModel postModel) { + if (postModel instanceof PostModel) { + selectedItems.remove(postModel); + postModel.setSelected(false); + if (postsAdapter != null) notifyAdapter((PostModel) postModel); + } + } + + public static int indexOfIntArray(Object[] array, Object key) { + int returnvalue = -1; + for (int i = 0; i < array.length; ++i) { + if (key == array[i]) { + returnvalue = i; + break; + } + } + return returnvalue; + } + + class MyTask extends AsyncTask { + private Bitmap mIcon_val; + + protected Void doInBackground(Void... voids) { + try { + mIcon_val = BitmapFactory.decodeStream((InputStream) new URL( + (hashtagModel != null) ? hashtagModel.getSdProfilePic() : ( + (locationModel != null) ? locationModel.getSdProfilePic() : + profileModel.getSdProfilePic()) + ).getContent()); + } catch (Throwable ex) { + Log.e("austin_debug", "bitmap: " + ex); + } + return null; + } + + @Override + protected void onPostExecute(Void result) { + if (hashtagModel != null) profileBinding.profileView.mainHashtagImage.setImageBitmap(mIcon_val); + else if (locationModel != null) profileBinding.profileView.mainLocationImage.setImageBitmap(mIcon_val); + else profileBinding.profileView.mainProfileImage.setImageBitmap(mIcon_val); + } + } + + private final View.OnClickListener profileActionListener = new View.OnClickListener() { + @Override + public void onClick(final View v) { + final boolean iamme = (isLoggedIn && profileModel != null) + ? Utils.getUserIdFromCookie(cookie).equals(profileModel.getId()) + : false; + if (!isLoggedIn && Utils.dataBox.getFavorite(userQuery) != null && v == profileBinding.profileView.btnFollow) { + Utils.dataBox.delFavorite(new DataBox.FavoriteModel(userQuery, + Long.parseLong(Utils.dataBox.getFavorite(userQuery).split("/")[1]), + locationModel != null ? locationModel.getName() : userQuery.replaceAll("^@", ""))); + onRefresh(); + } else if (!isLoggedIn && (v == profileBinding.profileView.btnFollow || v == profileBinding.profileView.btnFollowTag)) { + Utils.dataBox.addFavorite(new DataBox.FavoriteModel(userQuery, System.currentTimeMillis(), + locationModel != null ? locationModel.getName() : userQuery.replaceAll("^@", ""))); + onRefresh(); + } else if (v == profileBinding.profileView.btnFollow) { + new ProfileAction().execute("follow"); + } else if (v == profileBinding.profileView.btnRestrict && isLoggedIn) { + new ProfileAction().execute("restrict"); + } else if (v == profileBinding.profileView.btnSaved && !iamme) { + new ProfileAction().execute("block"); + } else if (v == profileBinding.profileView.btnFollowTag) { + new ProfileAction().execute("followtag"); + } else if (v == profileBinding.profileView.btnTagged || (v == profileBinding.profileView.btnRestrict && !isLoggedIn)) { + startActivity(new Intent(ProfileViewer.this, SavedViewer.class) + .putExtra(Constants.EXTRAS_INDEX, "%"+profileModel.getId()) + .putExtra(Constants.EXTRAS_USER, "@"+profileModel.getUsername()) + ); + } else if (v == profileBinding.profileView.btnSaved) { + startActivity(new Intent(ProfileViewer.this, SavedViewer.class) + .putExtra(Constants.EXTRAS_INDEX, "$"+profileModel.getId()) + .putExtra(Constants.EXTRAS_USER, "@"+profileModel.getUsername()) + ); + } else if (v == profileBinding.profileView.btnLiked) { + startActivity(new Intent(ProfileViewer.this, SavedViewer.class) + .putExtra(Constants.EXTRAS_INDEX, "^"+profileModel.getId()) + .putExtra(Constants.EXTRAS_USER, "@"+profileModel.getUsername()) + ); + } + } + }; + + class ProfileAction extends AsyncTask { + boolean ok = false; + String action; + + protected Void doInBackground(String... rawAction) { + action = rawAction[0]; + final String url = "https://www.instagram.com/web/"+ + ((action == "followtag" && hashtagModel != null) ? ("tags/"+ + (hashtagModel.getFollowing() == true ? "unfollow/" : "follow/")+hashtagModel.getName()+"/") : ( + ((action == "restrict" && profileModel != null) ? "restrict_action" : ("friendships/"+profileModel.getId()))+"/"+ + ((action == "follow" && profileModel != null) ? + ((profileModel.getFollowing() == true || + (profileModel.getFollowing() == false && profileModel.getRequested() == true)) + ? "unfollow/" : "follow/") : + ((action == "restrict" && profileModel != null) ? + (profileModel.getRestricted() == true ? "unrestrict/" : "restrict/") : + (profileModel.getBlocked() == true ? "unblock/" : "block/"))))); + try { + final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection(); + urlConnection.setRequestMethod("POST"); + urlConnection.setUseCaches(false); + urlConnection.setRequestProperty("User-Agent", Constants.USER_AGENT); + urlConnection.setRequestProperty("x-csrftoken", cookie.split("csrftoken=")[1].split(";")[0]); + if (action == "restrict") { + final String urlParameters = "target_user_id="+profileModel.getId(); + urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + urlConnection.setRequestProperty("Content-Length", "" + + Integer.toString(urlParameters.getBytes().length)); + urlConnection.setDoOutput(true); + DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream()); + wr.writeBytes(urlParameters); + wr.flush(); + wr.close(); + } + else urlConnection.connect(); + if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) { + ok = true; + } + else Toast.makeText(ProfileViewer.this, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); + urlConnection.disconnect(); + } catch (Throwable ex) { + Log.e("austin_debug", action+": " + ex); + } + return null; + } + + @Override + protected void onPostExecute(Void result) { + if (ok == true) { + onRefresh(); + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/activities/StoryViewer.java b/app/src/main/java/awais/instagrabber/activities/StoryViewer.java index a15f1d03..12b6efd7 100755 --- a/app/src/main/java/awais/instagrabber/activities/StoryViewer.java +++ b/app/src/main/java/awais/instagrabber/activities/StoryViewer.java @@ -527,13 +527,10 @@ public final class StoryViewer extends BaseLanguageActivity { } private void searchUsername(final String text) { - if (Main.scanHack != null) { - Main.scanHack.onResult(text); - setResult(6969); - Intent intent = new Intent(getApplicationContext(), Main.class); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - } + startActivity( + new Intent(getApplicationContext(), ProfileViewer.class) + .putExtra(Constants.EXTRAS_USERNAME, text) + ); } private void releasePlayer() { diff --git a/app/src/main/java/awais/instagrabber/asyncs/DownloadAsync.java b/app/src/main/java/awais/instagrabber/asyncs/DownloadAsync.java index a0e2cc5e..35b07ddc 100755 --- a/app/src/main/java/awais/instagrabber/asyncs/DownloadAsync.java +++ b/app/src/main/java/awais/instagrabber/asyncs/DownloadAsync.java @@ -34,7 +34,7 @@ import java.util.concurrent.atomic.AtomicReference; import awais.instagrabber.BuildConfig; import awais.instagrabber.R; -import awais.instagrabber.activities.ProfileViewer; +import awais.instagrabber.activities.ProfilePicViewer; import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.utils.Utils; @@ -76,7 +76,7 @@ public final class DownloadAsync extends AsyncTask { isChannelCreated = true; } - @StringRes final int titleRes = context instanceof ProfileViewer ? R.string.downloader_downloading_pfp : R.string.downloader_downloading_post; + @StringRes final int titleRes = context instanceof ProfilePicViewer ? R.string.downloader_downloading_pfp : R.string.downloader_downloading_post; downloadNotif = new NotificationCompat.Builder(context, CHANNEL_ID).setCategory(NotificationCompat.CATEGORY_STATUS) .setSmallIcon(R.mipmap.ic_launcher).setContentText(shortCode == null ? username : shortCode).setOngoing(true) diff --git a/app/src/main/java/awais/instagrabber/dialogs/AboutDialog.java b/app/src/main/java/awais/instagrabber/dialogs/AboutDialog.java index 79670c51..1f4773c0 100755 --- a/app/src/main/java/awais/instagrabber/dialogs/AboutDialog.java +++ b/app/src/main/java/awais/instagrabber/dialogs/AboutDialog.java @@ -42,7 +42,7 @@ public final class AboutDialog extends BottomSheetDialogFragment { else if (v == btnMatrix) { intent.setData(Uri.parse("https://matrix.to/#/#instagrabber:matrix.org")); } else - intent.setData(Uri.parse("https://github.com/austinhuang0131/instagrabber/")); + intent.setData(Uri.parse("https://instagrabber.austinhuang.me")); startActivity(intent); }; btnProject.setOnClickListener(onClickListener); diff --git a/app/src/main/res/layout/activity_profile.xml b/app/src/main/res/layout/activity_profile.xml index 35aaa8a3..6927fda5 100755 --- a/app/src/main/res/layout/activity_profile.xml +++ b/app/src/main/res/layout/activity_profile.xml @@ -10,28 +10,8 @@ android:id="@+id/toolbar" layout="@layout/layout_include_toolbar" /> - + - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_profilepic.xml b/app/src/main/res/layout/activity_profilepic.xml new file mode 100755 index 00000000..a667d5f1 --- /dev/null +++ b/app/src/main/res/layout/activity_profilepic.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_main_about.xml b/app/src/main/res/layout/dialog_main_about.xml index 16ef733f..c4b3b67e 100755 --- a/app/src/main/res/layout/dialog_main_about.xml +++ b/app/src/main/res/layout/dialog_main_about.xml @@ -75,6 +75,16 @@ android:textColor="@color/btn_green_text_color" android:textSize="20sp" app:backgroundTint="@color/btn_green_background" /> + + \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index aed4c2c8..b2b4716a 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -3,7 +3,6 @@ Der ursprüngliche Maintainer, AWAiS, entwickelte InstaGrabber als kleine und einfache App mit dem Ziel, Beiträge vom Instagram herunterzuladen. Leider wurde das Projekt aufgegeben und ich, Austin Huang, übernahm die Entwicklung. Die App ist vollständig Open Source, Werbe- und Trackerfrei [abgesehen von dem, was Instagram weiß]. Auch wenn Dir die Downloadfunktion egal ist, [genau so wie mir], ist es immer noch ein großartiger Instagram-Client!\n\nHast du Fragen, [oder andere Anmerkungen]? Kontaktiere mich unter instagrabber@austinhuang.me oder klicke auf einen der Buttons. Schnellzugriff Über - Benachrichtigungen Direktnachrichten Einstellungen (v%s) Einstellungen diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index d701be74..cb5031f4 100755 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -3,7 +3,6 @@ El mantenedor original, AWAiS, hizo de InstaGrabber una pequeña y básica aplicación personal con intenciones de [robar] descargar posts fuera de Instagram. Desafortunadamente lo abandonó, y yo, Austin Huang, me apoderé del proyecto. Esperemos que haya alguna ayuda económica. Después de todo, esta aplicación es completamente de código abierto, sin anuncios y sin seguimiento [aparte de lo que Instagram ya conoce]. Incluso si te da igual la función de descargar cosas [como yo], ¡sigue siendo un gran cliente Instagram para usar!\n\n¿Tienes preguntas [o simplemente quieres hablar]? Contacta con instagrabber@austinhuang. o haga clic en uno de los botones de abajo. Acceso rápido Sobre - Notificaciones Mensajes Directos Ajustes (v%s) Ajustes diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 26678009..e85f2985 100755 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -3,7 +3,6 @@ Le mainteneur original, AWAiS, a fait d\'InstaGrabber une petite application personnelle de base avec l\'intention de \"voler\" les messages d\'Instagram. Très malheureusement, elle a été abandonnée et moi, Austin Huang, j\'ai repris le navire. Après tout, cette application est entièrement open source, sans publicité, et sans suivi (en dehors de ce que sait Instagram). Même si vous ne vous souciez pas de télécharger des choses (comme moi), ça reste un excellent client Instagram à utiliser ! Contactez instagrabber@austinhuang.me ou cliquez sur l\'un des boutons ci-dessous. Accès rapide À propos - Notifications Messages privés Paramètres (v%s) Paramètres diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index f00652e9..5dff9de8 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -3,7 +3,6 @@ Pengembang original, AWAiS, membuat InstaGrabber sebagai aplikasi personal yang kecil dan sederhana untuk men[cu-]ngunduh kiriman di Instagram. Sayangnya, dia pergi dan saya, Austin Huang, mengambil alih kendali. [Mari berharap paling nggak sedikit uang.] Toh, aplikasi ini sepenuhnya bersumber terbuka, tanpa iklan, dan tanpa pelacak [disamping apa yang diketahui Instagram]. Meski anda tidak peduli dengan mengunduh kiriman [seperti saya], aplikasi ini masih menjadi klien Instagram yang bagus untuk digunakan!\n\nAda pertanyaan [atau mau ngobrol]? Hubungi instagrabber@austinhuang.me atau klik salah satu tombol di bawah ini. Akses Cepat Tentang - Pemberitahuan Pesan Langsung Pengaturan (v%s) Pengaturan diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index c404680b..57b38457 100755 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -3,7 +3,6 @@ Il manutentore originale, AWAiS, ha reso InstaGrabber una piccola e semplice app personale con le intenzioni di [rub-] scaricare post da Instagram. Sfortunatamente, questo è stato abbandonato e io, Austin Huang, ho preso il timone. [Speriamo che sia almeno un po \'di denaro contante.] Dopotutto, questa app è completamente open source, senza pubblicità e senza tracciamento [a parte ciò che Instagram sa]. Anche se non ti importa di scaricare roba [come me], è comunque un ottimo client Instagram da usare! \n\nFai domande [o vuoi solo parlare]? Contatta instagrabber@austinhuang.me o fai clic su uno dei pulsanti di seguito. Accesso rapido Riguardo - Notifiche Messaggi Direct Impostazioni (v%s) Impostazioni diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index d17794c8..876c87c5 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -3,7 +3,6 @@ Pierwotny opiekun projektu, AWAiS, stworzył InstaGrabber jako małą i podstawową, osobistą aplikację z zamiarem [kradzieży-]pobierania postów z Instagrama. Niestety, projekt został porzucony, a ja, Austin Huang, przejąłem statek. [Miejmy nadzieję, że przyniesie to trochę gotówki.] W końcu ta aplikacja jest w pełni open source, bez reklam i bez śledzenia [poza tym, co wie już o Tobie Instagram]. Nawet jeśli nie zależy ci na pobieraniu rzeczy, nadal jest to świetny klient Instagrama!\n\nMasz pytania [lub po prostu chcesz porozmawiać]? Skontaktuj się ze mną drogą mailową: instagrabber@austinhuang.me lub kliknij jeden z poniższych przycisków. Szybki dostęp O aplikacji - Powiadomienia Prywatne wiadomości Ustawienia (v%s) Ustawienia diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index de2aa21e..04b19e42 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -3,7 +3,6 @@ O mantenedor original, AWAiS, fez do InstaGrabber um pequeno e básico aplicativo pessoal com intenções de [roubar] baixar postagens do Instagram. Infelizmente, isso foi abandonado e eu, Austin Huang, assumi o navio. [Vamos torcer para que seja pelo menos um pouco de dinheiro em espécie.] Afinal, este aplicativo é totalmente de código aberto, sem anúncios e sem rastreamento [além do que o Instagram sabe]. Mesmo que você não se importe em baixar coisas [como eu], ainda é um ótimo cliente do Instagram para usar!Tem dúvidas [ou apenas quer conversar]? Entre em contato com instagrabber@austinhuang ou clique em um dos botões abaixo. Acesso rápido Sobre - Notificações Mensagens diretas Configurações (v%s) Configurações diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index fbc7b9a3..5f382963 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -3,7 +3,6 @@ The original maintainer, AWAiS, made InstaGrabber as a small and basic little personal app with intentions of [steali-]downloading posts off Instagram. Very unfortunately, this was abandoned and me, Austin Huang, took over the ship. [Let\'s hope that\'s at least a lil\' bit cash money.] After all, this app is fully open source, ad-less, and tracking-less [aside from what Instagram knows]. Even if you don\'t care about downloading stuff [like me], it\'s still a great Instagram client to use!\n\nGot questions [or just wanna talk]? Contact instagrabber@austinhuang.me or click one of the buttons below. Quick Access About - Notifications Direct Messages Settings (v%s) Settings diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 5ea46ab0..bdb2ecac 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -3,7 +3,6 @@ İlk geliştiricisi olan AWAIS, InstaGrabber adında küçük ve basit bir uygulama yaptı. Amacı Instagram paylaşımlarını [çalma-] indirmekti. Ne yazık ki bir süre sonra projeyi bıraktı ve ben, Austin Huang, devraldım. [Umarım biraz para da kazanırım.] Her şeyden önce bu uygulama tamamen açık kaynak kodlu, reklamsız ve gizliliğe önem veriyor [Instagram\'ın zaten bildikleri ayrı konu]. Eğer paylaşım indirmek amacınız olmasa bile [benim gibi], bir Instagram alternatifi olarak kullanmak için bile harika bir uygulama!\n\nSorun varsa ya da sadece konuşmak istiyorsan instagrabber@austinhuang.me üzerinden ulaşabilir ya da aşağıdaki butonlara tıklayabilirsin. Hatırlatmakta fayda var, Türkçe bilmiyorum. Hızlı Erişim Hakkında - Bildirimler Direkt Mesajlar Ayarlar (v%s) Ayarlar diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 7d1ac585..3ba7337b 100755 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -3,7 +3,6 @@ 原作者 AWAiS 将 InstaGrabber 做成了一个小巧玲珑的 Instagram 帖子下载 [写作下载读作剽窃] 程序,不过他跳槽了,所以我(Austin Huang)就把它接了下来。 [希望此举有所值得。] 不管怎样,InstaGrabber 完全开源,无广告,无跟踪 [Instagram 原生跟踪除外],998都不要,你还等啥???\n有问题 [或者只是想谈谈心] ?请联系 instagrabber@austinhuang.me 或点击下方各按钮。 快捷通道 关于 - 通知 私聊 设定 (v%s) 设定 @@ -209,4 +208,5 @@ 应用崩溃了 糟糕.. 应用崩溃了,不过别担心,你可以向开发者发送错误报告来帮助他修复问题。(: Use AMOLED mode for Dark theme + 动态 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c0ab2dea..b3502ee1 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -4,7 +4,6 @@ The original maintainer, AWAiS, made InstaGrabber as a small and basic little personal app with intentions of [steali-]downloading posts off Instagram. Very unfortunately, this was abandoned and me, Austin Huang, took over the ship. [Let\'s hope that\'s at least a lil\' bit cash money.] After all, this app is fully open source, ad-less, and tracking-less [aside from what Instagram knows]. Even if you don\'t care about downloading stuff [like me], it\'s still a great Instagram client to use!\n\nGot questions [or just wanna talk]? Contact instagrabber@austinhuang.me or click one of the buttons below. Quick Access About - Notifications Direct Messages Settings (v%s) Settings @@ -212,4 +211,6 @@ App crashed Oops.. the app crashed, but don\'t worry you can send error report to the developer to help him fix the issue. (: Use AMOLED mode for Dark theme + Activity + InstaGrabber\nCopyright (C) 2019 AWAiS\nCopyright (C) 2020 Austin Huang, Ammar Githam\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses/.\n\nTHIS SOFTWARE IS PROVIDED BY THE AUTHOR \'\'AS IS\'\' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nSee project page for third-party attributions. diff --git a/build.gradle b/build.gradle index c8447ae8..156a6372 100755 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.0.0' + classpath 'com.android.tools.build:gradle:4.0.1' } }